1da607e19SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 26eb6c81eSTakashi Sakamoto /* 36eb6c81eSTakashi Sakamoto * dice_stream.c - a part of driver for DICE based devices 46eb6c81eSTakashi Sakamoto * 56eb6c81eSTakashi Sakamoto * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 66eb6c81eSTakashi Sakamoto * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp> 76eb6c81eSTakashi Sakamoto */ 86eb6c81eSTakashi Sakamoto 96eb6c81eSTakashi Sakamoto #include "dice.h" 106eb6c81eSTakashi Sakamoto 11bdaedca7STakashi Sakamoto #define READY_TIMEOUT_MS 200 12dfabc0eeSTakashi Sakamoto #define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC) 13288a8d0cSTakashi Sakamoto 148cc1a8abSTakashi Sakamoto struct reg_params { 158cc1a8abSTakashi Sakamoto unsigned int count; 168cc1a8abSTakashi Sakamoto unsigned int size; 178cc1a8abSTakashi Sakamoto }; 188cc1a8abSTakashi Sakamoto 196eb6c81eSTakashi Sakamoto const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { 206eb6c81eSTakashi Sakamoto /* mode 0 */ 216eb6c81eSTakashi Sakamoto [0] = 32000, 226eb6c81eSTakashi Sakamoto [1] = 44100, 236eb6c81eSTakashi Sakamoto [2] = 48000, 246eb6c81eSTakashi Sakamoto /* mode 1 */ 256eb6c81eSTakashi Sakamoto [3] = 88200, 266eb6c81eSTakashi Sakamoto [4] = 96000, 276eb6c81eSTakashi Sakamoto /* mode 2 */ 286eb6c81eSTakashi Sakamoto [5] = 176400, 296eb6c81eSTakashi Sakamoto [6] = 192000, 306eb6c81eSTakashi Sakamoto }; 316eb6c81eSTakashi Sakamoto 32b60152f7STakashi Sakamoto int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, 33b60152f7STakashi Sakamoto enum snd_dice_rate_mode *mode) 34b60152f7STakashi Sakamoto { 35b60152f7STakashi Sakamoto /* Corresponding to each entry in snd_dice_rates. */ 36b60152f7STakashi Sakamoto static const enum snd_dice_rate_mode modes[] = { 37b60152f7STakashi Sakamoto [0] = SND_DICE_RATE_MODE_LOW, 38b60152f7STakashi Sakamoto [1] = SND_DICE_RATE_MODE_LOW, 39b60152f7STakashi Sakamoto [2] = SND_DICE_RATE_MODE_LOW, 40b60152f7STakashi Sakamoto [3] = SND_DICE_RATE_MODE_MIDDLE, 41b60152f7STakashi Sakamoto [4] = SND_DICE_RATE_MODE_MIDDLE, 42b60152f7STakashi Sakamoto [5] = SND_DICE_RATE_MODE_HIGH, 43b60152f7STakashi Sakamoto [6] = SND_DICE_RATE_MODE_HIGH, 44b60152f7STakashi Sakamoto }; 45b60152f7STakashi Sakamoto int i; 46b60152f7STakashi Sakamoto 47b60152f7STakashi Sakamoto for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) { 48b60152f7STakashi Sakamoto if (!(dice->clock_caps & BIT(i))) 49b60152f7STakashi Sakamoto continue; 50b60152f7STakashi Sakamoto if (snd_dice_rates[i] != rate) 51b60152f7STakashi Sakamoto continue; 52b60152f7STakashi Sakamoto 53b60152f7STakashi Sakamoto *mode = modes[i]; 54b60152f7STakashi Sakamoto return 0; 55b60152f7STakashi Sakamoto } 56b60152f7STakashi Sakamoto 57b60152f7STakashi Sakamoto return -EINVAL; 58b60152f7STakashi Sakamoto } 59b60152f7STakashi Sakamoto 60dfabc0eeSTakashi Sakamoto /* 61dfabc0eeSTakashi Sakamoto * This operation has an effect to synchronize GLOBAL_STATUS/GLOBAL_SAMPLE_RATE 62dfabc0eeSTakashi Sakamoto * to GLOBAL_STATUS. Especially, just after powering on, these are different. 63dfabc0eeSTakashi Sakamoto */ 64afa617f2STakashi Sakamoto static int ensure_phase_lock(struct snd_dice *dice, unsigned int rate) 65dfabc0eeSTakashi Sakamoto { 66fbeac84dSTakashi Sakamoto __be32 reg, nominal; 67afa617f2STakashi Sakamoto u32 data; 68afa617f2STakashi Sakamoto int i; 69dfabc0eeSTakashi Sakamoto int err; 70dfabc0eeSTakashi Sakamoto 71dfabc0eeSTakashi Sakamoto err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT, 72dfabc0eeSTakashi Sakamoto ®, sizeof(reg)); 73dfabc0eeSTakashi Sakamoto if (err < 0) 74dfabc0eeSTakashi Sakamoto return err; 75dfabc0eeSTakashi Sakamoto 76afa617f2STakashi Sakamoto data = be32_to_cpu(reg); 77afa617f2STakashi Sakamoto 78afa617f2STakashi Sakamoto data &= ~CLOCK_RATE_MASK; 79afa617f2STakashi Sakamoto for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { 80afa617f2STakashi Sakamoto if (snd_dice_rates[i] == rate) 81afa617f2STakashi Sakamoto break; 82afa617f2STakashi Sakamoto } 83afa617f2STakashi Sakamoto if (i == ARRAY_SIZE(snd_dice_rates)) 84afa617f2STakashi Sakamoto return -EINVAL; 85afa617f2STakashi Sakamoto data |= i << CLOCK_RATE_SHIFT; 86afa617f2STakashi Sakamoto 87dfabc0eeSTakashi Sakamoto if (completion_done(&dice->clock_accepted)) 88dfabc0eeSTakashi Sakamoto reinit_completion(&dice->clock_accepted); 89dfabc0eeSTakashi Sakamoto 90afa617f2STakashi Sakamoto reg = cpu_to_be32(data); 91dfabc0eeSTakashi Sakamoto err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT, 92dfabc0eeSTakashi Sakamoto ®, sizeof(reg)); 93dfabc0eeSTakashi Sakamoto if (err < 0) 94dfabc0eeSTakashi Sakamoto return err; 95dfabc0eeSTakashi Sakamoto 96dfabc0eeSTakashi Sakamoto if (wait_for_completion_timeout(&dice->clock_accepted, 97fbeac84dSTakashi Sakamoto msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) { 98fbeac84dSTakashi Sakamoto /* 99fbeac84dSTakashi Sakamoto * Old versions of Dice firmware transfer no notification when 100fbeac84dSTakashi Sakamoto * the same clock status as current one is set. In this case, 101fbeac84dSTakashi Sakamoto * just check current clock status. 102fbeac84dSTakashi Sakamoto */ 103fbeac84dSTakashi Sakamoto err = snd_dice_transaction_read_global(dice, GLOBAL_STATUS, 104fbeac84dSTakashi Sakamoto &nominal, sizeof(nominal)); 105fbeac84dSTakashi Sakamoto if (err < 0) 106fbeac84dSTakashi Sakamoto return err; 107fbeac84dSTakashi Sakamoto if (!(be32_to_cpu(nominal) & STATUS_SOURCE_LOCKED)) 108dfabc0eeSTakashi Sakamoto return -ETIMEDOUT; 109fbeac84dSTakashi Sakamoto } 110dfabc0eeSTakashi Sakamoto 111dfabc0eeSTakashi Sakamoto return 0; 112dfabc0eeSTakashi Sakamoto } 113dfabc0eeSTakashi Sakamoto 1148cc1a8abSTakashi Sakamoto static int get_register_params(struct snd_dice *dice, 1158cc1a8abSTakashi Sakamoto struct reg_params *tx_params, 1168cc1a8abSTakashi Sakamoto struct reg_params *rx_params) 1176eb6c81eSTakashi Sakamoto { 118436b5abeSTakashi Sakamoto __be32 reg[2]; 119436b5abeSTakashi Sakamoto int err; 1206eb6c81eSTakashi Sakamoto 121436b5abeSTakashi Sakamoto err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, sizeof(reg)); 122436b5abeSTakashi Sakamoto if (err < 0) 123436b5abeSTakashi Sakamoto return err; 1248cc1a8abSTakashi Sakamoto tx_params->count = 1258cc1a8abSTakashi Sakamoto min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); 1268cc1a8abSTakashi Sakamoto tx_params->size = be32_to_cpu(reg[1]) * 4; 127288a8d0cSTakashi Sakamoto 128436b5abeSTakashi Sakamoto err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, sizeof(reg)); 129436b5abeSTakashi Sakamoto if (err < 0) 130436b5abeSTakashi Sakamoto return err; 1318cc1a8abSTakashi Sakamoto rx_params->count = 1328cc1a8abSTakashi Sakamoto min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); 1338cc1a8abSTakashi Sakamoto rx_params->size = be32_to_cpu(reg[1]) * 4; 134436b5abeSTakashi Sakamoto 135436b5abeSTakashi Sakamoto return 0; 136436b5abeSTakashi Sakamoto } 137436b5abeSTakashi Sakamoto 138436b5abeSTakashi Sakamoto static void release_resources(struct snd_dice *dice) 139436b5abeSTakashi Sakamoto { 1403cd2c2d7STakashi Sakamoto int i; 141436b5abeSTakashi Sakamoto 1423cd2c2d7STakashi Sakamoto for (i = 0; i < MAX_STREAMS; ++i) { 143436b5abeSTakashi Sakamoto fw_iso_resources_free(&dice->tx_resources[i]); 144436b5abeSTakashi Sakamoto fw_iso_resources_free(&dice->rx_resources[i]); 145436b5abeSTakashi Sakamoto } 146436b5abeSTakashi Sakamoto } 147436b5abeSTakashi Sakamoto 148436b5abeSTakashi Sakamoto static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, 1498cc1a8abSTakashi Sakamoto struct reg_params *params) 150436b5abeSTakashi Sakamoto { 151436b5abeSTakashi Sakamoto __be32 reg; 152436b5abeSTakashi Sakamoto unsigned int i; 153436b5abeSTakashi Sakamoto 1548cc1a8abSTakashi Sakamoto for (i = 0; i < params->count; i++) { 155436b5abeSTakashi Sakamoto reg = cpu_to_be32((u32)-1); 156436b5abeSTakashi Sakamoto if (dir == AMDTP_IN_STREAM) { 157436b5abeSTakashi Sakamoto snd_dice_transaction_write_tx(dice, 1588cc1a8abSTakashi Sakamoto params->size * i + TX_ISOCHRONOUS, 159436b5abeSTakashi Sakamoto ®, sizeof(reg)); 160436b5abeSTakashi Sakamoto } else { 161436b5abeSTakashi Sakamoto snd_dice_transaction_write_rx(dice, 1628cc1a8abSTakashi Sakamoto params->size * i + RX_ISOCHRONOUS, 163436b5abeSTakashi Sakamoto ®, sizeof(reg)); 164436b5abeSTakashi Sakamoto } 165436b5abeSTakashi Sakamoto } 166288a8d0cSTakashi Sakamoto } 167288a8d0cSTakashi Sakamoto 168c738aed1STakashi Sakamoto static int keep_resources(struct snd_dice *dice, struct amdtp_stream *stream, 169c738aed1STakashi Sakamoto struct fw_iso_resources *resources, unsigned int rate, 170c738aed1STakashi Sakamoto unsigned int pcm_chs, unsigned int midi_ports) 171288a8d0cSTakashi Sakamoto { 17227ec83b5STakashi Sakamoto bool double_pcm_frames; 173436b5abeSTakashi Sakamoto unsigned int i; 174288a8d0cSTakashi Sakamoto int err; 175288a8d0cSTakashi Sakamoto 176c738aed1STakashi Sakamoto // At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in 177c738aed1STakashi Sakamoto // one data block of AMDTP packet. Thus sampling transfer frequency is 178c738aed1STakashi Sakamoto // a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are 179c738aed1STakashi Sakamoto // transferred on AMDTP packets at 96 kHz. Two successive samples of a 180c738aed1STakashi Sakamoto // channel are stored consecutively in the packet. This quirk is called 181c738aed1STakashi Sakamoto // as 'Dual Wire'. 182c738aed1STakashi Sakamoto // For this quirk, blocking mode is required and PCM buffer size should 183c738aed1STakashi Sakamoto // be aligned to SYT_INTERVAL. 1849f079c1bSTakashi Sakamoto double_pcm_frames = (rate > 96000 && !dice->disable_double_pcm_frames); 18527ec83b5STakashi Sakamoto if (double_pcm_frames) { 186288a8d0cSTakashi Sakamoto rate /= 2; 187288a8d0cSTakashi Sakamoto pcm_chs *= 2; 188288a8d0cSTakashi Sakamoto } 189288a8d0cSTakashi Sakamoto 19051c29fd2STakashi Sakamoto err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports, 19151c29fd2STakashi Sakamoto double_pcm_frames); 192547e631cSTakashi Sakamoto if (err < 0) 193436b5abeSTakashi Sakamoto return err; 194547e631cSTakashi Sakamoto 19527ec83b5STakashi Sakamoto if (double_pcm_frames) { 196288a8d0cSTakashi Sakamoto pcm_chs /= 2; 197288a8d0cSTakashi Sakamoto 198288a8d0cSTakashi Sakamoto for (i = 0; i < pcm_chs; i++) { 199f65be911STakashi Sakamoto amdtp_am824_set_pcm_position(stream, i, i * 2); 200f65be911STakashi Sakamoto amdtp_am824_set_pcm_position(stream, i + pcm_chs, 201f65be911STakashi Sakamoto i * 2 + 1); 202288a8d0cSTakashi Sakamoto } 203288a8d0cSTakashi Sakamoto } 204288a8d0cSTakashi Sakamoto 205436b5abeSTakashi Sakamoto return fw_iso_resources_allocate(resources, 206436b5abeSTakashi Sakamoto amdtp_stream_get_max_payload(stream), 207436b5abeSTakashi Sakamoto fw_parent_device(dice->unit)->max_speed); 208288a8d0cSTakashi Sakamoto } 209288a8d0cSTakashi Sakamoto 210c738aed1STakashi Sakamoto static int keep_dual_resources(struct snd_dice *dice, unsigned int rate, 211c738aed1STakashi Sakamoto enum amdtp_stream_direction dir, 212c738aed1STakashi Sakamoto struct reg_params *params) 213c738aed1STakashi Sakamoto { 214c738aed1STakashi Sakamoto enum snd_dice_rate_mode mode; 215c738aed1STakashi Sakamoto int i; 216c738aed1STakashi Sakamoto int err; 217c738aed1STakashi Sakamoto 218c738aed1STakashi Sakamoto err = snd_dice_stream_get_rate_mode(dice, rate, &mode); 219c738aed1STakashi Sakamoto if (err < 0) 220c738aed1STakashi Sakamoto return err; 221c738aed1STakashi Sakamoto 222c738aed1STakashi Sakamoto for (i = 0; i < params->count; ++i) { 223c738aed1STakashi Sakamoto __be32 reg[2]; 224c738aed1STakashi Sakamoto struct amdtp_stream *stream; 225c738aed1STakashi Sakamoto struct fw_iso_resources *resources; 226c738aed1STakashi Sakamoto unsigned int pcm_cache; 227c738aed1STakashi Sakamoto unsigned int pcm_chs; 228c738aed1STakashi Sakamoto unsigned int midi_ports; 229c738aed1STakashi Sakamoto 230c738aed1STakashi Sakamoto if (dir == AMDTP_IN_STREAM) { 231c738aed1STakashi Sakamoto stream = &dice->tx_stream[i]; 232c738aed1STakashi Sakamoto resources = &dice->tx_resources[i]; 233c738aed1STakashi Sakamoto 234c738aed1STakashi Sakamoto pcm_cache = dice->tx_pcm_chs[i][mode]; 235c738aed1STakashi Sakamoto err = snd_dice_transaction_read_tx(dice, 236c738aed1STakashi Sakamoto params->size * i + TX_NUMBER_AUDIO, 237c738aed1STakashi Sakamoto reg, sizeof(reg)); 238c738aed1STakashi Sakamoto } else { 239c738aed1STakashi Sakamoto stream = &dice->rx_stream[i]; 240c738aed1STakashi Sakamoto resources = &dice->rx_resources[i]; 241c738aed1STakashi Sakamoto 242c738aed1STakashi Sakamoto pcm_cache = dice->rx_pcm_chs[i][mode]; 243c738aed1STakashi Sakamoto err = snd_dice_transaction_read_rx(dice, 244c738aed1STakashi Sakamoto params->size * i + RX_NUMBER_AUDIO, 245c738aed1STakashi Sakamoto reg, sizeof(reg)); 246c738aed1STakashi Sakamoto } 247c738aed1STakashi Sakamoto if (err < 0) 248c738aed1STakashi Sakamoto return err; 249c738aed1STakashi Sakamoto pcm_chs = be32_to_cpu(reg[0]); 250c738aed1STakashi Sakamoto midi_ports = be32_to_cpu(reg[1]); 251c738aed1STakashi Sakamoto 252c738aed1STakashi Sakamoto // These are important for developer of this driver. 253a9f47fcbSTakashi Sakamoto if (pcm_chs != pcm_cache) { 254c738aed1STakashi Sakamoto dev_info(&dice->unit->device, 255a9f47fcbSTakashi Sakamoto "cache mismatch: pcm: %u:%u, midi: %u\n", 256a9f47fcbSTakashi Sakamoto pcm_chs, pcm_cache, midi_ports); 257c738aed1STakashi Sakamoto return -EPROTO; 258c738aed1STakashi Sakamoto } 259c738aed1STakashi Sakamoto 260c738aed1STakashi Sakamoto err = keep_resources(dice, stream, resources, rate, pcm_chs, 261c738aed1STakashi Sakamoto midi_ports); 262c738aed1STakashi Sakamoto if (err < 0) 263c738aed1STakashi Sakamoto return err; 264c738aed1STakashi Sakamoto } 265c738aed1STakashi Sakamoto 266c738aed1STakashi Sakamoto return 0; 267c738aed1STakashi Sakamoto } 268c738aed1STakashi Sakamoto 269b3480638STakashi Sakamoto static void finish_session(struct snd_dice *dice, struct reg_params *tx_params, 270b3480638STakashi Sakamoto struct reg_params *rx_params) 271b3480638STakashi Sakamoto { 272b3480638STakashi Sakamoto stop_streams(dice, AMDTP_IN_STREAM, tx_params); 273b3480638STakashi Sakamoto stop_streams(dice, AMDTP_OUT_STREAM, rx_params); 274b3480638STakashi Sakamoto 275b3480638STakashi Sakamoto snd_dice_transaction_clear_enable(dice); 276b3480638STakashi Sakamoto } 277b3480638STakashi Sakamoto 27894c8101aSTakashi Sakamoto int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate, 279ecb40fd2STakashi Sakamoto unsigned int events_per_period, 280ecb40fd2STakashi Sakamoto unsigned int events_per_buffer) 2813cd2c2d7STakashi Sakamoto { 2823cd2c2d7STakashi Sakamoto unsigned int curr_rate; 2833cd2c2d7STakashi Sakamoto int err; 2843cd2c2d7STakashi Sakamoto 2853cd2c2d7STakashi Sakamoto // Check sampling transmission frequency. 2863cd2c2d7STakashi Sakamoto err = snd_dice_transaction_get_rate(dice, &curr_rate); 2873cd2c2d7STakashi Sakamoto if (err < 0) 2883cd2c2d7STakashi Sakamoto return err; 2893cd2c2d7STakashi Sakamoto if (rate == 0) 2903cd2c2d7STakashi Sakamoto rate = curr_rate; 2913cd2c2d7STakashi Sakamoto 2923cd2c2d7STakashi Sakamoto if (dice->substreams_counter == 0 || curr_rate != rate) { 2933cd2c2d7STakashi Sakamoto struct reg_params tx_params, rx_params; 2943cd2c2d7STakashi Sakamoto 295e9f21129STakashi Sakamoto amdtp_domain_stop(&dice->domain); 296e9f21129STakashi Sakamoto 2973cd2c2d7STakashi Sakamoto err = get_register_params(dice, &tx_params, &rx_params); 2983cd2c2d7STakashi Sakamoto if (err < 0) 2993cd2c2d7STakashi Sakamoto return err; 3003cd2c2d7STakashi Sakamoto finish_session(dice, &tx_params, &rx_params); 3013cd2c2d7STakashi Sakamoto 3023cd2c2d7STakashi Sakamoto release_resources(dice); 3033cd2c2d7STakashi Sakamoto 3043cd2c2d7STakashi Sakamoto // Just after owning the unit (GLOBAL_OWNER), the unit can 3053cd2c2d7STakashi Sakamoto // return invalid stream formats. Selecting clock parameters 3063cd2c2d7STakashi Sakamoto // have an effect for the unit to refine it. 3073cd2c2d7STakashi Sakamoto err = ensure_phase_lock(dice, rate); 3083cd2c2d7STakashi Sakamoto if (err < 0) 3093cd2c2d7STakashi Sakamoto return err; 3103cd2c2d7STakashi Sakamoto 3113cd2c2d7STakashi Sakamoto // After changing sampling transfer frequency, the value of 3123cd2c2d7STakashi Sakamoto // register can be changed. 3133cd2c2d7STakashi Sakamoto err = get_register_params(dice, &tx_params, &rx_params); 3143cd2c2d7STakashi Sakamoto if (err < 0) 3153cd2c2d7STakashi Sakamoto return err; 3163cd2c2d7STakashi Sakamoto 3173cd2c2d7STakashi Sakamoto err = keep_dual_resources(dice, rate, AMDTP_IN_STREAM, 3183cd2c2d7STakashi Sakamoto &tx_params); 3193cd2c2d7STakashi Sakamoto if (err < 0) 3203cd2c2d7STakashi Sakamoto goto error; 3213cd2c2d7STakashi Sakamoto 3223cd2c2d7STakashi Sakamoto err = keep_dual_resources(dice, rate, AMDTP_OUT_STREAM, 3233cd2c2d7STakashi Sakamoto &rx_params); 3243cd2c2d7STakashi Sakamoto if (err < 0) 3253cd2c2d7STakashi Sakamoto goto error; 32694c8101aSTakashi Sakamoto 32794c8101aSTakashi Sakamoto err = amdtp_domain_set_events_per_period(&dice->domain, 328ecb40fd2STakashi Sakamoto events_per_period, events_per_buffer); 32994c8101aSTakashi Sakamoto if (err < 0) 33094c8101aSTakashi Sakamoto goto error; 3313cd2c2d7STakashi Sakamoto } 3323cd2c2d7STakashi Sakamoto 3333cd2c2d7STakashi Sakamoto return 0; 3343cd2c2d7STakashi Sakamoto error: 3353cd2c2d7STakashi Sakamoto release_resources(dice); 3363cd2c2d7STakashi Sakamoto return err; 3373cd2c2d7STakashi Sakamoto } 3383cd2c2d7STakashi Sakamoto 339436b5abeSTakashi Sakamoto static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, 3408cc1a8abSTakashi Sakamoto unsigned int rate, struct reg_params *params) 341436b5abeSTakashi Sakamoto { 342c738aed1STakashi Sakamoto unsigned int max_speed = fw_parent_device(dice->unit)->max_speed; 343c738aed1STakashi Sakamoto int i; 344c738aed1STakashi Sakamoto int err; 345436b5abeSTakashi Sakamoto 3468cc1a8abSTakashi Sakamoto for (i = 0; i < params->count; i++) { 347c738aed1STakashi Sakamoto struct amdtp_stream *stream; 348c738aed1STakashi Sakamoto struct fw_iso_resources *resources; 349c738aed1STakashi Sakamoto __be32 reg; 350afa617f2STakashi Sakamoto 351436b5abeSTakashi Sakamoto if (dir == AMDTP_IN_STREAM) { 352c738aed1STakashi Sakamoto stream = dice->tx_stream + i; 353c738aed1STakashi Sakamoto resources = dice->tx_resources + i; 354436b5abeSTakashi Sakamoto } else { 355c738aed1STakashi Sakamoto stream = dice->rx_stream + i; 356c738aed1STakashi Sakamoto resources = dice->rx_resources + i; 357afa617f2STakashi Sakamoto } 358afa617f2STakashi Sakamoto 359c738aed1STakashi Sakamoto reg = cpu_to_be32(resources->channel); 360436b5abeSTakashi Sakamoto if (dir == AMDTP_IN_STREAM) { 361436b5abeSTakashi Sakamoto err = snd_dice_transaction_write_tx(dice, 3628cc1a8abSTakashi Sakamoto params->size * i + TX_ISOCHRONOUS, 363c738aed1STakashi Sakamoto ®, sizeof(reg)); 364436b5abeSTakashi Sakamoto } else { 365436b5abeSTakashi Sakamoto err = snd_dice_transaction_write_rx(dice, 3668cc1a8abSTakashi Sakamoto params->size * i + RX_ISOCHRONOUS, 367c738aed1STakashi Sakamoto ®, sizeof(reg)); 368436b5abeSTakashi Sakamoto } 369436b5abeSTakashi Sakamoto if (err < 0) 370436b5abeSTakashi Sakamoto return err; 371436b5abeSTakashi Sakamoto 372b0e159feSTakashi Sakamoto if (dir == AMDTP_IN_STREAM) { 373c738aed1STakashi Sakamoto reg = cpu_to_be32(max_speed); 374b0e159feSTakashi Sakamoto err = snd_dice_transaction_write_tx(dice, 375b0e159feSTakashi Sakamoto params->size * i + TX_SPEED, 376c738aed1STakashi Sakamoto ®, sizeof(reg)); 377b0e159feSTakashi Sakamoto if (err < 0) 378b0e159feSTakashi Sakamoto return err; 379b0e159feSTakashi Sakamoto } 380b0e159feSTakashi Sakamoto 381e9f21129STakashi Sakamoto err = amdtp_domain_add_stream(&dice->domain, stream, 382e9f21129STakashi Sakamoto resources->channel, max_speed); 383288a8d0cSTakashi Sakamoto if (err < 0) 384288a8d0cSTakashi Sakamoto return err; 385288a8d0cSTakashi Sakamoto } 386288a8d0cSTakashi Sakamoto 387c72d3a0aSTakashi Sakamoto return 0; 388436b5abeSTakashi Sakamoto } 389436b5abeSTakashi Sakamoto 3903cd2c2d7STakashi Sakamoto /* 3913cd2c2d7STakashi Sakamoto * MEMO: After this function, there're two states of streams: 3923cd2c2d7STakashi Sakamoto * - None streams are running. 3933cd2c2d7STakashi Sakamoto * - All streams are running. 3943cd2c2d7STakashi Sakamoto */ 3953cd2c2d7STakashi Sakamoto int snd_dice_stream_start_duplex(struct snd_dice *dice) 39620b94544STakashi Sakamoto { 397d5553026STakashi Sakamoto unsigned int generation = dice->rx_resources[0].generation; 39820b94544STakashi Sakamoto struct reg_params tx_params, rx_params; 3993cd2c2d7STakashi Sakamoto unsigned int i; 4003cd2c2d7STakashi Sakamoto unsigned int rate; 4013cd2c2d7STakashi Sakamoto enum snd_dice_rate_mode mode; 40220b94544STakashi Sakamoto int err; 40320b94544STakashi Sakamoto 4043cd2c2d7STakashi Sakamoto if (dice->substreams_counter == 0) 4053cd2c2d7STakashi Sakamoto return -EIO; 4063cd2c2d7STakashi Sakamoto 40720b94544STakashi Sakamoto err = get_register_params(dice, &tx_params, &rx_params); 40820b94544STakashi Sakamoto if (err < 0) 40920b94544STakashi Sakamoto return err; 41020b94544STakashi Sakamoto 4113cd2c2d7STakashi Sakamoto // Check error of packet streaming. 4123cd2c2d7STakashi Sakamoto for (i = 0; i < MAX_STREAMS; ++i) { 4133cd2c2d7STakashi Sakamoto if (amdtp_streaming_error(&dice->tx_stream[i]) || 4143cd2c2d7STakashi Sakamoto amdtp_streaming_error(&dice->rx_stream[i])) { 415e9f21129STakashi Sakamoto amdtp_domain_stop(&dice->domain); 416b3480638STakashi Sakamoto finish_session(dice, &tx_params, &rx_params); 4173cd2c2d7STakashi Sakamoto break; 4183cd2c2d7STakashi Sakamoto } 41920b94544STakashi Sakamoto } 42020b94544STakashi Sakamoto 421d5553026STakashi Sakamoto if (generation != fw_parent_device(dice->unit)->card->generation) { 422d5553026STakashi Sakamoto for (i = 0; i < MAX_STREAMS; ++i) { 423d5553026STakashi Sakamoto if (i < tx_params.count) 424d5553026STakashi Sakamoto fw_iso_resources_update(dice->tx_resources + i); 425d5553026STakashi Sakamoto if (i < rx_params.count) 426d5553026STakashi Sakamoto fw_iso_resources_update(dice->rx_resources + i); 427d5553026STakashi Sakamoto } 428d5553026STakashi Sakamoto } 429d5553026STakashi Sakamoto 4303cd2c2d7STakashi Sakamoto // Check required streams are running or not. 4313cd2c2d7STakashi Sakamoto err = snd_dice_transaction_get_rate(dice, &rate); 432afa617f2STakashi Sakamoto if (err < 0) 433afa617f2STakashi Sakamoto return err; 4343cd2c2d7STakashi Sakamoto err = snd_dice_stream_get_rate_mode(dice, rate, &mode); 4353cd2c2d7STakashi Sakamoto if (err < 0) 4363cd2c2d7STakashi Sakamoto return err; 4373cd2c2d7STakashi Sakamoto for (i = 0; i < MAX_STREAMS; ++i) { 4383cd2c2d7STakashi Sakamoto if (dice->tx_pcm_chs[i][mode] > 0 && 4393cd2c2d7STakashi Sakamoto !amdtp_stream_running(&dice->tx_stream[i])) 4403cd2c2d7STakashi Sakamoto break; 4413cd2c2d7STakashi Sakamoto if (dice->rx_pcm_chs[i][mode] > 0 && 4423cd2c2d7STakashi Sakamoto !amdtp_stream_running(&dice->rx_stream[i])) 4433cd2c2d7STakashi Sakamoto break; 4443cd2c2d7STakashi Sakamoto } 4453cd2c2d7STakashi Sakamoto if (i < MAX_STREAMS) { 4463cd2c2d7STakashi Sakamoto // Start both streams. 44720b94544STakashi Sakamoto err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params); 44820b94544STakashi Sakamoto if (err < 0) 44920b94544STakashi Sakamoto goto error; 4503cd2c2d7STakashi Sakamoto 45120b94544STakashi Sakamoto err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params); 45220b94544STakashi Sakamoto if (err < 0) 45320b94544STakashi Sakamoto goto error; 45420b94544STakashi Sakamoto 45520b94544STakashi Sakamoto err = snd_dice_transaction_set_enable(dice); 45620b94544STakashi Sakamoto if (err < 0) { 4573cd2c2d7STakashi Sakamoto dev_err(&dice->unit->device, 4583cd2c2d7STakashi Sakamoto "fail to enable interface\n"); 45920b94544STakashi Sakamoto goto error; 46020b94544STakashi Sakamoto } 46120b94544STakashi Sakamoto 462*2f21a177STakashi Sakamoto err = amdtp_domain_start(&dice->domain, 0, false, false); 463e9f21129STakashi Sakamoto if (err < 0) 464e9f21129STakashi Sakamoto goto error; 465e9f21129STakashi Sakamoto 466bdaedca7STakashi Sakamoto if (!amdtp_domain_wait_ready(&dice->domain, READY_TIMEOUT_MS)) { 46720b94544STakashi Sakamoto err = -ETIMEDOUT; 46820b94544STakashi Sakamoto goto error; 46920b94544STakashi Sakamoto } 47020b94544STakashi Sakamoto } 47120b94544STakashi Sakamoto 47220b94544STakashi Sakamoto return 0; 47320b94544STakashi Sakamoto error: 474e9f21129STakashi Sakamoto amdtp_domain_stop(&dice->domain); 475b3480638STakashi Sakamoto finish_session(dice, &tx_params, &rx_params); 47620b94544STakashi Sakamoto return err; 47720b94544STakashi Sakamoto } 47820b94544STakashi Sakamoto 479436b5abeSTakashi Sakamoto /* 480436b5abeSTakashi Sakamoto * MEMO: After this function, there're two states of streams: 481436b5abeSTakashi Sakamoto * - None streams are running. 482436b5abeSTakashi Sakamoto * - All streams are running. 483436b5abeSTakashi Sakamoto */ 4849a02843cSTakashi Sakamoto void snd_dice_stream_stop_duplex(struct snd_dice *dice) 4856eb6c81eSTakashi Sakamoto { 4868cc1a8abSTakashi Sakamoto struct reg_params tx_params, rx_params; 487436b5abeSTakashi Sakamoto 4883cd2c2d7STakashi Sakamoto if (dice->substreams_counter == 0) { 489dd7b836dSTakashi Sakamoto if (get_register_params(dice, &tx_params, &rx_params) >= 0) 490b3480638STakashi Sakamoto finish_session(dice, &tx_params, &rx_params); 491740680f2STakashi Sakamoto 492dd7b836dSTakashi Sakamoto amdtp_domain_stop(&dice->domain); 493740680f2STakashi Sakamoto release_resources(dice); 4943cd2c2d7STakashi Sakamoto } 495436b5abeSTakashi Sakamoto } 4969a02843cSTakashi Sakamoto 497436b5abeSTakashi Sakamoto static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir, 498436b5abeSTakashi Sakamoto unsigned int index) 499436b5abeSTakashi Sakamoto { 500436b5abeSTakashi Sakamoto struct amdtp_stream *stream; 501436b5abeSTakashi Sakamoto struct fw_iso_resources *resources; 502436b5abeSTakashi Sakamoto int err; 503436b5abeSTakashi Sakamoto 504436b5abeSTakashi Sakamoto if (dir == AMDTP_IN_STREAM) { 505436b5abeSTakashi Sakamoto stream = &dice->tx_stream[index]; 506436b5abeSTakashi Sakamoto resources = &dice->tx_resources[index]; 5079a02843cSTakashi Sakamoto } else { 508436b5abeSTakashi Sakamoto stream = &dice->rx_stream[index]; 509436b5abeSTakashi Sakamoto resources = &dice->rx_resources[index]; 5109a02843cSTakashi Sakamoto } 5119a02843cSTakashi Sakamoto 5129a02843cSTakashi Sakamoto err = fw_iso_resources_init(resources, dice->unit); 5139a02843cSTakashi Sakamoto if (err < 0) 5149a02843cSTakashi Sakamoto goto end; 5159a02843cSTakashi Sakamoto resources->channels_mask = 0x00000000ffffffffuLL; 5169a02843cSTakashi Sakamoto 5175955815eSTakashi Sakamoto err = amdtp_am824_init(stream, dice->unit, dir, CIP_BLOCKING); 5189a02843cSTakashi Sakamoto if (err < 0) { 5199a02843cSTakashi Sakamoto amdtp_stream_destroy(stream); 5209a02843cSTakashi Sakamoto fw_iso_resources_destroy(resources); 5219a02843cSTakashi Sakamoto } 5229a02843cSTakashi Sakamoto end: 5239a02843cSTakashi Sakamoto return err; 5249a02843cSTakashi Sakamoto } 5259a02843cSTakashi Sakamoto 526d23c2cc4STakashi Sakamoto /* 527d23c2cc4STakashi Sakamoto * This function should be called before starting streams or after stopping 528d23c2cc4STakashi Sakamoto * streams. 529d23c2cc4STakashi Sakamoto */ 530436b5abeSTakashi Sakamoto static void destroy_stream(struct snd_dice *dice, 531436b5abeSTakashi Sakamoto enum amdtp_stream_direction dir, 532436b5abeSTakashi Sakamoto unsigned int index) 5339a02843cSTakashi Sakamoto { 534436b5abeSTakashi Sakamoto struct amdtp_stream *stream; 535d23c2cc4STakashi Sakamoto struct fw_iso_resources *resources; 5369a02843cSTakashi Sakamoto 537436b5abeSTakashi Sakamoto if (dir == AMDTP_IN_STREAM) { 538436b5abeSTakashi Sakamoto stream = &dice->tx_stream[index]; 539436b5abeSTakashi Sakamoto resources = &dice->tx_resources[index]; 540436b5abeSTakashi Sakamoto } else { 541436b5abeSTakashi Sakamoto stream = &dice->rx_stream[index]; 542436b5abeSTakashi Sakamoto resources = &dice->rx_resources[index]; 543436b5abeSTakashi Sakamoto } 544d23c2cc4STakashi Sakamoto 545d23c2cc4STakashi Sakamoto amdtp_stream_destroy(stream); 546d23c2cc4STakashi Sakamoto fw_iso_resources_destroy(resources); 5479a02843cSTakashi Sakamoto } 5489a02843cSTakashi Sakamoto 5499a02843cSTakashi Sakamoto int snd_dice_stream_init_duplex(struct snd_dice *dice) 5506eb6c81eSTakashi Sakamoto { 551436b5abeSTakashi Sakamoto int i, err; 5526eb6c81eSTakashi Sakamoto 553436b5abeSTakashi Sakamoto for (i = 0; i < MAX_STREAMS; i++) { 554436b5abeSTakashi Sakamoto err = init_stream(dice, AMDTP_IN_STREAM, i); 555436b5abeSTakashi Sakamoto if (err < 0) { 556436b5abeSTakashi Sakamoto for (; i >= 0; i--) 5570f925660STakashi Sakamoto destroy_stream(dice, AMDTP_IN_STREAM, i); 5586eb6c81eSTakashi Sakamoto goto end; 559436b5abeSTakashi Sakamoto } 560436b5abeSTakashi Sakamoto } 5616eb6c81eSTakashi Sakamoto 562436b5abeSTakashi Sakamoto for (i = 0; i < MAX_STREAMS; i++) { 563436b5abeSTakashi Sakamoto err = init_stream(dice, AMDTP_OUT_STREAM, i); 564436b5abeSTakashi Sakamoto if (err < 0) { 565436b5abeSTakashi Sakamoto for (; i >= 0; i--) 566436b5abeSTakashi Sakamoto destroy_stream(dice, AMDTP_OUT_STREAM, i); 567436b5abeSTakashi Sakamoto for (i = 0; i < MAX_STREAMS; i++) 568436b5abeSTakashi Sakamoto destroy_stream(dice, AMDTP_IN_STREAM, i); 5696e26d193STakashi Sakamoto goto end; 570436b5abeSTakashi Sakamoto } 571436b5abeSTakashi Sakamoto } 572e9f21129STakashi Sakamoto 573e9f21129STakashi Sakamoto err = amdtp_domain_init(&dice->domain); 574e9f21129STakashi Sakamoto if (err < 0) { 575e9f21129STakashi Sakamoto for (i = 0; i < MAX_STREAMS; ++i) { 576e9f21129STakashi Sakamoto destroy_stream(dice, AMDTP_OUT_STREAM, i); 577e9f21129STakashi Sakamoto destroy_stream(dice, AMDTP_IN_STREAM, i); 578e9f21129STakashi Sakamoto } 579e9f21129STakashi Sakamoto } 5806eb6c81eSTakashi Sakamoto end: 5816eb6c81eSTakashi Sakamoto return err; 5826eb6c81eSTakashi Sakamoto } 5836eb6c81eSTakashi Sakamoto 5849a02843cSTakashi Sakamoto void snd_dice_stream_destroy_duplex(struct snd_dice *dice) 5856eb6c81eSTakashi Sakamoto { 5866b94fb14STakashi Sakamoto unsigned int i; 587436b5abeSTakashi Sakamoto 5886b94fb14STakashi Sakamoto for (i = 0; i < MAX_STREAMS; i++) { 5896b94fb14STakashi Sakamoto destroy_stream(dice, AMDTP_IN_STREAM, i); 5906b94fb14STakashi Sakamoto destroy_stream(dice, AMDTP_OUT_STREAM, i); 591436b5abeSTakashi Sakamoto } 592e9f21129STakashi Sakamoto 593e9f21129STakashi Sakamoto amdtp_domain_destroy(&dice->domain); 5946eb6c81eSTakashi Sakamoto } 5956eb6c81eSTakashi Sakamoto 5969a02843cSTakashi Sakamoto void snd_dice_stream_update_duplex(struct snd_dice *dice) 5976eb6c81eSTakashi Sakamoto { 5988cc1a8abSTakashi Sakamoto struct reg_params tx_params, rx_params; 599436b5abeSTakashi Sakamoto 6006eb6c81eSTakashi Sakamoto /* 6016eb6c81eSTakashi Sakamoto * On a bus reset, the DICE firmware disables streaming and then goes 6026eb6c81eSTakashi Sakamoto * off contemplating its own navel for hundreds of milliseconds before 6036eb6c81eSTakashi Sakamoto * it can react to any of our attempts to reenable streaming. This 6046eb6c81eSTakashi Sakamoto * means that we lose synchronization anyway, so we force our streams 6056eb6c81eSTakashi Sakamoto * to stop so that the application can restart them in an orderly 6066eb6c81eSTakashi Sakamoto * manner. 6076eb6c81eSTakashi Sakamoto */ 6086eb6c81eSTakashi Sakamoto dice->global_enabled = false; 6096eb6c81eSTakashi Sakamoto 6108cc1a8abSTakashi Sakamoto if (get_register_params(dice, &tx_params, &rx_params) == 0) { 611e9f21129STakashi Sakamoto amdtp_domain_stop(&dice->domain); 612e9f21129STakashi Sakamoto 6138cc1a8abSTakashi Sakamoto stop_streams(dice, AMDTP_IN_STREAM, &tx_params); 6148cc1a8abSTakashi Sakamoto stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); 615436b5abeSTakashi Sakamoto } 6166eb6c81eSTakashi Sakamoto } 6176eb6c81eSTakashi Sakamoto 618b60152f7STakashi Sakamoto int snd_dice_stream_detect_current_formats(struct snd_dice *dice) 619b60152f7STakashi Sakamoto { 620b60152f7STakashi Sakamoto unsigned int rate; 621b60152f7STakashi Sakamoto enum snd_dice_rate_mode mode; 622b60152f7STakashi Sakamoto __be32 reg[2]; 623b60152f7STakashi Sakamoto struct reg_params tx_params, rx_params; 624b60152f7STakashi Sakamoto int i; 625b60152f7STakashi Sakamoto int err; 626b60152f7STakashi Sakamoto 62758579c05STakashi Sakamoto /* If extended protocol is available, detect detail spec. */ 62858579c05STakashi Sakamoto err = snd_dice_detect_extension_formats(dice); 62958579c05STakashi Sakamoto if (err >= 0) 63058579c05STakashi Sakamoto return err; 63158579c05STakashi Sakamoto 632b60152f7STakashi Sakamoto /* 633b60152f7STakashi Sakamoto * Available stream format is restricted at current mode of sampling 634b60152f7STakashi Sakamoto * clock. 635b60152f7STakashi Sakamoto */ 636b60152f7STakashi Sakamoto err = snd_dice_transaction_get_rate(dice, &rate); 637b60152f7STakashi Sakamoto if (err < 0) 638b60152f7STakashi Sakamoto return err; 639b60152f7STakashi Sakamoto 640b60152f7STakashi Sakamoto err = snd_dice_stream_get_rate_mode(dice, rate, &mode); 641b60152f7STakashi Sakamoto if (err < 0) 642b60152f7STakashi Sakamoto return err; 643b60152f7STakashi Sakamoto 644b60152f7STakashi Sakamoto /* 645b60152f7STakashi Sakamoto * Just after owning the unit (GLOBAL_OWNER), the unit can return 646b60152f7STakashi Sakamoto * invalid stream formats. Selecting clock parameters have an effect 647b60152f7STakashi Sakamoto * for the unit to refine it. 648b60152f7STakashi Sakamoto */ 649afa617f2STakashi Sakamoto err = ensure_phase_lock(dice, rate); 650b60152f7STakashi Sakamoto if (err < 0) 651b60152f7STakashi Sakamoto return err; 652b60152f7STakashi Sakamoto 653b60152f7STakashi Sakamoto err = get_register_params(dice, &tx_params, &rx_params); 654b60152f7STakashi Sakamoto if (err < 0) 655b60152f7STakashi Sakamoto return err; 656b60152f7STakashi Sakamoto 657b60152f7STakashi Sakamoto for (i = 0; i < tx_params.count; ++i) { 658b60152f7STakashi Sakamoto err = snd_dice_transaction_read_tx(dice, 659b60152f7STakashi Sakamoto tx_params.size * i + TX_NUMBER_AUDIO, 660b60152f7STakashi Sakamoto reg, sizeof(reg)); 661b60152f7STakashi Sakamoto if (err < 0) 662b60152f7STakashi Sakamoto return err; 663b60152f7STakashi Sakamoto dice->tx_pcm_chs[i][mode] = be32_to_cpu(reg[0]); 664b60152f7STakashi Sakamoto dice->tx_midi_ports[i] = max_t(unsigned int, 665b60152f7STakashi Sakamoto be32_to_cpu(reg[1]), dice->tx_midi_ports[i]); 666b60152f7STakashi Sakamoto } 667b60152f7STakashi Sakamoto for (i = 0; i < rx_params.count; ++i) { 668b60152f7STakashi Sakamoto err = snd_dice_transaction_read_rx(dice, 669b60152f7STakashi Sakamoto rx_params.size * i + RX_NUMBER_AUDIO, 670b60152f7STakashi Sakamoto reg, sizeof(reg)); 671b60152f7STakashi Sakamoto if (err < 0) 672b60152f7STakashi Sakamoto return err; 673b60152f7STakashi Sakamoto dice->rx_pcm_chs[i][mode] = be32_to_cpu(reg[0]); 674b60152f7STakashi Sakamoto dice->rx_midi_ports[i] = max_t(unsigned int, 675b60152f7STakashi Sakamoto be32_to_cpu(reg[1]), dice->rx_midi_ports[i]); 676b60152f7STakashi Sakamoto } 677b60152f7STakashi Sakamoto 678b60152f7STakashi Sakamoto return 0; 679b60152f7STakashi Sakamoto } 680b60152f7STakashi Sakamoto 6816eb6c81eSTakashi Sakamoto static void dice_lock_changed(struct snd_dice *dice) 6826eb6c81eSTakashi Sakamoto { 6836eb6c81eSTakashi Sakamoto dice->dev_lock_changed = true; 6846eb6c81eSTakashi Sakamoto wake_up(&dice->hwdep_wait); 6856eb6c81eSTakashi Sakamoto } 6866eb6c81eSTakashi Sakamoto 6876eb6c81eSTakashi Sakamoto int snd_dice_stream_lock_try(struct snd_dice *dice) 6886eb6c81eSTakashi Sakamoto { 6896eb6c81eSTakashi Sakamoto int err; 6906eb6c81eSTakashi Sakamoto 6916eb6c81eSTakashi Sakamoto spin_lock_irq(&dice->lock); 6926eb6c81eSTakashi Sakamoto 6936eb6c81eSTakashi Sakamoto if (dice->dev_lock_count < 0) { 6946eb6c81eSTakashi Sakamoto err = -EBUSY; 6956eb6c81eSTakashi Sakamoto goto out; 6966eb6c81eSTakashi Sakamoto } 6976eb6c81eSTakashi Sakamoto 6986eb6c81eSTakashi Sakamoto if (dice->dev_lock_count++ == 0) 6996eb6c81eSTakashi Sakamoto dice_lock_changed(dice); 7006eb6c81eSTakashi Sakamoto err = 0; 7016eb6c81eSTakashi Sakamoto out: 7026eb6c81eSTakashi Sakamoto spin_unlock_irq(&dice->lock); 7036eb6c81eSTakashi Sakamoto return err; 7046eb6c81eSTakashi Sakamoto } 7056eb6c81eSTakashi Sakamoto 7066eb6c81eSTakashi Sakamoto void snd_dice_stream_lock_release(struct snd_dice *dice) 7076eb6c81eSTakashi Sakamoto { 7086eb6c81eSTakashi Sakamoto spin_lock_irq(&dice->lock); 7096eb6c81eSTakashi Sakamoto 7106eb6c81eSTakashi Sakamoto if (WARN_ON(dice->dev_lock_count <= 0)) 7116eb6c81eSTakashi Sakamoto goto out; 7126eb6c81eSTakashi Sakamoto 7136eb6c81eSTakashi Sakamoto if (--dice->dev_lock_count == 0) 7146eb6c81eSTakashi Sakamoto dice_lock_changed(dice); 7156eb6c81eSTakashi Sakamoto out: 7166eb6c81eSTakashi Sakamoto spin_unlock_irq(&dice->lock); 7176eb6c81eSTakashi Sakamoto } 718