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