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

digi00x-stream.c (10722B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family
      4 *
      5 * Copyright (c) 2014-2015 Takashi Sakamoto
      6 */
      7
      8#include "digi00x.h"
      9
     10#define READY_TIMEOUT_MS	200
     11
     12const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {
     13	[SND_DG00X_RATE_44100] = 44100,
     14	[SND_DG00X_RATE_48000] = 48000,
     15	[SND_DG00X_RATE_88200] = 88200,
     16	[SND_DG00X_RATE_96000] = 96000,
     17};
     18
     19/* Multi Bit Linear Audio data channels for each sampling transfer frequency. */
     20const unsigned int
     21snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = {
     22	/* Analog/ADAT/SPDIF */
     23	[SND_DG00X_RATE_44100] = (8 + 8 + 2),
     24	[SND_DG00X_RATE_48000] = (8 + 8 + 2),
     25	/* Analog/SPDIF */
     26	[SND_DG00X_RATE_88200] = (8 + 2),
     27	[SND_DG00X_RATE_96000] = (8 + 2),
     28};
     29
     30int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate)
     31{
     32	u32 data;
     33	__be32 reg;
     34	int err;
     35
     36	err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
     37				 DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
     38				 &reg, sizeof(reg), 0);
     39	if (err < 0)
     40		return err;
     41
     42	data = be32_to_cpu(reg) & 0x0f;
     43	if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
     44		*rate = snd_dg00x_stream_rates[data];
     45	else
     46		err = -EIO;
     47
     48	return err;
     49}
     50
     51int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate)
     52{
     53	__be32 reg;
     54	unsigned int i;
     55
     56	for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) {
     57		if (rate == snd_dg00x_stream_rates[i])
     58			break;
     59	}
     60	if (i == ARRAY_SIZE(snd_dg00x_stream_rates))
     61		return -EINVAL;
     62
     63	reg = cpu_to_be32(i);
     64	return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
     65				  DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
     66				  &reg, sizeof(reg), 0);
     67}
     68
     69int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
     70			       enum snd_dg00x_clock *clock)
     71{
     72	__be32 reg;
     73	int err;
     74
     75	err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
     76				 DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE,
     77				 &reg, sizeof(reg), 0);
     78	if (err < 0)
     79		return err;
     80
     81	*clock = be32_to_cpu(reg) & 0x0f;
     82	if (*clock >= SND_DG00X_CLOCK_COUNT)
     83		err = -EIO;
     84
     85	return err;
     86}
     87
     88int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect)
     89{
     90	__be32 reg;
     91	int err;
     92
     93	err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
     94				 DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL,
     95				 &reg, sizeof(reg), 0);
     96	if (err >= 0)
     97		*detect = be32_to_cpu(reg) > 0;
     98
     99	return err;
    100}
    101
    102int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
    103				       unsigned int *rate)
    104{
    105	u32 data;
    106	__be32 reg;
    107	int err;
    108
    109	err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
    110				 DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE,
    111				 &reg, sizeof(reg), 0);
    112	if (err < 0)
    113		return err;
    114
    115	data = be32_to_cpu(reg) & 0x0f;
    116	if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
    117		*rate = snd_dg00x_stream_rates[data];
    118	/* This means desync. */
    119	else
    120		err = -EBUSY;
    121
    122	return err;
    123}
    124
    125static void finish_session(struct snd_dg00x *dg00x)
    126{
    127	__be32 data;
    128
    129	data = cpu_to_be32(0x00000003);
    130	snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
    131			   DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
    132			   &data, sizeof(data), 0);
    133
    134	// Unregister isochronous channels for both direction.
    135	data = 0;
    136	snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
    137			   DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
    138			   &data, sizeof(data), 0);
    139
    140	// Just after finishing the session, the device may lost transmitting
    141	// functionality for a short time.
    142	msleep(50);
    143}
    144
    145static int begin_session(struct snd_dg00x *dg00x)
    146{
    147	__be32 data;
    148	u32 curr;
    149	int err;
    150
    151	// Register isochronous channels for both direction.
    152	data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
    153			   dg00x->rx_resources.channel);
    154	err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
    155				 DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
    156				 &data, sizeof(data), 0);
    157	if (err < 0)
    158		return err;
    159
    160	err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
    161				 DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,
    162				 &data, sizeof(data), 0);
    163	if (err < 0)
    164		return err;
    165	curr = be32_to_cpu(data);
    166
    167	if (curr == 0)
    168		curr = 2;
    169
    170	curr--;
    171	while (curr > 0) {
    172		data = cpu_to_be32(curr);
    173		err = snd_fw_transaction(dg00x->unit,
    174					 TCODE_WRITE_QUADLET_REQUEST,
    175					 DG00X_ADDR_BASE +
    176					 DG00X_OFFSET_STREAMING_SET,
    177					 &data, sizeof(data), 0);
    178		if (err < 0)
    179			break;
    180
    181		msleep(20);
    182		curr--;
    183	}
    184
    185	return err;
    186}
    187
    188static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream,
    189			  unsigned int rate)
    190{
    191	struct fw_iso_resources *resources;
    192	int i;
    193	int err;
    194
    195	// Check sampling rate.
    196	for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
    197		if (snd_dg00x_stream_rates[i] == rate)
    198			break;
    199	}
    200	if (i == SND_DG00X_RATE_COUNT)
    201		return -EINVAL;
    202
    203	if (stream == &dg00x->tx_stream)
    204		resources = &dg00x->tx_resources;
    205	else
    206		resources = &dg00x->rx_resources;
    207
    208	err = amdtp_dot_set_parameters(stream, rate,
    209				       snd_dg00x_stream_pcm_channels[i]);
    210	if (err < 0)
    211		return err;
    212
    213	return fw_iso_resources_allocate(resources,
    214				amdtp_stream_get_max_payload(stream),
    215				fw_parent_device(dg00x->unit)->max_speed);
    216}
    217
    218static int init_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
    219{
    220	struct fw_iso_resources *resources;
    221	enum amdtp_stream_direction dir;
    222	int err;
    223
    224	if (s == &dg00x->tx_stream) {
    225		resources = &dg00x->tx_resources;
    226		dir = AMDTP_IN_STREAM;
    227	} else {
    228		resources = &dg00x->rx_resources;
    229		dir = AMDTP_OUT_STREAM;
    230	}
    231
    232	err = fw_iso_resources_init(resources, dg00x->unit);
    233	if (err < 0)
    234		return err;
    235
    236	err = amdtp_dot_init(s, dg00x->unit, dir);
    237	if (err < 0)
    238		fw_iso_resources_destroy(resources);
    239
    240	return err;
    241}
    242
    243static void destroy_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
    244{
    245	amdtp_stream_destroy(s);
    246
    247	if (s == &dg00x->tx_stream)
    248		fw_iso_resources_destroy(&dg00x->tx_resources);
    249	else
    250		fw_iso_resources_destroy(&dg00x->rx_resources);
    251}
    252
    253int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
    254{
    255	int err;
    256
    257	err = init_stream(dg00x, &dg00x->rx_stream);
    258	if (err < 0)
    259		return err;
    260
    261	err = init_stream(dg00x, &dg00x->tx_stream);
    262	if (err < 0)
    263		destroy_stream(dg00x, &dg00x->rx_stream);
    264
    265	err = amdtp_domain_init(&dg00x->domain);
    266	if (err < 0) {
    267		destroy_stream(dg00x, &dg00x->rx_stream);
    268		destroy_stream(dg00x, &dg00x->tx_stream);
    269	}
    270
    271	return err;
    272}
    273
    274/*
    275 * This function should be called before starting streams or after stopping
    276 * streams.
    277 */
    278void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
    279{
    280	amdtp_domain_destroy(&dg00x->domain);
    281
    282	destroy_stream(dg00x, &dg00x->rx_stream);
    283	destroy_stream(dg00x, &dg00x->tx_stream);
    284}
    285
    286int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate,
    287				    unsigned int frames_per_period,
    288				    unsigned int frames_per_buffer)
    289{
    290	unsigned int curr_rate;
    291	int err;
    292
    293	err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
    294	if (err < 0)
    295		return err;
    296	if (rate == 0)
    297		rate = curr_rate;
    298
    299	if (dg00x->substreams_counter == 0 || curr_rate != rate) {
    300		amdtp_domain_stop(&dg00x->domain);
    301
    302		finish_session(dg00x);
    303
    304		fw_iso_resources_free(&dg00x->tx_resources);
    305		fw_iso_resources_free(&dg00x->rx_resources);
    306
    307		err = snd_dg00x_stream_set_local_rate(dg00x, rate);
    308		if (err < 0)
    309			return err;
    310
    311		err = keep_resources(dg00x, &dg00x->rx_stream, rate);
    312		if (err < 0)
    313			return err;
    314
    315		err = keep_resources(dg00x, &dg00x->tx_stream, rate);
    316		if (err < 0) {
    317			fw_iso_resources_free(&dg00x->rx_resources);
    318			return err;
    319		}
    320
    321		err = amdtp_domain_set_events_per_period(&dg00x->domain,
    322					frames_per_period, frames_per_buffer);
    323		if (err < 0) {
    324			fw_iso_resources_free(&dg00x->rx_resources);
    325			fw_iso_resources_free(&dg00x->tx_resources);
    326			return err;
    327		}
    328	}
    329
    330	return 0;
    331}
    332
    333int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
    334{
    335	unsigned int generation = dg00x->rx_resources.generation;
    336	int err = 0;
    337
    338	if (dg00x->substreams_counter == 0)
    339		return 0;
    340
    341	if (amdtp_streaming_error(&dg00x->tx_stream) ||
    342	    amdtp_streaming_error(&dg00x->rx_stream)) {
    343		amdtp_domain_stop(&dg00x->domain);
    344		finish_session(dg00x);
    345	}
    346
    347	if (generation != fw_parent_device(dg00x->unit)->card->generation) {
    348		err = fw_iso_resources_update(&dg00x->tx_resources);
    349		if (err < 0)
    350			goto error;
    351
    352		err = fw_iso_resources_update(&dg00x->rx_resources);
    353		if (err < 0)
    354			goto error;
    355	}
    356
    357	/*
    358	 * No packets are transmitted without receiving packets, reagardless of
    359	 * which source of clock is used.
    360	 */
    361	if (!amdtp_stream_running(&dg00x->rx_stream)) {
    362		int spd = fw_parent_device(dg00x->unit)->max_speed;
    363
    364		err = begin_session(dg00x);
    365		if (err < 0)
    366			goto error;
    367
    368		err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream,
    369					      dg00x->rx_resources.channel, spd);
    370		if (err < 0)
    371			goto error;
    372
    373		err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream,
    374					      dg00x->tx_resources.channel, spd);
    375		if (err < 0)
    376			goto error;
    377
    378		// NOTE: The device doesn't start packet transmission till receiving any packet.
    379		// It ignores presentation time expressed by the value of syt field of CIP header
    380		// in received packets. The sequence of the number of data blocks per packet is
    381		// important for media clock recovery.
    382		err = amdtp_domain_start(&dg00x->domain, 0, true, true);
    383		if (err < 0)
    384			goto error;
    385
    386		if (!amdtp_domain_wait_ready(&dg00x->domain, READY_TIMEOUT_MS)) {
    387			err = -ETIMEDOUT;
    388			goto error;
    389		}
    390	}
    391
    392	return 0;
    393error:
    394	amdtp_domain_stop(&dg00x->domain);
    395	finish_session(dg00x);
    396
    397	return err;
    398}
    399
    400void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
    401{
    402	if (dg00x->substreams_counter == 0) {
    403		amdtp_domain_stop(&dg00x->domain);
    404		finish_session(dg00x);
    405
    406		fw_iso_resources_free(&dg00x->tx_resources);
    407		fw_iso_resources_free(&dg00x->rx_resources);
    408	}
    409}
    410
    411void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
    412{
    413	fw_iso_resources_update(&dg00x->tx_resources);
    414	fw_iso_resources_update(&dg00x->rx_resources);
    415
    416	amdtp_stream_update(&dg00x->tx_stream);
    417	amdtp_stream_update(&dg00x->rx_stream);
    418}
    419
    420void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)
    421{
    422	dg00x->dev_lock_changed = true;
    423	wake_up(&dg00x->hwdep_wait);
    424}
    425
    426int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)
    427{
    428	int err;
    429
    430	spin_lock_irq(&dg00x->lock);
    431
    432	/* user land lock this */
    433	if (dg00x->dev_lock_count < 0) {
    434		err = -EBUSY;
    435		goto end;
    436	}
    437
    438	/* this is the first time */
    439	if (dg00x->dev_lock_count++ == 0)
    440		snd_dg00x_stream_lock_changed(dg00x);
    441	err = 0;
    442end:
    443	spin_unlock_irq(&dg00x->lock);
    444	return err;
    445}
    446
    447void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)
    448{
    449	spin_lock_irq(&dg00x->lock);
    450
    451	if (WARN_ON(dg00x->dev_lock_count <= 0))
    452		goto end;
    453	if (--dg00x->dev_lock_count == 0)
    454		snd_dg00x_stream_lock_changed(dg00x);
    455end:
    456	spin_unlock_irq(&dg00x->lock);
    457}