18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 48c2ecf20Sopenharmony_ci * Routines for control of CS4235/4236B/4237B/4238B/4239 chips 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Note: 78c2ecf20Sopenharmony_ci * ----- 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Bugs: 108c2ecf20Sopenharmony_ci * ----- 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * Indirect control registers (CS4236B+) 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * C0 178c2ecf20Sopenharmony_ci * D8: WSS reset (all chips) 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * C1 (all chips except CS4236) 208c2ecf20Sopenharmony_ci * D7-D5: version 218c2ecf20Sopenharmony_ci * D4-D0: chip id 228c2ecf20Sopenharmony_ci * 11101 - CS4235 238c2ecf20Sopenharmony_ci * 01011 - CS4236B 248c2ecf20Sopenharmony_ci * 01000 - CS4237B 258c2ecf20Sopenharmony_ci * 01001 - CS4238B 268c2ecf20Sopenharmony_ci * 11110 - CS4239 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * C2 298c2ecf20Sopenharmony_ci * D7-D4: 3D Space (CS4235,CS4237B,CS4238B,CS4239) 308c2ecf20Sopenharmony_ci * D3-D0: 3D Center (CS4237B); 3D Volume (CS4238B) 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * C3 338c2ecf20Sopenharmony_ci * D7: 3D Enable (CS4237B) 348c2ecf20Sopenharmony_ci * D6: 3D Mono Enable (CS4237B) 358c2ecf20Sopenharmony_ci * D5: 3D Serial Output (CS4237B,CS4238B) 368c2ecf20Sopenharmony_ci * D4: 3D Enable (CS4235,CS4238B,CS4239) 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * C4 398c2ecf20Sopenharmony_ci * D7: consumer serial port enable (CS4237B,CS4238B) 408c2ecf20Sopenharmony_ci * D6: channels status block reset (CS4237B,CS4238B) 418c2ecf20Sopenharmony_ci * D5: user bit in sub-frame of digital audio data (CS4237B,CS4238B) 428c2ecf20Sopenharmony_ci * D4: validity bit in sub-frame of digital audio data (CS4237B,CS4238B) 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * C5 lower channel status (digital serial data description) (CS4237B,CS4238B) 458c2ecf20Sopenharmony_ci * D7-D6: first two bits of category code 468c2ecf20Sopenharmony_ci * D5: lock 478c2ecf20Sopenharmony_ci * D4-D3: pre-emphasis (0 = none, 1 = 50/15us) 488c2ecf20Sopenharmony_ci * D2: copy/copyright (0 = copy inhibited) 498c2ecf20Sopenharmony_ci * D1: 0 = digital audio / 1 = non-digital audio 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * C6 upper channel status (digital serial data description) (CS4237B,CS4238B) 528c2ecf20Sopenharmony_ci * D7-D6: sample frequency (0 = 44.1kHz) 538c2ecf20Sopenharmony_ci * D5: generation status (0 = no indication, 1 = original/commercially precaptureed data) 548c2ecf20Sopenharmony_ci * D4-D0: category code (upper bits) 558c2ecf20Sopenharmony_ci * 568c2ecf20Sopenharmony_ci * C7 reserved (must write 0) 578c2ecf20Sopenharmony_ci * 588c2ecf20Sopenharmony_ci * C8 wavetable control 598c2ecf20Sopenharmony_ci * D7: volume control interrupt enable (CS4235,CS4239) 608c2ecf20Sopenharmony_ci * D6: hardware volume control format (CS4235,CS4239) 618c2ecf20Sopenharmony_ci * D3: wavetable serial port enable (all chips) 628c2ecf20Sopenharmony_ci * D2: DSP serial port switch (all chips) 638c2ecf20Sopenharmony_ci * D1: disable MCLK (all chips) 648c2ecf20Sopenharmony_ci * D0: force BRESET low (all chips) 658c2ecf20Sopenharmony_ci * 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#include <linux/io.h> 698c2ecf20Sopenharmony_ci#include <linux/delay.h> 708c2ecf20Sopenharmony_ci#include <linux/init.h> 718c2ecf20Sopenharmony_ci#include <linux/time.h> 728c2ecf20Sopenharmony_ci#include <linux/wait.h> 738c2ecf20Sopenharmony_ci#include <sound/core.h> 748c2ecf20Sopenharmony_ci#include <sound/wss.h> 758c2ecf20Sopenharmony_ci#include <sound/asoundef.h> 768c2ecf20Sopenharmony_ci#include <sound/initval.h> 778c2ecf20Sopenharmony_ci#include <sound/tlv.h> 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic const unsigned char snd_cs4236_ext_map[18] = { 848c2ecf20Sopenharmony_ci /* CS4236_LEFT_LINE */ 0xff, 858c2ecf20Sopenharmony_ci /* CS4236_RIGHT_LINE */ 0xff, 868c2ecf20Sopenharmony_ci /* CS4236_LEFT_MIC */ 0xdf, 878c2ecf20Sopenharmony_ci /* CS4236_RIGHT_MIC */ 0xdf, 888c2ecf20Sopenharmony_ci /* CS4236_LEFT_MIX_CTRL */ 0xe0 | 0x18, 898c2ecf20Sopenharmony_ci /* CS4236_RIGHT_MIX_CTRL */ 0xe0, 908c2ecf20Sopenharmony_ci /* CS4236_LEFT_FM */ 0xbf, 918c2ecf20Sopenharmony_ci /* CS4236_RIGHT_FM */ 0xbf, 928c2ecf20Sopenharmony_ci /* CS4236_LEFT_DSP */ 0xbf, 938c2ecf20Sopenharmony_ci /* CS4236_RIGHT_DSP */ 0xbf, 948c2ecf20Sopenharmony_ci /* CS4236_RIGHT_LOOPBACK */ 0xbf, 958c2ecf20Sopenharmony_ci /* CS4236_DAC_MUTE */ 0xe0, 968c2ecf20Sopenharmony_ci /* CS4236_ADC_RATE */ 0x01, /* 48kHz */ 978c2ecf20Sopenharmony_ci /* CS4236_DAC_RATE */ 0x01, /* 48kHz */ 988c2ecf20Sopenharmony_ci /* CS4236_LEFT_MASTER */ 0xbf, 998c2ecf20Sopenharmony_ci /* CS4236_RIGHT_MASTER */ 0xbf, 1008c2ecf20Sopenharmony_ci /* CS4236_LEFT_WAVE */ 0xbf, 1018c2ecf20Sopenharmony_ci /* CS4236_RIGHT_WAVE */ 0xbf 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* 1058c2ecf20Sopenharmony_ci * 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic void snd_cs4236_ctrl_out(struct snd_wss *chip, 1098c2ecf20Sopenharmony_ci unsigned char reg, unsigned char val) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci outb(reg, chip->cport + 3); 1128c2ecf20Sopenharmony_ci outb(chip->cimage[reg] = val, chip->cport + 4); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic unsigned char snd_cs4236_ctrl_in(struct snd_wss *chip, unsigned char reg) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci outb(reg, chip->cport + 3); 1188c2ecf20Sopenharmony_ci return inb(chip->cport + 4); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* 1228c2ecf20Sopenharmony_ci * PCM 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci#define CLOCKS 8 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic const struct snd_ratnum clocks[CLOCKS] = { 1288c2ecf20Sopenharmony_ci { .num = 16934400, .den_min = 353, .den_max = 353, .den_step = 1 }, 1298c2ecf20Sopenharmony_ci { .num = 16934400, .den_min = 529, .den_max = 529, .den_step = 1 }, 1308c2ecf20Sopenharmony_ci { .num = 16934400, .den_min = 617, .den_max = 617, .den_step = 1 }, 1318c2ecf20Sopenharmony_ci { .num = 16934400, .den_min = 1058, .den_max = 1058, .den_step = 1 }, 1328c2ecf20Sopenharmony_ci { .num = 16934400, .den_min = 1764, .den_max = 1764, .den_step = 1 }, 1338c2ecf20Sopenharmony_ci { .num = 16934400, .den_min = 2117, .den_max = 2117, .den_step = 1 }, 1348c2ecf20Sopenharmony_ci { .num = 16934400, .den_min = 2558, .den_max = 2558, .den_step = 1 }, 1358c2ecf20Sopenharmony_ci { .num = 16934400/16, .den_min = 21, .den_max = 192, .den_step = 1 } 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = { 1398c2ecf20Sopenharmony_ci .nrats = CLOCKS, 1408c2ecf20Sopenharmony_ci .rats = clocks, 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int snd_cs4236_xrate(struct snd_pcm_runtime *runtime) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci return snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 1468c2ecf20Sopenharmony_ci &hw_constraints_clocks); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic unsigned char divisor_to_rate_register(unsigned int divisor) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci switch (divisor) { 1528c2ecf20Sopenharmony_ci case 353: return 1; 1538c2ecf20Sopenharmony_ci case 529: return 2; 1548c2ecf20Sopenharmony_ci case 617: return 3; 1558c2ecf20Sopenharmony_ci case 1058: return 4; 1568c2ecf20Sopenharmony_ci case 1764: return 5; 1578c2ecf20Sopenharmony_ci case 2117: return 6; 1588c2ecf20Sopenharmony_ci case 2558: return 7; 1598c2ecf20Sopenharmony_ci default: 1608c2ecf20Sopenharmony_ci if (divisor < 21 || divisor > 192) { 1618c2ecf20Sopenharmony_ci snd_BUG(); 1628c2ecf20Sopenharmony_ci return 192; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci return divisor; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic void snd_cs4236_playback_format(struct snd_wss *chip, 1698c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 1708c2ecf20Sopenharmony_ci unsigned char pdfr) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci unsigned long flags; 1738c2ecf20Sopenharmony_ci unsigned char rate = divisor_to_rate_register(params->rate_den); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 1768c2ecf20Sopenharmony_ci /* set fast playback format change and clean playback FIFO */ 1778c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4231_ALT_FEATURE_1, 1788c2ecf20Sopenharmony_ci chip->image[CS4231_ALT_FEATURE_1] | 0x10); 1798c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4231_PLAYBK_FORMAT, pdfr & 0xf0); 1808c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4231_ALT_FEATURE_1, 1818c2ecf20Sopenharmony_ci chip->image[CS4231_ALT_FEATURE_1] & ~0x10); 1828c2ecf20Sopenharmony_ci snd_cs4236_ext_out(chip, CS4236_DAC_RATE, rate); 1838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void snd_cs4236_capture_format(struct snd_wss *chip, 1878c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 1888c2ecf20Sopenharmony_ci unsigned char cdfr) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci unsigned long flags; 1918c2ecf20Sopenharmony_ci unsigned char rate = divisor_to_rate_register(params->rate_den); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 1948c2ecf20Sopenharmony_ci /* set fast capture format change and clean capture FIFO */ 1958c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4231_ALT_FEATURE_1, 1968c2ecf20Sopenharmony_ci chip->image[CS4231_ALT_FEATURE_1] | 0x20); 1978c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4231_REC_FORMAT, cdfr & 0xf0); 1988c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4231_ALT_FEATURE_1, 1998c2ecf20Sopenharmony_ci chip->image[CS4231_ALT_FEATURE_1] & ~0x20); 2008c2ecf20Sopenharmony_ci snd_cs4236_ext_out(chip, CS4236_ADC_RATE, rate); 2018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void snd_cs4236_suspend(struct snd_wss *chip) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci int reg; 2098c2ecf20Sopenharmony_ci unsigned long flags; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 2128c2ecf20Sopenharmony_ci for (reg = 0; reg < 32; reg++) 2138c2ecf20Sopenharmony_ci chip->image[reg] = snd_wss_in(chip, reg); 2148c2ecf20Sopenharmony_ci for (reg = 0; reg < 18; reg++) 2158c2ecf20Sopenharmony_ci chip->eimage[reg] = snd_cs4236_ext_in(chip, CS4236_I23VAL(reg)); 2168c2ecf20Sopenharmony_ci for (reg = 2; reg < 9; reg++) 2178c2ecf20Sopenharmony_ci chip->cimage[reg] = snd_cs4236_ctrl_in(chip, reg); 2188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic void snd_cs4236_resume(struct snd_wss *chip) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci int reg; 2248c2ecf20Sopenharmony_ci unsigned long flags; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci snd_wss_mce_up(chip); 2278c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 2288c2ecf20Sopenharmony_ci for (reg = 0; reg < 32; reg++) { 2298c2ecf20Sopenharmony_ci switch (reg) { 2308c2ecf20Sopenharmony_ci case CS4236_EXT_REG: 2318c2ecf20Sopenharmony_ci case CS4231_VERSION: 2328c2ecf20Sopenharmony_ci case 27: /* why? CS4235 - master left */ 2338c2ecf20Sopenharmony_ci case 29: /* why? CS4235 - master right */ 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci default: 2368c2ecf20Sopenharmony_ci snd_wss_out(chip, reg, chip->image[reg]); 2378c2ecf20Sopenharmony_ci break; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci for (reg = 0; reg < 18; reg++) 2418c2ecf20Sopenharmony_ci snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), chip->eimage[reg]); 2428c2ecf20Sopenharmony_ci for (reg = 2; reg < 9; reg++) { 2438c2ecf20Sopenharmony_ci switch (reg) { 2448c2ecf20Sopenharmony_ci case 7: 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci default: 2478c2ecf20Sopenharmony_ci snd_cs4236_ctrl_out(chip, reg, chip->cimage[reg]); 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 2518c2ecf20Sopenharmony_ci snd_wss_mce_down(chip); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 2558c2ecf20Sopenharmony_ci/* 2568c2ecf20Sopenharmony_ci * This function does no fail if the chip is not CS4236B or compatible. 2578c2ecf20Sopenharmony_ci * It just an equivalent to the snd_wss_create() then. 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ciint snd_cs4236_create(struct snd_card *card, 2608c2ecf20Sopenharmony_ci unsigned long port, 2618c2ecf20Sopenharmony_ci unsigned long cport, 2628c2ecf20Sopenharmony_ci int irq, int dma1, int dma2, 2638c2ecf20Sopenharmony_ci unsigned short hardware, 2648c2ecf20Sopenharmony_ci unsigned short hwshare, 2658c2ecf20Sopenharmony_ci struct snd_wss **rchip) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct snd_wss *chip; 2688c2ecf20Sopenharmony_ci unsigned char ver1, ver2; 2698c2ecf20Sopenharmony_ci unsigned int reg; 2708c2ecf20Sopenharmony_ci int err; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci *rchip = NULL; 2738c2ecf20Sopenharmony_ci if (hardware == WSS_HW_DETECT) 2748c2ecf20Sopenharmony_ci hardware = WSS_HW_DETECT3; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci err = snd_wss_create(card, port, cport, 2778c2ecf20Sopenharmony_ci irq, dma1, dma2, hardware, hwshare, &chip); 2788c2ecf20Sopenharmony_ci if (err < 0) 2798c2ecf20Sopenharmony_ci return err; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if ((chip->hardware & WSS_HW_CS4236B_MASK) == 0) { 2828c2ecf20Sopenharmony_ci snd_printd("chip is not CS4236+, hardware=0x%x\n", 2838c2ecf20Sopenharmony_ci chip->hardware); 2848c2ecf20Sopenharmony_ci *rchip = chip; 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci#if 0 2888c2ecf20Sopenharmony_ci { 2898c2ecf20Sopenharmony_ci int idx; 2908c2ecf20Sopenharmony_ci for (idx = 0; idx < 8; idx++) 2918c2ecf20Sopenharmony_ci snd_printk(KERN_DEBUG "CD%i = 0x%x\n", 2928c2ecf20Sopenharmony_ci idx, inb(chip->cport + idx)); 2938c2ecf20Sopenharmony_ci for (idx = 0; idx < 9; idx++) 2948c2ecf20Sopenharmony_ci snd_printk(KERN_DEBUG "C%i = 0x%x\n", 2958c2ecf20Sopenharmony_ci idx, snd_cs4236_ctrl_in(chip, idx)); 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci#endif 2988c2ecf20Sopenharmony_ci if (cport < 0x100 || cport == SNDRV_AUTO_PORT) { 2998c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "please, specify control port " 3008c2ecf20Sopenharmony_ci "for CS4236+ chips\n"); 3018c2ecf20Sopenharmony_ci snd_device_free(card, chip); 3028c2ecf20Sopenharmony_ci return -ENODEV; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci ver1 = snd_cs4236_ctrl_in(chip, 1); 3058c2ecf20Sopenharmony_ci ver2 = snd_cs4236_ext_in(chip, CS4236_VERSION); 3068c2ecf20Sopenharmony_ci snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n", 3078c2ecf20Sopenharmony_ci cport, ver1, ver2); 3088c2ecf20Sopenharmony_ci if (ver1 != ver2) { 3098c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "CS4236+ chip detected, but " 3108c2ecf20Sopenharmony_ci "control port 0x%lx is not valid\n", cport); 3118c2ecf20Sopenharmony_ci snd_device_free(card, chip); 3128c2ecf20Sopenharmony_ci return -ENODEV; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci snd_cs4236_ctrl_out(chip, 0, 0x00); 3158c2ecf20Sopenharmony_ci snd_cs4236_ctrl_out(chip, 2, 0xff); 3168c2ecf20Sopenharmony_ci snd_cs4236_ctrl_out(chip, 3, 0x00); 3178c2ecf20Sopenharmony_ci snd_cs4236_ctrl_out(chip, 4, 0x80); 3188c2ecf20Sopenharmony_ci reg = ((IEC958_AES1_CON_PCM_CODER & 3) << 6) | 3198c2ecf20Sopenharmony_ci IEC958_AES0_CON_EMPHASIS_NONE; 3208c2ecf20Sopenharmony_ci snd_cs4236_ctrl_out(chip, 5, reg); 3218c2ecf20Sopenharmony_ci snd_cs4236_ctrl_out(chip, 6, IEC958_AES1_CON_PCM_CODER >> 2); 3228c2ecf20Sopenharmony_ci snd_cs4236_ctrl_out(chip, 7, 0x00); 3238c2ecf20Sopenharmony_ci /* 3248c2ecf20Sopenharmony_ci * 0x8c for C8 is valid for Turtle Beach Malibu - the IEC-958 3258c2ecf20Sopenharmony_ci * output is working with this setup, other hardware should 3268c2ecf20Sopenharmony_ci * have different signal paths and this value should be 3278c2ecf20Sopenharmony_ci * selectable in the future 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ci snd_cs4236_ctrl_out(chip, 8, 0x8c); 3308c2ecf20Sopenharmony_ci chip->rate_constraint = snd_cs4236_xrate; 3318c2ecf20Sopenharmony_ci chip->set_playback_format = snd_cs4236_playback_format; 3328c2ecf20Sopenharmony_ci chip->set_capture_format = snd_cs4236_capture_format; 3338c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 3348c2ecf20Sopenharmony_ci chip->suspend = snd_cs4236_suspend; 3358c2ecf20Sopenharmony_ci chip->resume = snd_cs4236_resume; 3368c2ecf20Sopenharmony_ci#endif 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* initialize extended registers */ 3398c2ecf20Sopenharmony_ci for (reg = 0; reg < sizeof(snd_cs4236_ext_map); reg++) 3408c2ecf20Sopenharmony_ci snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), 3418c2ecf20Sopenharmony_ci snd_cs4236_ext_map[reg]); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* initialize compatible but more featured registers */ 3448c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4231_LEFT_INPUT, 0x40); 3458c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4231_RIGHT_INPUT, 0x40); 3468c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff); 3478c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4231_AUX1_RIGHT_INPUT, 0xff); 3488c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4231_AUX2_LEFT_INPUT, 0xdf); 3498c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4231_AUX2_RIGHT_INPUT, 0xdf); 3508c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4231_RIGHT_LINE_IN, 0xff); 3518c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4231_LEFT_LINE_IN, 0xff); 3528c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4231_RIGHT_LINE_IN, 0xff); 3538c2ecf20Sopenharmony_ci switch (chip->hardware) { 3548c2ecf20Sopenharmony_ci case WSS_HW_CS4235: 3558c2ecf20Sopenharmony_ci case WSS_HW_CS4239: 3568c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4235_LEFT_MASTER, 0xff); 3578c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4235_RIGHT_MASTER, 0xff); 3588c2ecf20Sopenharmony_ci break; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci *rchip = chip; 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ciint snd_cs4236_pcm(struct snd_wss *chip, int device) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci int err; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci err = snd_wss_pcm(chip, device); 3708c2ecf20Sopenharmony_ci if (err < 0) 3718c2ecf20Sopenharmony_ci return err; 3728c2ecf20Sopenharmony_ci chip->pcm->info_flags &= ~SNDRV_PCM_INFO_JOINT_DUPLEX; 3738c2ecf20Sopenharmony_ci return 0; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci/* 3778c2ecf20Sopenharmony_ci * MIXER 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci#define CS4236_SINGLE(xname, xindex, reg, shift, mask, invert) \ 3818c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 3828c2ecf20Sopenharmony_ci .info = snd_cs4236_info_single, \ 3838c2ecf20Sopenharmony_ci .get = snd_cs4236_get_single, .put = snd_cs4236_put_single, \ 3848c2ecf20Sopenharmony_ci .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci#define CS4236_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \ 3878c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 3888c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 3898c2ecf20Sopenharmony_ci .info = snd_cs4236_info_single, \ 3908c2ecf20Sopenharmony_ci .get = snd_cs4236_get_single, .put = snd_cs4236_put_single, \ 3918c2ecf20Sopenharmony_ci .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \ 3928c2ecf20Sopenharmony_ci .tlv = { .p = (xtlv) } } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic int snd_cs4236_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 3998c2ecf20Sopenharmony_ci uinfo->count = 1; 4008c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 4018c2ecf20Sopenharmony_ci uinfo->value.integer.max = mask; 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic int snd_cs4236_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 4088c2ecf20Sopenharmony_ci unsigned long flags; 4098c2ecf20Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 4108c2ecf20Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 4118c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 4128c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 4158c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(reg)] >> shift) & mask; 4168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 4178c2ecf20Sopenharmony_ci if (invert) 4188c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic int snd_cs4236_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 4258c2ecf20Sopenharmony_ci unsigned long flags; 4268c2ecf20Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 4278c2ecf20Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 4288c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 4298c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 4308c2ecf20Sopenharmony_ci int change; 4318c2ecf20Sopenharmony_ci unsigned short val; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci val = (ucontrol->value.integer.value[0] & mask); 4348c2ecf20Sopenharmony_ci if (invert) 4358c2ecf20Sopenharmony_ci val = mask - val; 4368c2ecf20Sopenharmony_ci val <<= shift; 4378c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 4388c2ecf20Sopenharmony_ci val = (chip->eimage[CS4236_REG(reg)] & ~(mask << shift)) | val; 4398c2ecf20Sopenharmony_ci change = val != chip->eimage[CS4236_REG(reg)]; 4408c2ecf20Sopenharmony_ci snd_cs4236_ext_out(chip, reg, val); 4418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 4428c2ecf20Sopenharmony_ci return change; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci#define CS4236_SINGLEC(xname, xindex, reg, shift, mask, invert) \ 4468c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 4478c2ecf20Sopenharmony_ci .info = snd_cs4236_info_single, \ 4488c2ecf20Sopenharmony_ci .get = snd_cs4236_get_singlec, .put = snd_cs4236_put_singlec, \ 4498c2ecf20Sopenharmony_ci .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic int snd_cs4236_get_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 4548c2ecf20Sopenharmony_ci unsigned long flags; 4558c2ecf20Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 4568c2ecf20Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 4578c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 4588c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 4618c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = (chip->cimage[reg] >> shift) & mask; 4628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 4638c2ecf20Sopenharmony_ci if (invert) 4648c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 4658c2ecf20Sopenharmony_ci return 0; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic int snd_cs4236_put_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 4718c2ecf20Sopenharmony_ci unsigned long flags; 4728c2ecf20Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 4738c2ecf20Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 4748c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 4758c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 4768c2ecf20Sopenharmony_ci int change; 4778c2ecf20Sopenharmony_ci unsigned short val; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci val = (ucontrol->value.integer.value[0] & mask); 4808c2ecf20Sopenharmony_ci if (invert) 4818c2ecf20Sopenharmony_ci val = mask - val; 4828c2ecf20Sopenharmony_ci val <<= shift; 4838c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 4848c2ecf20Sopenharmony_ci val = (chip->cimage[reg] & ~(mask << shift)) | val; 4858c2ecf20Sopenharmony_ci change = val != chip->cimage[reg]; 4868c2ecf20Sopenharmony_ci snd_cs4236_ctrl_out(chip, reg, val); 4878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 4888c2ecf20Sopenharmony_ci return change; 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci#define CS4236_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ 4928c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 4938c2ecf20Sopenharmony_ci .info = snd_cs4236_info_double, \ 4948c2ecf20Sopenharmony_ci .get = snd_cs4236_get_double, .put = snd_cs4236_put_double, \ 4958c2ecf20Sopenharmony_ci .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci#define CS4236_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, \ 4988c2ecf20Sopenharmony_ci shift_right, mask, invert, xtlv) \ 4998c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 5008c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 5018c2ecf20Sopenharmony_ci .info = snd_cs4236_info_double, \ 5028c2ecf20Sopenharmony_ci .get = snd_cs4236_get_double, .put = snd_cs4236_put_double, \ 5038c2ecf20Sopenharmony_ci .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | \ 5048c2ecf20Sopenharmony_ci (shift_right << 19) | (mask << 24) | (invert << 22), \ 5058c2ecf20Sopenharmony_ci .tlv = { .p = (xtlv) } } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int snd_cs4236_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 5128c2ecf20Sopenharmony_ci uinfo->count = 2; 5138c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 5148c2ecf20Sopenharmony_ci uinfo->value.integer.max = mask; 5158c2ecf20Sopenharmony_ci return 0; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic int snd_cs4236_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 5218c2ecf20Sopenharmony_ci unsigned long flags; 5228c2ecf20Sopenharmony_ci int left_reg = kcontrol->private_value & 0xff; 5238c2ecf20Sopenharmony_ci int right_reg = (kcontrol->private_value >> 8) & 0xff; 5248c2ecf20Sopenharmony_ci int shift_left = (kcontrol->private_value >> 16) & 0x07; 5258c2ecf20Sopenharmony_ci int shift_right = (kcontrol->private_value >> 19) & 0x07; 5268c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 5278c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 22) & 1; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 5308c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(left_reg)] >> shift_left) & mask; 5318c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask; 5328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 5338c2ecf20Sopenharmony_ci if (invert) { 5348c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 5358c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci return 0; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 5438c2ecf20Sopenharmony_ci unsigned long flags; 5448c2ecf20Sopenharmony_ci int left_reg = kcontrol->private_value & 0xff; 5458c2ecf20Sopenharmony_ci int right_reg = (kcontrol->private_value >> 8) & 0xff; 5468c2ecf20Sopenharmony_ci int shift_left = (kcontrol->private_value >> 16) & 0x07; 5478c2ecf20Sopenharmony_ci int shift_right = (kcontrol->private_value >> 19) & 0x07; 5488c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 5498c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 22) & 1; 5508c2ecf20Sopenharmony_ci int change; 5518c2ecf20Sopenharmony_ci unsigned short val1, val2; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci val1 = ucontrol->value.integer.value[0] & mask; 5548c2ecf20Sopenharmony_ci val2 = ucontrol->value.integer.value[1] & mask; 5558c2ecf20Sopenharmony_ci if (invert) { 5568c2ecf20Sopenharmony_ci val1 = mask - val1; 5578c2ecf20Sopenharmony_ci val2 = mask - val2; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci val1 <<= shift_left; 5608c2ecf20Sopenharmony_ci val2 <<= shift_right; 5618c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 5628c2ecf20Sopenharmony_ci if (left_reg != right_reg) { 5638c2ecf20Sopenharmony_ci val1 = (chip->eimage[CS4236_REG(left_reg)] & ~(mask << shift_left)) | val1; 5648c2ecf20Sopenharmony_ci val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2; 5658c2ecf20Sopenharmony_ci change = val1 != chip->eimage[CS4236_REG(left_reg)] || val2 != chip->eimage[CS4236_REG(right_reg)]; 5668c2ecf20Sopenharmony_ci snd_cs4236_ext_out(chip, left_reg, val1); 5678c2ecf20Sopenharmony_ci snd_cs4236_ext_out(chip, right_reg, val2); 5688c2ecf20Sopenharmony_ci } else { 5698c2ecf20Sopenharmony_ci val1 = (chip->eimage[CS4236_REG(left_reg)] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; 5708c2ecf20Sopenharmony_ci change = val1 != chip->eimage[CS4236_REG(left_reg)]; 5718c2ecf20Sopenharmony_ci snd_cs4236_ext_out(chip, left_reg, val1); 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 5748c2ecf20Sopenharmony_ci return change; 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci#define CS4236_DOUBLE1(xname, xindex, left_reg, right_reg, shift_left, \ 5788c2ecf20Sopenharmony_ci shift_right, mask, invert) \ 5798c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 5808c2ecf20Sopenharmony_ci .info = snd_cs4236_info_double, \ 5818c2ecf20Sopenharmony_ci .get = snd_cs4236_get_double1, .put = snd_cs4236_put_double1, \ 5828c2ecf20Sopenharmony_ci .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci#define CS4236_DOUBLE1_TLV(xname, xindex, left_reg, right_reg, shift_left, \ 5858c2ecf20Sopenharmony_ci shift_right, mask, invert, xtlv) \ 5868c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 5878c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 5888c2ecf20Sopenharmony_ci .info = snd_cs4236_info_double, \ 5898c2ecf20Sopenharmony_ci .get = snd_cs4236_get_double1, .put = snd_cs4236_put_double1, \ 5908c2ecf20Sopenharmony_ci .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | \ 5918c2ecf20Sopenharmony_ci (shift_right << 19) | (mask << 24) | (invert << 22), \ 5928c2ecf20Sopenharmony_ci .tlv = { .p = (xtlv) } } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic int snd_cs4236_get_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 5978c2ecf20Sopenharmony_ci unsigned long flags; 5988c2ecf20Sopenharmony_ci int left_reg = kcontrol->private_value & 0xff; 5998c2ecf20Sopenharmony_ci int right_reg = (kcontrol->private_value >> 8) & 0xff; 6008c2ecf20Sopenharmony_ci int shift_left = (kcontrol->private_value >> 16) & 0x07; 6018c2ecf20Sopenharmony_ci int shift_right = (kcontrol->private_value >> 19) & 0x07; 6028c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 6038c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 22) & 1; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 6068c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; 6078c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask; 6088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 6098c2ecf20Sopenharmony_ci if (invert) { 6108c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 6118c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci return 0; 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cistatic int snd_cs4236_put_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 6198c2ecf20Sopenharmony_ci unsigned long flags; 6208c2ecf20Sopenharmony_ci int left_reg = kcontrol->private_value & 0xff; 6218c2ecf20Sopenharmony_ci int right_reg = (kcontrol->private_value >> 8) & 0xff; 6228c2ecf20Sopenharmony_ci int shift_left = (kcontrol->private_value >> 16) & 0x07; 6238c2ecf20Sopenharmony_ci int shift_right = (kcontrol->private_value >> 19) & 0x07; 6248c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 6258c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 22) & 1; 6268c2ecf20Sopenharmony_ci int change; 6278c2ecf20Sopenharmony_ci unsigned short val1, val2; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci val1 = ucontrol->value.integer.value[0] & mask; 6308c2ecf20Sopenharmony_ci val2 = ucontrol->value.integer.value[1] & mask; 6318c2ecf20Sopenharmony_ci if (invert) { 6328c2ecf20Sopenharmony_ci val1 = mask - val1; 6338c2ecf20Sopenharmony_ci val2 = mask - val2; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci val1 <<= shift_left; 6368c2ecf20Sopenharmony_ci val2 <<= shift_right; 6378c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 6388c2ecf20Sopenharmony_ci val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; 6398c2ecf20Sopenharmony_ci val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2; 6408c2ecf20Sopenharmony_ci change = val1 != chip->image[left_reg] || val2 != chip->eimage[CS4236_REG(right_reg)]; 6418c2ecf20Sopenharmony_ci snd_wss_out(chip, left_reg, val1); 6428c2ecf20Sopenharmony_ci snd_cs4236_ext_out(chip, right_reg, val2); 6438c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 6448c2ecf20Sopenharmony_ci return change; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci#define CS4236_MASTER_DIGITAL(xname, xindex, xtlv) \ 6488c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 6498c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 6508c2ecf20Sopenharmony_ci .info = snd_cs4236_info_double, \ 6518c2ecf20Sopenharmony_ci .get = snd_cs4236_get_master_digital, .put = snd_cs4236_put_master_digital, \ 6528c2ecf20Sopenharmony_ci .private_value = 71 << 24, \ 6538c2ecf20Sopenharmony_ci .tlv = { .p = (xtlv) } } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic inline int snd_cs4236_mixer_master_digital_invert_volume(int vol) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci return (vol < 64) ? 63 - vol : 64 + (71 - vol); 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic int snd_cs4236_get_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 6638c2ecf20Sopenharmony_ci unsigned long flags; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 6668c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & 0x7f); 6678c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & 0x7f); 6688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 6698c2ecf20Sopenharmony_ci return 0; 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic int snd_cs4236_put_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 6758c2ecf20Sopenharmony_ci unsigned long flags; 6768c2ecf20Sopenharmony_ci int change; 6778c2ecf20Sopenharmony_ci unsigned short val1, val2; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci val1 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[0] & 0x7f); 6808c2ecf20Sopenharmony_ci val2 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[1] & 0x7f); 6818c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 6828c2ecf20Sopenharmony_ci val1 = (chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & ~0x7f) | val1; 6838c2ecf20Sopenharmony_ci val2 = (chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & ~0x7f) | val2; 6848c2ecf20Sopenharmony_ci change = val1 != chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] || val2 != chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)]; 6858c2ecf20Sopenharmony_ci snd_cs4236_ext_out(chip, CS4236_LEFT_MASTER, val1); 6868c2ecf20Sopenharmony_ci snd_cs4236_ext_out(chip, CS4236_RIGHT_MASTER, val2); 6878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 6888c2ecf20Sopenharmony_ci return change; 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci#define CS4235_OUTPUT_ACCU(xname, xindex, xtlv) \ 6928c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 6938c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 6948c2ecf20Sopenharmony_ci .info = snd_cs4236_info_double, \ 6958c2ecf20Sopenharmony_ci .get = snd_cs4235_get_output_accu, .put = snd_cs4235_put_output_accu, \ 6968c2ecf20Sopenharmony_ci .private_value = 3 << 24, \ 6978c2ecf20Sopenharmony_ci .tlv = { .p = (xtlv) } } 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic inline int snd_cs4235_mixer_output_accu_get_volume(int vol) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci switch ((vol >> 5) & 3) { 7028c2ecf20Sopenharmony_ci case 0: return 1; 7038c2ecf20Sopenharmony_ci case 1: return 3; 7048c2ecf20Sopenharmony_ci case 2: return 2; 7058c2ecf20Sopenharmony_ci case 3: return 0; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci return 3; 7088c2ecf20Sopenharmony_ci} 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cistatic inline int snd_cs4235_mixer_output_accu_set_volume(int vol) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci switch (vol & 3) { 7138c2ecf20Sopenharmony_ci case 0: return 3 << 5; 7148c2ecf20Sopenharmony_ci case 1: return 0 << 5; 7158c2ecf20Sopenharmony_ci case 2: return 2 << 5; 7168c2ecf20Sopenharmony_ci case 3: return 1 << 5; 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci return 1 << 5; 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_cistatic int snd_cs4235_get_output_accu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 7248c2ecf20Sopenharmony_ci unsigned long flags; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 7278c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_LEFT_MASTER]); 7288c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_RIGHT_MASTER]); 7298c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 7308c2ecf20Sopenharmony_ci return 0; 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic int snd_cs4235_put_output_accu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 7368c2ecf20Sopenharmony_ci unsigned long flags; 7378c2ecf20Sopenharmony_ci int change; 7388c2ecf20Sopenharmony_ci unsigned short val1, val2; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci val1 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[0]); 7418c2ecf20Sopenharmony_ci val2 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[1]); 7428c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 7438c2ecf20Sopenharmony_ci val1 = (chip->image[CS4235_LEFT_MASTER] & ~(3 << 5)) | val1; 7448c2ecf20Sopenharmony_ci val2 = (chip->image[CS4235_RIGHT_MASTER] & ~(3 << 5)) | val2; 7458c2ecf20Sopenharmony_ci change = val1 != chip->image[CS4235_LEFT_MASTER] || val2 != chip->image[CS4235_RIGHT_MASTER]; 7468c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4235_LEFT_MASTER, val1); 7478c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4235_RIGHT_MASTER, val2); 7488c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 7498c2ecf20Sopenharmony_ci return change; 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_7bit, -9450, 150, 0); 7538c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0); 7548c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_6bit_12db_max, -8250, 150, 0); 7558c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0); 7568c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_5bit_22db_max, -2400, 150, 0); 7578c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0); 7588c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_2bit, -1800, 600, 0); 7598c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cs4236_controls[] = { 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ciCS4236_DOUBLE("Master Digital Playback Switch", 0, 7648c2ecf20Sopenharmony_ci CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1), 7658c2ecf20Sopenharmony_ciCS4236_DOUBLE("Master Digital Capture Switch", 0, 7668c2ecf20Sopenharmony_ci CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1), 7678c2ecf20Sopenharmony_ciCS4236_MASTER_DIGITAL("Master Digital Volume", 0, db_scale_7bit), 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ciCS4236_DOUBLE_TLV("Capture Boost Volume", 0, 7708c2ecf20Sopenharmony_ci CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1, 7718c2ecf20Sopenharmony_ci db_scale_2bit), 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ciWSS_DOUBLE("PCM Playback Switch", 0, 7748c2ecf20Sopenharmony_ci CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), 7758c2ecf20Sopenharmony_ciWSS_DOUBLE_TLV("PCM Playback Volume", 0, 7768c2ecf20Sopenharmony_ci CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1, 7778c2ecf20Sopenharmony_ci db_scale_6bit), 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ciCS4236_DOUBLE("DSP Playback Switch", 0, 7808c2ecf20Sopenharmony_ci CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1), 7818c2ecf20Sopenharmony_ciCS4236_DOUBLE_TLV("DSP Playback Volume", 0, 7828c2ecf20Sopenharmony_ci CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1, 7838c2ecf20Sopenharmony_ci db_scale_6bit), 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ciCS4236_DOUBLE("FM Playback Switch", 0, 7868c2ecf20Sopenharmony_ci CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1), 7878c2ecf20Sopenharmony_ciCS4236_DOUBLE_TLV("FM Playback Volume", 0, 7888c2ecf20Sopenharmony_ci CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1, 7898c2ecf20Sopenharmony_ci db_scale_6bit), 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ciCS4236_DOUBLE("Wavetable Playback Switch", 0, 7928c2ecf20Sopenharmony_ci CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1), 7938c2ecf20Sopenharmony_ciCS4236_DOUBLE_TLV("Wavetable Playback Volume", 0, 7948c2ecf20Sopenharmony_ci CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1, 7958c2ecf20Sopenharmony_ci db_scale_6bit_12db_max), 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ciWSS_DOUBLE("Synth Playback Switch", 0, 7988c2ecf20Sopenharmony_ci CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), 7998c2ecf20Sopenharmony_ciWSS_DOUBLE_TLV("Synth Volume", 0, 8008c2ecf20Sopenharmony_ci CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1, 8018c2ecf20Sopenharmony_ci db_scale_5bit_12db_max), 8028c2ecf20Sopenharmony_ciWSS_DOUBLE("Synth Capture Switch", 0, 8038c2ecf20Sopenharmony_ci CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1), 8048c2ecf20Sopenharmony_ciWSS_DOUBLE("Synth Capture Bypass", 0, 8058c2ecf20Sopenharmony_ci CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 5, 5, 1, 1), 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ciCS4236_DOUBLE("Mic Playback Switch", 0, 8088c2ecf20Sopenharmony_ci CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1), 8098c2ecf20Sopenharmony_ciCS4236_DOUBLE("Mic Capture Switch", 0, 8108c2ecf20Sopenharmony_ci CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1), 8118c2ecf20Sopenharmony_ciCS4236_DOUBLE_TLV("Mic Volume", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 8128c2ecf20Sopenharmony_ci 0, 0, 31, 1, db_scale_5bit_22db_max), 8138c2ecf20Sopenharmony_ciCS4236_DOUBLE("Mic Playback Boost (+20dB)", 0, 8148c2ecf20Sopenharmony_ci CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0), 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ciWSS_DOUBLE("Line Playback Switch", 0, 8178c2ecf20Sopenharmony_ci CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), 8188c2ecf20Sopenharmony_ciWSS_DOUBLE_TLV("Line Volume", 0, 8198c2ecf20Sopenharmony_ci CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1, 8208c2ecf20Sopenharmony_ci db_scale_5bit_12db_max), 8218c2ecf20Sopenharmony_ciWSS_DOUBLE("Line Capture Switch", 0, 8228c2ecf20Sopenharmony_ci CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1), 8238c2ecf20Sopenharmony_ciWSS_DOUBLE("Line Capture Bypass", 0, 8248c2ecf20Sopenharmony_ci CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 5, 5, 1, 1), 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ciWSS_DOUBLE("CD Playback Switch", 0, 8278c2ecf20Sopenharmony_ci CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), 8288c2ecf20Sopenharmony_ciWSS_DOUBLE_TLV("CD Volume", 0, 8298c2ecf20Sopenharmony_ci CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1, 8308c2ecf20Sopenharmony_ci db_scale_5bit_12db_max), 8318c2ecf20Sopenharmony_ciWSS_DOUBLE("CD Capture Switch", 0, 8328c2ecf20Sopenharmony_ci CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1), 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ciCS4236_DOUBLE1("Mono Output Playback Switch", 0, 8358c2ecf20Sopenharmony_ci CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1), 8368c2ecf20Sopenharmony_ciCS4236_DOUBLE1("Beep Playback Switch", 0, 8378c2ecf20Sopenharmony_ci CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1), 8388c2ecf20Sopenharmony_ciWSS_SINGLE_TLV("Beep Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1, 8398c2ecf20Sopenharmony_ci db_scale_4bit), 8408c2ecf20Sopenharmony_ciWSS_SINGLE("Beep Bypass Playback Switch", 0, CS4231_MONO_CTRL, 5, 1, 0), 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ciWSS_DOUBLE_TLV("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 8438c2ecf20Sopenharmony_ci 0, 0, 15, 0, db_scale_rec_gain), 8448c2ecf20Sopenharmony_ciWSS_DOUBLE("Analog Loopback Capture Switch", 0, 8458c2ecf20Sopenharmony_ci CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0), 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ciWSS_SINGLE("Loopback Digital Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0), 8488c2ecf20Sopenharmony_ciCS4236_DOUBLE1_TLV("Loopback Digital Playback Volume", 0, 8498c2ecf20Sopenharmony_ci CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1, 8508c2ecf20Sopenharmony_ci db_scale_6bit), 8518c2ecf20Sopenharmony_ci}; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_5bit_6db_max, -5600, 200, 0); 8548c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_2bit_16db_max, -2400, 800, 0); 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cs4235_controls[] = { 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ciWSS_DOUBLE("Master Playback Switch", 0, 8598c2ecf20Sopenharmony_ci CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1), 8608c2ecf20Sopenharmony_ciWSS_DOUBLE_TLV("Master Playback Volume", 0, 8618c2ecf20Sopenharmony_ci CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1, 8628c2ecf20Sopenharmony_ci db_scale_5bit_6db_max), 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ciCS4235_OUTPUT_ACCU("Playback Volume", 0, db_scale_2bit_16db_max), 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ciWSS_DOUBLE("Synth Playback Switch", 1, 8678c2ecf20Sopenharmony_ci CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), 8688c2ecf20Sopenharmony_ciWSS_DOUBLE("Synth Capture Switch", 1, 8698c2ecf20Sopenharmony_ci CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1), 8708c2ecf20Sopenharmony_ciWSS_DOUBLE_TLV("Synth Volume", 1, 8718c2ecf20Sopenharmony_ci CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1, 8728c2ecf20Sopenharmony_ci db_scale_5bit_12db_max), 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ciCS4236_DOUBLE_TLV("Capture Volume", 0, 8758c2ecf20Sopenharmony_ci CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1, 8768c2ecf20Sopenharmony_ci db_scale_2bit), 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ciWSS_DOUBLE("PCM Playback Switch", 0, 8798c2ecf20Sopenharmony_ci CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), 8808c2ecf20Sopenharmony_ciWSS_DOUBLE("PCM Capture Switch", 0, 8818c2ecf20Sopenharmony_ci CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1), 8828c2ecf20Sopenharmony_ciWSS_DOUBLE_TLV("PCM Volume", 0, 8838c2ecf20Sopenharmony_ci CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1, 8848c2ecf20Sopenharmony_ci db_scale_6bit), 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ciCS4236_DOUBLE("DSP Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1), 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ciCS4236_DOUBLE("FM Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1), 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ciCS4236_DOUBLE("Wavetable Switch", 0, 8918c2ecf20Sopenharmony_ci CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1), 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ciCS4236_DOUBLE("Mic Capture Switch", 0, 8948c2ecf20Sopenharmony_ci CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1), 8958c2ecf20Sopenharmony_ciCS4236_DOUBLE("Mic Playback Switch", 0, 8968c2ecf20Sopenharmony_ci CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1), 8978c2ecf20Sopenharmony_ciCS4236_SINGLE_TLV("Mic Volume", 0, CS4236_LEFT_MIC, 0, 31, 1, 8988c2ecf20Sopenharmony_ci db_scale_5bit_22db_max), 8998c2ecf20Sopenharmony_ciCS4236_SINGLE("Mic Boost (+20dB)", 0, CS4236_LEFT_MIC, 5, 1, 0), 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ciWSS_DOUBLE("Line Playback Switch", 0, 9028c2ecf20Sopenharmony_ci CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), 9038c2ecf20Sopenharmony_ciWSS_DOUBLE("Line Capture Switch", 0, 9048c2ecf20Sopenharmony_ci CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1), 9058c2ecf20Sopenharmony_ciWSS_DOUBLE_TLV("Line Volume", 0, 9068c2ecf20Sopenharmony_ci CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1, 9078c2ecf20Sopenharmony_ci db_scale_5bit_12db_max), 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ciWSS_DOUBLE("CD Playback Switch", 1, 9108c2ecf20Sopenharmony_ci CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), 9118c2ecf20Sopenharmony_ciWSS_DOUBLE("CD Capture Switch", 1, 9128c2ecf20Sopenharmony_ci CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1), 9138c2ecf20Sopenharmony_ciWSS_DOUBLE_TLV("CD Volume", 1, 9148c2ecf20Sopenharmony_ci CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1, 9158c2ecf20Sopenharmony_ci db_scale_5bit_12db_max), 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ciCS4236_DOUBLE1("Beep Playback Switch", 0, 9188c2ecf20Sopenharmony_ci CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1), 9198c2ecf20Sopenharmony_ciWSS_SINGLE("Beep Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ciWSS_DOUBLE("Analog Loopback Switch", 0, 9228c2ecf20Sopenharmony_ci CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0), 9238c2ecf20Sopenharmony_ci}; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci#define CS4236_IEC958_ENABLE(xname, xindex) \ 9268c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 9278c2ecf20Sopenharmony_ci .info = snd_cs4236_info_single, \ 9288c2ecf20Sopenharmony_ci .get = snd_cs4236_get_iec958_switch, .put = snd_cs4236_put_iec958_switch, \ 9298c2ecf20Sopenharmony_ci .private_value = 1 << 16 } 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_cistatic int snd_cs4236_get_iec958_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 9328c2ecf20Sopenharmony_ci{ 9338c2ecf20Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 9348c2ecf20Sopenharmony_ci unsigned long flags; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 9378c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = chip->image[CS4231_ALT_FEATURE_1] & 0x02 ? 1 : 0; 9388c2ecf20Sopenharmony_ci#if 0 9398c2ecf20Sopenharmony_ci printk(KERN_DEBUG "get valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, " 9408c2ecf20Sopenharmony_ci "C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n", 9418c2ecf20Sopenharmony_ci snd_wss_in(chip, CS4231_ALT_FEATURE_1), 9428c2ecf20Sopenharmony_ci snd_cs4236_ctrl_in(chip, 3), 9438c2ecf20Sopenharmony_ci snd_cs4236_ctrl_in(chip, 4), 9448c2ecf20Sopenharmony_ci snd_cs4236_ctrl_in(chip, 5), 9458c2ecf20Sopenharmony_ci snd_cs4236_ctrl_in(chip, 6), 9468c2ecf20Sopenharmony_ci snd_cs4236_ctrl_in(chip, 8)); 9478c2ecf20Sopenharmony_ci#endif 9488c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 9498c2ecf20Sopenharmony_ci return 0; 9508c2ecf20Sopenharmony_ci} 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_cistatic int snd_cs4236_put_iec958_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 9538c2ecf20Sopenharmony_ci{ 9548c2ecf20Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 9558c2ecf20Sopenharmony_ci unsigned long flags; 9568c2ecf20Sopenharmony_ci int change; 9578c2ecf20Sopenharmony_ci unsigned short enable, val; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci enable = ucontrol->value.integer.value[0] & 1; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci mutex_lock(&chip->mce_mutex); 9628c2ecf20Sopenharmony_ci snd_wss_mce_up(chip); 9638c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 9648c2ecf20Sopenharmony_ci val = (chip->image[CS4231_ALT_FEATURE_1] & ~0x0e) | (0<<2) | (enable << 1); 9658c2ecf20Sopenharmony_ci change = val != chip->image[CS4231_ALT_FEATURE_1]; 9668c2ecf20Sopenharmony_ci snd_wss_out(chip, CS4231_ALT_FEATURE_1, val); 9678c2ecf20Sopenharmony_ci val = snd_cs4236_ctrl_in(chip, 4) | 0xc0; 9688c2ecf20Sopenharmony_ci snd_cs4236_ctrl_out(chip, 4, val); 9698c2ecf20Sopenharmony_ci udelay(100); 9708c2ecf20Sopenharmony_ci val &= ~0x40; 9718c2ecf20Sopenharmony_ci snd_cs4236_ctrl_out(chip, 4, val); 9728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 9738c2ecf20Sopenharmony_ci snd_wss_mce_down(chip); 9748c2ecf20Sopenharmony_ci mutex_unlock(&chip->mce_mutex); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci#if 0 9778c2ecf20Sopenharmony_ci printk(KERN_DEBUG "set valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, " 9788c2ecf20Sopenharmony_ci "C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n", 9798c2ecf20Sopenharmony_ci snd_wss_in(chip, CS4231_ALT_FEATURE_1), 9808c2ecf20Sopenharmony_ci snd_cs4236_ctrl_in(chip, 3), 9818c2ecf20Sopenharmony_ci snd_cs4236_ctrl_in(chip, 4), 9828c2ecf20Sopenharmony_ci snd_cs4236_ctrl_in(chip, 5), 9838c2ecf20Sopenharmony_ci snd_cs4236_ctrl_in(chip, 6), 9848c2ecf20Sopenharmony_ci snd_cs4236_ctrl_in(chip, 8)); 9858c2ecf20Sopenharmony_ci#endif 9868c2ecf20Sopenharmony_ci return change; 9878c2ecf20Sopenharmony_ci} 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cs4236_iec958_controls[] = { 9908c2ecf20Sopenharmony_ciCS4236_IEC958_ENABLE("IEC958 Output Enable", 0), 9918c2ecf20Sopenharmony_ciCS4236_SINGLEC("IEC958 Output Validity", 0, 4, 4, 1, 0), 9928c2ecf20Sopenharmony_ciCS4236_SINGLEC("IEC958 Output User", 0, 4, 5, 1, 0), 9938c2ecf20Sopenharmony_ciCS4236_SINGLEC("IEC958 Output CSBR", 0, 4, 6, 1, 0), 9948c2ecf20Sopenharmony_ciCS4236_SINGLEC("IEC958 Output Channel Status Low", 0, 5, 1, 127, 0), 9958c2ecf20Sopenharmony_ciCS4236_SINGLEC("IEC958 Output Channel Status High", 0, 6, 0, 255, 0) 9968c2ecf20Sopenharmony_ci}; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cs4236_3d_controls_cs4235[] = { 9998c2ecf20Sopenharmony_ciCS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0), 10008c2ecf20Sopenharmony_ciCS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1) 10018c2ecf20Sopenharmony_ci}; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cs4236_3d_controls_cs4237[] = { 10048c2ecf20Sopenharmony_ciCS4236_SINGLEC("3D Control - Switch", 0, 3, 7, 1, 0), 10058c2ecf20Sopenharmony_ciCS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1), 10068c2ecf20Sopenharmony_ciCS4236_SINGLEC("3D Control - Center", 0, 2, 0, 15, 1), 10078c2ecf20Sopenharmony_ciCS4236_SINGLEC("3D Control - Mono", 0, 3, 6, 1, 0), 10088c2ecf20Sopenharmony_ciCS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0) 10098c2ecf20Sopenharmony_ci}; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cs4236_3d_controls_cs4238[] = { 10128c2ecf20Sopenharmony_ciCS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0), 10138c2ecf20Sopenharmony_ciCS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1), 10148c2ecf20Sopenharmony_ciCS4236_SINGLEC("3D Control - Volume", 0, 2, 0, 15, 1), 10158c2ecf20Sopenharmony_ciCS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0) 10168c2ecf20Sopenharmony_ci}; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ciint snd_cs4236_mixer(struct snd_wss *chip) 10198c2ecf20Sopenharmony_ci{ 10208c2ecf20Sopenharmony_ci struct snd_card *card; 10218c2ecf20Sopenharmony_ci unsigned int idx, count; 10228c2ecf20Sopenharmony_ci int err; 10238c2ecf20Sopenharmony_ci const struct snd_kcontrol_new *kcontrol; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci if (snd_BUG_ON(!chip || !chip->card)) 10268c2ecf20Sopenharmony_ci return -EINVAL; 10278c2ecf20Sopenharmony_ci card = chip->card; 10288c2ecf20Sopenharmony_ci strcpy(card->mixername, snd_wss_chip_id(chip)); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci if (chip->hardware == WSS_HW_CS4235 || 10318c2ecf20Sopenharmony_ci chip->hardware == WSS_HW_CS4239) { 10328c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_cs4235_controls); idx++) { 10338c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4235_controls[idx], chip))) < 0) 10348c2ecf20Sopenharmony_ci return err; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci } else { 10378c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_cs4236_controls); idx++) { 10388c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_controls[idx], chip))) < 0) 10398c2ecf20Sopenharmony_ci return err; 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci switch (chip->hardware) { 10438c2ecf20Sopenharmony_ci case WSS_HW_CS4235: 10448c2ecf20Sopenharmony_ci case WSS_HW_CS4239: 10458c2ecf20Sopenharmony_ci count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4235); 10468c2ecf20Sopenharmony_ci kcontrol = snd_cs4236_3d_controls_cs4235; 10478c2ecf20Sopenharmony_ci break; 10488c2ecf20Sopenharmony_ci case WSS_HW_CS4237B: 10498c2ecf20Sopenharmony_ci count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4237); 10508c2ecf20Sopenharmony_ci kcontrol = snd_cs4236_3d_controls_cs4237; 10518c2ecf20Sopenharmony_ci break; 10528c2ecf20Sopenharmony_ci case WSS_HW_CS4238B: 10538c2ecf20Sopenharmony_ci count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4238); 10548c2ecf20Sopenharmony_ci kcontrol = snd_cs4236_3d_controls_cs4238; 10558c2ecf20Sopenharmony_ci break; 10568c2ecf20Sopenharmony_ci default: 10578c2ecf20Sopenharmony_ci count = 0; 10588c2ecf20Sopenharmony_ci kcontrol = NULL; 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci for (idx = 0; idx < count; idx++, kcontrol++) { 10618c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(kcontrol, chip))) < 0) 10628c2ecf20Sopenharmony_ci return err; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci if (chip->hardware == WSS_HW_CS4237B || 10658c2ecf20Sopenharmony_ci chip->hardware == WSS_HW_CS4238B) { 10668c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_cs4236_iec958_controls); idx++) { 10678c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_iec958_controls[idx], chip))) < 0) 10688c2ecf20Sopenharmony_ci return err; 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci return 0; 10728c2ecf20Sopenharmony_ci} 1073