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

bebob_pcm.c (9540B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * bebob_pcm.c - a part of driver for BeBoB based devices
      4 *
      5 * Copyright (c) 2013-2014 Takashi Sakamoto
      6 */
      7
      8#include "./bebob.h"
      9
     10static int
     11hw_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 = 1
     20	};
     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 int
     39hw_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 = 1
     48	};
     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 void
     68limit_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 int
     95pcm_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 for
    155	// transmission of PCM frames, the available sampling rate is limited
    156	// 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 int
    204pcm_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->status->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->status->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 int
    250pcm_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 int
    262pcm_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 int
    275pcm_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 int
    293pcm_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	snprintf(pcm->name, sizeof(pcm->name),
    371		 "%s PCM", bebob->card->shortname);
    372	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
    373	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
    374	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
    375end:
    376	return err;
    377}