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

oxfw-pcm.c (11373B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * oxfw_pcm.c - a part of driver for OXFW970/971 based devices
      4 *
      5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
      6 */
      7
      8#include "oxfw.h"
      9
     10static int hw_rule_rate(struct snd_pcm_hw_params *params,
     11			struct snd_pcm_hw_rule *rule)
     12{
     13	u8 **formats = 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	struct snd_oxfw_stream_formation formation;
     22	int i, err;
     23
     24	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
     25		if (formats[i] == NULL)
     26			continue;
     27
     28		err = snd_oxfw_stream_parse_format(formats[i], &formation);
     29		if (err < 0)
     30			continue;
     31		if (!snd_interval_test(c, formation.pcm))
     32			continue;
     33
     34		t.min = min(t.min, formation.rate);
     35		t.max = max(t.max, formation.rate);
     36
     37	}
     38	return snd_interval_refine(r, &t);
     39}
     40
     41static int hw_rule_channels(struct snd_pcm_hw_params *params,
     42			    struct snd_pcm_hw_rule *rule)
     43{
     44	u8 **formats = rule->private;
     45	struct snd_interval *c =
     46		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
     47	const struct snd_interval *r =
     48		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
     49	struct snd_oxfw_stream_formation formation;
     50	int i, j, err;
     51	unsigned int count, list[SND_OXFW_STREAM_FORMAT_ENTRIES] = {0};
     52
     53	count = 0;
     54	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
     55		if (formats[i] == NULL)
     56			break;
     57
     58		err = snd_oxfw_stream_parse_format(formats[i], &formation);
     59		if (err < 0)
     60			continue;
     61		if (!snd_interval_test(r, formation.rate))
     62			continue;
     63		if (list[count] == formation.pcm)
     64			continue;
     65
     66		for (j = 0; j < ARRAY_SIZE(list); j++) {
     67			if (list[j] == formation.pcm)
     68				break;
     69		}
     70		if (j == ARRAY_SIZE(list)) {
     71			list[count] = formation.pcm;
     72			if (++count == ARRAY_SIZE(list))
     73				break;
     74		}
     75	}
     76
     77	return snd_interval_list(c, count, list, 0);
     78}
     79
     80static void limit_channels_and_rates(struct snd_pcm_hardware *hw, u8 **formats)
     81{
     82	struct snd_oxfw_stream_formation formation;
     83	int i, err;
     84
     85	hw->channels_min = UINT_MAX;
     86	hw->channels_max = 0;
     87
     88	hw->rate_min = UINT_MAX;
     89	hw->rate_max = 0;
     90	hw->rates = 0;
     91
     92	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
     93		if (formats[i] == NULL)
     94			break;
     95
     96		err = snd_oxfw_stream_parse_format(formats[i], &formation);
     97		if (err < 0)
     98			continue;
     99
    100		hw->channels_min = min(hw->channels_min, formation.pcm);
    101		hw->channels_max = max(hw->channels_max, formation.pcm);
    102
    103		hw->rate_min = min(hw->rate_min, formation.rate);
    104		hw->rate_max = max(hw->rate_max, formation.rate);
    105		hw->rates |= snd_pcm_rate_to_rate_bit(formation.rate);
    106	}
    107}
    108
    109static int init_hw_params(struct snd_oxfw *oxfw,
    110			  struct snd_pcm_substream *substream)
    111{
    112	struct snd_pcm_runtime *runtime = substream->runtime;
    113	u8 **formats;
    114	struct amdtp_stream *stream;
    115	int err;
    116
    117	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
    118		runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
    119		stream = &oxfw->tx_stream;
    120		formats = oxfw->tx_stream_formats;
    121	} else {
    122		runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
    123		stream = &oxfw->rx_stream;
    124		formats = oxfw->rx_stream_formats;
    125	}
    126
    127	limit_channels_and_rates(&runtime->hw, formats);
    128
    129	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
    130				  hw_rule_channels, formats,
    131				  SNDRV_PCM_HW_PARAM_RATE, -1);
    132	if (err < 0)
    133		goto end;
    134
    135	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
    136				  hw_rule_rate, formats,
    137				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
    138	if (err < 0)
    139		goto end;
    140
    141	err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
    142end:
    143	return err;
    144}
    145
    146static int limit_to_current_params(struct snd_pcm_substream *substream)
    147{
    148	struct snd_oxfw *oxfw = substream->private_data;
    149	struct snd_oxfw_stream_formation formation;
    150	enum avc_general_plug_dir dir;
    151	int err;
    152
    153	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
    154		dir = AVC_GENERAL_PLUG_DIR_OUT;
    155	else
    156		dir = AVC_GENERAL_PLUG_DIR_IN;
    157
    158	err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
    159	if (err < 0)
    160		goto end;
    161
    162	substream->runtime->hw.channels_min = formation.pcm;
    163	substream->runtime->hw.channels_max = formation.pcm;
    164	substream->runtime->hw.rate_min = formation.rate;
    165	substream->runtime->hw.rate_max = formation.rate;
    166end:
    167	return err;
    168}
    169
    170static int pcm_open(struct snd_pcm_substream *substream)
    171{
    172	struct snd_oxfw *oxfw = substream->private_data;
    173	struct amdtp_domain *d = &oxfw->domain;
    174	int err;
    175
    176	err = snd_oxfw_stream_lock_try(oxfw);
    177	if (err < 0)
    178		return err;
    179
    180	err = init_hw_params(oxfw, substream);
    181	if (err < 0)
    182		goto err_locked;
    183
    184	mutex_lock(&oxfw->mutex);
    185
    186	// When source of clock is not internal or any stream is reserved for
    187	// transmission of PCM frames, the available sampling rate is limited
    188	// at current one.
    189	if (oxfw->substreams_count > 0 && d->events_per_period > 0) {
    190		unsigned int frames_per_period = d->events_per_period;
    191		unsigned int frames_per_buffer = d->events_per_buffer;
    192
    193		err = limit_to_current_params(substream);
    194		if (err < 0) {
    195			mutex_unlock(&oxfw->mutex);
    196			goto err_locked;
    197		}
    198
    199		if (frames_per_period > 0) {
    200			err = snd_pcm_hw_constraint_minmax(substream->runtime,
    201					SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
    202					frames_per_period, frames_per_period);
    203			if (err < 0) {
    204				mutex_unlock(&oxfw->mutex);
    205				goto err_locked;
    206			}
    207
    208			err = snd_pcm_hw_constraint_minmax(substream->runtime,
    209					SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
    210					frames_per_buffer, frames_per_buffer);
    211			if (err < 0) {
    212				mutex_unlock(&oxfw->mutex);
    213				goto err_locked;
    214			}
    215		}
    216	}
    217
    218	mutex_unlock(&oxfw->mutex);
    219
    220	snd_pcm_set_sync(substream);
    221
    222	return 0;
    223err_locked:
    224	snd_oxfw_stream_lock_release(oxfw);
    225	return err;
    226}
    227
    228static int pcm_close(struct snd_pcm_substream *substream)
    229{
    230	struct snd_oxfw *oxfw = substream->private_data;
    231
    232	snd_oxfw_stream_lock_release(oxfw);
    233	return 0;
    234}
    235
    236static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
    237				 struct snd_pcm_hw_params *hw_params)
    238{
    239	struct snd_oxfw *oxfw = substream->private_data;
    240	int err = 0;
    241
    242	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
    243		unsigned int rate = params_rate(hw_params);
    244		unsigned int channels = params_channels(hw_params);
    245		unsigned int frames_per_period = params_period_size(hw_params);
    246		unsigned int frames_per_buffer = params_buffer_size(hw_params);
    247
    248		mutex_lock(&oxfw->mutex);
    249		err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream,
    250					rate, channels, frames_per_period,
    251					frames_per_buffer);
    252		if (err >= 0)
    253			++oxfw->substreams_count;
    254		mutex_unlock(&oxfw->mutex);
    255	}
    256
    257	return err;
    258}
    259static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
    260				  struct snd_pcm_hw_params *hw_params)
    261{
    262	struct snd_oxfw *oxfw = substream->private_data;
    263	int err = 0;
    264
    265	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
    266		unsigned int rate = params_rate(hw_params);
    267		unsigned int channels = params_channels(hw_params);
    268		unsigned int frames_per_period = params_period_size(hw_params);
    269		unsigned int frames_per_buffer = params_buffer_size(hw_params);
    270
    271		mutex_lock(&oxfw->mutex);
    272		err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream,
    273					rate, channels, frames_per_period,
    274					frames_per_buffer);
    275		if (err >= 0)
    276			++oxfw->substreams_count;
    277		mutex_unlock(&oxfw->mutex);
    278	}
    279
    280	return err;
    281}
    282
    283static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
    284{
    285	struct snd_oxfw *oxfw = substream->private_data;
    286
    287	mutex_lock(&oxfw->mutex);
    288
    289	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
    290		--oxfw->substreams_count;
    291
    292	snd_oxfw_stream_stop_duplex(oxfw);
    293
    294	mutex_unlock(&oxfw->mutex);
    295
    296	return 0;
    297}
    298static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
    299{
    300	struct snd_oxfw *oxfw = substream->private_data;
    301
    302	mutex_lock(&oxfw->mutex);
    303
    304	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
    305		--oxfw->substreams_count;
    306
    307	snd_oxfw_stream_stop_duplex(oxfw);
    308
    309	mutex_unlock(&oxfw->mutex);
    310
    311	return 0;
    312}
    313
    314static int pcm_capture_prepare(struct snd_pcm_substream *substream)
    315{
    316	struct snd_oxfw *oxfw = substream->private_data;
    317	int err;
    318
    319	mutex_lock(&oxfw->mutex);
    320	err = snd_oxfw_stream_start_duplex(oxfw);
    321	mutex_unlock(&oxfw->mutex);
    322	if (err < 0)
    323		goto end;
    324
    325	amdtp_stream_pcm_prepare(&oxfw->tx_stream);
    326end:
    327	return err;
    328}
    329static int pcm_playback_prepare(struct snd_pcm_substream *substream)
    330{
    331	struct snd_oxfw *oxfw = substream->private_data;
    332	int err;
    333
    334	mutex_lock(&oxfw->mutex);
    335	err = snd_oxfw_stream_start_duplex(oxfw);
    336	mutex_unlock(&oxfw->mutex);
    337	if (err < 0)
    338		goto end;
    339
    340	amdtp_stream_pcm_prepare(&oxfw->rx_stream);
    341end:
    342	return err;
    343}
    344
    345static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
    346{
    347	struct snd_oxfw *oxfw = substream->private_data;
    348	struct snd_pcm_substream *pcm;
    349
    350	switch (cmd) {
    351	case SNDRV_PCM_TRIGGER_START:
    352		pcm = substream;
    353		break;
    354	case SNDRV_PCM_TRIGGER_STOP:
    355		pcm = NULL;
    356		break;
    357	default:
    358		return -EINVAL;
    359	}
    360	amdtp_stream_pcm_trigger(&oxfw->tx_stream, pcm);
    361	return 0;
    362}
    363static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
    364{
    365	struct snd_oxfw *oxfw = substream->private_data;
    366	struct snd_pcm_substream *pcm;
    367
    368	switch (cmd) {
    369	case SNDRV_PCM_TRIGGER_START:
    370		pcm = substream;
    371		break;
    372	case SNDRV_PCM_TRIGGER_STOP:
    373		pcm = NULL;
    374		break;
    375	default:
    376		return -EINVAL;
    377	}
    378	amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm);
    379	return 0;
    380}
    381
    382static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm)
    383{
    384	struct snd_oxfw *oxfw = sbstm->private_data;
    385
    386	return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->tx_stream);
    387}
    388static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm)
    389{
    390	struct snd_oxfw *oxfw = sbstm->private_data;
    391
    392	return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->rx_stream);
    393}
    394
    395static int pcm_capture_ack(struct snd_pcm_substream *substream)
    396{
    397	struct snd_oxfw *oxfw = substream->private_data;
    398
    399	return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->tx_stream);
    400}
    401
    402static int pcm_playback_ack(struct snd_pcm_substream *substream)
    403{
    404	struct snd_oxfw *oxfw = substream->private_data;
    405
    406	return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->rx_stream);
    407}
    408
    409int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
    410{
    411	static const struct snd_pcm_ops capture_ops = {
    412		.open      = pcm_open,
    413		.close     = pcm_close,
    414		.hw_params = pcm_capture_hw_params,
    415		.hw_free   = pcm_capture_hw_free,
    416		.prepare   = pcm_capture_prepare,
    417		.trigger   = pcm_capture_trigger,
    418		.pointer   = pcm_capture_pointer,
    419		.ack       = pcm_capture_ack,
    420	};
    421	static const struct snd_pcm_ops playback_ops = {
    422		.open      = pcm_open,
    423		.close     = pcm_close,
    424		.hw_params = pcm_playback_hw_params,
    425		.hw_free   = pcm_playback_hw_free,
    426		.prepare   = pcm_playback_prepare,
    427		.trigger   = pcm_playback_trigger,
    428		.pointer   = pcm_playback_pointer,
    429		.ack       = pcm_playback_ack,
    430	};
    431	struct snd_pcm *pcm;
    432	unsigned int cap = 0;
    433	int err;
    434
    435	if (oxfw->has_output)
    436		cap = 1;
    437
    438	err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, cap, &pcm);
    439	if (err < 0)
    440		return err;
    441
    442	pcm->private_data = oxfw;
    443	strcpy(pcm->name, oxfw->card->shortname);
    444	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
    445	if (cap > 0)
    446		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
    447	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
    448
    449	return 0;
    450}