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

tascam-pcm.c (7244B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * tascam-pcm.c - a part of driver for TASCAM FireWire series
      4 *
      5 * Copyright (c) 2015 Takashi Sakamoto
      6 */
      7
      8#include "tascam.h"
      9
     10static int pcm_init_hw_params(struct snd_tscm *tscm,
     11			      struct snd_pcm_substream *substream)
     12{
     13	struct snd_pcm_runtime *runtime = substream->runtime;
     14	struct snd_pcm_hardware *hw = &runtime->hw;
     15	struct amdtp_stream *stream;
     16	unsigned int pcm_channels;
     17
     18	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
     19		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
     20		stream = &tscm->tx_stream;
     21		pcm_channels = tscm->spec->pcm_capture_analog_channels;
     22	} else {
     23		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
     24		stream = &tscm->rx_stream;
     25		pcm_channels = tscm->spec->pcm_playback_analog_channels;
     26	}
     27
     28	if (tscm->spec->has_adat)
     29		pcm_channels += 8;
     30	if (tscm->spec->has_spdif)
     31		pcm_channels += 2;
     32	runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels;
     33
     34	hw->rates = SNDRV_PCM_RATE_44100 |
     35		    SNDRV_PCM_RATE_48000 |
     36		    SNDRV_PCM_RATE_88200 |
     37		    SNDRV_PCM_RATE_96000;
     38	snd_pcm_limit_hw_rates(runtime);
     39
     40	return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);
     41}
     42
     43static int pcm_open(struct snd_pcm_substream *substream)
     44{
     45	struct snd_tscm *tscm = substream->private_data;
     46	struct amdtp_domain *d = &tscm->domain;
     47	enum snd_tscm_clock clock;
     48	int err;
     49
     50	err = snd_tscm_stream_lock_try(tscm);
     51	if (err < 0)
     52		return err;
     53
     54	err = pcm_init_hw_params(tscm, substream);
     55	if (err < 0)
     56		goto err_locked;
     57
     58	err = snd_tscm_stream_get_clock(tscm, &clock);
     59	if (err < 0)
     60		goto err_locked;
     61
     62	mutex_lock(&tscm->mutex);
     63
     64	// When source of clock is not internal or any stream is reserved for
     65	// transmission of PCM frames, the available sampling rate is limited
     66	// at current one.
     67	if (clock != SND_TSCM_CLOCK_INTERNAL || tscm->substreams_counter > 0) {
     68		unsigned int frames_per_period = d->events_per_period;
     69		unsigned int frames_per_buffer = d->events_per_buffer;
     70		unsigned int rate;
     71
     72		err = snd_tscm_stream_get_rate(tscm, &rate);
     73		if (err < 0) {
     74			mutex_unlock(&tscm->mutex);
     75			goto err_locked;
     76		}
     77		substream->runtime->hw.rate_min = rate;
     78		substream->runtime->hw.rate_max = rate;
     79
     80		err = snd_pcm_hw_constraint_minmax(substream->runtime,
     81					SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
     82					frames_per_period, frames_per_period);
     83		if (err < 0) {
     84			mutex_unlock(&tscm->mutex);
     85			goto err_locked;
     86		}
     87
     88		err = snd_pcm_hw_constraint_minmax(substream->runtime,
     89					SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
     90					frames_per_buffer, frames_per_buffer);
     91		if (err < 0) {
     92			mutex_unlock(&tscm->mutex);
     93			goto err_locked;
     94		}
     95	}
     96
     97	mutex_unlock(&tscm->mutex);
     98
     99	snd_pcm_set_sync(substream);
    100
    101	return 0;
    102err_locked:
    103	snd_tscm_stream_lock_release(tscm);
    104	return err;
    105}
    106
    107static int pcm_close(struct snd_pcm_substream *substream)
    108{
    109	struct snd_tscm *tscm = substream->private_data;
    110
    111	snd_tscm_stream_lock_release(tscm);
    112
    113	return 0;
    114}
    115
    116static int pcm_hw_params(struct snd_pcm_substream *substream,
    117			 struct snd_pcm_hw_params *hw_params)
    118{
    119	struct snd_tscm *tscm = substream->private_data;
    120	int err = 0;
    121
    122	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
    123		unsigned int rate = params_rate(hw_params);
    124		unsigned int frames_per_period = params_period_size(hw_params);
    125		unsigned int frames_per_buffer = params_buffer_size(hw_params);
    126
    127		mutex_lock(&tscm->mutex);
    128		err = snd_tscm_stream_reserve_duplex(tscm, rate,
    129					frames_per_period, frames_per_buffer);
    130		if (err >= 0)
    131			++tscm->substreams_counter;
    132		mutex_unlock(&tscm->mutex);
    133	}
    134
    135	return err;
    136}
    137
    138static int pcm_hw_free(struct snd_pcm_substream *substream)
    139{
    140	struct snd_tscm *tscm = substream->private_data;
    141
    142	mutex_lock(&tscm->mutex);
    143
    144	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
    145		--tscm->substreams_counter;
    146
    147	snd_tscm_stream_stop_duplex(tscm);
    148
    149	mutex_unlock(&tscm->mutex);
    150
    151	return 0;
    152}
    153
    154static int pcm_capture_prepare(struct snd_pcm_substream *substream)
    155{
    156	struct snd_tscm *tscm = substream->private_data;
    157	struct snd_pcm_runtime *runtime = substream->runtime;
    158	int err;
    159
    160	mutex_lock(&tscm->mutex);
    161
    162	err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
    163	if (err >= 0)
    164		amdtp_stream_pcm_prepare(&tscm->tx_stream);
    165
    166	mutex_unlock(&tscm->mutex);
    167
    168	return err;
    169}
    170
    171static int pcm_playback_prepare(struct snd_pcm_substream *substream)
    172{
    173	struct snd_tscm *tscm = substream->private_data;
    174	struct snd_pcm_runtime *runtime = substream->runtime;
    175	int err;
    176
    177	mutex_lock(&tscm->mutex);
    178
    179	err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
    180	if (err >= 0)
    181		amdtp_stream_pcm_prepare(&tscm->rx_stream);
    182
    183	mutex_unlock(&tscm->mutex);
    184
    185	return err;
    186}
    187
    188static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
    189{
    190	struct snd_tscm *tscm = substream->private_data;
    191
    192	switch (cmd) {
    193	case SNDRV_PCM_TRIGGER_START:
    194		amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);
    195		break;
    196	case SNDRV_PCM_TRIGGER_STOP:
    197		amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);
    198		break;
    199	default:
    200		return -EINVAL;
    201	}
    202
    203	return 0;
    204}
    205
    206static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
    207{
    208	struct snd_tscm *tscm = substream->private_data;
    209
    210	switch (cmd) {
    211	case SNDRV_PCM_TRIGGER_START:
    212		amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);
    213		break;
    214	case SNDRV_PCM_TRIGGER_STOP:
    215		amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);
    216		break;
    217	default:
    218		return -EINVAL;
    219	}
    220
    221	return 0;
    222}
    223
    224static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
    225{
    226	struct snd_tscm *tscm = sbstrm->private_data;
    227
    228	return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->tx_stream);
    229}
    230
    231static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
    232{
    233	struct snd_tscm *tscm = sbstrm->private_data;
    234
    235	return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->rx_stream);
    236}
    237
    238static int pcm_capture_ack(struct snd_pcm_substream *substream)
    239{
    240	struct snd_tscm *tscm = substream->private_data;
    241
    242	return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->tx_stream);
    243}
    244
    245static int pcm_playback_ack(struct snd_pcm_substream *substream)
    246{
    247	struct snd_tscm *tscm = substream->private_data;
    248
    249	return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->rx_stream);
    250}
    251
    252int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
    253{
    254	static const struct snd_pcm_ops capture_ops = {
    255		.open		= pcm_open,
    256		.close		= pcm_close,
    257		.hw_params	= pcm_hw_params,
    258		.hw_free	= pcm_hw_free,
    259		.prepare	= pcm_capture_prepare,
    260		.trigger	= pcm_capture_trigger,
    261		.pointer	= pcm_capture_pointer,
    262		.ack		= pcm_capture_ack,
    263	};
    264	static const struct snd_pcm_ops playback_ops = {
    265		.open		= pcm_open,
    266		.close		= pcm_close,
    267		.hw_params	= pcm_hw_params,
    268		.hw_free	= pcm_hw_free,
    269		.prepare	= pcm_playback_prepare,
    270		.trigger	= pcm_playback_trigger,
    271		.pointer	= pcm_playback_pointer,
    272		.ack		= pcm_playback_ack,
    273	};
    274	struct snd_pcm *pcm;
    275	int err;
    276
    277	err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);
    278	if (err < 0)
    279		return err;
    280
    281	pcm->private_data = tscm;
    282	snprintf(pcm->name, sizeof(pcm->name),
    283		 "%s PCM", tscm->card->shortname);
    284	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
    285	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
    286	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
    287
    288	return 0;
    289}