461 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * dice_pcm.c - a part of driver for DICE based devices4 *5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>6 * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>7 */8 9#include "dice.h"10 11static int dice_rate_constraint(struct snd_pcm_hw_params *params,12 struct snd_pcm_hw_rule *rule)13{14 struct snd_pcm_substream *substream = rule->private;15 struct snd_dice *dice = substream->private_data;16 unsigned int index = substream->pcm->device;17 18 const struct snd_interval *c =19 hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);20 struct snd_interval *r =21 hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);22 struct snd_interval rates = {23 .min = UINT_MAX, .max = 0, .integer = 124 };25 unsigned int *pcm_channels;26 enum snd_dice_rate_mode mode;27 unsigned int i, rate;28 29 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)30 pcm_channels = dice->tx_pcm_chs[index];31 else32 pcm_channels = dice->rx_pcm_chs[index];33 34 for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {35 rate = snd_dice_rates[i];36 if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)37 continue;38 39 if (!snd_interval_test(c, pcm_channels[mode]))40 continue;41 42 rates.min = min(rates.min, rate);43 rates.max = max(rates.max, rate);44 }45 46 return snd_interval_refine(r, &rates);47}48 49static int dice_channels_constraint(struct snd_pcm_hw_params *params,50 struct snd_pcm_hw_rule *rule)51{52 struct snd_pcm_substream *substream = rule->private;53 struct snd_dice *dice = substream->private_data;54 unsigned int index = substream->pcm->device;55 56 const struct snd_interval *r =57 hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);58 struct snd_interval *c =59 hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);60 struct snd_interval channels = {61 .min = UINT_MAX, .max = 0, .integer = 162 };63 unsigned int *pcm_channels;64 enum snd_dice_rate_mode mode;65 unsigned int i, rate;66 67 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)68 pcm_channels = dice->tx_pcm_chs[index];69 else70 pcm_channels = dice->rx_pcm_chs[index];71 72 for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {73 rate = snd_dice_rates[i];74 if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)75 continue;76 77 if (!snd_interval_test(r, rate))78 continue;79 80 channels.min = min(channels.min, pcm_channels[mode]);81 channels.max = max(channels.max, pcm_channels[mode]);82 }83 84 return snd_interval_refine(c, &channels);85}86 87static int limit_channels_and_rates(struct snd_dice *dice,88 struct snd_pcm_runtime *runtime,89 enum amdtp_stream_direction dir,90 unsigned int index)91{92 struct snd_pcm_hardware *hw = &runtime->hw;93 unsigned int *pcm_channels;94 unsigned int i;95 96 if (dir == AMDTP_IN_STREAM)97 pcm_channels = dice->tx_pcm_chs[index];98 else99 pcm_channels = dice->rx_pcm_chs[index];100 101 hw->channels_min = UINT_MAX;102 hw->channels_max = 0;103 104 for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {105 enum snd_dice_rate_mode mode;106 unsigned int rate, channels;107 108 rate = snd_dice_rates[i];109 if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)110 continue;111 hw->rates |= snd_pcm_rate_to_rate_bit(rate);112 113 channels = pcm_channels[mode];114 if (channels == 0)115 continue;116 hw->channels_min = min(hw->channels_min, channels);117 hw->channels_max = max(hw->channels_max, channels);118 }119 120 snd_pcm_limit_hw_rates(runtime);121 122 return 0;123}124 125static int init_hw_info(struct snd_dice *dice,126 struct snd_pcm_substream *substream)127{128 struct snd_pcm_runtime *runtime = substream->runtime;129 struct snd_pcm_hardware *hw = &runtime->hw;130 unsigned int index = substream->pcm->device;131 enum amdtp_stream_direction dir;132 struct amdtp_stream *stream;133 int err;134 135 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {136 hw->formats = AM824_IN_PCM_FORMAT_BITS;137 dir = AMDTP_IN_STREAM;138 stream = &dice->tx_stream[index];139 } else {140 hw->formats = AM824_OUT_PCM_FORMAT_BITS;141 dir = AMDTP_OUT_STREAM;142 stream = &dice->rx_stream[index];143 }144 145 err = limit_channels_and_rates(dice, substream->runtime, dir,146 index);147 if (err < 0)148 return err;149 150 err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,151 dice_rate_constraint, substream,152 SNDRV_PCM_HW_PARAM_CHANNELS, -1);153 if (err < 0)154 return err;155 err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,156 dice_channels_constraint, substream,157 SNDRV_PCM_HW_PARAM_RATE, -1);158 if (err < 0)159 return err;160 161 return amdtp_am824_add_pcm_hw_constraints(stream, runtime);162}163 164static int pcm_open(struct snd_pcm_substream *substream)165{166 struct snd_dice *dice = substream->private_data;167 struct amdtp_domain *d = &dice->domain;168 unsigned int source;169 bool internal;170 int err;171 172 err = snd_dice_stream_lock_try(dice);173 if (err < 0)174 return err;175 176 err = init_hw_info(dice, substream);177 if (err < 0)178 goto err_locked;179 180 err = snd_dice_transaction_get_clock_source(dice, &source);181 if (err < 0)182 goto err_locked;183 switch (source) {184 case CLOCK_SOURCE_AES1:185 case CLOCK_SOURCE_AES2:186 case CLOCK_SOURCE_AES3:187 case CLOCK_SOURCE_AES4:188 case CLOCK_SOURCE_AES_ANY:189 case CLOCK_SOURCE_ADAT:190 case CLOCK_SOURCE_TDIF:191 case CLOCK_SOURCE_WC:192 internal = false;193 break;194 default:195 internal = true;196 break;197 }198 199 mutex_lock(&dice->mutex);200 201 // When source of clock is not internal or any stream is reserved for202 // transmission of PCM frames, the available sampling rate is limited203 // at current one.204 if (!internal ||205 (dice->substreams_counter > 0 && d->events_per_period > 0)) {206 unsigned int frames_per_period = d->events_per_period;207 unsigned int frames_per_buffer = d->events_per_buffer;208 unsigned int rate;209 210 err = snd_dice_transaction_get_rate(dice, &rate);211 if (err < 0) {212 mutex_unlock(&dice->mutex);213 goto err_locked;214 }215 216 substream->runtime->hw.rate_min = rate;217 substream->runtime->hw.rate_max = rate;218 219 if (frames_per_period > 0) {220 // For double_pcm_frame quirk.221 if (rate > 96000 && !dice->disable_double_pcm_frames) {222 frames_per_period *= 2;223 frames_per_buffer *= 2;224 }225 226 err = snd_pcm_hw_constraint_minmax(substream->runtime,227 SNDRV_PCM_HW_PARAM_PERIOD_SIZE,228 frames_per_period, frames_per_period);229 if (err < 0) {230 mutex_unlock(&dice->mutex);231 goto err_locked;232 }233 234 err = snd_pcm_hw_constraint_minmax(substream->runtime,235 SNDRV_PCM_HW_PARAM_BUFFER_SIZE,236 frames_per_buffer, frames_per_buffer);237 if (err < 0) {238 mutex_unlock(&dice->mutex);239 goto err_locked;240 }241 }242 }243 244 mutex_unlock(&dice->mutex);245 246 snd_pcm_set_sync(substream);247 248 return 0;249err_locked:250 snd_dice_stream_lock_release(dice);251 return err;252}253 254static int pcm_close(struct snd_pcm_substream *substream)255{256 struct snd_dice *dice = substream->private_data;257 258 snd_dice_stream_lock_release(dice);259 260 return 0;261}262 263static int pcm_hw_params(struct snd_pcm_substream *substream,264 struct snd_pcm_hw_params *hw_params)265{266 struct snd_dice *dice = substream->private_data;267 int err = 0;268 269 if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {270 unsigned int rate = params_rate(hw_params);271 unsigned int events_per_period = params_period_size(hw_params);272 unsigned int events_per_buffer = params_buffer_size(hw_params);273 274 mutex_lock(&dice->mutex);275 // For double_pcm_frame quirk.276 if (rate > 96000 && !dice->disable_double_pcm_frames) {277 events_per_period /= 2;278 events_per_buffer /= 2;279 }280 err = snd_dice_stream_reserve_duplex(dice, rate,281 events_per_period, events_per_buffer);282 if (err >= 0)283 ++dice->substreams_counter;284 mutex_unlock(&dice->mutex);285 }286 287 return err;288}289 290static int pcm_hw_free(struct snd_pcm_substream *substream)291{292 struct snd_dice *dice = substream->private_data;293 294 mutex_lock(&dice->mutex);295 296 if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)297 --dice->substreams_counter;298 299 snd_dice_stream_stop_duplex(dice);300 301 mutex_unlock(&dice->mutex);302 303 return 0;304}305 306static int capture_prepare(struct snd_pcm_substream *substream)307{308 struct snd_dice *dice = substream->private_data;309 struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];310 int err;311 312 mutex_lock(&dice->mutex);313 err = snd_dice_stream_start_duplex(dice);314 mutex_unlock(&dice->mutex);315 if (err >= 0)316 amdtp_stream_pcm_prepare(stream);317 318 return 0;319}320static int playback_prepare(struct snd_pcm_substream *substream)321{322 struct snd_dice *dice = substream->private_data;323 struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];324 int err;325 326 mutex_lock(&dice->mutex);327 err = snd_dice_stream_start_duplex(dice);328 mutex_unlock(&dice->mutex);329 if (err >= 0)330 amdtp_stream_pcm_prepare(stream);331 332 return err;333}334 335static int capture_trigger(struct snd_pcm_substream *substream, int cmd)336{337 struct snd_dice *dice = substream->private_data;338 struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];339 340 switch (cmd) {341 case SNDRV_PCM_TRIGGER_START:342 amdtp_stream_pcm_trigger(stream, substream);343 break;344 case SNDRV_PCM_TRIGGER_STOP:345 amdtp_stream_pcm_trigger(stream, NULL);346 break;347 default:348 return -EINVAL;349 }350 351 return 0;352}353static int playback_trigger(struct snd_pcm_substream *substream, int cmd)354{355 struct snd_dice *dice = substream->private_data;356 struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];357 358 switch (cmd) {359 case SNDRV_PCM_TRIGGER_START:360 amdtp_stream_pcm_trigger(stream, substream);361 break;362 case SNDRV_PCM_TRIGGER_STOP:363 amdtp_stream_pcm_trigger(stream, NULL);364 break;365 default:366 return -EINVAL;367 }368 369 return 0;370}371 372static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)373{374 struct snd_dice *dice = substream->private_data;375 struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];376 377 return amdtp_domain_stream_pcm_pointer(&dice->domain, stream);378}379static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)380{381 struct snd_dice *dice = substream->private_data;382 struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];383 384 return amdtp_domain_stream_pcm_pointer(&dice->domain, stream);385}386 387static int capture_ack(struct snd_pcm_substream *substream)388{389 struct snd_dice *dice = substream->private_data;390 struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];391 392 return amdtp_domain_stream_pcm_ack(&dice->domain, stream);393}394 395static int playback_ack(struct snd_pcm_substream *substream)396{397 struct snd_dice *dice = substream->private_data;398 struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];399 400 return amdtp_domain_stream_pcm_ack(&dice->domain, stream);401}402 403int snd_dice_create_pcm(struct snd_dice *dice)404{405 static const struct snd_pcm_ops capture_ops = {406 .open = pcm_open,407 .close = pcm_close,408 .hw_params = pcm_hw_params,409 .hw_free = pcm_hw_free,410 .prepare = capture_prepare,411 .trigger = capture_trigger,412 .pointer = capture_pointer,413 .ack = capture_ack,414 };415 static const struct snd_pcm_ops playback_ops = {416 .open = pcm_open,417 .close = pcm_close,418 .hw_params = pcm_hw_params,419 .hw_free = pcm_hw_free,420 .prepare = playback_prepare,421 .trigger = playback_trigger,422 .pointer = playback_pointer,423 .ack = playback_ack,424 };425 struct snd_pcm *pcm;426 unsigned int capture, playback;427 int i, j;428 int err;429 430 for (i = 0; i < MAX_STREAMS; i++) {431 capture = playback = 0;432 for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j) {433 if (dice->tx_pcm_chs[i][j] > 0)434 capture = 1;435 if (dice->rx_pcm_chs[i][j] > 0)436 playback = 1;437 }438 439 err = snd_pcm_new(dice->card, "DICE", i, playback, capture,440 &pcm);441 if (err < 0)442 return err;443 pcm->private_data = dice;444 pcm->nonatomic = true;445 strcpy(pcm->name, dice->card->shortname);446 447 if (capture > 0)448 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,449 &capture_ops);450 451 if (playback > 0)452 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,453 &playback_ops);454 455 snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC,456 NULL, 0, 0);457 }458 459 return 0;460}461