oxfw-pcm.c (11373B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * oxfw_pcm.c - a part of driver for OXFW970/971 based devices 4 * 5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 6 */ 7 8#include "oxfw.h" 9 10static int hw_rule_rate(struct snd_pcm_hw_params *params, 11 struct snd_pcm_hw_rule *rule) 12{ 13 u8 **formats = 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 struct snd_oxfw_stream_formation formation; 22 int i, err; 23 24 for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { 25 if (formats[i] == NULL) 26 continue; 27 28 err = snd_oxfw_stream_parse_format(formats[i], &formation); 29 if (err < 0) 30 continue; 31 if (!snd_interval_test(c, formation.pcm)) 32 continue; 33 34 t.min = min(t.min, formation.rate); 35 t.max = max(t.max, formation.rate); 36 37 } 38 return snd_interval_refine(r, &t); 39} 40 41static int hw_rule_channels(struct snd_pcm_hw_params *params, 42 struct snd_pcm_hw_rule *rule) 43{ 44 u8 **formats = rule->private; 45 struct snd_interval *c = 46 hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); 47 const struct snd_interval *r = 48 hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); 49 struct snd_oxfw_stream_formation formation; 50 int i, j, err; 51 unsigned int count, list[SND_OXFW_STREAM_FORMAT_ENTRIES] = {0}; 52 53 count = 0; 54 for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { 55 if (formats[i] == NULL) 56 break; 57 58 err = snd_oxfw_stream_parse_format(formats[i], &formation); 59 if (err < 0) 60 continue; 61 if (!snd_interval_test(r, formation.rate)) 62 continue; 63 if (list[count] == formation.pcm) 64 continue; 65 66 for (j = 0; j < ARRAY_SIZE(list); j++) { 67 if (list[j] == formation.pcm) 68 break; 69 } 70 if (j == ARRAY_SIZE(list)) { 71 list[count] = formation.pcm; 72 if (++count == ARRAY_SIZE(list)) 73 break; 74 } 75 } 76 77 return snd_interval_list(c, count, list, 0); 78} 79 80static void limit_channels_and_rates(struct snd_pcm_hardware *hw, u8 **formats) 81{ 82 struct snd_oxfw_stream_formation formation; 83 int i, err; 84 85 hw->channels_min = UINT_MAX; 86 hw->channels_max = 0; 87 88 hw->rate_min = UINT_MAX; 89 hw->rate_max = 0; 90 hw->rates = 0; 91 92 for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { 93 if (formats[i] == NULL) 94 break; 95 96 err = snd_oxfw_stream_parse_format(formats[i], &formation); 97 if (err < 0) 98 continue; 99 100 hw->channels_min = min(hw->channels_min, formation.pcm); 101 hw->channels_max = max(hw->channels_max, formation.pcm); 102 103 hw->rate_min = min(hw->rate_min, formation.rate); 104 hw->rate_max = max(hw->rate_max, formation.rate); 105 hw->rates |= snd_pcm_rate_to_rate_bit(formation.rate); 106 } 107} 108 109static int init_hw_params(struct snd_oxfw *oxfw, 110 struct snd_pcm_substream *substream) 111{ 112 struct snd_pcm_runtime *runtime = substream->runtime; 113 u8 **formats; 114 struct amdtp_stream *stream; 115 int err; 116 117 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 118 runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS; 119 stream = &oxfw->tx_stream; 120 formats = oxfw->tx_stream_formats; 121 } else { 122 runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS; 123 stream = &oxfw->rx_stream; 124 formats = oxfw->rx_stream_formats; 125 } 126 127 limit_channels_and_rates(&runtime->hw, formats); 128 129 err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 130 hw_rule_channels, formats, 131 SNDRV_PCM_HW_PARAM_RATE, -1); 132 if (err < 0) 133 goto end; 134 135 err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 136 hw_rule_rate, formats, 137 SNDRV_PCM_HW_PARAM_CHANNELS, -1); 138 if (err < 0) 139 goto end; 140 141 err = amdtp_am824_add_pcm_hw_constraints(stream, runtime); 142end: 143 return err; 144} 145 146static int limit_to_current_params(struct snd_pcm_substream *substream) 147{ 148 struct snd_oxfw *oxfw = substream->private_data; 149 struct snd_oxfw_stream_formation formation; 150 enum avc_general_plug_dir dir; 151 int err; 152 153 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 154 dir = AVC_GENERAL_PLUG_DIR_OUT; 155 else 156 dir = AVC_GENERAL_PLUG_DIR_IN; 157 158 err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation); 159 if (err < 0) 160 goto end; 161 162 substream->runtime->hw.channels_min = formation.pcm; 163 substream->runtime->hw.channels_max = formation.pcm; 164 substream->runtime->hw.rate_min = formation.rate; 165 substream->runtime->hw.rate_max = formation.rate; 166end: 167 return err; 168} 169 170static int pcm_open(struct snd_pcm_substream *substream) 171{ 172 struct snd_oxfw *oxfw = substream->private_data; 173 struct amdtp_domain *d = &oxfw->domain; 174 int err; 175 176 err = snd_oxfw_stream_lock_try(oxfw); 177 if (err < 0) 178 return err; 179 180 err = init_hw_params(oxfw, substream); 181 if (err < 0) 182 goto err_locked; 183 184 mutex_lock(&oxfw->mutex); 185 186 // When source of clock is not internal or any stream is reserved for 187 // transmission of PCM frames, the available sampling rate is limited 188 // at current one. 189 if (oxfw->substreams_count > 0 && d->events_per_period > 0) { 190 unsigned int frames_per_period = d->events_per_period; 191 unsigned int frames_per_buffer = d->events_per_buffer; 192 193 err = limit_to_current_params(substream); 194 if (err < 0) { 195 mutex_unlock(&oxfw->mutex); 196 goto err_locked; 197 } 198 199 if (frames_per_period > 0) { 200 err = snd_pcm_hw_constraint_minmax(substream->runtime, 201 SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 202 frames_per_period, frames_per_period); 203 if (err < 0) { 204 mutex_unlock(&oxfw->mutex); 205 goto err_locked; 206 } 207 208 err = snd_pcm_hw_constraint_minmax(substream->runtime, 209 SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 210 frames_per_buffer, frames_per_buffer); 211 if (err < 0) { 212 mutex_unlock(&oxfw->mutex); 213 goto err_locked; 214 } 215 } 216 } 217 218 mutex_unlock(&oxfw->mutex); 219 220 snd_pcm_set_sync(substream); 221 222 return 0; 223err_locked: 224 snd_oxfw_stream_lock_release(oxfw); 225 return err; 226} 227 228static int pcm_close(struct snd_pcm_substream *substream) 229{ 230 struct snd_oxfw *oxfw = substream->private_data; 231 232 snd_oxfw_stream_lock_release(oxfw); 233 return 0; 234} 235 236static int pcm_capture_hw_params(struct snd_pcm_substream *substream, 237 struct snd_pcm_hw_params *hw_params) 238{ 239 struct snd_oxfw *oxfw = substream->private_data; 240 int err = 0; 241 242 if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { 243 unsigned int rate = params_rate(hw_params); 244 unsigned int channels = params_channels(hw_params); 245 unsigned int frames_per_period = params_period_size(hw_params); 246 unsigned int frames_per_buffer = params_buffer_size(hw_params); 247 248 mutex_lock(&oxfw->mutex); 249 err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, 250 rate, channels, frames_per_period, 251 frames_per_buffer); 252 if (err >= 0) 253 ++oxfw->substreams_count; 254 mutex_unlock(&oxfw->mutex); 255 } 256 257 return err; 258} 259static int pcm_playback_hw_params(struct snd_pcm_substream *substream, 260 struct snd_pcm_hw_params *hw_params) 261{ 262 struct snd_oxfw *oxfw = substream->private_data; 263 int err = 0; 264 265 if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { 266 unsigned int rate = params_rate(hw_params); 267 unsigned int channels = params_channels(hw_params); 268 unsigned int frames_per_period = params_period_size(hw_params); 269 unsigned int frames_per_buffer = params_buffer_size(hw_params); 270 271 mutex_lock(&oxfw->mutex); 272 err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, 273 rate, channels, frames_per_period, 274 frames_per_buffer); 275 if (err >= 0) 276 ++oxfw->substreams_count; 277 mutex_unlock(&oxfw->mutex); 278 } 279 280 return err; 281} 282 283static int pcm_capture_hw_free(struct snd_pcm_substream *substream) 284{ 285 struct snd_oxfw *oxfw = substream->private_data; 286 287 mutex_lock(&oxfw->mutex); 288 289 if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) 290 --oxfw->substreams_count; 291 292 snd_oxfw_stream_stop_duplex(oxfw); 293 294 mutex_unlock(&oxfw->mutex); 295 296 return 0; 297} 298static int pcm_playback_hw_free(struct snd_pcm_substream *substream) 299{ 300 struct snd_oxfw *oxfw = substream->private_data; 301 302 mutex_lock(&oxfw->mutex); 303 304 if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) 305 --oxfw->substreams_count; 306 307 snd_oxfw_stream_stop_duplex(oxfw); 308 309 mutex_unlock(&oxfw->mutex); 310 311 return 0; 312} 313 314static int pcm_capture_prepare(struct snd_pcm_substream *substream) 315{ 316 struct snd_oxfw *oxfw = substream->private_data; 317 int err; 318 319 mutex_lock(&oxfw->mutex); 320 err = snd_oxfw_stream_start_duplex(oxfw); 321 mutex_unlock(&oxfw->mutex); 322 if (err < 0) 323 goto end; 324 325 amdtp_stream_pcm_prepare(&oxfw->tx_stream); 326end: 327 return err; 328} 329static int pcm_playback_prepare(struct snd_pcm_substream *substream) 330{ 331 struct snd_oxfw *oxfw = substream->private_data; 332 int err; 333 334 mutex_lock(&oxfw->mutex); 335 err = snd_oxfw_stream_start_duplex(oxfw); 336 mutex_unlock(&oxfw->mutex); 337 if (err < 0) 338 goto end; 339 340 amdtp_stream_pcm_prepare(&oxfw->rx_stream); 341end: 342 return err; 343} 344 345static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) 346{ 347 struct snd_oxfw *oxfw = substream->private_data; 348 struct snd_pcm_substream *pcm; 349 350 switch (cmd) { 351 case SNDRV_PCM_TRIGGER_START: 352 pcm = substream; 353 break; 354 case SNDRV_PCM_TRIGGER_STOP: 355 pcm = NULL; 356 break; 357 default: 358 return -EINVAL; 359 } 360 amdtp_stream_pcm_trigger(&oxfw->tx_stream, pcm); 361 return 0; 362} 363static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) 364{ 365 struct snd_oxfw *oxfw = substream->private_data; 366 struct snd_pcm_substream *pcm; 367 368 switch (cmd) { 369 case SNDRV_PCM_TRIGGER_START: 370 pcm = substream; 371 break; 372 case SNDRV_PCM_TRIGGER_STOP: 373 pcm = NULL; 374 break; 375 default: 376 return -EINVAL; 377 } 378 amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm); 379 return 0; 380} 381 382static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm) 383{ 384 struct snd_oxfw *oxfw = sbstm->private_data; 385 386 return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->tx_stream); 387} 388static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm) 389{ 390 struct snd_oxfw *oxfw = sbstm->private_data; 391 392 return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->rx_stream); 393} 394 395static int pcm_capture_ack(struct snd_pcm_substream *substream) 396{ 397 struct snd_oxfw *oxfw = substream->private_data; 398 399 return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->tx_stream); 400} 401 402static int pcm_playback_ack(struct snd_pcm_substream *substream) 403{ 404 struct snd_oxfw *oxfw = substream->private_data; 405 406 return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->rx_stream); 407} 408 409int snd_oxfw_create_pcm(struct snd_oxfw *oxfw) 410{ 411 static const struct snd_pcm_ops capture_ops = { 412 .open = pcm_open, 413 .close = pcm_close, 414 .hw_params = pcm_capture_hw_params, 415 .hw_free = pcm_capture_hw_free, 416 .prepare = pcm_capture_prepare, 417 .trigger = pcm_capture_trigger, 418 .pointer = pcm_capture_pointer, 419 .ack = pcm_capture_ack, 420 }; 421 static const struct snd_pcm_ops playback_ops = { 422 .open = pcm_open, 423 .close = pcm_close, 424 .hw_params = pcm_playback_hw_params, 425 .hw_free = pcm_playback_hw_free, 426 .prepare = pcm_playback_prepare, 427 .trigger = pcm_playback_trigger, 428 .pointer = pcm_playback_pointer, 429 .ack = pcm_playback_ack, 430 }; 431 struct snd_pcm *pcm; 432 unsigned int cap = 0; 433 int err; 434 435 if (oxfw->has_output) 436 cap = 1; 437 438 err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, cap, &pcm); 439 if (err < 0) 440 return err; 441 442 pcm->private_data = oxfw; 443 strcpy(pcm->name, oxfw->card->shortname); 444 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); 445 if (cap > 0) 446 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); 447 snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); 448 449 return 0; 450}