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

dice-stream.c (17385B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * dice_stream.c - a part of driver for DICE based devices
      4 *
      5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
      6 * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
      7 */
      8
      9#include "dice.h"
     10
     11#define	READY_TIMEOUT_MS	200
     12#define NOTIFICATION_TIMEOUT_MS	100
     13
     14struct reg_params {
     15	unsigned int count;
     16	unsigned int size;
     17};
     18
     19const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
     20	/* mode 0 */
     21	[0] =  32000,
     22	[1] =  44100,
     23	[2] =  48000,
     24	/* mode 1 */
     25	[3] =  88200,
     26	[4] =  96000,
     27	/* mode 2 */
     28	[5] = 176400,
     29	[6] = 192000,
     30};
     31
     32int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
     33				  enum snd_dice_rate_mode *mode)
     34{
     35	/* Corresponding to each entry in snd_dice_rates. */
     36	static const enum snd_dice_rate_mode modes[] = {
     37		[0] = SND_DICE_RATE_MODE_LOW,
     38		[1] = SND_DICE_RATE_MODE_LOW,
     39		[2] = SND_DICE_RATE_MODE_LOW,
     40		[3] = SND_DICE_RATE_MODE_MIDDLE,
     41		[4] = SND_DICE_RATE_MODE_MIDDLE,
     42		[5] = SND_DICE_RATE_MODE_HIGH,
     43		[6] = SND_DICE_RATE_MODE_HIGH,
     44	};
     45	int i;
     46
     47	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
     48		if (!(dice->clock_caps & BIT(i)))
     49			continue;
     50		if (snd_dice_rates[i] != rate)
     51			continue;
     52
     53		*mode = modes[i];
     54		return 0;
     55	}
     56
     57	return -EINVAL;
     58}
     59
     60static int select_clock(struct snd_dice *dice, unsigned int rate)
     61{
     62	__be32 reg;
     63	u32 data;
     64	int i;
     65	int err;
     66
     67	err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
     68					       &reg, sizeof(reg));
     69	if (err < 0)
     70		return err;
     71
     72	data = be32_to_cpu(reg);
     73
     74	data &= ~CLOCK_RATE_MASK;
     75	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
     76		if (snd_dice_rates[i] == rate)
     77			break;
     78	}
     79	if (i == ARRAY_SIZE(snd_dice_rates))
     80		return -EINVAL;
     81	data |= i << CLOCK_RATE_SHIFT;
     82
     83	if (completion_done(&dice->clock_accepted))
     84		reinit_completion(&dice->clock_accepted);
     85
     86	reg = cpu_to_be32(data);
     87	err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
     88						&reg, sizeof(reg));
     89	if (err < 0)
     90		return err;
     91
     92	if (wait_for_completion_timeout(&dice->clock_accepted,
     93			msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0)
     94		return -ETIMEDOUT;
     95
     96	return 0;
     97}
     98
     99static int get_register_params(struct snd_dice *dice,
    100			       struct reg_params *tx_params,
    101			       struct reg_params *rx_params)
    102{
    103	__be32 reg[2];
    104	int err;
    105
    106	err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, sizeof(reg));
    107	if (err < 0)
    108		return err;
    109	tx_params->count =
    110			min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
    111	tx_params->size = be32_to_cpu(reg[1]) * 4;
    112
    113	err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, sizeof(reg));
    114	if (err < 0)
    115		return err;
    116	rx_params->count =
    117			min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
    118	rx_params->size = be32_to_cpu(reg[1]) * 4;
    119
    120	return 0;
    121}
    122
    123static void release_resources(struct snd_dice *dice)
    124{
    125	int i;
    126
    127	for (i = 0; i < MAX_STREAMS; ++i) {
    128		fw_iso_resources_free(&dice->tx_resources[i]);
    129		fw_iso_resources_free(&dice->rx_resources[i]);
    130	}
    131}
    132
    133static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
    134			 struct reg_params *params)
    135{
    136	__be32 reg;
    137	unsigned int i;
    138
    139	for (i = 0; i < params->count; i++) {
    140		reg = cpu_to_be32((u32)-1);
    141		if (dir == AMDTP_IN_STREAM) {
    142			snd_dice_transaction_write_tx(dice,
    143					params->size * i + TX_ISOCHRONOUS,
    144					&reg, sizeof(reg));
    145		} else {
    146			snd_dice_transaction_write_rx(dice,
    147					params->size * i + RX_ISOCHRONOUS,
    148					&reg, sizeof(reg));
    149		}
    150	}
    151}
    152
    153static int keep_resources(struct snd_dice *dice, struct amdtp_stream *stream,
    154			  struct fw_iso_resources *resources, unsigned int rate,
    155			  unsigned int pcm_chs, unsigned int midi_ports)
    156{
    157	bool double_pcm_frames;
    158	unsigned int i;
    159	int err;
    160
    161	// At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
    162	// one data block of AMDTP packet. Thus sampling transfer frequency is
    163	// a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
    164	// transferred on AMDTP packets at 96 kHz. Two successive samples of a
    165	// channel are stored consecutively in the packet. This quirk is called
    166	// as 'Dual Wire'.
    167	// For this quirk, blocking mode is required and PCM buffer size should
    168	// be aligned to SYT_INTERVAL.
    169	double_pcm_frames = (rate > 96000 && !dice->disable_double_pcm_frames);
    170	if (double_pcm_frames) {
    171		rate /= 2;
    172		pcm_chs *= 2;
    173	}
    174
    175	err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports,
    176					 double_pcm_frames);
    177	if (err < 0)
    178		return err;
    179
    180	if (double_pcm_frames) {
    181		pcm_chs /= 2;
    182
    183		for (i = 0; i < pcm_chs; i++) {
    184			amdtp_am824_set_pcm_position(stream, i, i * 2);
    185			amdtp_am824_set_pcm_position(stream, i + pcm_chs,
    186						     i * 2 + 1);
    187		}
    188	}
    189
    190	return fw_iso_resources_allocate(resources,
    191				amdtp_stream_get_max_payload(stream),
    192				fw_parent_device(dice->unit)->max_speed);
    193}
    194
    195static int keep_dual_resources(struct snd_dice *dice, unsigned int rate,
    196			       enum amdtp_stream_direction dir,
    197			       struct reg_params *params)
    198{
    199	enum snd_dice_rate_mode mode;
    200	int i;
    201	int err;
    202
    203	err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
    204	if (err < 0)
    205		return err;
    206
    207	for (i = 0; i < params->count; ++i) {
    208		__be32 reg[2];
    209		struct amdtp_stream *stream;
    210		struct fw_iso_resources *resources;
    211		unsigned int pcm_cache;
    212		unsigned int pcm_chs;
    213		unsigned int midi_ports;
    214
    215		if (dir == AMDTP_IN_STREAM) {
    216			stream = &dice->tx_stream[i];
    217			resources = &dice->tx_resources[i];
    218
    219			pcm_cache = dice->tx_pcm_chs[i][mode];
    220			err = snd_dice_transaction_read_tx(dice,
    221					params->size * i + TX_NUMBER_AUDIO,
    222					reg, sizeof(reg));
    223		} else {
    224			stream = &dice->rx_stream[i];
    225			resources = &dice->rx_resources[i];
    226
    227			pcm_cache = dice->rx_pcm_chs[i][mode];
    228			err = snd_dice_transaction_read_rx(dice,
    229					params->size * i + RX_NUMBER_AUDIO,
    230					reg, sizeof(reg));
    231		}
    232		if (err < 0)
    233			return err;
    234		pcm_chs = be32_to_cpu(reg[0]);
    235		midi_ports = be32_to_cpu(reg[1]);
    236
    237		// These are important for developer of this driver.
    238		if (pcm_chs != pcm_cache) {
    239			dev_info(&dice->unit->device,
    240				 "cache mismatch: pcm: %u:%u, midi: %u\n",
    241				 pcm_chs, pcm_cache, midi_ports);
    242			return -EPROTO;
    243		}
    244
    245		err = keep_resources(dice, stream, resources, rate, pcm_chs,
    246				     midi_ports);
    247		if (err < 0)
    248			return err;
    249	}
    250
    251	return 0;
    252}
    253
    254static void finish_session(struct snd_dice *dice, struct reg_params *tx_params,
    255			   struct reg_params *rx_params)
    256{
    257	stop_streams(dice, AMDTP_IN_STREAM, tx_params);
    258	stop_streams(dice, AMDTP_OUT_STREAM, rx_params);
    259
    260	snd_dice_transaction_clear_enable(dice);
    261}
    262
    263int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate,
    264				   unsigned int events_per_period,
    265				   unsigned int events_per_buffer)
    266{
    267	unsigned int curr_rate;
    268	int err;
    269
    270	// Check sampling transmission frequency.
    271	err = snd_dice_transaction_get_rate(dice, &curr_rate);
    272	if (err < 0)
    273		return err;
    274	if (rate == 0)
    275		rate = curr_rate;
    276
    277	if (dice->substreams_counter == 0 || curr_rate != rate) {
    278		struct reg_params tx_params, rx_params;
    279
    280		amdtp_domain_stop(&dice->domain);
    281
    282		err = get_register_params(dice, &tx_params, &rx_params);
    283		if (err < 0)
    284			return err;
    285		finish_session(dice, &tx_params, &rx_params);
    286
    287		release_resources(dice);
    288
    289		// Just after owning the unit (GLOBAL_OWNER), the unit can
    290		// return invalid stream formats. Selecting clock parameters
    291		// have an effect for the unit to refine it.
    292		err = select_clock(dice, rate);
    293		if (err < 0)
    294			return err;
    295
    296		// After changing sampling transfer frequency, the value of
    297		// register can be changed.
    298		err = get_register_params(dice, &tx_params, &rx_params);
    299		if (err < 0)
    300			return err;
    301
    302		err = keep_dual_resources(dice, rate, AMDTP_IN_STREAM,
    303					  &tx_params);
    304		if (err < 0)
    305			goto error;
    306
    307		err = keep_dual_resources(dice, rate, AMDTP_OUT_STREAM,
    308					  &rx_params);
    309		if (err < 0)
    310			goto error;
    311
    312		err = amdtp_domain_set_events_per_period(&dice->domain,
    313					events_per_period, events_per_buffer);
    314		if (err < 0)
    315			goto error;
    316	}
    317
    318	return 0;
    319error:
    320	release_resources(dice);
    321	return err;
    322}
    323
    324static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
    325			 unsigned int rate, struct reg_params *params)
    326{
    327	unsigned int max_speed = fw_parent_device(dice->unit)->max_speed;
    328	int i;
    329	int err;
    330
    331	for (i = 0; i < params->count; i++) {
    332		struct amdtp_stream *stream;
    333		struct fw_iso_resources *resources;
    334		__be32 reg;
    335
    336		if (dir == AMDTP_IN_STREAM) {
    337			stream = dice->tx_stream + i;
    338			resources = dice->tx_resources + i;
    339		} else {
    340			stream = dice->rx_stream + i;
    341			resources = dice->rx_resources + i;
    342		}
    343
    344		reg = cpu_to_be32(resources->channel);
    345		if (dir == AMDTP_IN_STREAM) {
    346			err = snd_dice_transaction_write_tx(dice,
    347					params->size * i + TX_ISOCHRONOUS,
    348					&reg, sizeof(reg));
    349		} else {
    350			err = snd_dice_transaction_write_rx(dice,
    351					params->size * i + RX_ISOCHRONOUS,
    352					&reg, sizeof(reg));
    353		}
    354		if (err < 0)
    355			return err;
    356
    357		if (dir == AMDTP_IN_STREAM) {
    358			reg = cpu_to_be32(max_speed);
    359			err = snd_dice_transaction_write_tx(dice,
    360					params->size * i + TX_SPEED,
    361					&reg, sizeof(reg));
    362			if (err < 0)
    363				return err;
    364		}
    365
    366		err = amdtp_domain_add_stream(&dice->domain, stream,
    367					      resources->channel, max_speed);
    368		if (err < 0)
    369			return err;
    370	}
    371
    372	return 0;
    373}
    374
    375/*
    376 * MEMO: After this function, there're two states of streams:
    377 *  - None streams are running.
    378 *  - All streams are running.
    379 */
    380int snd_dice_stream_start_duplex(struct snd_dice *dice)
    381{
    382	unsigned int generation = dice->rx_resources[0].generation;
    383	struct reg_params tx_params, rx_params;
    384	unsigned int i;
    385	unsigned int rate;
    386	enum snd_dice_rate_mode mode;
    387	int err;
    388
    389	if (dice->substreams_counter == 0)
    390		return -EIO;
    391
    392	err = get_register_params(dice, &tx_params, &rx_params);
    393	if (err < 0)
    394		return err;
    395
    396	// Check error of packet streaming.
    397	for (i = 0; i < MAX_STREAMS; ++i) {
    398		if (amdtp_streaming_error(&dice->tx_stream[i]) ||
    399		    amdtp_streaming_error(&dice->rx_stream[i])) {
    400			amdtp_domain_stop(&dice->domain);
    401			finish_session(dice, &tx_params, &rx_params);
    402			break;
    403		}
    404	}
    405
    406	if (generation != fw_parent_device(dice->unit)->card->generation) {
    407		for (i = 0; i < MAX_STREAMS; ++i) {
    408			if (i < tx_params.count)
    409				fw_iso_resources_update(dice->tx_resources + i);
    410			if (i < rx_params.count)
    411				fw_iso_resources_update(dice->rx_resources + i);
    412		}
    413	}
    414
    415	// Check required streams are running or not.
    416	err = snd_dice_transaction_get_rate(dice, &rate);
    417	if (err < 0)
    418		return err;
    419	err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
    420	if (err < 0)
    421		return err;
    422	for (i = 0; i < MAX_STREAMS; ++i) {
    423		if (dice->tx_pcm_chs[i][mode] > 0 &&
    424		    !amdtp_stream_running(&dice->tx_stream[i]))
    425			break;
    426		if (dice->rx_pcm_chs[i][mode] > 0 &&
    427		    !amdtp_stream_running(&dice->rx_stream[i]))
    428			break;
    429	}
    430	if (i < MAX_STREAMS) {
    431		// Start both streams.
    432		err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
    433		if (err < 0)
    434			goto error;
    435
    436		err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
    437		if (err < 0)
    438			goto error;
    439
    440		err = snd_dice_transaction_set_enable(dice);
    441		if (err < 0) {
    442			dev_err(&dice->unit->device,
    443				"fail to enable interface\n");
    444			goto error;
    445		}
    446
    447		// MEMO: The device immediately starts packet transmission when enabled. Some
    448		// devices are strictly to generate any discontinuity in the sequence of tx packet
    449		// when they receives invalid sequence of presentation time in CIP header. The
    450		// sequence replay for media clock recovery can suppress the behaviour.
    451		err = amdtp_domain_start(&dice->domain, 0, true, false);
    452		if (err < 0)
    453			goto error;
    454
    455		if (!amdtp_domain_wait_ready(&dice->domain, READY_TIMEOUT_MS)) {
    456			err = -ETIMEDOUT;
    457			goto error;
    458		}
    459	}
    460
    461	return 0;
    462error:
    463	amdtp_domain_stop(&dice->domain);
    464	finish_session(dice, &tx_params, &rx_params);
    465	return err;
    466}
    467
    468/*
    469 * MEMO: After this function, there're two states of streams:
    470 *  - None streams are running.
    471 *  - All streams are running.
    472 */
    473void snd_dice_stream_stop_duplex(struct snd_dice *dice)
    474{
    475	struct reg_params tx_params, rx_params;
    476
    477	if (dice->substreams_counter == 0) {
    478		if (get_register_params(dice, &tx_params, &rx_params) >= 0)
    479			finish_session(dice, &tx_params, &rx_params);
    480
    481		amdtp_domain_stop(&dice->domain);
    482		release_resources(dice);
    483	}
    484}
    485
    486static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir,
    487		       unsigned int index)
    488{
    489	struct amdtp_stream *stream;
    490	struct fw_iso_resources *resources;
    491	int err;
    492
    493	if (dir == AMDTP_IN_STREAM) {
    494		stream = &dice->tx_stream[index];
    495		resources = &dice->tx_resources[index];
    496	} else {
    497		stream = &dice->rx_stream[index];
    498		resources = &dice->rx_resources[index];
    499	}
    500
    501	err = fw_iso_resources_init(resources, dice->unit);
    502	if (err < 0)
    503		goto end;
    504	resources->channels_mask = 0x00000000ffffffffuLL;
    505
    506	err = amdtp_am824_init(stream, dice->unit, dir, CIP_BLOCKING);
    507	if (err < 0) {
    508		amdtp_stream_destroy(stream);
    509		fw_iso_resources_destroy(resources);
    510	}
    511end:
    512	return err;
    513}
    514
    515/*
    516 * This function should be called before starting streams or after stopping
    517 * streams.
    518 */
    519static void destroy_stream(struct snd_dice *dice,
    520			   enum amdtp_stream_direction dir,
    521			   unsigned int index)
    522{
    523	struct amdtp_stream *stream;
    524	struct fw_iso_resources *resources;
    525
    526	if (dir == AMDTP_IN_STREAM) {
    527		stream = &dice->tx_stream[index];
    528		resources = &dice->tx_resources[index];
    529	} else {
    530		stream = &dice->rx_stream[index];
    531		resources = &dice->rx_resources[index];
    532	}
    533
    534	amdtp_stream_destroy(stream);
    535	fw_iso_resources_destroy(resources);
    536}
    537
    538int snd_dice_stream_init_duplex(struct snd_dice *dice)
    539{
    540	int i, err;
    541
    542	for (i = 0; i < MAX_STREAMS; i++) {
    543		err = init_stream(dice, AMDTP_IN_STREAM, i);
    544		if (err < 0) {
    545			for (; i >= 0; i--)
    546				destroy_stream(dice, AMDTP_IN_STREAM, i);
    547			goto end;
    548		}
    549	}
    550
    551	for (i = 0; i < MAX_STREAMS; i++) {
    552		err = init_stream(dice, AMDTP_OUT_STREAM, i);
    553		if (err < 0) {
    554			for (; i >= 0; i--)
    555				destroy_stream(dice, AMDTP_OUT_STREAM, i);
    556			for (i = 0; i < MAX_STREAMS; i++)
    557				destroy_stream(dice, AMDTP_IN_STREAM, i);
    558			goto end;
    559		}
    560	}
    561
    562	err = amdtp_domain_init(&dice->domain);
    563	if (err < 0) {
    564		for (i = 0; i < MAX_STREAMS; ++i) {
    565			destroy_stream(dice, AMDTP_OUT_STREAM, i);
    566			destroy_stream(dice, AMDTP_IN_STREAM, i);
    567		}
    568	}
    569end:
    570	return err;
    571}
    572
    573void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
    574{
    575	unsigned int i;
    576
    577	for (i = 0; i < MAX_STREAMS; i++) {
    578		destroy_stream(dice, AMDTP_IN_STREAM, i);
    579		destroy_stream(dice, AMDTP_OUT_STREAM, i);
    580	}
    581
    582	amdtp_domain_destroy(&dice->domain);
    583}
    584
    585void snd_dice_stream_update_duplex(struct snd_dice *dice)
    586{
    587	struct reg_params tx_params, rx_params;
    588
    589	/*
    590	 * On a bus reset, the DICE firmware disables streaming and then goes
    591	 * off contemplating its own navel for hundreds of milliseconds before
    592	 * it can react to any of our attempts to reenable streaming.  This
    593	 * means that we lose synchronization anyway, so we force our streams
    594	 * to stop so that the application can restart them in an orderly
    595	 * manner.
    596	 */
    597	dice->global_enabled = false;
    598
    599	if (get_register_params(dice, &tx_params, &rx_params) == 0) {
    600		amdtp_domain_stop(&dice->domain);
    601
    602		stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
    603		stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
    604	}
    605}
    606
    607int snd_dice_stream_detect_current_formats(struct snd_dice *dice)
    608{
    609	unsigned int rate;
    610	enum snd_dice_rate_mode mode;
    611	__be32 reg[2];
    612	struct reg_params tx_params, rx_params;
    613	int i;
    614	int err;
    615
    616	/* If extended protocol is available, detect detail spec. */
    617	err = snd_dice_detect_extension_formats(dice);
    618	if (err >= 0)
    619		return err;
    620
    621	/*
    622	 * Available stream format is restricted at current mode of sampling
    623	 * clock.
    624	 */
    625	err = snd_dice_transaction_get_rate(dice, &rate);
    626	if (err < 0)
    627		return err;
    628
    629	err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
    630	if (err < 0)
    631		return err;
    632
    633	/*
    634	 * Just after owning the unit (GLOBAL_OWNER), the unit can return
    635	 * invalid stream formats. Selecting clock parameters have an effect
    636	 * for the unit to refine it.
    637	 */
    638	err = select_clock(dice, rate);
    639	if (err < 0)
    640		return err;
    641
    642	err = get_register_params(dice, &tx_params, &rx_params);
    643	if (err < 0)
    644		return err;
    645
    646	for (i = 0; i < tx_params.count; ++i) {
    647		err = snd_dice_transaction_read_tx(dice,
    648				tx_params.size * i + TX_NUMBER_AUDIO,
    649				reg, sizeof(reg));
    650		if (err < 0)
    651			return err;
    652		dice->tx_pcm_chs[i][mode] = be32_to_cpu(reg[0]);
    653		dice->tx_midi_ports[i] = max_t(unsigned int,
    654				be32_to_cpu(reg[1]), dice->tx_midi_ports[i]);
    655	}
    656	for (i = 0; i < rx_params.count; ++i) {
    657		err = snd_dice_transaction_read_rx(dice,
    658				rx_params.size * i + RX_NUMBER_AUDIO,
    659				reg, sizeof(reg));
    660		if (err < 0)
    661			return err;
    662		dice->rx_pcm_chs[i][mode] = be32_to_cpu(reg[0]);
    663		dice->rx_midi_ports[i] = max_t(unsigned int,
    664				be32_to_cpu(reg[1]), dice->rx_midi_ports[i]);
    665	}
    666
    667	return 0;
    668}
    669
    670static void dice_lock_changed(struct snd_dice *dice)
    671{
    672	dice->dev_lock_changed = true;
    673	wake_up(&dice->hwdep_wait);
    674}
    675
    676int snd_dice_stream_lock_try(struct snd_dice *dice)
    677{
    678	int err;
    679
    680	spin_lock_irq(&dice->lock);
    681
    682	if (dice->dev_lock_count < 0) {
    683		err = -EBUSY;
    684		goto out;
    685	}
    686
    687	if (dice->dev_lock_count++ == 0)
    688		dice_lock_changed(dice);
    689	err = 0;
    690out:
    691	spin_unlock_irq(&dice->lock);
    692	return err;
    693}
    694
    695void snd_dice_stream_lock_release(struct snd_dice *dice)
    696{
    697	spin_lock_irq(&dice->lock);
    698
    699	if (WARN_ON(dice->dev_lock_count <= 0))
    700		goto out;
    701
    702	if (--dice->dev_lock_count == 0)
    703		dice_lock_changed(dice);
    704out:
    705	spin_unlock_irq(&dice->lock);
    706}