cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

motu-pcm.c (9562B)


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