FILE
bebob_pcm.c
File preview with syntax highlighting.
linux-6.19.14/sound/firewire/bebob/bebob_pcm.c
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * bebob_pcm.c - a part of driver for BeBoB based devices 4 * 5 * Copyright (c) 2013-2014 Takashi Sakamoto 6 */ 7 8#include "./bebob.h" 9 10static int 11hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) 12{ 13 struct snd_bebob_stream_formation *formations = rule->private; 14 struct snd_interval *r = 15 hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); 16 const struct snd_interval *c = 17 hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); 18 struct snd_interval t = { 19 .min = UINT_MAX, .max = 0, .integer = 1 20 }; 21 unsigned int i; 22 23 for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { 24 /* entry is invalid */ 25 if (formations[i].pcm == 0) 26 continue; 27 28 if (!snd_interval_test(c, formations[i].pcm)) 29 continue; 30 31 t.min = min(t.min, snd_bebob_rate_table[i]); 32 t.max = max(t.max, snd_bebob_rate_table[i]); 33 34 } 35 return snd_interval_refine(r, &t); 36} 37 38static int 39hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) 40{ 41 struct snd_bebob_stream_formation *formations = rule->private; 42 struct snd_interval *c = 43 hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); 44 const struct snd_interval *r = 45 hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); 46 struct snd_interval t = { 47 .min = UINT_MAX, .max = 0, .integer = 1 48 }; 49 50 unsigned int i; 51 52 for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { 53 /* entry is invalid */ 54 if (formations[i].pcm == 0) 55 continue; 56 57 if (!snd_interval_test(r, snd_bebob_rate_table[i])) 58 continue; 59 60 t.min = min(t.min, formations[i].pcm); 61 t.max = max(t.max, formations[i].pcm); 62 } 63 64 return snd_interval_refine(c, &t); 65} 66 67static void 68limit_channels_and_rates(struct snd_pcm_hardware *hw, 69 struct snd_bebob_stream_formation *formations) 70{ 71 unsigned int i; 72 73 hw->channels_min = UINT_MAX; 74 hw->channels_max = 0; 75 76 hw->rate_min = UINT_MAX; 77 hw->rate_max = 0; 78 hw->rates = 0; 79 80 for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { 81 /* entry has no PCM channels */ 82 if (formations[i].pcm == 0) 83 continue; 84 85 hw->channels_min = min(hw->channels_min, formations[i].pcm); 86 hw->channels_max = max(hw->channels_max, formations[i].pcm); 87 88 hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]); 89 hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]); 90 hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]); 91 } 92} 93 94static int 95pcm_init_hw_params(struct snd_bebob *bebob, 96 struct snd_pcm_substream *substream) 97{ 98 struct snd_pcm_runtime *runtime = substream->runtime; 99 struct amdtp_stream *s; 100 struct snd_bebob_stream_formation *formations; 101 int err; 102 103 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 104 runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS; 105 s = &bebob->tx_stream; 106 formations = bebob->tx_stream_formations; 107 } else { 108 runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS; 109 s = &bebob->rx_stream; 110 formations = bebob->rx_stream_formations; 111 } 112 113 limit_channels_and_rates(&runtime->hw, formations); 114 115 err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 116 hw_rule_channels, formations, 117 SNDRV_PCM_HW_PARAM_RATE, -1); 118 if (err < 0) 119 goto end; 120 121 err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 122 hw_rule_rate, formations, 123 SNDRV_PCM_HW_PARAM_CHANNELS, -1); 124 if (err < 0) 125 goto end; 126 127 err = amdtp_am824_add_pcm_hw_constraints(s, runtime); 128end: 129 return err; 130} 131 132static int pcm_open(struct snd_pcm_substream *substream) 133{ 134 struct snd_bebob *bebob = substream->private_data; 135 const struct snd_bebob_rate_spec *spec = bebob->spec->rate; 136 struct amdtp_domain *d = &bebob->domain; 137 enum snd_bebob_clock_type src; 138 int err; 139 140 err = snd_bebob_stream_lock_try(bebob); 141 if (err < 0) 142 return err; 143 144 err = pcm_init_hw_params(bebob, substream); 145 if (err < 0) 146 goto err_locked; 147 148 err = snd_bebob_stream_get_clock_src(bebob, &src); 149 if (err < 0) 150 goto err_locked; 151 152 scoped_guard(mutex, &bebob->mutex) { 153 // When source of clock is not internal or any stream is reserved for 154 // transmission of PCM frames, the available sampling rate is limited 155 // at current one. 156 if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL || 157 (bebob->substreams_counter > 0 && d->events_per_period > 0)) { 158 unsigned int frames_per_period = d->events_per_period; 159 unsigned int frames_per_buffer = d->events_per_buffer; 160 unsigned int sampling_rate; 161 162 err = spec->get(bebob, &sampling_rate); 163 if (err < 0) { 164 dev_err(&bebob->unit->device, 165 "fail to get sampling rate: %d\n", err); 166 goto err_locked; 167 } 168 169 substream->runtime->hw.rate_min = sampling_rate; 170 substream->runtime->hw.rate_max = sampling_rate; 171 172 if (frames_per_period > 0) { 173 err = snd_pcm_hw_constraint_minmax(substream->runtime, 174 SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 175 frames_per_period, frames_per_period); 176 if (err < 0) 177 goto err_locked; 178 179 err = snd_pcm_hw_constraint_minmax(substream->runtime, 180 SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 181 frames_per_buffer, frames_per_buffer); 182 if (err < 0) 183 goto err_locked; 184 } 185 } 186 } 187 188 snd_pcm_set_sync(substream); 189 190 return 0; 191err_locked: 192 snd_bebob_stream_lock_release(bebob); 193 return err; 194} 195 196static int 197pcm_close(struct snd_pcm_substream *substream) 198{ 199 struct snd_bebob *bebob = substream->private_data; 200 snd_bebob_stream_lock_release(bebob); 201 return 0; 202} 203 204static int pcm_hw_params(struct snd_pcm_substream *substream, 205 struct snd_pcm_hw_params *hw_params) 206{ 207 struct snd_bebob *bebob = substream->private_data; 208 int err = 0; 209 210 if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) { 211 unsigned int rate = params_rate(hw_params); 212 unsigned int frames_per_period = params_period_size(hw_params); 213 unsigned int frames_per_buffer = params_buffer_size(hw_params); 214 215 guard(mutex)(&bebob->mutex); 216 err = snd_bebob_stream_reserve_duplex(bebob, rate, 217 frames_per_period, frames_per_buffer); 218 if (err >= 0) 219 ++bebob->substreams_counter; 220 } 221 222 return err; 223} 224 225static int pcm_hw_free(struct snd_pcm_substream *substream) 226{ 227 struct snd_bebob *bebob = substream->private_data; 228 229 guard(mutex)(&bebob->mutex); 230 231 if (substream->runtime->state != SNDRV_PCM_STATE_OPEN) 232 bebob->substreams_counter--; 233 234 snd_bebob_stream_stop_duplex(bebob); 235 236 return 0; 237} 238 239static int 240pcm_capture_prepare(struct snd_pcm_substream *substream) 241{ 242 struct snd_bebob *bebob = substream->private_data; 243 int err; 244 245 err = snd_bebob_stream_start_duplex(bebob); 246 if (err >= 0) 247 amdtp_stream_pcm_prepare(&bebob->tx_stream); 248 249 return err; 250} 251static int 252pcm_playback_prepare(struct snd_pcm_substream *substream) 253{ 254 struct snd_bebob *bebob = substream->private_data; 255 int err; 256 257 err = snd_bebob_stream_start_duplex(bebob); 258 if (err >= 0) 259 amdtp_stream_pcm_prepare(&bebob->rx_stream); 260 261 return err; 262} 263 264static int 265pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) 266{ 267 struct snd_bebob *bebob = substream->private_data; 268 269 switch (cmd) { 270 case SNDRV_PCM_TRIGGER_START: 271 amdtp_stream_pcm_trigger(&bebob->tx_stream, substream); 272 break; 273 case SNDRV_PCM_TRIGGER_STOP: 274 amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL); 275 break; 276 default: 277 return -EINVAL; 278 } 279 280 return 0; 281} 282static int 283pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) 284{ 285 struct snd_bebob *bebob = substream->private_data; 286 287 switch (cmd) { 288 case SNDRV_PCM_TRIGGER_START: 289 amdtp_stream_pcm_trigger(&bebob->rx_stream, substream); 290 break; 291 case SNDRV_PCM_TRIGGER_STOP: 292 amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL); 293 break; 294 default: 295 return -EINVAL; 296 } 297 298 return 0; 299} 300 301static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) 302{ 303 struct snd_bebob *bebob = sbstrm->private_data; 304 305 return amdtp_domain_stream_pcm_pointer(&bebob->domain, 306 &bebob->tx_stream); 307} 308static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) 309{ 310 struct snd_bebob *bebob = sbstrm->private_data; 311 312 return amdtp_domain_stream_pcm_pointer(&bebob->domain, 313 &bebob->rx_stream); 314} 315 316static int pcm_capture_ack(struct snd_pcm_substream *substream) 317{ 318 struct snd_bebob *bebob = substream->private_data; 319 320 return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->tx_stream); 321} 322 323static int pcm_playback_ack(struct snd_pcm_substream *substream) 324{ 325 struct snd_bebob *bebob = substream->private_data; 326 327 return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->rx_stream); 328} 329 330int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) 331{ 332 static const struct snd_pcm_ops capture_ops = { 333 .open = pcm_open, 334 .close = pcm_close, 335 .hw_params = pcm_hw_params, 336 .hw_free = pcm_hw_free, 337 .prepare = pcm_capture_prepare, 338 .trigger = pcm_capture_trigger, 339 .pointer = pcm_capture_pointer, 340 .ack = pcm_capture_ack, 341 }; 342 static const struct snd_pcm_ops playback_ops = { 343 .open = pcm_open, 344 .close = pcm_close, 345 .hw_params = pcm_hw_params, 346 .hw_free = pcm_hw_free, 347 .prepare = pcm_playback_prepare, 348 .trigger = pcm_playback_trigger, 349 .pointer = pcm_playback_pointer, 350 .ack = pcm_playback_ack, 351 }; 352 struct snd_pcm *pcm; 353 int err; 354 355 err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm); 356 if (err < 0) 357 goto end; 358 359 pcm->private_data = bebob; 360 pcm->nonatomic = true; 361 snprintf(pcm->name, sizeof(pcm->name), 362 "%s PCM", bebob->card->shortname); 363 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); 364 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); 365 snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); 366end: 367 return err; 368}
~/
Read-only file preview.