452 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * oxfw_pcm.c - a part of driver for OXFW970/971 based devices4 *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 = 120 };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 else156 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 for187 // transmission of PCM frames, the available sampling rate is limited188 // 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->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->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->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->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 pcm->nonatomic = true;444 strcpy(pcm->name, oxfw->card->shortname);445 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);446 if (cap > 0)447 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);448 snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);449 450 return 0;451}452