18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ALSA driver for Echoaudio soundcards. 48c2ecf20Sopenharmony_ci * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it> 58c2ecf20Sopenharmony_ci * Copyright (C) 2020 Mark Hills <mark@xwax.org> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ciMODULE_AUTHOR("Giuliano Pochini <pochini@shiny.it>"); 118c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 128c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Echoaudio " ECHOCARD_NAME " soundcards driver"); 138c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Echoaudio," ECHOCARD_NAME "}}"); 148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_echo_ids); 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 178c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 188c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 218c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for " ECHOCARD_NAME " soundcard."); 228c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for " ECHOCARD_NAME " soundcard."); 248c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 258c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable " ECHOCARD_NAME " soundcard."); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic const unsigned int channels_list[10] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 999999}; 288c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_output_gain, -12800, 100, 1); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int get_firmware(const struct firmware **fw_entry, 338c2ecf20Sopenharmony_ci struct echoaudio *chip, const short fw_index) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci int err; 368c2ecf20Sopenharmony_ci char name[30]; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 398c2ecf20Sopenharmony_ci if (chip->fw_cache[fw_index]) { 408c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, 418c2ecf20Sopenharmony_ci "firmware requested: %s is cached\n", 428c2ecf20Sopenharmony_ci card_fw[fw_index].data); 438c2ecf20Sopenharmony_ci *fw_entry = chip->fw_cache[fw_index]; 448c2ecf20Sopenharmony_ci return 0; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci#endif 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, 498c2ecf20Sopenharmony_ci "firmware requested: %s\n", card_fw[fw_index].data); 508c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "ea/%s", card_fw[fw_index].data); 518c2ecf20Sopenharmony_ci err = request_firmware(fw_entry, name, &chip->pci->dev); 528c2ecf20Sopenharmony_ci if (err < 0) 538c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 548c2ecf20Sopenharmony_ci "get_firmware(): Firmware not available (%d)\n", err); 558c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 568c2ecf20Sopenharmony_ci else 578c2ecf20Sopenharmony_ci chip->fw_cache[fw_index] = *fw_entry; 588c2ecf20Sopenharmony_ci#endif 598c2ecf20Sopenharmony_ci return err; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void free_firmware(const struct firmware *fw_entry, 658c2ecf20Sopenharmony_ci struct echoaudio *chip) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 688c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "firmware not released (kept in cache)\n"); 698c2ecf20Sopenharmony_ci#else 708c2ecf20Sopenharmony_ci release_firmware(fw_entry); 718c2ecf20Sopenharmony_ci#endif 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void free_firmware_cache(struct echoaudio *chip) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 798c2ecf20Sopenharmony_ci int i; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci for (i = 0; i < 8 ; i++) 828c2ecf20Sopenharmony_ci if (chip->fw_cache[i]) { 838c2ecf20Sopenharmony_ci release_firmware(chip->fw_cache[i]); 848c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "release_firmware(%d)\n", i); 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#endif 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/****************************************************************************** 938c2ecf20Sopenharmony_ci PCM interface 948c2ecf20Sopenharmony_ci******************************************************************************/ 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic void audiopipe_free(struct snd_pcm_runtime *runtime) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct audiopipe *pipe = runtime->private_data; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (pipe->sgpage.area) 1018c2ecf20Sopenharmony_ci snd_dma_free_pages(&pipe->sgpage); 1028c2ecf20Sopenharmony_ci kfree(pipe); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params, 1088c2ecf20Sopenharmony_ci struct snd_pcm_hw_rule *rule) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct snd_interval *c = hw_param_interval(params, 1118c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS); 1128c2ecf20Sopenharmony_ci struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); 1138c2ecf20Sopenharmony_ci struct snd_mask fmt; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci snd_mask_any(&fmt); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci#ifndef ECHOCARD_HAS_STEREO_BIG_ENDIAN32 1188c2ecf20Sopenharmony_ci /* >=2 channels cannot be S32_BE */ 1198c2ecf20Sopenharmony_ci if (c->min == 2) { 1208c2ecf20Sopenharmony_ci fmt.bits[0] &= ~SNDRV_PCM_FMTBIT_S32_BE; 1218c2ecf20Sopenharmony_ci return snd_mask_refine(f, &fmt); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci#endif 1248c2ecf20Sopenharmony_ci /* > 2 channels cannot be U8 and S32_BE */ 1258c2ecf20Sopenharmony_ci if (c->min > 2) { 1268c2ecf20Sopenharmony_ci fmt.bits[0] &= ~(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_BE); 1278c2ecf20Sopenharmony_ci return snd_mask_refine(f, &fmt); 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci /* Mono is ok with any format */ 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params, 1368c2ecf20Sopenharmony_ci struct snd_pcm_hw_rule *rule) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct snd_interval *c = hw_param_interval(params, 1398c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS); 1408c2ecf20Sopenharmony_ci struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); 1418c2ecf20Sopenharmony_ci struct snd_interval ch; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci snd_interval_any(&ch); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* S32_BE is mono (and stereo) only */ 1468c2ecf20Sopenharmony_ci if (f->bits[0] == SNDRV_PCM_FMTBIT_S32_BE) { 1478c2ecf20Sopenharmony_ci ch.min = 1; 1488c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32 1498c2ecf20Sopenharmony_ci ch.max = 2; 1508c2ecf20Sopenharmony_ci#else 1518c2ecf20Sopenharmony_ci ch.max = 1; 1528c2ecf20Sopenharmony_ci#endif 1538c2ecf20Sopenharmony_ci ch.integer = 1; 1548c2ecf20Sopenharmony_ci return snd_interval_refine(c, &ch); 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci /* U8 can be only mono or stereo */ 1578c2ecf20Sopenharmony_ci if (f->bits[0] == SNDRV_PCM_FMTBIT_U8) { 1588c2ecf20Sopenharmony_ci ch.min = 1; 1598c2ecf20Sopenharmony_ci ch.max = 2; 1608c2ecf20Sopenharmony_ci ch.integer = 1; 1618c2ecf20Sopenharmony_ci return snd_interval_refine(c, &ch); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci /* S16_LE, S24_3LE and S32_LE support any number of channels. */ 1648c2ecf20Sopenharmony_ci return 0; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic int hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params, 1708c2ecf20Sopenharmony_ci struct snd_pcm_hw_rule *rule) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct snd_interval *c = hw_param_interval(params, 1738c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS); 1748c2ecf20Sopenharmony_ci struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); 1758c2ecf20Sopenharmony_ci struct snd_mask fmt; 1768c2ecf20Sopenharmony_ci u64 fmask; 1778c2ecf20Sopenharmony_ci snd_mask_any(&fmt); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci fmask = fmt.bits[0] + ((u64)fmt.bits[1] << 32); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* >2 channels must be S16_LE, S24_3LE or S32_LE */ 1828c2ecf20Sopenharmony_ci if (c->min > 2) { 1838c2ecf20Sopenharmony_ci fmask &= SNDRV_PCM_FMTBIT_S16_LE | 1848c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE | 1858c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE; 1868c2ecf20Sopenharmony_ci /* 1 channel must be S32_BE or S32_LE */ 1878c2ecf20Sopenharmony_ci } else if (c->max == 1) 1888c2ecf20Sopenharmony_ci fmask &= SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE; 1898c2ecf20Sopenharmony_ci#ifndef ECHOCARD_HAS_STEREO_BIG_ENDIAN32 1908c2ecf20Sopenharmony_ci /* 2 channels cannot be S32_BE */ 1918c2ecf20Sopenharmony_ci else if (c->min == 2 && c->max == 2) 1928c2ecf20Sopenharmony_ci fmask &= ~SNDRV_PCM_FMTBIT_S32_BE; 1938c2ecf20Sopenharmony_ci#endif 1948c2ecf20Sopenharmony_ci else 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci fmt.bits[0] &= (u32)fmask; 1988c2ecf20Sopenharmony_ci fmt.bits[1] &= (u32)(fmask >> 32); 1998c2ecf20Sopenharmony_ci return snd_mask_refine(f, &fmt); 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params, 2058c2ecf20Sopenharmony_ci struct snd_pcm_hw_rule *rule) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct snd_interval *c = hw_param_interval(params, 2088c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS); 2098c2ecf20Sopenharmony_ci struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); 2108c2ecf20Sopenharmony_ci struct snd_interval ch; 2118c2ecf20Sopenharmony_ci u64 fmask; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci snd_interval_any(&ch); 2148c2ecf20Sopenharmony_ci ch.integer = 1; 2158c2ecf20Sopenharmony_ci fmask = f->bits[0] + ((u64)f->bits[1] << 32); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* S32_BE is mono (and stereo) only */ 2188c2ecf20Sopenharmony_ci if (fmask == SNDRV_PCM_FMTBIT_S32_BE) { 2198c2ecf20Sopenharmony_ci ch.min = 1; 2208c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32 2218c2ecf20Sopenharmony_ci ch.max = 2; 2228c2ecf20Sopenharmony_ci#else 2238c2ecf20Sopenharmony_ci ch.max = 1; 2248c2ecf20Sopenharmony_ci#endif 2258c2ecf20Sopenharmony_ci /* U8 is stereo only */ 2268c2ecf20Sopenharmony_ci } else if (fmask == SNDRV_PCM_FMTBIT_U8) 2278c2ecf20Sopenharmony_ci ch.min = ch.max = 2; 2288c2ecf20Sopenharmony_ci /* S16_LE and S24_3LE must be at least stereo */ 2298c2ecf20Sopenharmony_ci else if (!(fmask & ~(SNDRV_PCM_FMTBIT_S16_LE | 2308c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE))) 2318c2ecf20Sopenharmony_ci ch.min = 2; 2328c2ecf20Sopenharmony_ci else 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return snd_interval_refine(c, &ch); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci/* Since the sample rate is a global setting, do allow the user to change the 2418c2ecf20Sopenharmony_cisample rate only if there is only one pcm device open. */ 2428c2ecf20Sopenharmony_cistatic int hw_rule_sample_rate(struct snd_pcm_hw_params *params, 2438c2ecf20Sopenharmony_ci struct snd_pcm_hw_rule *rule) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci struct snd_interval *rate = hw_param_interval(params, 2468c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE); 2478c2ecf20Sopenharmony_ci struct echoaudio *chip = rule->private; 2488c2ecf20Sopenharmony_ci struct snd_interval fixed; 2498c2ecf20Sopenharmony_ci int err; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci mutex_lock(&chip->mode_mutex); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (chip->can_set_rate) { 2548c2ecf20Sopenharmony_ci err = 0; 2558c2ecf20Sopenharmony_ci } else { 2568c2ecf20Sopenharmony_ci snd_interval_any(&fixed); 2578c2ecf20Sopenharmony_ci fixed.min = fixed.max = chip->sample_rate; 2588c2ecf20Sopenharmony_ci err = snd_interval_refine(rate, &fixed); 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci mutex_unlock(&chip->mode_mutex); 2628c2ecf20Sopenharmony_ci return err; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic int pcm_open(struct snd_pcm_substream *substream, 2678c2ecf20Sopenharmony_ci signed char max_channels) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct echoaudio *chip; 2708c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 2718c2ecf20Sopenharmony_ci struct audiopipe *pipe; 2728c2ecf20Sopenharmony_ci int err, i; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (max_channels <= 0) 2758c2ecf20Sopenharmony_ci return -EAGAIN; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci chip = snd_pcm_substream_chip(substream); 2788c2ecf20Sopenharmony_ci runtime = substream->runtime; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci pipe = kzalloc(sizeof(struct audiopipe), GFP_KERNEL); 2818c2ecf20Sopenharmony_ci if (!pipe) 2828c2ecf20Sopenharmony_ci return -ENOMEM; 2838c2ecf20Sopenharmony_ci pipe->index = -1; /* Not configured yet */ 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* Set up hw capabilities and contraints */ 2868c2ecf20Sopenharmony_ci memcpy(&pipe->hw, &pcm_hardware_skel, sizeof(struct snd_pcm_hardware)); 2878c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "max_channels=%d\n", max_channels); 2888c2ecf20Sopenharmony_ci pipe->constr.list = channels_list; 2898c2ecf20Sopenharmony_ci pipe->constr.mask = 0; 2908c2ecf20Sopenharmony_ci for (i = 0; channels_list[i] <= max_channels; i++); 2918c2ecf20Sopenharmony_ci pipe->constr.count = i; 2928c2ecf20Sopenharmony_ci if (pipe->hw.channels_max > max_channels) 2938c2ecf20Sopenharmony_ci pipe->hw.channels_max = max_channels; 2948c2ecf20Sopenharmony_ci if (chip->digital_mode == DIGITAL_MODE_ADAT) { 2958c2ecf20Sopenharmony_ci pipe->hw.rate_max = 48000; 2968c2ecf20Sopenharmony_ci pipe->hw.rates &= SNDRV_PCM_RATE_8000_48000; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci runtime->hw = pipe->hw; 3008c2ecf20Sopenharmony_ci runtime->private_data = pipe; 3018c2ecf20Sopenharmony_ci runtime->private_free = audiopipe_free; 3028c2ecf20Sopenharmony_ci snd_pcm_set_sync(substream); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* Only mono and any even number of channels are allowed */ 3058c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_constraint_list(runtime, 0, 3068c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, 3078c2ecf20Sopenharmony_ci &pipe->constr)) < 0) 3088c2ecf20Sopenharmony_ci return err; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* All periods should have the same size */ 3118c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_constraint_integer(runtime, 3128c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIODS)) < 0) 3138c2ecf20Sopenharmony_ci return err; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* The hw accesses memory in chunks 32 frames long and they should be 3168c2ecf20Sopenharmony_ci 32-bytes-aligned. It's not a requirement, but it seems that IRQs are 3178c2ecf20Sopenharmony_ci generated with a resolution of 32 frames. Thus we need the following */ 3188c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_constraint_step(runtime, 0, 3198c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 3208c2ecf20Sopenharmony_ci 32)) < 0) 3218c2ecf20Sopenharmony_ci return err; 3228c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_constraint_step(runtime, 0, 3238c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 3248c2ecf20Sopenharmony_ci 32)) < 0) 3258c2ecf20Sopenharmony_ci return err; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, 3288c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 3298c2ecf20Sopenharmony_ci hw_rule_sample_rate, chip, 3308c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, -1)) < 0) 3318c2ecf20Sopenharmony_ci return err; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* Allocate a page for the scatter-gather list */ 3348c2ecf20Sopenharmony_ci if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, 3358c2ecf20Sopenharmony_ci &chip->pci->dev, 3368c2ecf20Sopenharmony_ci PAGE_SIZE, &pipe->sgpage)) < 0) { 3378c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "s-g list allocation failed\n"); 3388c2ecf20Sopenharmony_ci return err; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* 3428c2ecf20Sopenharmony_ci * Sole ownership required to set the rate 3438c2ecf20Sopenharmony_ci */ 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "pcm_open opencount=%d can_set_rate=%d, rate_set=%d", 3468c2ecf20Sopenharmony_ci chip->opencount, chip->can_set_rate, chip->rate_set); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci chip->opencount++; 3498c2ecf20Sopenharmony_ci if (chip->opencount > 1 && chip->rate_set) 3508c2ecf20Sopenharmony_ci chip->can_set_rate = 0; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return 0; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic int pcm_analog_in_open(struct snd_pcm_substream *substream) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct echoaudio *chip = snd_pcm_substream_chip(substream); 3608c2ecf20Sopenharmony_ci int err; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if ((err = pcm_open(substream, num_analog_busses_in(chip) - 3638c2ecf20Sopenharmony_ci substream->number)) < 0) 3648c2ecf20Sopenharmony_ci return err; 3658c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, 3668c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, 3678c2ecf20Sopenharmony_ci hw_rule_capture_channels_by_format, NULL, 3688c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0) 3698c2ecf20Sopenharmony_ci return err; 3708c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, 3718c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_FORMAT, 3728c2ecf20Sopenharmony_ci hw_rule_capture_format_by_channels, NULL, 3738c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0) 3748c2ecf20Sopenharmony_ci return err; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int pcm_analog_out_open(struct snd_pcm_substream *substream) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct echoaudio *chip = snd_pcm_substream_chip(substream); 3848c2ecf20Sopenharmony_ci int max_channels, err; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_VMIXER 3878c2ecf20Sopenharmony_ci max_channels = num_pipes_out(chip); 3888c2ecf20Sopenharmony_ci#else 3898c2ecf20Sopenharmony_ci max_channels = num_analog_busses_out(chip); 3908c2ecf20Sopenharmony_ci#endif 3918c2ecf20Sopenharmony_ci if ((err = pcm_open(substream, max_channels - substream->number)) < 0) 3928c2ecf20Sopenharmony_ci return err; 3938c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, 3948c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, 3958c2ecf20Sopenharmony_ci hw_rule_playback_channels_by_format, 3968c2ecf20Sopenharmony_ci NULL, 3978c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0) 3988c2ecf20Sopenharmony_ci return err; 3998c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, 4008c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_FORMAT, 4018c2ecf20Sopenharmony_ci hw_rule_playback_format_by_channels, 4028c2ecf20Sopenharmony_ci NULL, 4038c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0) 4048c2ecf20Sopenharmony_ci return err; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci return 0; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic int pcm_digital_in_open(struct snd_pcm_substream *substream) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci struct echoaudio *chip = snd_pcm_substream_chip(substream); 4168c2ecf20Sopenharmony_ci int err, max_channels; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci max_channels = num_digital_busses_in(chip) - substream->number; 4198c2ecf20Sopenharmony_ci mutex_lock(&chip->mode_mutex); 4208c2ecf20Sopenharmony_ci if (chip->digital_mode == DIGITAL_MODE_ADAT) 4218c2ecf20Sopenharmony_ci err = pcm_open(substream, max_channels); 4228c2ecf20Sopenharmony_ci else /* If the card has ADAT, subtract the 6 channels 4238c2ecf20Sopenharmony_ci * that S/PDIF doesn't have 4248c2ecf20Sopenharmony_ci */ 4258c2ecf20Sopenharmony_ci err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (err < 0) 4288c2ecf20Sopenharmony_ci goto din_exit; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, 4318c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, 4328c2ecf20Sopenharmony_ci hw_rule_capture_channels_by_format, NULL, 4338c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0) 4348c2ecf20Sopenharmony_ci goto din_exit; 4358c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, 4368c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_FORMAT, 4378c2ecf20Sopenharmony_ci hw_rule_capture_format_by_channels, NULL, 4388c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0) 4398c2ecf20Sopenharmony_ci goto din_exit; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cidin_exit: 4428c2ecf20Sopenharmony_ci mutex_unlock(&chip->mode_mutex); 4438c2ecf20Sopenharmony_ci return err; 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci#ifndef ECHOCARD_HAS_VMIXER /* See the note in snd_echo_new_pcm() */ 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic int pcm_digital_out_open(struct snd_pcm_substream *substream) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct echoaudio *chip = snd_pcm_substream_chip(substream); 4538c2ecf20Sopenharmony_ci int err, max_channels; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci max_channels = num_digital_busses_out(chip) - substream->number; 4568c2ecf20Sopenharmony_ci mutex_lock(&chip->mode_mutex); 4578c2ecf20Sopenharmony_ci if (chip->digital_mode == DIGITAL_MODE_ADAT) 4588c2ecf20Sopenharmony_ci err = pcm_open(substream, max_channels); 4598c2ecf20Sopenharmony_ci else /* If the card has ADAT, subtract the 6 channels 4608c2ecf20Sopenharmony_ci * that S/PDIF doesn't have 4618c2ecf20Sopenharmony_ci */ 4628c2ecf20Sopenharmony_ci err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (err < 0) 4658c2ecf20Sopenharmony_ci goto dout_exit; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, 4688c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, 4698c2ecf20Sopenharmony_ci hw_rule_playback_channels_by_format, 4708c2ecf20Sopenharmony_ci NULL, SNDRV_PCM_HW_PARAM_FORMAT, 4718c2ecf20Sopenharmony_ci -1)) < 0) 4728c2ecf20Sopenharmony_ci goto dout_exit; 4738c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, 4748c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_FORMAT, 4758c2ecf20Sopenharmony_ci hw_rule_playback_format_by_channels, 4768c2ecf20Sopenharmony_ci NULL, SNDRV_PCM_HW_PARAM_CHANNELS, 4778c2ecf20Sopenharmony_ci -1)) < 0) 4788c2ecf20Sopenharmony_ci goto dout_exit; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cidout_exit: 4818c2ecf20Sopenharmony_ci mutex_unlock(&chip->mode_mutex); 4828c2ecf20Sopenharmony_ci return err; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci#endif /* !ECHOCARD_HAS_VMIXER */ 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IO */ 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic int pcm_close(struct snd_pcm_substream *substream) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct echoaudio *chip = snd_pcm_substream_chip(substream); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* Nothing to do here. Audio is already off and pipe will be 4968c2ecf20Sopenharmony_ci * freed by its callback 4978c2ecf20Sopenharmony_ci */ 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci mutex_lock(&chip->mode_mutex); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "pcm_open opencount=%d can_set_rate=%d, rate_set=%d", 5028c2ecf20Sopenharmony_ci chip->opencount, chip->can_set_rate, chip->rate_set); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci chip->opencount--; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci switch (chip->opencount) { 5078c2ecf20Sopenharmony_ci case 1: 5088c2ecf20Sopenharmony_ci chip->can_set_rate = 1; 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci case 0: 5128c2ecf20Sopenharmony_ci chip->rate_set = 0; 5138c2ecf20Sopenharmony_ci break; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci mutex_unlock(&chip->mode_mutex); 5178c2ecf20Sopenharmony_ci return 0; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci/* Channel allocation and scatter-gather list setup */ 5238c2ecf20Sopenharmony_cistatic int init_engine(struct snd_pcm_substream *substream, 5248c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params, 5258c2ecf20Sopenharmony_ci int pipe_index, int interleave) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci struct echoaudio *chip; 5288c2ecf20Sopenharmony_ci int err, per, rest, page, edge, offs; 5298c2ecf20Sopenharmony_ci struct audiopipe *pipe; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci chip = snd_pcm_substream_chip(substream); 5328c2ecf20Sopenharmony_ci pipe = (struct audiopipe *) substream->runtime->private_data; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* Sets up che hardware. If it's already initialized, reset and 5358c2ecf20Sopenharmony_ci * redo with the new parameters 5368c2ecf20Sopenharmony_ci */ 5378c2ecf20Sopenharmony_ci spin_lock_irq(&chip->lock); 5388c2ecf20Sopenharmony_ci if (pipe->index >= 0) { 5398c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "hwp_ie free(%d)\n", pipe->index); 5408c2ecf20Sopenharmony_ci err = free_pipes(chip, pipe); 5418c2ecf20Sopenharmony_ci snd_BUG_ON(err); 5428c2ecf20Sopenharmony_ci chip->substream[pipe->index] = NULL; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci err = allocate_pipes(chip, pipe, pipe_index, interleave); 5468c2ecf20Sopenharmony_ci if (err < 0) { 5478c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 5488c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "allocate_pipes(%d) err=%d\n", 5498c2ecf20Sopenharmony_ci pipe_index, err); 5508c2ecf20Sopenharmony_ci return err; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 5538c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "allocate_pipes()=%d\n", pipe_index); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, 5568c2ecf20Sopenharmony_ci "pcm_hw_params (bufsize=%dB periods=%d persize=%dB)\n", 5578c2ecf20Sopenharmony_ci params_buffer_bytes(hw_params), params_periods(hw_params), 5588c2ecf20Sopenharmony_ci params_period_bytes(hw_params)); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci sglist_init(chip, pipe); 5618c2ecf20Sopenharmony_ci edge = PAGE_SIZE; 5628c2ecf20Sopenharmony_ci for (offs = page = per = 0; offs < params_buffer_bytes(hw_params); 5638c2ecf20Sopenharmony_ci per++) { 5648c2ecf20Sopenharmony_ci rest = params_period_bytes(hw_params); 5658c2ecf20Sopenharmony_ci if (offs + rest > params_buffer_bytes(hw_params)) 5668c2ecf20Sopenharmony_ci rest = params_buffer_bytes(hw_params) - offs; 5678c2ecf20Sopenharmony_ci while (rest) { 5688c2ecf20Sopenharmony_ci dma_addr_t addr; 5698c2ecf20Sopenharmony_ci addr = snd_pcm_sgbuf_get_addr(substream, offs); 5708c2ecf20Sopenharmony_ci if (rest <= edge - offs) { 5718c2ecf20Sopenharmony_ci sglist_add_mapping(chip, pipe, addr, rest); 5728c2ecf20Sopenharmony_ci sglist_add_irq(chip, pipe); 5738c2ecf20Sopenharmony_ci offs += rest; 5748c2ecf20Sopenharmony_ci rest = 0; 5758c2ecf20Sopenharmony_ci } else { 5768c2ecf20Sopenharmony_ci sglist_add_mapping(chip, pipe, addr, 5778c2ecf20Sopenharmony_ci edge - offs); 5788c2ecf20Sopenharmony_ci rest -= edge - offs; 5798c2ecf20Sopenharmony_ci offs = edge; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci if (offs == edge) { 5828c2ecf20Sopenharmony_ci edge += PAGE_SIZE; 5838c2ecf20Sopenharmony_ci page++; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci /* Close the ring buffer */ 5898c2ecf20Sopenharmony_ci sglist_wrap(chip, pipe); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci /* This stuff is used by the irq handler, so it must be 5928c2ecf20Sopenharmony_ci * initialized before chip->substream 5938c2ecf20Sopenharmony_ci */ 5948c2ecf20Sopenharmony_ci pipe->last_period = 0; 5958c2ecf20Sopenharmony_ci pipe->last_counter = 0; 5968c2ecf20Sopenharmony_ci pipe->position = 0; 5978c2ecf20Sopenharmony_ci smp_wmb(); 5988c2ecf20Sopenharmony_ci chip->substream[pipe_index] = substream; 5998c2ecf20Sopenharmony_ci chip->rate_set = 1; 6008c2ecf20Sopenharmony_ci spin_lock_irq(&chip->lock); 6018c2ecf20Sopenharmony_ci set_sample_rate(chip, hw_params->rate_num / hw_params->rate_den); 6028c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 6038c2ecf20Sopenharmony_ci return 0; 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic int pcm_analog_in_hw_params(struct snd_pcm_substream *substream, 6098c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci struct echoaudio *chip = snd_pcm_substream_chip(substream); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci return init_engine(substream, hw_params, px_analog_in(chip) + 6148c2ecf20Sopenharmony_ci substream->number, params_channels(hw_params)); 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic int pcm_analog_out_hw_params(struct snd_pcm_substream *substream, 6208c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci return init_engine(substream, hw_params, substream->number, 6238c2ecf20Sopenharmony_ci params_channels(hw_params)); 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic int pcm_digital_in_hw_params(struct snd_pcm_substream *substream, 6318c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci struct echoaudio *chip = snd_pcm_substream_chip(substream); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci return init_engine(substream, hw_params, px_digital_in(chip) + 6368c2ecf20Sopenharmony_ci substream->number, params_channels(hw_params)); 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci#ifndef ECHOCARD_HAS_VMIXER /* See the note in snd_echo_new_pcm() */ 6428c2ecf20Sopenharmony_cistatic int pcm_digital_out_hw_params(struct snd_pcm_substream *substream, 6438c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci struct echoaudio *chip = snd_pcm_substream_chip(substream); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci return init_engine(substream, hw_params, px_digital_out(chip) + 6488c2ecf20Sopenharmony_ci substream->number, params_channels(hw_params)); 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci#endif /* !ECHOCARD_HAS_VMIXER */ 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IO */ 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cistatic int pcm_hw_free(struct snd_pcm_substream *substream) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci struct echoaudio *chip; 6598c2ecf20Sopenharmony_ci struct audiopipe *pipe; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci chip = snd_pcm_substream_chip(substream); 6628c2ecf20Sopenharmony_ci pipe = (struct audiopipe *) substream->runtime->private_data; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci spin_lock_irq(&chip->lock); 6658c2ecf20Sopenharmony_ci if (pipe->index >= 0) { 6668c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "pcm_hw_free(%d)\n", pipe->index); 6678c2ecf20Sopenharmony_ci free_pipes(chip, pipe); 6688c2ecf20Sopenharmony_ci chip->substream[pipe->index] = NULL; 6698c2ecf20Sopenharmony_ci pipe->index = -1; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci return 0; 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic int pcm_prepare(struct snd_pcm_substream *substream) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci struct echoaudio *chip = snd_pcm_substream_chip(substream); 6818c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 6828c2ecf20Sopenharmony_ci struct audioformat format; 6838c2ecf20Sopenharmony_ci int pipe_index = ((struct audiopipe *)runtime->private_data)->index; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "Prepare rate=%d format=%d channels=%d\n", 6868c2ecf20Sopenharmony_ci runtime->rate, runtime->format, runtime->channels); 6878c2ecf20Sopenharmony_ci format.interleave = runtime->channels; 6888c2ecf20Sopenharmony_ci format.data_are_bigendian = 0; 6898c2ecf20Sopenharmony_ci format.mono_to_stereo = 0; 6908c2ecf20Sopenharmony_ci switch (runtime->format) { 6918c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_U8: 6928c2ecf20Sopenharmony_ci format.bits_per_sample = 8; 6938c2ecf20Sopenharmony_ci break; 6948c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 6958c2ecf20Sopenharmony_ci format.bits_per_sample = 16; 6968c2ecf20Sopenharmony_ci break; 6978c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_3LE: 6988c2ecf20Sopenharmony_ci format.bits_per_sample = 24; 6998c2ecf20Sopenharmony_ci break; 7008c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_BE: 7018c2ecf20Sopenharmony_ci format.data_are_bigendian = 1; 7028c2ecf20Sopenharmony_ci fallthrough; 7038c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 7048c2ecf20Sopenharmony_ci format.bits_per_sample = 32; 7058c2ecf20Sopenharmony_ci break; 7068c2ecf20Sopenharmony_ci default: 7078c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 7088c2ecf20Sopenharmony_ci "Prepare error: unsupported format %d\n", 7098c2ecf20Sopenharmony_ci runtime->format); 7108c2ecf20Sopenharmony_ci return -EINVAL; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (snd_BUG_ON(pipe_index >= px_num(chip))) 7148c2ecf20Sopenharmony_ci return -EINVAL; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci /* 7178c2ecf20Sopenharmony_ci * We passed checks we can do independently; now take 7188c2ecf20Sopenharmony_ci * exclusive control 7198c2ecf20Sopenharmony_ci */ 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci spin_lock_irq(&chip->lock); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (snd_BUG_ON(!is_pipe_allocated(chip, pipe_index))) { 7248c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 7258c2ecf20Sopenharmony_ci return -EINVAL; 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci set_audio_format(chip, pipe_index, &format); 7298c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci return 0; 7328c2ecf20Sopenharmony_ci} 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cistatic int pcm_trigger(struct snd_pcm_substream *substream, int cmd) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci struct echoaudio *chip = snd_pcm_substream_chip(substream); 7398c2ecf20Sopenharmony_ci struct audiopipe *pipe; 7408c2ecf20Sopenharmony_ci int i, err; 7418c2ecf20Sopenharmony_ci u32 channelmask = 0; 7428c2ecf20Sopenharmony_ci struct snd_pcm_substream *s; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci snd_pcm_group_for_each_entry(s, substream) { 7458c2ecf20Sopenharmony_ci for (i = 0; i < DSP_MAXPIPES; i++) { 7468c2ecf20Sopenharmony_ci if (s == chip->substream[i]) { 7478c2ecf20Sopenharmony_ci channelmask |= 1 << i; 7488c2ecf20Sopenharmony_ci snd_pcm_trigger_done(s, substream); 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci spin_lock(&chip->lock); 7548c2ecf20Sopenharmony_ci switch (cmd) { 7558c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 7568c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 7578c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 7588c2ecf20Sopenharmony_ci for (i = 0; i < DSP_MAXPIPES; i++) { 7598c2ecf20Sopenharmony_ci if (channelmask & (1 << i)) { 7608c2ecf20Sopenharmony_ci pipe = chip->substream[i]->runtime->private_data; 7618c2ecf20Sopenharmony_ci switch (pipe->state) { 7628c2ecf20Sopenharmony_ci case PIPE_STATE_STOPPED: 7638c2ecf20Sopenharmony_ci pipe->last_period = 0; 7648c2ecf20Sopenharmony_ci pipe->last_counter = 0; 7658c2ecf20Sopenharmony_ci pipe->position = 0; 7668c2ecf20Sopenharmony_ci *pipe->dma_counter = 0; 7678c2ecf20Sopenharmony_ci fallthrough; 7688c2ecf20Sopenharmony_ci case PIPE_STATE_PAUSED: 7698c2ecf20Sopenharmony_ci pipe->state = PIPE_STATE_STARTED; 7708c2ecf20Sopenharmony_ci break; 7718c2ecf20Sopenharmony_ci case PIPE_STATE_STARTED: 7728c2ecf20Sopenharmony_ci break; 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci err = start_transport(chip, channelmask, 7778c2ecf20Sopenharmony_ci chip->pipe_cyclic_mask); 7788c2ecf20Sopenharmony_ci break; 7798c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 7808c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 7818c2ecf20Sopenharmony_ci for (i = 0; i < DSP_MAXPIPES; i++) { 7828c2ecf20Sopenharmony_ci if (channelmask & (1 << i)) { 7838c2ecf20Sopenharmony_ci pipe = chip->substream[i]->runtime->private_data; 7848c2ecf20Sopenharmony_ci pipe->state = PIPE_STATE_STOPPED; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci err = stop_transport(chip, channelmask); 7888c2ecf20Sopenharmony_ci break; 7898c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 7908c2ecf20Sopenharmony_ci for (i = 0; i < DSP_MAXPIPES; i++) { 7918c2ecf20Sopenharmony_ci if (channelmask & (1 << i)) { 7928c2ecf20Sopenharmony_ci pipe = chip->substream[i]->runtime->private_data; 7938c2ecf20Sopenharmony_ci pipe->state = PIPE_STATE_PAUSED; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci err = pause_transport(chip, channelmask); 7978c2ecf20Sopenharmony_ci break; 7988c2ecf20Sopenharmony_ci default: 7998c2ecf20Sopenharmony_ci err = -EINVAL; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci spin_unlock(&chip->lock); 8028c2ecf20Sopenharmony_ci return err; 8038c2ecf20Sopenharmony_ci} 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 8108c2ecf20Sopenharmony_ci struct audiopipe *pipe = runtime->private_data; 8118c2ecf20Sopenharmony_ci u32 counter, step; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci /* 8148c2ecf20Sopenharmony_ci * IRQ handling runs concurrently. Do not share tracking of 8158c2ecf20Sopenharmony_ci * counter with it, which would race or require locking 8168c2ecf20Sopenharmony_ci */ 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci counter = le32_to_cpu(*pipe->dma_counter); /* presumed atomic */ 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci step = counter - pipe->last_counter; /* handles wrapping */ 8218c2ecf20Sopenharmony_ci pipe->last_counter = counter; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci /* counter doesn't neccessarily wrap on a multiple of 8248c2ecf20Sopenharmony_ci * buffer_size, so can't derive the position; must 8258c2ecf20Sopenharmony_ci * accumulate */ 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci pipe->position += step; 8288c2ecf20Sopenharmony_ci pipe->position %= frames_to_bytes(runtime, runtime->buffer_size); /* wrap */ 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci return bytes_to_frames(runtime, pipe->position); 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci/* pcm *_ops structures */ 8368c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops analog_playback_ops = { 8378c2ecf20Sopenharmony_ci .open = pcm_analog_out_open, 8388c2ecf20Sopenharmony_ci .close = pcm_close, 8398c2ecf20Sopenharmony_ci .hw_params = pcm_analog_out_hw_params, 8408c2ecf20Sopenharmony_ci .hw_free = pcm_hw_free, 8418c2ecf20Sopenharmony_ci .prepare = pcm_prepare, 8428c2ecf20Sopenharmony_ci .trigger = pcm_trigger, 8438c2ecf20Sopenharmony_ci .pointer = pcm_pointer, 8448c2ecf20Sopenharmony_ci}; 8458c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops analog_capture_ops = { 8468c2ecf20Sopenharmony_ci .open = pcm_analog_in_open, 8478c2ecf20Sopenharmony_ci .close = pcm_close, 8488c2ecf20Sopenharmony_ci .hw_params = pcm_analog_in_hw_params, 8498c2ecf20Sopenharmony_ci .hw_free = pcm_hw_free, 8508c2ecf20Sopenharmony_ci .prepare = pcm_prepare, 8518c2ecf20Sopenharmony_ci .trigger = pcm_trigger, 8528c2ecf20Sopenharmony_ci .pointer = pcm_pointer, 8538c2ecf20Sopenharmony_ci}; 8548c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO 8558c2ecf20Sopenharmony_ci#ifndef ECHOCARD_HAS_VMIXER 8568c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops digital_playback_ops = { 8578c2ecf20Sopenharmony_ci .open = pcm_digital_out_open, 8588c2ecf20Sopenharmony_ci .close = pcm_close, 8598c2ecf20Sopenharmony_ci .hw_params = pcm_digital_out_hw_params, 8608c2ecf20Sopenharmony_ci .hw_free = pcm_hw_free, 8618c2ecf20Sopenharmony_ci .prepare = pcm_prepare, 8628c2ecf20Sopenharmony_ci .trigger = pcm_trigger, 8638c2ecf20Sopenharmony_ci .pointer = pcm_pointer, 8648c2ecf20Sopenharmony_ci}; 8658c2ecf20Sopenharmony_ci#endif /* !ECHOCARD_HAS_VMIXER */ 8668c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops digital_capture_ops = { 8678c2ecf20Sopenharmony_ci .open = pcm_digital_in_open, 8688c2ecf20Sopenharmony_ci .close = pcm_close, 8698c2ecf20Sopenharmony_ci .hw_params = pcm_digital_in_hw_params, 8708c2ecf20Sopenharmony_ci .hw_free = pcm_hw_free, 8718c2ecf20Sopenharmony_ci .prepare = pcm_prepare, 8728c2ecf20Sopenharmony_ci .trigger = pcm_trigger, 8738c2ecf20Sopenharmony_ci .pointer = pcm_pointer, 8748c2ecf20Sopenharmony_ci}; 8758c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IO */ 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci/* Preallocate memory only for the first substream because it's the most 8808c2ecf20Sopenharmony_ci * used one 8818c2ecf20Sopenharmony_ci */ 8828c2ecf20Sopenharmony_cistatic void snd_echo_preallocate_pages(struct snd_pcm *pcm, struct device *dev) 8838c2ecf20Sopenharmony_ci{ 8848c2ecf20Sopenharmony_ci struct snd_pcm_substream *ss; 8858c2ecf20Sopenharmony_ci int stream; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci for (stream = 0; stream < 2; stream++) 8888c2ecf20Sopenharmony_ci for (ss = pcm->streams[stream].substream; ss; ss = ss->next) 8898c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer(ss, SNDRV_DMA_TYPE_DEV_SG, 8908c2ecf20Sopenharmony_ci dev, 8918c2ecf20Sopenharmony_ci ss->number ? 0 : 128<<10, 8928c2ecf20Sopenharmony_ci 256<<10); 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci/*<--snd_echo_probe() */ 8988c2ecf20Sopenharmony_cistatic int snd_echo_new_pcm(struct echoaudio *chip) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 9018c2ecf20Sopenharmony_ci int err; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_VMIXER 9048c2ecf20Sopenharmony_ci /* This card has a Vmixer, that is there is no direct mapping from PCM 9058c2ecf20Sopenharmony_ci streams to physical outputs. The user can mix the streams as he wishes 9068c2ecf20Sopenharmony_ci via control interface and it's possible to send any stream to any 9078c2ecf20Sopenharmony_ci output, thus it makes no sense to keep analog and digital outputs 9088c2ecf20Sopenharmony_ci separated */ 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* PCM#0 Virtual outputs and analog inputs */ 9118c2ecf20Sopenharmony_ci if ((err = snd_pcm_new(chip->card, "PCM", 0, num_pipes_out(chip), 9128c2ecf20Sopenharmony_ci num_analog_busses_in(chip), &pcm)) < 0) 9138c2ecf20Sopenharmony_ci return err; 9148c2ecf20Sopenharmony_ci pcm->private_data = chip; 9158c2ecf20Sopenharmony_ci chip->analog_pcm = pcm; 9168c2ecf20Sopenharmony_ci strcpy(pcm->name, chip->card->shortname); 9178c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops); 9188c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops); 9198c2ecf20Sopenharmony_ci snd_echo_preallocate_pages(pcm, &chip->pci->dev); 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO 9228c2ecf20Sopenharmony_ci /* PCM#1 Digital inputs, no outputs */ 9238c2ecf20Sopenharmony_ci if ((err = snd_pcm_new(chip->card, "Digital PCM", 1, 0, 9248c2ecf20Sopenharmony_ci num_digital_busses_in(chip), &pcm)) < 0) 9258c2ecf20Sopenharmony_ci return err; 9268c2ecf20Sopenharmony_ci pcm->private_data = chip; 9278c2ecf20Sopenharmony_ci chip->digital_pcm = pcm; 9288c2ecf20Sopenharmony_ci strcpy(pcm->name, chip->card->shortname); 9298c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops); 9308c2ecf20Sopenharmony_ci snd_echo_preallocate_pages(pcm, &chip->pci->dev); 9318c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IO */ 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci#else /* ECHOCARD_HAS_VMIXER */ 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci /* The card can manage substreams formed by analog and digital channels 9368c2ecf20Sopenharmony_ci at the same time, but I prefer to keep analog and digital channels 9378c2ecf20Sopenharmony_ci separated, because that mixed thing is confusing and useless. So we 9388c2ecf20Sopenharmony_ci register two PCM devices: */ 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci /* PCM#0 Analog i/o */ 9418c2ecf20Sopenharmony_ci if ((err = snd_pcm_new(chip->card, "Analog PCM", 0, 9428c2ecf20Sopenharmony_ci num_analog_busses_out(chip), 9438c2ecf20Sopenharmony_ci num_analog_busses_in(chip), &pcm)) < 0) 9448c2ecf20Sopenharmony_ci return err; 9458c2ecf20Sopenharmony_ci pcm->private_data = chip; 9468c2ecf20Sopenharmony_ci chip->analog_pcm = pcm; 9478c2ecf20Sopenharmony_ci strcpy(pcm->name, chip->card->shortname); 9488c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops); 9498c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops); 9508c2ecf20Sopenharmony_ci snd_echo_preallocate_pages(pcm, &chip->pci->dev); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO 9538c2ecf20Sopenharmony_ci /* PCM#1 Digital i/o */ 9548c2ecf20Sopenharmony_ci if ((err = snd_pcm_new(chip->card, "Digital PCM", 1, 9558c2ecf20Sopenharmony_ci num_digital_busses_out(chip), 9568c2ecf20Sopenharmony_ci num_digital_busses_in(chip), &pcm)) < 0) 9578c2ecf20Sopenharmony_ci return err; 9588c2ecf20Sopenharmony_ci pcm->private_data = chip; 9598c2ecf20Sopenharmony_ci chip->digital_pcm = pcm; 9608c2ecf20Sopenharmony_ci strcpy(pcm->name, chip->card->shortname); 9618c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &digital_playback_ops); 9628c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops); 9638c2ecf20Sopenharmony_ci snd_echo_preallocate_pages(pcm, &chip->pci->dev); 9648c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IO */ 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_VMIXER */ 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci return 0; 9698c2ecf20Sopenharmony_ci} 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci/****************************************************************************** 9758c2ecf20Sopenharmony_ci Control interface 9768c2ecf20Sopenharmony_ci******************************************************************************/ 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci#if !defined(ECHOCARD_HAS_VMIXER) || defined(ECHOCARD_HAS_LINE_OUT_GAIN) 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci/******************* PCM output volume *******************/ 9818c2ecf20Sopenharmony_cistatic int snd_echo_output_gain_info(struct snd_kcontrol *kcontrol, 9828c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 9838c2ecf20Sopenharmony_ci{ 9848c2ecf20Sopenharmony_ci struct echoaudio *chip; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 9878c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 9888c2ecf20Sopenharmony_ci uinfo->count = num_busses_out(chip); 9898c2ecf20Sopenharmony_ci uinfo->value.integer.min = ECHOGAIN_MINOUT; 9908c2ecf20Sopenharmony_ci uinfo->value.integer.max = ECHOGAIN_MAXOUT; 9918c2ecf20Sopenharmony_ci return 0; 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_cistatic int snd_echo_output_gain_get(struct snd_kcontrol *kcontrol, 9958c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 9968c2ecf20Sopenharmony_ci{ 9978c2ecf20Sopenharmony_ci struct echoaudio *chip; 9988c2ecf20Sopenharmony_ci int c; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 10018c2ecf20Sopenharmony_ci for (c = 0; c < num_busses_out(chip); c++) 10028c2ecf20Sopenharmony_ci ucontrol->value.integer.value[c] = chip->output_gain[c]; 10038c2ecf20Sopenharmony_ci return 0; 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_cistatic int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol, 10078c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 10088c2ecf20Sopenharmony_ci{ 10098c2ecf20Sopenharmony_ci struct echoaudio *chip; 10108c2ecf20Sopenharmony_ci int c, changed, gain; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci changed = 0; 10138c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 10148c2ecf20Sopenharmony_ci spin_lock_irq(&chip->lock); 10158c2ecf20Sopenharmony_ci for (c = 0; c < num_busses_out(chip); c++) { 10168c2ecf20Sopenharmony_ci gain = ucontrol->value.integer.value[c]; 10178c2ecf20Sopenharmony_ci /* Ignore out of range values */ 10188c2ecf20Sopenharmony_ci if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT) 10198c2ecf20Sopenharmony_ci continue; 10208c2ecf20Sopenharmony_ci if (chip->output_gain[c] != gain) { 10218c2ecf20Sopenharmony_ci set_output_gain(chip, c, gain); 10228c2ecf20Sopenharmony_ci changed = 1; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci if (changed) 10268c2ecf20Sopenharmony_ci update_output_line_level(chip); 10278c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 10288c2ecf20Sopenharmony_ci return changed; 10298c2ecf20Sopenharmony_ci} 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_LINE_OUT_GAIN 10328c2ecf20Sopenharmony_ci/* On the Mia this one controls the line-out volume */ 10338c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_line_output_gain = { 10348c2ecf20Sopenharmony_ci .name = "Line Playback Volume", 10358c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 10368c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 10378c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ, 10388c2ecf20Sopenharmony_ci .info = snd_echo_output_gain_info, 10398c2ecf20Sopenharmony_ci .get = snd_echo_output_gain_get, 10408c2ecf20Sopenharmony_ci .put = snd_echo_output_gain_put, 10418c2ecf20Sopenharmony_ci .tlv = {.p = db_scale_output_gain}, 10428c2ecf20Sopenharmony_ci}; 10438c2ecf20Sopenharmony_ci#else 10448c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_pcm_output_gain = { 10458c2ecf20Sopenharmony_ci .name = "PCM Playback Volume", 10468c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 10478c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, 10488c2ecf20Sopenharmony_ci .info = snd_echo_output_gain_info, 10498c2ecf20Sopenharmony_ci .get = snd_echo_output_gain_get, 10508c2ecf20Sopenharmony_ci .put = snd_echo_output_gain_put, 10518c2ecf20Sopenharmony_ci .tlv = {.p = db_scale_output_gain}, 10528c2ecf20Sopenharmony_ci}; 10538c2ecf20Sopenharmony_ci#endif 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci#endif /* !ECHOCARD_HAS_VMIXER || ECHOCARD_HAS_LINE_OUT_GAIN */ 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_INPUT_GAIN 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci/******************* Analog input volume *******************/ 10628c2ecf20Sopenharmony_cistatic int snd_echo_input_gain_info(struct snd_kcontrol *kcontrol, 10638c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci struct echoaudio *chip; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 10688c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 10698c2ecf20Sopenharmony_ci uinfo->count = num_analog_busses_in(chip); 10708c2ecf20Sopenharmony_ci uinfo->value.integer.min = ECHOGAIN_MININP; 10718c2ecf20Sopenharmony_ci uinfo->value.integer.max = ECHOGAIN_MAXINP; 10728c2ecf20Sopenharmony_ci return 0; 10738c2ecf20Sopenharmony_ci} 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_cistatic int snd_echo_input_gain_get(struct snd_kcontrol *kcontrol, 10768c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci struct echoaudio *chip; 10798c2ecf20Sopenharmony_ci int c; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 10828c2ecf20Sopenharmony_ci for (c = 0; c < num_analog_busses_in(chip); c++) 10838c2ecf20Sopenharmony_ci ucontrol->value.integer.value[c] = chip->input_gain[c]; 10848c2ecf20Sopenharmony_ci return 0; 10858c2ecf20Sopenharmony_ci} 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_cistatic int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol, 10888c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci struct echoaudio *chip; 10918c2ecf20Sopenharmony_ci int c, gain, changed; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci changed = 0; 10948c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 10958c2ecf20Sopenharmony_ci spin_lock_irq(&chip->lock); 10968c2ecf20Sopenharmony_ci for (c = 0; c < num_analog_busses_in(chip); c++) { 10978c2ecf20Sopenharmony_ci gain = ucontrol->value.integer.value[c]; 10988c2ecf20Sopenharmony_ci /* Ignore out of range values */ 10998c2ecf20Sopenharmony_ci if (gain < ECHOGAIN_MININP || gain > ECHOGAIN_MAXINP) 11008c2ecf20Sopenharmony_ci continue; 11018c2ecf20Sopenharmony_ci if (chip->input_gain[c] != gain) { 11028c2ecf20Sopenharmony_ci set_input_gain(chip, c, gain); 11038c2ecf20Sopenharmony_ci changed = 1; 11048c2ecf20Sopenharmony_ci } 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci if (changed) 11078c2ecf20Sopenharmony_ci update_input_line_level(chip); 11088c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 11098c2ecf20Sopenharmony_ci return changed; 11108c2ecf20Sopenharmony_ci} 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_input_gain, -2500, 50, 0); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_line_input_gain = { 11158c2ecf20Sopenharmony_ci .name = "Line Capture Volume", 11168c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 11178c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, 11188c2ecf20Sopenharmony_ci .info = snd_echo_input_gain_info, 11198c2ecf20Sopenharmony_ci .get = snd_echo_input_gain_get, 11208c2ecf20Sopenharmony_ci .put = snd_echo_input_gain_put, 11218c2ecf20Sopenharmony_ci .tlv = {.p = db_scale_input_gain}, 11228c2ecf20Sopenharmony_ci}; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_INPUT_GAIN */ 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci/************ Analog output nominal level (+4dBu / -10dBV) ***************/ 11318c2ecf20Sopenharmony_cistatic int snd_echo_output_nominal_info (struct snd_kcontrol *kcontrol, 11328c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 11338c2ecf20Sopenharmony_ci{ 11348c2ecf20Sopenharmony_ci struct echoaudio *chip; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 11378c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 11388c2ecf20Sopenharmony_ci uinfo->count = num_analog_busses_out(chip); 11398c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 11408c2ecf20Sopenharmony_ci uinfo->value.integer.max = 1; 11418c2ecf20Sopenharmony_ci return 0; 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_cistatic int snd_echo_output_nominal_get(struct snd_kcontrol *kcontrol, 11458c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 11468c2ecf20Sopenharmony_ci{ 11478c2ecf20Sopenharmony_ci struct echoaudio *chip; 11488c2ecf20Sopenharmony_ci int c; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 11518c2ecf20Sopenharmony_ci for (c = 0; c < num_analog_busses_out(chip); c++) 11528c2ecf20Sopenharmony_ci ucontrol->value.integer.value[c] = chip->nominal_level[c]; 11538c2ecf20Sopenharmony_ci return 0; 11548c2ecf20Sopenharmony_ci} 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_cistatic int snd_echo_output_nominal_put(struct snd_kcontrol *kcontrol, 11578c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 11588c2ecf20Sopenharmony_ci{ 11598c2ecf20Sopenharmony_ci struct echoaudio *chip; 11608c2ecf20Sopenharmony_ci int c, changed; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci changed = 0; 11638c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 11648c2ecf20Sopenharmony_ci spin_lock_irq(&chip->lock); 11658c2ecf20Sopenharmony_ci for (c = 0; c < num_analog_busses_out(chip); c++) { 11668c2ecf20Sopenharmony_ci if (chip->nominal_level[c] != ucontrol->value.integer.value[c]) { 11678c2ecf20Sopenharmony_ci set_nominal_level(chip, c, 11688c2ecf20Sopenharmony_ci ucontrol->value.integer.value[c]); 11698c2ecf20Sopenharmony_ci changed = 1; 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci } 11728c2ecf20Sopenharmony_ci if (changed) 11738c2ecf20Sopenharmony_ci update_output_line_level(chip); 11748c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 11758c2ecf20Sopenharmony_ci return changed; 11768c2ecf20Sopenharmony_ci} 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_output_nominal_level = { 11798c2ecf20Sopenharmony_ci .name = "Line Playback Switch (-10dBV)", 11808c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 11818c2ecf20Sopenharmony_ci .info = snd_echo_output_nominal_info, 11828c2ecf20Sopenharmony_ci .get = snd_echo_output_nominal_get, 11838c2ecf20Sopenharmony_ci .put = snd_echo_output_nominal_put, 11848c2ecf20Sopenharmony_ci}; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL */ 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci/*************** Analog input nominal level (+4dBu / -10dBV) ***************/ 11938c2ecf20Sopenharmony_cistatic int snd_echo_input_nominal_info(struct snd_kcontrol *kcontrol, 11948c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 11958c2ecf20Sopenharmony_ci{ 11968c2ecf20Sopenharmony_ci struct echoaudio *chip; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 11998c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 12008c2ecf20Sopenharmony_ci uinfo->count = num_analog_busses_in(chip); 12018c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 12028c2ecf20Sopenharmony_ci uinfo->value.integer.max = 1; 12038c2ecf20Sopenharmony_ci return 0; 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_cistatic int snd_echo_input_nominal_get(struct snd_kcontrol *kcontrol, 12078c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 12088c2ecf20Sopenharmony_ci{ 12098c2ecf20Sopenharmony_ci struct echoaudio *chip; 12108c2ecf20Sopenharmony_ci int c; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 12138c2ecf20Sopenharmony_ci for (c = 0; c < num_analog_busses_in(chip); c++) 12148c2ecf20Sopenharmony_ci ucontrol->value.integer.value[c] = 12158c2ecf20Sopenharmony_ci chip->nominal_level[bx_analog_in(chip) + c]; 12168c2ecf20Sopenharmony_ci return 0; 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_cistatic int snd_echo_input_nominal_put(struct snd_kcontrol *kcontrol, 12208c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 12218c2ecf20Sopenharmony_ci{ 12228c2ecf20Sopenharmony_ci struct echoaudio *chip; 12238c2ecf20Sopenharmony_ci int c, changed; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci changed = 0; 12268c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 12278c2ecf20Sopenharmony_ci spin_lock_irq(&chip->lock); 12288c2ecf20Sopenharmony_ci for (c = 0; c < num_analog_busses_in(chip); c++) { 12298c2ecf20Sopenharmony_ci if (chip->nominal_level[bx_analog_in(chip) + c] != 12308c2ecf20Sopenharmony_ci ucontrol->value.integer.value[c]) { 12318c2ecf20Sopenharmony_ci set_nominal_level(chip, bx_analog_in(chip) + c, 12328c2ecf20Sopenharmony_ci ucontrol->value.integer.value[c]); 12338c2ecf20Sopenharmony_ci changed = 1; 12348c2ecf20Sopenharmony_ci } 12358c2ecf20Sopenharmony_ci } 12368c2ecf20Sopenharmony_ci if (changed) 12378c2ecf20Sopenharmony_ci update_output_line_level(chip); /* "Output" is not a mistake 12388c2ecf20Sopenharmony_ci * here. 12398c2ecf20Sopenharmony_ci */ 12408c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 12418c2ecf20Sopenharmony_ci return changed; 12428c2ecf20Sopenharmony_ci} 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_intput_nominal_level = { 12458c2ecf20Sopenharmony_ci .name = "Line Capture Switch (-10dBV)", 12468c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 12478c2ecf20Sopenharmony_ci .info = snd_echo_input_nominal_info, 12488c2ecf20Sopenharmony_ci .get = snd_echo_input_nominal_get, 12498c2ecf20Sopenharmony_ci .put = snd_echo_input_nominal_put, 12508c2ecf20Sopenharmony_ci}; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_INPUT_NOMINAL_LEVEL */ 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_MONITOR 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci/******************* Monitor mixer *******************/ 12598c2ecf20Sopenharmony_cistatic int snd_echo_mixer_info(struct snd_kcontrol *kcontrol, 12608c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 12618c2ecf20Sopenharmony_ci{ 12628c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 12638c2ecf20Sopenharmony_ci uinfo->count = 1; 12648c2ecf20Sopenharmony_ci uinfo->value.integer.min = ECHOGAIN_MINOUT; 12658c2ecf20Sopenharmony_ci uinfo->value.integer.max = ECHOGAIN_MAXOUT; 12668c2ecf20Sopenharmony_ci return 0; 12678c2ecf20Sopenharmony_ci} 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_cistatic int snd_echo_mixer_get(struct snd_kcontrol *kcontrol, 12708c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 12718c2ecf20Sopenharmony_ci{ 12728c2ecf20Sopenharmony_ci struct echoaudio *chip = snd_kcontrol_chip(kcontrol); 12738c2ecf20Sopenharmony_ci unsigned int out = ucontrol->id.index / num_busses_in(chip); 12748c2ecf20Sopenharmony_ci unsigned int in = ucontrol->id.index % num_busses_in(chip); 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci if (out >= ECHO_MAXAUDIOOUTPUTS || in >= ECHO_MAXAUDIOINPUTS) 12778c2ecf20Sopenharmony_ci return -EINVAL; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = chip->monitor_gain[out][in]; 12808c2ecf20Sopenharmony_ci return 0; 12818c2ecf20Sopenharmony_ci} 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_cistatic int snd_echo_mixer_put(struct snd_kcontrol *kcontrol, 12848c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 12858c2ecf20Sopenharmony_ci{ 12868c2ecf20Sopenharmony_ci struct echoaudio *chip; 12878c2ecf20Sopenharmony_ci int changed, gain; 12888c2ecf20Sopenharmony_ci unsigned int out, in; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci changed = 0; 12918c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 12928c2ecf20Sopenharmony_ci out = ucontrol->id.index / num_busses_in(chip); 12938c2ecf20Sopenharmony_ci in = ucontrol->id.index % num_busses_in(chip); 12948c2ecf20Sopenharmony_ci if (out >= ECHO_MAXAUDIOOUTPUTS || in >= ECHO_MAXAUDIOINPUTS) 12958c2ecf20Sopenharmony_ci return -EINVAL; 12968c2ecf20Sopenharmony_ci gain = ucontrol->value.integer.value[0]; 12978c2ecf20Sopenharmony_ci if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT) 12988c2ecf20Sopenharmony_ci return -EINVAL; 12998c2ecf20Sopenharmony_ci if (chip->monitor_gain[out][in] != gain) { 13008c2ecf20Sopenharmony_ci spin_lock_irq(&chip->lock); 13018c2ecf20Sopenharmony_ci set_monitor_gain(chip, out, in, gain); 13028c2ecf20Sopenharmony_ci update_output_line_level(chip); 13038c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 13048c2ecf20Sopenharmony_ci changed = 1; 13058c2ecf20Sopenharmony_ci } 13068c2ecf20Sopenharmony_ci return changed; 13078c2ecf20Sopenharmony_ci} 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new snd_echo_monitor_mixer = { 13108c2ecf20Sopenharmony_ci .name = "Monitor Mixer Volume", 13118c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 13128c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, 13138c2ecf20Sopenharmony_ci .info = snd_echo_mixer_info, 13148c2ecf20Sopenharmony_ci .get = snd_echo_mixer_get, 13158c2ecf20Sopenharmony_ci .put = snd_echo_mixer_put, 13168c2ecf20Sopenharmony_ci .tlv = {.p = db_scale_output_gain}, 13178c2ecf20Sopenharmony_ci}; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_MONITOR */ 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_VMIXER 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci/******************* Vmixer *******************/ 13268c2ecf20Sopenharmony_cistatic int snd_echo_vmixer_info(struct snd_kcontrol *kcontrol, 13278c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 13288c2ecf20Sopenharmony_ci{ 13298c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 13308c2ecf20Sopenharmony_ci uinfo->count = 1; 13318c2ecf20Sopenharmony_ci uinfo->value.integer.min = ECHOGAIN_MINOUT; 13328c2ecf20Sopenharmony_ci uinfo->value.integer.max = ECHOGAIN_MAXOUT; 13338c2ecf20Sopenharmony_ci return 0; 13348c2ecf20Sopenharmony_ci} 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_cistatic int snd_echo_vmixer_get(struct snd_kcontrol *kcontrol, 13378c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 13388c2ecf20Sopenharmony_ci{ 13398c2ecf20Sopenharmony_ci struct echoaudio *chip; 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 13428c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 13438c2ecf20Sopenharmony_ci chip->vmixer_gain[ucontrol->id.index / num_pipes_out(chip)] 13448c2ecf20Sopenharmony_ci [ucontrol->id.index % num_pipes_out(chip)]; 13458c2ecf20Sopenharmony_ci return 0; 13468c2ecf20Sopenharmony_ci} 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_cistatic int snd_echo_vmixer_put(struct snd_kcontrol *kcontrol, 13498c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 13508c2ecf20Sopenharmony_ci{ 13518c2ecf20Sopenharmony_ci struct echoaudio *chip; 13528c2ecf20Sopenharmony_ci int gain, changed; 13538c2ecf20Sopenharmony_ci short vch, out; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci changed = 0; 13568c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 13578c2ecf20Sopenharmony_ci out = ucontrol->id.index / num_pipes_out(chip); 13588c2ecf20Sopenharmony_ci vch = ucontrol->id.index % num_pipes_out(chip); 13598c2ecf20Sopenharmony_ci gain = ucontrol->value.integer.value[0]; 13608c2ecf20Sopenharmony_ci if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT) 13618c2ecf20Sopenharmony_ci return -EINVAL; 13628c2ecf20Sopenharmony_ci if (chip->vmixer_gain[out][vch] != ucontrol->value.integer.value[0]) { 13638c2ecf20Sopenharmony_ci spin_lock_irq(&chip->lock); 13648c2ecf20Sopenharmony_ci set_vmixer_gain(chip, out, vch, ucontrol->value.integer.value[0]); 13658c2ecf20Sopenharmony_ci update_vmixer_level(chip); 13668c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 13678c2ecf20Sopenharmony_ci changed = 1; 13688c2ecf20Sopenharmony_ci } 13698c2ecf20Sopenharmony_ci return changed; 13708c2ecf20Sopenharmony_ci} 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new snd_echo_vmixer = { 13738c2ecf20Sopenharmony_ci .name = "VMixer Volume", 13748c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 13758c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, 13768c2ecf20Sopenharmony_ci .info = snd_echo_vmixer_info, 13778c2ecf20Sopenharmony_ci .get = snd_echo_vmixer_get, 13788c2ecf20Sopenharmony_ci .put = snd_echo_vmixer_put, 13798c2ecf20Sopenharmony_ci .tlv = {.p = db_scale_output_gain}, 13808c2ecf20Sopenharmony_ci}; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_VMIXER */ 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci/******************* Digital mode switch *******************/ 13898c2ecf20Sopenharmony_cistatic int snd_echo_digital_mode_info(struct snd_kcontrol *kcontrol, 13908c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 13918c2ecf20Sopenharmony_ci{ 13928c2ecf20Sopenharmony_ci static const char * const names[4] = { 13938c2ecf20Sopenharmony_ci "S/PDIF Coaxial", "S/PDIF Optical", "ADAT Optical", 13948c2ecf20Sopenharmony_ci "S/PDIF Cdrom" 13958c2ecf20Sopenharmony_ci }; 13968c2ecf20Sopenharmony_ci struct echoaudio *chip; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 13998c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, chip->num_digital_modes, names); 14008c2ecf20Sopenharmony_ci} 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_cistatic int snd_echo_digital_mode_get(struct snd_kcontrol *kcontrol, 14038c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 14048c2ecf20Sopenharmony_ci{ 14058c2ecf20Sopenharmony_ci struct echoaudio *chip; 14068c2ecf20Sopenharmony_ci int i, mode; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 14098c2ecf20Sopenharmony_ci mode = chip->digital_mode; 14108c2ecf20Sopenharmony_ci for (i = chip->num_digital_modes - 1; i >= 0; i--) 14118c2ecf20Sopenharmony_ci if (mode == chip->digital_mode_list[i]) { 14128c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = i; 14138c2ecf20Sopenharmony_ci break; 14148c2ecf20Sopenharmony_ci } 14158c2ecf20Sopenharmony_ci return 0; 14168c2ecf20Sopenharmony_ci} 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_cistatic int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol, 14198c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 14208c2ecf20Sopenharmony_ci{ 14218c2ecf20Sopenharmony_ci struct echoaudio *chip; 14228c2ecf20Sopenharmony_ci int changed; 14238c2ecf20Sopenharmony_ci unsigned short emode, dmode; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci changed = 0; 14268c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci emode = ucontrol->value.enumerated.item[0]; 14298c2ecf20Sopenharmony_ci if (emode >= chip->num_digital_modes) 14308c2ecf20Sopenharmony_ci return -EINVAL; 14318c2ecf20Sopenharmony_ci dmode = chip->digital_mode_list[emode]; 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci if (dmode != chip->digital_mode) { 14348c2ecf20Sopenharmony_ci /* mode_mutex is required to make this operation atomic wrt 14358c2ecf20Sopenharmony_ci pcm_digital_*_open() and set_input_clock() functions. */ 14368c2ecf20Sopenharmony_ci mutex_lock(&chip->mode_mutex); 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci /* Do not allow the user to change the digital mode when a pcm 14398c2ecf20Sopenharmony_ci device is open because it also changes the number of channels 14408c2ecf20Sopenharmony_ci and the allowed sample rates */ 14418c2ecf20Sopenharmony_ci if (chip->opencount) { 14428c2ecf20Sopenharmony_ci changed = -EAGAIN; 14438c2ecf20Sopenharmony_ci } else { 14448c2ecf20Sopenharmony_ci changed = set_digital_mode(chip, dmode); 14458c2ecf20Sopenharmony_ci /* If we had to change the clock source, report it */ 14468c2ecf20Sopenharmony_ci if (changed > 0 && chip->clock_src_ctl) { 14478c2ecf20Sopenharmony_ci snd_ctl_notify(chip->card, 14488c2ecf20Sopenharmony_ci SNDRV_CTL_EVENT_MASK_VALUE, 14498c2ecf20Sopenharmony_ci &chip->clock_src_ctl->id); 14508c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, 14518c2ecf20Sopenharmony_ci "SDM() =%d\n", changed); 14528c2ecf20Sopenharmony_ci } 14538c2ecf20Sopenharmony_ci if (changed >= 0) 14548c2ecf20Sopenharmony_ci changed = 1; /* No errors */ 14558c2ecf20Sopenharmony_ci } 14568c2ecf20Sopenharmony_ci mutex_unlock(&chip->mode_mutex); 14578c2ecf20Sopenharmony_ci } 14588c2ecf20Sopenharmony_ci return changed; 14598c2ecf20Sopenharmony_ci} 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_digital_mode_switch = { 14628c2ecf20Sopenharmony_ci .name = "Digital mode Switch", 14638c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_CARD, 14648c2ecf20Sopenharmony_ci .info = snd_echo_digital_mode_info, 14658c2ecf20Sopenharmony_ci .get = snd_echo_digital_mode_get, 14668c2ecf20Sopenharmony_ci .put = snd_echo_digital_mode_put, 14678c2ecf20Sopenharmony_ci}; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */ 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci/******************* S/PDIF mode switch *******************/ 14768c2ecf20Sopenharmony_cistatic int snd_echo_spdif_mode_info(struct snd_kcontrol *kcontrol, 14778c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 14788c2ecf20Sopenharmony_ci{ 14798c2ecf20Sopenharmony_ci static const char * const names[2] = {"Consumer", "Professional"}; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 2, names); 14828c2ecf20Sopenharmony_ci} 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_cistatic int snd_echo_spdif_mode_get(struct snd_kcontrol *kcontrol, 14858c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 14868c2ecf20Sopenharmony_ci{ 14878c2ecf20Sopenharmony_ci struct echoaudio *chip; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 14908c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = !!chip->professional_spdif; 14918c2ecf20Sopenharmony_ci return 0; 14928c2ecf20Sopenharmony_ci} 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_cistatic int snd_echo_spdif_mode_put(struct snd_kcontrol *kcontrol, 14958c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 14968c2ecf20Sopenharmony_ci{ 14978c2ecf20Sopenharmony_ci struct echoaudio *chip; 14988c2ecf20Sopenharmony_ci int mode; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 15018c2ecf20Sopenharmony_ci mode = !!ucontrol->value.enumerated.item[0]; 15028c2ecf20Sopenharmony_ci if (mode != chip->professional_spdif) { 15038c2ecf20Sopenharmony_ci spin_lock_irq(&chip->lock); 15048c2ecf20Sopenharmony_ci set_professional_spdif(chip, mode); 15058c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 15068c2ecf20Sopenharmony_ci return 1; 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci return 0; 15098c2ecf20Sopenharmony_ci} 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_spdif_mode_switch = { 15128c2ecf20Sopenharmony_ci .name = "S/PDIF mode Switch", 15138c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_CARD, 15148c2ecf20Sopenharmony_ci .info = snd_echo_spdif_mode_info, 15158c2ecf20Sopenharmony_ci .get = snd_echo_spdif_mode_get, 15168c2ecf20Sopenharmony_ci .put = snd_echo_spdif_mode_put, 15178c2ecf20Sopenharmony_ci}; 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IO */ 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci/******************* Select input clock source *******************/ 15268c2ecf20Sopenharmony_cistatic int snd_echo_clock_source_info(struct snd_kcontrol *kcontrol, 15278c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 15288c2ecf20Sopenharmony_ci{ 15298c2ecf20Sopenharmony_ci static const char * const names[8] = { 15308c2ecf20Sopenharmony_ci "Internal", "Word", "Super", "S/PDIF", "ADAT", "ESync", 15318c2ecf20Sopenharmony_ci "ESync96", "MTC" 15328c2ecf20Sopenharmony_ci }; 15338c2ecf20Sopenharmony_ci struct echoaudio *chip; 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 15368c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, chip->num_clock_sources, names); 15378c2ecf20Sopenharmony_ci} 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_cistatic int snd_echo_clock_source_get(struct snd_kcontrol *kcontrol, 15408c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 15418c2ecf20Sopenharmony_ci{ 15428c2ecf20Sopenharmony_ci struct echoaudio *chip; 15438c2ecf20Sopenharmony_ci int i, clock; 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 15468c2ecf20Sopenharmony_ci clock = chip->input_clock; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci for (i = 0; i < chip->num_clock_sources; i++) 15498c2ecf20Sopenharmony_ci if (clock == chip->clock_source_list[i]) 15508c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = i; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci return 0; 15538c2ecf20Sopenharmony_ci} 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_cistatic int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol, 15568c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 15578c2ecf20Sopenharmony_ci{ 15588c2ecf20Sopenharmony_ci struct echoaudio *chip; 15598c2ecf20Sopenharmony_ci int changed; 15608c2ecf20Sopenharmony_ci unsigned int eclock, dclock; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci changed = 0; 15638c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 15648c2ecf20Sopenharmony_ci eclock = ucontrol->value.enumerated.item[0]; 15658c2ecf20Sopenharmony_ci if (eclock >= chip->input_clock_types) 15668c2ecf20Sopenharmony_ci return -EINVAL; 15678c2ecf20Sopenharmony_ci dclock = chip->clock_source_list[eclock]; 15688c2ecf20Sopenharmony_ci if (chip->input_clock != dclock) { 15698c2ecf20Sopenharmony_ci mutex_lock(&chip->mode_mutex); 15708c2ecf20Sopenharmony_ci spin_lock_irq(&chip->lock); 15718c2ecf20Sopenharmony_ci if ((changed = set_input_clock(chip, dclock)) == 0) 15728c2ecf20Sopenharmony_ci changed = 1; /* no errors */ 15738c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 15748c2ecf20Sopenharmony_ci mutex_unlock(&chip->mode_mutex); 15758c2ecf20Sopenharmony_ci } 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci if (changed < 0) 15788c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, 15798c2ecf20Sopenharmony_ci "seticlk val%d err 0x%x\n", dclock, changed); 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci return changed; 15828c2ecf20Sopenharmony_ci} 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_clock_source_switch = { 15858c2ecf20Sopenharmony_ci .name = "Sample Clock Source", 15868c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 15878c2ecf20Sopenharmony_ci .info = snd_echo_clock_source_info, 15888c2ecf20Sopenharmony_ci .get = snd_echo_clock_source_get, 15898c2ecf20Sopenharmony_ci .put = snd_echo_clock_source_put, 15908c2ecf20Sopenharmony_ci}; 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */ 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_PHANTOM_POWER 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci/******************* Phantom power switch *******************/ 15998c2ecf20Sopenharmony_ci#define snd_echo_phantom_power_info snd_ctl_boolean_mono_info 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_cistatic int snd_echo_phantom_power_get(struct snd_kcontrol *kcontrol, 16028c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 16038c2ecf20Sopenharmony_ci{ 16048c2ecf20Sopenharmony_ci struct echoaudio *chip = snd_kcontrol_chip(kcontrol); 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = chip->phantom_power; 16078c2ecf20Sopenharmony_ci return 0; 16088c2ecf20Sopenharmony_ci} 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_cistatic int snd_echo_phantom_power_put(struct snd_kcontrol *kcontrol, 16118c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 16128c2ecf20Sopenharmony_ci{ 16138c2ecf20Sopenharmony_ci struct echoaudio *chip = snd_kcontrol_chip(kcontrol); 16148c2ecf20Sopenharmony_ci int power, changed = 0; 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci power = !!ucontrol->value.integer.value[0]; 16178c2ecf20Sopenharmony_ci if (chip->phantom_power != power) { 16188c2ecf20Sopenharmony_ci spin_lock_irq(&chip->lock); 16198c2ecf20Sopenharmony_ci changed = set_phantom_power(chip, power); 16208c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 16218c2ecf20Sopenharmony_ci if (changed == 0) 16228c2ecf20Sopenharmony_ci changed = 1; /* no errors */ 16238c2ecf20Sopenharmony_ci } 16248c2ecf20Sopenharmony_ci return changed; 16258c2ecf20Sopenharmony_ci} 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_phantom_power_switch = { 16288c2ecf20Sopenharmony_ci .name = "Phantom power Switch", 16298c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_CARD, 16308c2ecf20Sopenharmony_ci .info = snd_echo_phantom_power_info, 16318c2ecf20Sopenharmony_ci .get = snd_echo_phantom_power_get, 16328c2ecf20Sopenharmony_ci .put = snd_echo_phantom_power_put, 16338c2ecf20Sopenharmony_ci}; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_PHANTOM_POWER */ 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci/******************* Digital input automute switch *******************/ 16428c2ecf20Sopenharmony_ci#define snd_echo_automute_info snd_ctl_boolean_mono_info 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_cistatic int snd_echo_automute_get(struct snd_kcontrol *kcontrol, 16458c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 16468c2ecf20Sopenharmony_ci{ 16478c2ecf20Sopenharmony_ci struct echoaudio *chip = snd_kcontrol_chip(kcontrol); 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = chip->digital_in_automute; 16508c2ecf20Sopenharmony_ci return 0; 16518c2ecf20Sopenharmony_ci} 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_cistatic int snd_echo_automute_put(struct snd_kcontrol *kcontrol, 16548c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 16558c2ecf20Sopenharmony_ci{ 16568c2ecf20Sopenharmony_ci struct echoaudio *chip = snd_kcontrol_chip(kcontrol); 16578c2ecf20Sopenharmony_ci int automute, changed = 0; 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci automute = !!ucontrol->value.integer.value[0]; 16608c2ecf20Sopenharmony_ci if (chip->digital_in_automute != automute) { 16618c2ecf20Sopenharmony_ci spin_lock_irq(&chip->lock); 16628c2ecf20Sopenharmony_ci changed = set_input_auto_mute(chip, automute); 16638c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 16648c2ecf20Sopenharmony_ci if (changed == 0) 16658c2ecf20Sopenharmony_ci changed = 1; /* no errors */ 16668c2ecf20Sopenharmony_ci } 16678c2ecf20Sopenharmony_ci return changed; 16688c2ecf20Sopenharmony_ci} 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_automute_switch = { 16718c2ecf20Sopenharmony_ci .name = "Digital Capture Switch (automute)", 16728c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_CARD, 16738c2ecf20Sopenharmony_ci .info = snd_echo_automute_info, 16748c2ecf20Sopenharmony_ci .get = snd_echo_automute_get, 16758c2ecf20Sopenharmony_ci .put = snd_echo_automute_put, 16768c2ecf20Sopenharmony_ci}; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE */ 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci/******************* VU-meters switch *******************/ 16838c2ecf20Sopenharmony_ci#define snd_echo_vumeters_switch_info snd_ctl_boolean_mono_info 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_cistatic int snd_echo_vumeters_switch_put(struct snd_kcontrol *kcontrol, 16868c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 16878c2ecf20Sopenharmony_ci{ 16888c2ecf20Sopenharmony_ci struct echoaudio *chip; 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 16918c2ecf20Sopenharmony_ci spin_lock_irq(&chip->lock); 16928c2ecf20Sopenharmony_ci set_meters_on(chip, ucontrol->value.integer.value[0]); 16938c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 16948c2ecf20Sopenharmony_ci return 1; 16958c2ecf20Sopenharmony_ci} 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_vumeters_switch = { 16988c2ecf20Sopenharmony_ci .name = "VU-meters Switch", 16998c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_CARD, 17008c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_WRITE, 17018c2ecf20Sopenharmony_ci .info = snd_echo_vumeters_switch_info, 17028c2ecf20Sopenharmony_ci .put = snd_echo_vumeters_switch_put, 17038c2ecf20Sopenharmony_ci}; 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci/***** Read VU-meters (input, output, analog and digital together) *****/ 17088c2ecf20Sopenharmony_cistatic int snd_echo_vumeters_info(struct snd_kcontrol *kcontrol, 17098c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 17108c2ecf20Sopenharmony_ci{ 17118c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 17128c2ecf20Sopenharmony_ci uinfo->count = 96; 17138c2ecf20Sopenharmony_ci uinfo->value.integer.min = ECHOGAIN_MINOUT; 17148c2ecf20Sopenharmony_ci uinfo->value.integer.max = 0; 17158c2ecf20Sopenharmony_ci return 0; 17168c2ecf20Sopenharmony_ci} 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_cistatic int snd_echo_vumeters_get(struct snd_kcontrol *kcontrol, 17198c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 17208c2ecf20Sopenharmony_ci{ 17218c2ecf20Sopenharmony_ci struct echoaudio *chip; 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 17248c2ecf20Sopenharmony_ci get_audio_meters(chip, ucontrol->value.integer.value); 17258c2ecf20Sopenharmony_ci return 0; 17268c2ecf20Sopenharmony_ci} 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_vumeters = { 17298c2ecf20Sopenharmony_ci .name = "VU-meters", 17308c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 17318c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ | 17328c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_VOLATILE | 17338c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ, 17348c2ecf20Sopenharmony_ci .info = snd_echo_vumeters_info, 17358c2ecf20Sopenharmony_ci .get = snd_echo_vumeters_get, 17368c2ecf20Sopenharmony_ci .tlv = {.p = db_scale_output_gain}, 17378c2ecf20Sopenharmony_ci}; 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci/*** Channels info - it exports informations about the number of channels ***/ 17428c2ecf20Sopenharmony_cistatic int snd_echo_channels_info_info(struct snd_kcontrol *kcontrol, 17438c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 17448c2ecf20Sopenharmony_ci{ 17458c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 17468c2ecf20Sopenharmony_ci uinfo->count = 6; 17478c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 17488c2ecf20Sopenharmony_ci uinfo->value.integer.max = 1 << ECHO_CLOCK_NUMBER; 17498c2ecf20Sopenharmony_ci return 0; 17508c2ecf20Sopenharmony_ci} 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_cistatic int snd_echo_channels_info_get(struct snd_kcontrol *kcontrol, 17538c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 17548c2ecf20Sopenharmony_ci{ 17558c2ecf20Sopenharmony_ci struct echoaudio *chip; 17568c2ecf20Sopenharmony_ci int detected, clocks, bit, src; 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci chip = snd_kcontrol_chip(kcontrol); 17598c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = num_busses_in(chip); 17608c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = num_analog_busses_in(chip); 17618c2ecf20Sopenharmony_ci ucontrol->value.integer.value[2] = num_busses_out(chip); 17628c2ecf20Sopenharmony_ci ucontrol->value.integer.value[3] = num_analog_busses_out(chip); 17638c2ecf20Sopenharmony_ci ucontrol->value.integer.value[4] = num_pipes_out(chip); 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci /* Compute the bitmask of the currently valid input clocks */ 17668c2ecf20Sopenharmony_ci detected = detect_input_clocks(chip); 17678c2ecf20Sopenharmony_ci clocks = 0; 17688c2ecf20Sopenharmony_ci src = chip->num_clock_sources - 1; 17698c2ecf20Sopenharmony_ci for (bit = ECHO_CLOCK_NUMBER - 1; bit >= 0; bit--) 17708c2ecf20Sopenharmony_ci if (detected & (1 << bit)) 17718c2ecf20Sopenharmony_ci for (; src >= 0; src--) 17728c2ecf20Sopenharmony_ci if (bit == chip->clock_source_list[src]) { 17738c2ecf20Sopenharmony_ci clocks |= 1 << src; 17748c2ecf20Sopenharmony_ci break; 17758c2ecf20Sopenharmony_ci } 17768c2ecf20Sopenharmony_ci ucontrol->value.integer.value[5] = clocks; 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci return 0; 17798c2ecf20Sopenharmony_ci} 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_channels_info = { 17828c2ecf20Sopenharmony_ci .name = "Channels info", 17838c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, 17848c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, 17858c2ecf20Sopenharmony_ci .info = snd_echo_channels_info_info, 17868c2ecf20Sopenharmony_ci .get = snd_echo_channels_info_get, 17878c2ecf20Sopenharmony_ci}; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci/****************************************************************************** 17938c2ecf20Sopenharmony_ci IRQ Handling 17948c2ecf20Sopenharmony_ci******************************************************************************/ 17958c2ecf20Sopenharmony_ci/* Check if a period has elapsed since last interrupt 17968c2ecf20Sopenharmony_ci * 17978c2ecf20Sopenharmony_ci * Don't make any updates to state; PCM core handles this with the 17988c2ecf20Sopenharmony_ci * correct locks. 17998c2ecf20Sopenharmony_ci * 18008c2ecf20Sopenharmony_ci * \return true if a period has elapsed, otherwise false 18018c2ecf20Sopenharmony_ci */ 18028c2ecf20Sopenharmony_cistatic bool period_has_elapsed(struct snd_pcm_substream *substream) 18038c2ecf20Sopenharmony_ci{ 18048c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 18058c2ecf20Sopenharmony_ci struct audiopipe *pipe = runtime->private_data; 18068c2ecf20Sopenharmony_ci u32 counter, step; 18078c2ecf20Sopenharmony_ci size_t period_bytes; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci if (pipe->state != PIPE_STATE_STARTED) 18108c2ecf20Sopenharmony_ci return false; 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci period_bytes = frames_to_bytes(runtime, runtime->period_size); 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci counter = le32_to_cpu(*pipe->dma_counter); /* presumed atomic */ 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci step = counter - pipe->last_period; /* handles wrapping */ 18178c2ecf20Sopenharmony_ci step -= step % period_bytes; /* acknowledge whole periods only */ 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci if (step == 0) 18208c2ecf20Sopenharmony_ci return false; /* haven't advanced a whole period yet */ 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci pipe->last_period += step; /* used exclusively by us */ 18238c2ecf20Sopenharmony_ci return true; 18248c2ecf20Sopenharmony_ci} 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_cistatic irqreturn_t snd_echo_interrupt(int irq, void *dev_id) 18278c2ecf20Sopenharmony_ci{ 18288c2ecf20Sopenharmony_ci struct echoaudio *chip = dev_id; 18298c2ecf20Sopenharmony_ci int ss, st; 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci spin_lock(&chip->lock); 18328c2ecf20Sopenharmony_ci st = service_irq(chip); 18338c2ecf20Sopenharmony_ci if (st < 0) { 18348c2ecf20Sopenharmony_ci spin_unlock(&chip->lock); 18358c2ecf20Sopenharmony_ci return IRQ_NONE; 18368c2ecf20Sopenharmony_ci } 18378c2ecf20Sopenharmony_ci /* The hardware doesn't tell us which substream caused the irq, 18388c2ecf20Sopenharmony_ci thus we have to check all running substreams. */ 18398c2ecf20Sopenharmony_ci for (ss = 0; ss < DSP_MAXPIPES; ss++) { 18408c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci substream = chip->substream[ss]; 18438c2ecf20Sopenharmony_ci if (substream && period_has_elapsed(substream)) { 18448c2ecf20Sopenharmony_ci spin_unlock(&chip->lock); 18458c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(substream); 18468c2ecf20Sopenharmony_ci spin_lock(&chip->lock); 18478c2ecf20Sopenharmony_ci } 18488c2ecf20Sopenharmony_ci } 18498c2ecf20Sopenharmony_ci spin_unlock(&chip->lock); 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_MIDI 18528c2ecf20Sopenharmony_ci if (st > 0 && chip->midi_in) { 18538c2ecf20Sopenharmony_ci snd_rawmidi_receive(chip->midi_in, chip->midi_buffer, st); 18548c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "rawmidi_iread=%d\n", st); 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci#endif 18578c2ecf20Sopenharmony_ci return IRQ_HANDLED; 18588c2ecf20Sopenharmony_ci} 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci/****************************************************************************** 18648c2ecf20Sopenharmony_ci Module construction / destruction 18658c2ecf20Sopenharmony_ci******************************************************************************/ 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_cistatic int snd_echo_free(struct echoaudio *chip) 18688c2ecf20Sopenharmony_ci{ 18698c2ecf20Sopenharmony_ci if (chip->comm_page) 18708c2ecf20Sopenharmony_ci rest_in_peace(chip); 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci if (chip->irq >= 0) 18738c2ecf20Sopenharmony_ci free_irq(chip->irq, chip); 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci if (chip->comm_page) 18768c2ecf20Sopenharmony_ci snd_dma_free_pages(&chip->commpage_dma_buf); 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci iounmap(chip->dsp_registers); 18798c2ecf20Sopenharmony_ci release_and_free_resource(chip->iores); 18808c2ecf20Sopenharmony_ci pci_disable_device(chip->pci); 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci /* release chip data */ 18838c2ecf20Sopenharmony_ci free_firmware_cache(chip); 18848c2ecf20Sopenharmony_ci kfree(chip); 18858c2ecf20Sopenharmony_ci return 0; 18868c2ecf20Sopenharmony_ci} 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_cistatic int snd_echo_dev_free(struct snd_device *device) 18918c2ecf20Sopenharmony_ci{ 18928c2ecf20Sopenharmony_ci struct echoaudio *chip = device->device_data; 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci return snd_echo_free(chip); 18958c2ecf20Sopenharmony_ci} 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci/* <--snd_echo_probe() */ 19008c2ecf20Sopenharmony_cistatic int snd_echo_create(struct snd_card *card, 19018c2ecf20Sopenharmony_ci struct pci_dev *pci, 19028c2ecf20Sopenharmony_ci struct echoaudio **rchip) 19038c2ecf20Sopenharmony_ci{ 19048c2ecf20Sopenharmony_ci struct echoaudio *chip; 19058c2ecf20Sopenharmony_ci int err; 19068c2ecf20Sopenharmony_ci size_t sz; 19078c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 19088c2ecf20Sopenharmony_ci .dev_free = snd_echo_dev_free, 19098c2ecf20Sopenharmony_ci }; 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci *rchip = NULL; 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0xC0); 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci if ((err = pci_enable_device(pci)) < 0) 19168c2ecf20Sopenharmony_ci return err; 19178c2ecf20Sopenharmony_ci pci_set_master(pci); 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci /* Allocate chip if needed */ 19208c2ecf20Sopenharmony_ci if (!*rchip) { 19218c2ecf20Sopenharmony_ci chip = kzalloc(sizeof(*chip), GFP_KERNEL); 19228c2ecf20Sopenharmony_ci if (!chip) { 19238c2ecf20Sopenharmony_ci pci_disable_device(pci); 19248c2ecf20Sopenharmony_ci return -ENOMEM; 19258c2ecf20Sopenharmony_ci } 19268c2ecf20Sopenharmony_ci dev_dbg(card->dev, "chip=%p\n", chip); 19278c2ecf20Sopenharmony_ci spin_lock_init(&chip->lock); 19288c2ecf20Sopenharmony_ci chip->card = card; 19298c2ecf20Sopenharmony_ci chip->pci = pci; 19308c2ecf20Sopenharmony_ci chip->irq = -1; 19318c2ecf20Sopenharmony_ci chip->opencount = 0; 19328c2ecf20Sopenharmony_ci mutex_init(&chip->mode_mutex); 19338c2ecf20Sopenharmony_ci chip->can_set_rate = 1; 19348c2ecf20Sopenharmony_ci } else { 19358c2ecf20Sopenharmony_ci /* If this was called from the resume function, chip is 19368c2ecf20Sopenharmony_ci * already allocated and it contains current card settings. 19378c2ecf20Sopenharmony_ci */ 19388c2ecf20Sopenharmony_ci chip = *rchip; 19398c2ecf20Sopenharmony_ci } 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci /* PCI resource allocation */ 19428c2ecf20Sopenharmony_ci chip->dsp_registers_phys = pci_resource_start(pci, 0); 19438c2ecf20Sopenharmony_ci sz = pci_resource_len(pci, 0); 19448c2ecf20Sopenharmony_ci if (sz > PAGE_SIZE) 19458c2ecf20Sopenharmony_ci sz = PAGE_SIZE; /* We map only the required part */ 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci if ((chip->iores = request_mem_region(chip->dsp_registers_phys, sz, 19488c2ecf20Sopenharmony_ci ECHOCARD_NAME)) == NULL) { 19498c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "cannot get memory region\n"); 19508c2ecf20Sopenharmony_ci snd_echo_free(chip); 19518c2ecf20Sopenharmony_ci return -EBUSY; 19528c2ecf20Sopenharmony_ci } 19538c2ecf20Sopenharmony_ci chip->dsp_registers = ioremap(chip->dsp_registers_phys, sz); 19548c2ecf20Sopenharmony_ci if (!chip->dsp_registers) { 19558c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "ioremap failed\n"); 19568c2ecf20Sopenharmony_ci snd_echo_free(chip); 19578c2ecf20Sopenharmony_ci return -ENOMEM; 19588c2ecf20Sopenharmony_ci } 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED, 19618c2ecf20Sopenharmony_ci KBUILD_MODNAME, chip)) { 19628c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "cannot grab irq\n"); 19638c2ecf20Sopenharmony_ci snd_echo_free(chip); 19648c2ecf20Sopenharmony_ci return -EBUSY; 19658c2ecf20Sopenharmony_ci } 19668c2ecf20Sopenharmony_ci chip->irq = pci->irq; 19678c2ecf20Sopenharmony_ci card->sync_irq = chip->irq; 19688c2ecf20Sopenharmony_ci dev_dbg(card->dev, "pci=%p irq=%d subdev=%04x Init hardware...\n", 19698c2ecf20Sopenharmony_ci chip->pci, chip->irq, chip->pci->subsystem_device); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci /* Create the DSP comm page - this is the area of memory used for most 19728c2ecf20Sopenharmony_ci of the communication with the DSP, which accesses it via bus mastering */ 19738c2ecf20Sopenharmony_ci if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev, 19748c2ecf20Sopenharmony_ci sizeof(struct comm_page), 19758c2ecf20Sopenharmony_ci &chip->commpage_dma_buf) < 0) { 19768c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "cannot allocate the comm page\n"); 19778c2ecf20Sopenharmony_ci snd_echo_free(chip); 19788c2ecf20Sopenharmony_ci return -ENOMEM; 19798c2ecf20Sopenharmony_ci } 19808c2ecf20Sopenharmony_ci chip->comm_page_phys = chip->commpage_dma_buf.addr; 19818c2ecf20Sopenharmony_ci chip->comm_page = (struct comm_page *)chip->commpage_dma_buf.area; 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device); 19848c2ecf20Sopenharmony_ci if (err >= 0) 19858c2ecf20Sopenharmony_ci err = set_mixer_defaults(chip); 19868c2ecf20Sopenharmony_ci if (err < 0) { 19878c2ecf20Sopenharmony_ci dev_err(card->dev, "init_hw err=%d\n", err); 19888c2ecf20Sopenharmony_ci snd_echo_free(chip); 19898c2ecf20Sopenharmony_ci return err; 19908c2ecf20Sopenharmony_ci } 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { 19938c2ecf20Sopenharmony_ci snd_echo_free(chip); 19948c2ecf20Sopenharmony_ci return err; 19958c2ecf20Sopenharmony_ci } 19968c2ecf20Sopenharmony_ci *rchip = chip; 19978c2ecf20Sopenharmony_ci /* Init done ! */ 19988c2ecf20Sopenharmony_ci return 0; 19998c2ecf20Sopenharmony_ci} 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci/* constructor */ 20048c2ecf20Sopenharmony_cistatic int snd_echo_probe(struct pci_dev *pci, 20058c2ecf20Sopenharmony_ci const struct pci_device_id *pci_id) 20068c2ecf20Sopenharmony_ci{ 20078c2ecf20Sopenharmony_ci static int dev; 20088c2ecf20Sopenharmony_ci struct snd_card *card; 20098c2ecf20Sopenharmony_ci struct echoaudio *chip; 20108c2ecf20Sopenharmony_ci char *dsp; 20118c2ecf20Sopenharmony_ci __maybe_unused int i; 20128c2ecf20Sopenharmony_ci int err; 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci if (dev >= SNDRV_CARDS) 20158c2ecf20Sopenharmony_ci return -ENODEV; 20168c2ecf20Sopenharmony_ci if (!enable[dev]) { 20178c2ecf20Sopenharmony_ci dev++; 20188c2ecf20Sopenharmony_ci return -ENOENT; 20198c2ecf20Sopenharmony_ci } 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci i = 0; 20228c2ecf20Sopenharmony_ci err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 20238c2ecf20Sopenharmony_ci 0, &card); 20248c2ecf20Sopenharmony_ci if (err < 0) 20258c2ecf20Sopenharmony_ci return err; 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci chip = NULL; /* Tells snd_echo_create to allocate chip */ 20288c2ecf20Sopenharmony_ci if ((err = snd_echo_create(card, pci, &chip)) < 0) { 20298c2ecf20Sopenharmony_ci snd_card_free(card); 20308c2ecf20Sopenharmony_ci return err; 20318c2ecf20Sopenharmony_ci } 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci strcpy(card->driver, "Echo_" ECHOCARD_NAME); 20348c2ecf20Sopenharmony_ci strcpy(card->shortname, chip->card_name); 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci dsp = "56301"; 20378c2ecf20Sopenharmony_ci if (pci_id->device == 0x3410) 20388c2ecf20Sopenharmony_ci dsp = "56361"; 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci sprintf(card->longname, "%s rev.%d (DSP%s) at 0x%lx irq %i", 20418c2ecf20Sopenharmony_ci card->shortname, pci_id->subdevice & 0x000f, dsp, 20428c2ecf20Sopenharmony_ci chip->dsp_registers_phys, chip->irq); 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci if ((err = snd_echo_new_pcm(chip)) < 0) { 20458c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "new pcm error %d\n", err); 20468c2ecf20Sopenharmony_ci snd_card_free(card); 20478c2ecf20Sopenharmony_ci return err; 20488c2ecf20Sopenharmony_ci } 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_MIDI 20518c2ecf20Sopenharmony_ci if (chip->has_midi) { /* Some Mia's do not have midi */ 20528c2ecf20Sopenharmony_ci if ((err = snd_echo_midi_create(card, chip)) < 0) { 20538c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "new midi error %d\n", err); 20548c2ecf20Sopenharmony_ci snd_card_free(card); 20558c2ecf20Sopenharmony_ci return err; 20568c2ecf20Sopenharmony_ci } 20578c2ecf20Sopenharmony_ci } 20588c2ecf20Sopenharmony_ci#endif 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_VMIXER 20618c2ecf20Sopenharmony_ci snd_echo_vmixer.count = num_pipes_out(chip) * num_busses_out(chip); 20628c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vmixer, chip))) < 0) 20638c2ecf20Sopenharmony_ci goto ctl_error; 20648c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_LINE_OUT_GAIN 20658c2ecf20Sopenharmony_ci err = snd_ctl_add(chip->card, 20668c2ecf20Sopenharmony_ci snd_ctl_new1(&snd_echo_line_output_gain, chip)); 20678c2ecf20Sopenharmony_ci if (err < 0) 20688c2ecf20Sopenharmony_ci goto ctl_error; 20698c2ecf20Sopenharmony_ci#endif 20708c2ecf20Sopenharmony_ci#else /* ECHOCARD_HAS_VMIXER */ 20718c2ecf20Sopenharmony_ci err = snd_ctl_add(chip->card, 20728c2ecf20Sopenharmony_ci snd_ctl_new1(&snd_echo_pcm_output_gain, chip)); 20738c2ecf20Sopenharmony_ci if (err < 0) 20748c2ecf20Sopenharmony_ci goto ctl_error; 20758c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_VMIXER */ 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_INPUT_GAIN 20788c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_input_gain, chip))) < 0) 20798c2ecf20Sopenharmony_ci goto ctl_error; 20808c2ecf20Sopenharmony_ci#endif 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL 20838c2ecf20Sopenharmony_ci if (!chip->hasnt_input_nominal_level) 20848c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_intput_nominal_level, chip))) < 0) 20858c2ecf20Sopenharmony_ci goto ctl_error; 20868c2ecf20Sopenharmony_ci#endif 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL 20898c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_output_nominal_level, chip))) < 0) 20908c2ecf20Sopenharmony_ci goto ctl_error; 20918c2ecf20Sopenharmony_ci#endif 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters_switch, chip))) < 0) 20948c2ecf20Sopenharmony_ci goto ctl_error; 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters, chip))) < 0) 20978c2ecf20Sopenharmony_ci goto ctl_error; 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_MONITOR 21008c2ecf20Sopenharmony_ci snd_echo_monitor_mixer.count = num_busses_in(chip) * num_busses_out(chip); 21018c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_monitor_mixer, chip))) < 0) 21028c2ecf20Sopenharmony_ci goto ctl_error; 21038c2ecf20Sopenharmony_ci#endif 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE 21068c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_automute_switch, chip))) < 0) 21078c2ecf20Sopenharmony_ci goto ctl_error; 21088c2ecf20Sopenharmony_ci#endif 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_channels_info, chip))) < 0) 21118c2ecf20Sopenharmony_ci goto ctl_error; 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH 21148c2ecf20Sopenharmony_ci /* Creates a list of available digital modes */ 21158c2ecf20Sopenharmony_ci chip->num_digital_modes = 0; 21168c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) 21178c2ecf20Sopenharmony_ci if (chip->digital_modes & (1 << i)) 21188c2ecf20Sopenharmony_ci chip->digital_mode_list[chip->num_digital_modes++] = i; 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_digital_mode_switch, chip))) < 0) 21218c2ecf20Sopenharmony_ci goto ctl_error; 21228c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */ 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK 21258c2ecf20Sopenharmony_ci /* Creates a list of available clock sources */ 21268c2ecf20Sopenharmony_ci chip->num_clock_sources = 0; 21278c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) 21288c2ecf20Sopenharmony_ci if (chip->input_clock_types & (1 << i)) 21298c2ecf20Sopenharmony_ci chip->clock_source_list[chip->num_clock_sources++] = i; 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci if (chip->num_clock_sources > 1) { 21328c2ecf20Sopenharmony_ci chip->clock_src_ctl = snd_ctl_new1(&snd_echo_clock_source_switch, chip); 21338c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(chip->card, chip->clock_src_ctl)) < 0) 21348c2ecf20Sopenharmony_ci goto ctl_error; 21358c2ecf20Sopenharmony_ci } 21368c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */ 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO 21398c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_spdif_mode_switch, chip))) < 0) 21408c2ecf20Sopenharmony_ci goto ctl_error; 21418c2ecf20Sopenharmony_ci#endif 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_PHANTOM_POWER 21448c2ecf20Sopenharmony_ci if (chip->has_phantom_power) 21458c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_phantom_power_switch, chip))) < 0) 21468c2ecf20Sopenharmony_ci goto ctl_error; 21478c2ecf20Sopenharmony_ci#endif 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci err = snd_card_register(card); 21508c2ecf20Sopenharmony_ci if (err < 0) 21518c2ecf20Sopenharmony_ci goto ctl_error; 21528c2ecf20Sopenharmony_ci dev_info(card->dev, "Card registered: %s\n", card->longname); 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci pci_set_drvdata(pci, chip); 21558c2ecf20Sopenharmony_ci dev++; 21568c2ecf20Sopenharmony_ci return 0; 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_cictl_error: 21598c2ecf20Sopenharmony_ci dev_err(card->dev, "new control error %d\n", err); 21608c2ecf20Sopenharmony_ci snd_card_free(card); 21618c2ecf20Sopenharmony_ci return err; 21628c2ecf20Sopenharmony_ci} 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci#if defined(CONFIG_PM_SLEEP) 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_cistatic int snd_echo_suspend(struct device *dev) 21698c2ecf20Sopenharmony_ci{ 21708c2ecf20Sopenharmony_ci struct echoaudio *chip = dev_get_drvdata(dev); 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_MIDI 21738c2ecf20Sopenharmony_ci /* This call can sleep */ 21748c2ecf20Sopenharmony_ci if (chip->midi_out) 21758c2ecf20Sopenharmony_ci snd_echo_midi_output_trigger(chip->midi_out, 0); 21768c2ecf20Sopenharmony_ci#endif 21778c2ecf20Sopenharmony_ci spin_lock_irq(&chip->lock); 21788c2ecf20Sopenharmony_ci if (wait_handshake(chip)) { 21798c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 21808c2ecf20Sopenharmony_ci return -EIO; 21818c2ecf20Sopenharmony_ci } 21828c2ecf20Sopenharmony_ci clear_handshake(chip); 21838c2ecf20Sopenharmony_ci if (send_vector(chip, DSP_VC_GO_COMATOSE) < 0) { 21848c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 21858c2ecf20Sopenharmony_ci return -EIO; 21868c2ecf20Sopenharmony_ci } 21878c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci chip->dsp_code = NULL; 21908c2ecf20Sopenharmony_ci free_irq(chip->irq, chip); 21918c2ecf20Sopenharmony_ci chip->irq = -1; 21928c2ecf20Sopenharmony_ci chip->card->sync_irq = -1; 21938c2ecf20Sopenharmony_ci return 0; 21948c2ecf20Sopenharmony_ci} 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_cistatic int snd_echo_resume(struct device *dev) 21998c2ecf20Sopenharmony_ci{ 22008c2ecf20Sopenharmony_ci struct pci_dev *pci = to_pci_dev(dev); 22018c2ecf20Sopenharmony_ci struct echoaudio *chip = dev_get_drvdata(dev); 22028c2ecf20Sopenharmony_ci struct comm_page *commpage, *commpage_bak; 22038c2ecf20Sopenharmony_ci u32 pipe_alloc_mask; 22048c2ecf20Sopenharmony_ci int err; 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci commpage = chip->comm_page; 22078c2ecf20Sopenharmony_ci commpage_bak = kmemdup(commpage, sizeof(*commpage), GFP_KERNEL); 22088c2ecf20Sopenharmony_ci if (commpage_bak == NULL) 22098c2ecf20Sopenharmony_ci return -ENOMEM; 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_ci err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device); 22128c2ecf20Sopenharmony_ci if (err < 0) { 22138c2ecf20Sopenharmony_ci kfree(commpage_bak); 22148c2ecf20Sopenharmony_ci dev_err(dev, "resume init_hw err=%d\n", err); 22158c2ecf20Sopenharmony_ci return err; 22168c2ecf20Sopenharmony_ci } 22178c2ecf20Sopenharmony_ci 22188c2ecf20Sopenharmony_ci /* Temporarily set chip->pipe_alloc_mask=0 otherwise 22198c2ecf20Sopenharmony_ci * restore_dsp_settings() fails. 22208c2ecf20Sopenharmony_ci */ 22218c2ecf20Sopenharmony_ci pipe_alloc_mask = chip->pipe_alloc_mask; 22228c2ecf20Sopenharmony_ci chip->pipe_alloc_mask = 0; 22238c2ecf20Sopenharmony_ci err = restore_dsp_rettings(chip); 22248c2ecf20Sopenharmony_ci chip->pipe_alloc_mask = pipe_alloc_mask; 22258c2ecf20Sopenharmony_ci if (err < 0) { 22268c2ecf20Sopenharmony_ci kfree(commpage_bak); 22278c2ecf20Sopenharmony_ci return err; 22288c2ecf20Sopenharmony_ci } 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci memcpy(&commpage->audio_format, &commpage_bak->audio_format, 22318c2ecf20Sopenharmony_ci sizeof(commpage->audio_format)); 22328c2ecf20Sopenharmony_ci memcpy(&commpage->sglist_addr, &commpage_bak->sglist_addr, 22338c2ecf20Sopenharmony_ci sizeof(commpage->sglist_addr)); 22348c2ecf20Sopenharmony_ci memcpy(&commpage->midi_output, &commpage_bak->midi_output, 22358c2ecf20Sopenharmony_ci sizeof(commpage->midi_output)); 22368c2ecf20Sopenharmony_ci kfree(commpage_bak); 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED, 22398c2ecf20Sopenharmony_ci KBUILD_MODNAME, chip)) { 22408c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "cannot grab irq\n"); 22418c2ecf20Sopenharmony_ci return -EBUSY; 22428c2ecf20Sopenharmony_ci } 22438c2ecf20Sopenharmony_ci chip->irq = pci->irq; 22448c2ecf20Sopenharmony_ci chip->card->sync_irq = chip->irq; 22458c2ecf20Sopenharmony_ci dev_dbg(dev, "resume irq=%d\n", chip->irq); 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_MIDI 22488c2ecf20Sopenharmony_ci if (chip->midi_input_enabled) 22498c2ecf20Sopenharmony_ci enable_midi_input(chip, true); 22508c2ecf20Sopenharmony_ci if (chip->midi_out) 22518c2ecf20Sopenharmony_ci snd_echo_midi_output_trigger(chip->midi_out, 1); 22528c2ecf20Sopenharmony_ci#endif 22538c2ecf20Sopenharmony_ci 22548c2ecf20Sopenharmony_ci return 0; 22558c2ecf20Sopenharmony_ci} 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(snd_echo_pm, snd_echo_suspend, snd_echo_resume); 22588c2ecf20Sopenharmony_ci#define SND_ECHO_PM_OPS &snd_echo_pm 22598c2ecf20Sopenharmony_ci#else 22608c2ecf20Sopenharmony_ci#define SND_ECHO_PM_OPS NULL 22618c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_cistatic void snd_echo_remove(struct pci_dev *pci) 22658c2ecf20Sopenharmony_ci{ 22668c2ecf20Sopenharmony_ci struct echoaudio *chip; 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci chip = pci_get_drvdata(pci); 22698c2ecf20Sopenharmony_ci if (chip) 22708c2ecf20Sopenharmony_ci snd_card_free(chip->card); 22718c2ecf20Sopenharmony_ci} 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_ci 22748c2ecf20Sopenharmony_ci 22758c2ecf20Sopenharmony_ci/****************************************************************************** 22768c2ecf20Sopenharmony_ci Everything starts and ends here 22778c2ecf20Sopenharmony_ci******************************************************************************/ 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci/* pci_driver definition */ 22808c2ecf20Sopenharmony_cistatic struct pci_driver echo_driver = { 22818c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 22828c2ecf20Sopenharmony_ci .id_table = snd_echo_ids, 22838c2ecf20Sopenharmony_ci .probe = snd_echo_probe, 22848c2ecf20Sopenharmony_ci .remove = snd_echo_remove, 22858c2ecf20Sopenharmony_ci .driver = { 22868c2ecf20Sopenharmony_ci .pm = SND_ECHO_PM_OPS, 22878c2ecf20Sopenharmony_ci }, 22888c2ecf20Sopenharmony_ci}; 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_cimodule_pci_driver(echo_driver); 2291