379 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * bebob_pcm.c - a part of driver for BeBoB based devices4 *5 * Copyright (c) 2013-2014 Takashi Sakamoto6 */7 8#include "./bebob.h"9 10static int11hw_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 = 120 };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 int39hw_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 = 148 };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 void68limit_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 int95pcm_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 mutex_lock(&bebob->mutex);153 154 // When source of clock is not internal or any stream is reserved for155 // transmission of PCM frames, the available sampling rate is limited156 // at current one.157 if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL ||158 (bebob->substreams_counter > 0 && d->events_per_period > 0)) {159 unsigned int frames_per_period = d->events_per_period;160 unsigned int frames_per_buffer = d->events_per_buffer;161 unsigned int sampling_rate;162 163 err = spec->get(bebob, &sampling_rate);164 if (err < 0) {165 mutex_unlock(&bebob->mutex);166 dev_err(&bebob->unit->device,167 "fail to get sampling rate: %d\n", err);168 goto err_locked;169 }170 171 substream->runtime->hw.rate_min = sampling_rate;172 substream->runtime->hw.rate_max = sampling_rate;173 174 if (frames_per_period > 0) {175 err = snd_pcm_hw_constraint_minmax(substream->runtime,176 SNDRV_PCM_HW_PARAM_PERIOD_SIZE,177 frames_per_period, frames_per_period);178 if (err < 0) {179 mutex_unlock(&bebob->mutex);180 goto err_locked;181 }182 183 err = snd_pcm_hw_constraint_minmax(substream->runtime,184 SNDRV_PCM_HW_PARAM_BUFFER_SIZE,185 frames_per_buffer, frames_per_buffer);186 if (err < 0) {187 mutex_unlock(&bebob->mutex);188 goto err_locked;189 }190 }191 }192 193 mutex_unlock(&bebob->mutex);194 195 snd_pcm_set_sync(substream);196 197 return 0;198err_locked:199 snd_bebob_stream_lock_release(bebob);200 return err;201}202 203static int204pcm_close(struct snd_pcm_substream *substream)205{206 struct snd_bebob *bebob = substream->private_data;207 snd_bebob_stream_lock_release(bebob);208 return 0;209}210 211static int pcm_hw_params(struct snd_pcm_substream *substream,212 struct snd_pcm_hw_params *hw_params)213{214 struct snd_bebob *bebob = substream->private_data;215 int err = 0;216 217 if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {218 unsigned int rate = params_rate(hw_params);219 unsigned int frames_per_period = params_period_size(hw_params);220 unsigned int frames_per_buffer = params_buffer_size(hw_params);221 222 mutex_lock(&bebob->mutex);223 err = snd_bebob_stream_reserve_duplex(bebob, rate,224 frames_per_period, frames_per_buffer);225 if (err >= 0)226 ++bebob->substreams_counter;227 mutex_unlock(&bebob->mutex);228 }229 230 return err;231}232 233static int pcm_hw_free(struct snd_pcm_substream *substream)234{235 struct snd_bebob *bebob = substream->private_data;236 237 mutex_lock(&bebob->mutex);238 239 if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)240 bebob->substreams_counter--;241 242 snd_bebob_stream_stop_duplex(bebob);243 244 mutex_unlock(&bebob->mutex);245 246 return 0;247}248 249static int250pcm_capture_prepare(struct snd_pcm_substream *substream)251{252 struct snd_bebob *bebob = substream->private_data;253 int err;254 255 err = snd_bebob_stream_start_duplex(bebob);256 if (err >= 0)257 amdtp_stream_pcm_prepare(&bebob->tx_stream);258 259 return err;260}261static int262pcm_playback_prepare(struct snd_pcm_substream *substream)263{264 struct snd_bebob *bebob = substream->private_data;265 int err;266 267 err = snd_bebob_stream_start_duplex(bebob);268 if (err >= 0)269 amdtp_stream_pcm_prepare(&bebob->rx_stream);270 271 return err;272}273 274static int275pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)276{277 struct snd_bebob *bebob = substream->private_data;278 279 switch (cmd) {280 case SNDRV_PCM_TRIGGER_START:281 amdtp_stream_pcm_trigger(&bebob->tx_stream, substream);282 break;283 case SNDRV_PCM_TRIGGER_STOP:284 amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL);285 break;286 default:287 return -EINVAL;288 }289 290 return 0;291}292static int293pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)294{295 struct snd_bebob *bebob = substream->private_data;296 297 switch (cmd) {298 case SNDRV_PCM_TRIGGER_START:299 amdtp_stream_pcm_trigger(&bebob->rx_stream, substream);300 break;301 case SNDRV_PCM_TRIGGER_STOP:302 amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL);303 break;304 default:305 return -EINVAL;306 }307 308 return 0;309}310 311static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)312{313 struct snd_bebob *bebob = sbstrm->private_data;314 315 return amdtp_domain_stream_pcm_pointer(&bebob->domain,316 &bebob->tx_stream);317}318static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)319{320 struct snd_bebob *bebob = sbstrm->private_data;321 322 return amdtp_domain_stream_pcm_pointer(&bebob->domain,323 &bebob->rx_stream);324}325 326static int pcm_capture_ack(struct snd_pcm_substream *substream)327{328 struct snd_bebob *bebob = substream->private_data;329 330 return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->tx_stream);331}332 333static int pcm_playback_ack(struct snd_pcm_substream *substream)334{335 struct snd_bebob *bebob = substream->private_data;336 337 return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->rx_stream);338}339 340int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)341{342 static const struct snd_pcm_ops capture_ops = {343 .open = pcm_open,344 .close = pcm_close,345 .hw_params = pcm_hw_params,346 .hw_free = pcm_hw_free,347 .prepare = pcm_capture_prepare,348 .trigger = pcm_capture_trigger,349 .pointer = pcm_capture_pointer,350 .ack = pcm_capture_ack,351 };352 static const struct snd_pcm_ops playback_ops = {353 .open = pcm_open,354 .close = pcm_close,355 .hw_params = pcm_hw_params,356 .hw_free = pcm_hw_free,357 .prepare = pcm_playback_prepare,358 .trigger = pcm_playback_trigger,359 .pointer = pcm_playback_pointer,360 .ack = pcm_playback_ack,361 };362 struct snd_pcm *pcm;363 int err;364 365 err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm);366 if (err < 0)367 goto end;368 369 pcm->private_data = bebob;370 pcm->nonatomic = true;371 snprintf(pcm->name, sizeof(pcm->name),372 "%s PCM", bebob->card->shortname);373 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);374 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);375 snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);376end:377 return err;378}379