162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 462306a36Sopenharmony_ci * Routines for control of CS4235/4236B/4237B/4238B/4239 chips 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Note: 762306a36Sopenharmony_ci * ----- 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Bugs: 1062306a36Sopenharmony_ci * ----- 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* 1462306a36Sopenharmony_ci * Indirect control registers (CS4236B+) 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * C0 1762306a36Sopenharmony_ci * D8: WSS reset (all chips) 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * C1 (all chips except CS4236) 2062306a36Sopenharmony_ci * D7-D5: version 2162306a36Sopenharmony_ci * D4-D0: chip id 2262306a36Sopenharmony_ci * 11101 - CS4235 2362306a36Sopenharmony_ci * 01011 - CS4236B 2462306a36Sopenharmony_ci * 01000 - CS4237B 2562306a36Sopenharmony_ci * 01001 - CS4238B 2662306a36Sopenharmony_ci * 11110 - CS4239 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * C2 2962306a36Sopenharmony_ci * D7-D4: 3D Space (CS4235,CS4237B,CS4238B,CS4239) 3062306a36Sopenharmony_ci * D3-D0: 3D Center (CS4237B); 3D Volume (CS4238B) 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * C3 3362306a36Sopenharmony_ci * D7: 3D Enable (CS4237B) 3462306a36Sopenharmony_ci * D6: 3D Mono Enable (CS4237B) 3562306a36Sopenharmony_ci * D5: 3D Serial Output (CS4237B,CS4238B) 3662306a36Sopenharmony_ci * D4: 3D Enable (CS4235,CS4238B,CS4239) 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * C4 3962306a36Sopenharmony_ci * D7: consumer serial port enable (CS4237B,CS4238B) 4062306a36Sopenharmony_ci * D6: channels status block reset (CS4237B,CS4238B) 4162306a36Sopenharmony_ci * D5: user bit in sub-frame of digital audio data (CS4237B,CS4238B) 4262306a36Sopenharmony_ci * D4: validity bit in sub-frame of digital audio data (CS4237B,CS4238B) 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * C5 lower channel status (digital serial data description) (CS4237B,CS4238B) 4562306a36Sopenharmony_ci * D7-D6: first two bits of category code 4662306a36Sopenharmony_ci * D5: lock 4762306a36Sopenharmony_ci * D4-D3: pre-emphasis (0 = none, 1 = 50/15us) 4862306a36Sopenharmony_ci * D2: copy/copyright (0 = copy inhibited) 4962306a36Sopenharmony_ci * D1: 0 = digital audio / 1 = non-digital audio 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * C6 upper channel status (digital serial data description) (CS4237B,CS4238B) 5262306a36Sopenharmony_ci * D7-D6: sample frequency (0 = 44.1kHz) 5362306a36Sopenharmony_ci * D5: generation status (0 = no indication, 1 = original/commercially precaptureed data) 5462306a36Sopenharmony_ci * D4-D0: category code (upper bits) 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * C7 reserved (must write 0) 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * C8 wavetable control 5962306a36Sopenharmony_ci * D7: volume control interrupt enable (CS4235,CS4239) 6062306a36Sopenharmony_ci * D6: hardware volume control format (CS4235,CS4239) 6162306a36Sopenharmony_ci * D3: wavetable serial port enable (all chips) 6262306a36Sopenharmony_ci * D2: DSP serial port switch (all chips) 6362306a36Sopenharmony_ci * D1: disable MCLK (all chips) 6462306a36Sopenharmony_ci * D0: force BRESET low (all chips) 6562306a36Sopenharmony_ci * 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#include <linux/io.h> 6962306a36Sopenharmony_ci#include <linux/delay.h> 7062306a36Sopenharmony_ci#include <linux/init.h> 7162306a36Sopenharmony_ci#include <linux/time.h> 7262306a36Sopenharmony_ci#include <linux/wait.h> 7362306a36Sopenharmony_ci#include <sound/core.h> 7462306a36Sopenharmony_ci#include <sound/wss.h> 7562306a36Sopenharmony_ci#include <sound/asoundef.h> 7662306a36Sopenharmony_ci#include <sound/initval.h> 7762306a36Sopenharmony_ci#include <sound/tlv.h> 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* 8062306a36Sopenharmony_ci * 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic const unsigned char snd_cs4236_ext_map[18] = { 8462306a36Sopenharmony_ci /* CS4236_LEFT_LINE */ 0xff, 8562306a36Sopenharmony_ci /* CS4236_RIGHT_LINE */ 0xff, 8662306a36Sopenharmony_ci /* CS4236_LEFT_MIC */ 0xdf, 8762306a36Sopenharmony_ci /* CS4236_RIGHT_MIC */ 0xdf, 8862306a36Sopenharmony_ci /* CS4236_LEFT_MIX_CTRL */ 0xe0 | 0x18, 8962306a36Sopenharmony_ci /* CS4236_RIGHT_MIX_CTRL */ 0xe0, 9062306a36Sopenharmony_ci /* CS4236_LEFT_FM */ 0xbf, 9162306a36Sopenharmony_ci /* CS4236_RIGHT_FM */ 0xbf, 9262306a36Sopenharmony_ci /* CS4236_LEFT_DSP */ 0xbf, 9362306a36Sopenharmony_ci /* CS4236_RIGHT_DSP */ 0xbf, 9462306a36Sopenharmony_ci /* CS4236_RIGHT_LOOPBACK */ 0xbf, 9562306a36Sopenharmony_ci /* CS4236_DAC_MUTE */ 0xe0, 9662306a36Sopenharmony_ci /* CS4236_ADC_RATE */ 0x01, /* 48kHz */ 9762306a36Sopenharmony_ci /* CS4236_DAC_RATE */ 0x01, /* 48kHz */ 9862306a36Sopenharmony_ci /* CS4236_LEFT_MASTER */ 0xbf, 9962306a36Sopenharmony_ci /* CS4236_RIGHT_MASTER */ 0xbf, 10062306a36Sopenharmony_ci /* CS4236_LEFT_WAVE */ 0xbf, 10162306a36Sopenharmony_ci /* CS4236_RIGHT_WAVE */ 0xbf 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* 10562306a36Sopenharmony_ci * 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic void snd_cs4236_ctrl_out(struct snd_wss *chip, 10962306a36Sopenharmony_ci unsigned char reg, unsigned char val) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci outb(reg, chip->cport + 3); 11262306a36Sopenharmony_ci outb(chip->cimage[reg] = val, chip->cport + 4); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic unsigned char snd_cs4236_ctrl_in(struct snd_wss *chip, unsigned char reg) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci outb(reg, chip->cport + 3); 11862306a36Sopenharmony_ci return inb(chip->cport + 4); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* 12262306a36Sopenharmony_ci * PCM 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci#define CLOCKS 8 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic const struct snd_ratnum clocks[CLOCKS] = { 12862306a36Sopenharmony_ci { .num = 16934400, .den_min = 353, .den_max = 353, .den_step = 1 }, 12962306a36Sopenharmony_ci { .num = 16934400, .den_min = 529, .den_max = 529, .den_step = 1 }, 13062306a36Sopenharmony_ci { .num = 16934400, .den_min = 617, .den_max = 617, .den_step = 1 }, 13162306a36Sopenharmony_ci { .num = 16934400, .den_min = 1058, .den_max = 1058, .den_step = 1 }, 13262306a36Sopenharmony_ci { .num = 16934400, .den_min = 1764, .den_max = 1764, .den_step = 1 }, 13362306a36Sopenharmony_ci { .num = 16934400, .den_min = 2117, .den_max = 2117, .den_step = 1 }, 13462306a36Sopenharmony_ci { .num = 16934400, .den_min = 2558, .den_max = 2558, .den_step = 1 }, 13562306a36Sopenharmony_ci { .num = 16934400/16, .den_min = 21, .den_max = 192, .den_step = 1 } 13662306a36Sopenharmony_ci}; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = { 13962306a36Sopenharmony_ci .nrats = CLOCKS, 14062306a36Sopenharmony_ci .rats = clocks, 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int snd_cs4236_xrate(struct snd_pcm_runtime *runtime) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci return snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 14662306a36Sopenharmony_ci &hw_constraints_clocks); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic unsigned char divisor_to_rate_register(unsigned int divisor) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci switch (divisor) { 15262306a36Sopenharmony_ci case 353: return 1; 15362306a36Sopenharmony_ci case 529: return 2; 15462306a36Sopenharmony_ci case 617: return 3; 15562306a36Sopenharmony_ci case 1058: return 4; 15662306a36Sopenharmony_ci case 1764: return 5; 15762306a36Sopenharmony_ci case 2117: return 6; 15862306a36Sopenharmony_ci case 2558: return 7; 15962306a36Sopenharmony_ci default: 16062306a36Sopenharmony_ci if (divisor < 21 || divisor > 192) { 16162306a36Sopenharmony_ci snd_BUG(); 16262306a36Sopenharmony_ci return 192; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci return divisor; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void snd_cs4236_playback_format(struct snd_wss *chip, 16962306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 17062306a36Sopenharmony_ci unsigned char pdfr) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci unsigned long flags; 17362306a36Sopenharmony_ci unsigned char rate = divisor_to_rate_register(params->rate_den); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 17662306a36Sopenharmony_ci /* set fast playback format change and clean playback FIFO */ 17762306a36Sopenharmony_ci snd_wss_out(chip, CS4231_ALT_FEATURE_1, 17862306a36Sopenharmony_ci chip->image[CS4231_ALT_FEATURE_1] | 0x10); 17962306a36Sopenharmony_ci snd_wss_out(chip, CS4231_PLAYBK_FORMAT, pdfr & 0xf0); 18062306a36Sopenharmony_ci snd_wss_out(chip, CS4231_ALT_FEATURE_1, 18162306a36Sopenharmony_ci chip->image[CS4231_ALT_FEATURE_1] & ~0x10); 18262306a36Sopenharmony_ci snd_cs4236_ext_out(chip, CS4236_DAC_RATE, rate); 18362306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic void snd_cs4236_capture_format(struct snd_wss *chip, 18762306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 18862306a36Sopenharmony_ci unsigned char cdfr) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci unsigned long flags; 19162306a36Sopenharmony_ci unsigned char rate = divisor_to_rate_register(params->rate_den); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 19462306a36Sopenharmony_ci /* set fast capture format change and clean capture FIFO */ 19562306a36Sopenharmony_ci snd_wss_out(chip, CS4231_ALT_FEATURE_1, 19662306a36Sopenharmony_ci chip->image[CS4231_ALT_FEATURE_1] | 0x20); 19762306a36Sopenharmony_ci snd_wss_out(chip, CS4231_REC_FORMAT, cdfr & 0xf0); 19862306a36Sopenharmony_ci snd_wss_out(chip, CS4231_ALT_FEATURE_1, 19962306a36Sopenharmony_ci chip->image[CS4231_ALT_FEATURE_1] & ~0x20); 20062306a36Sopenharmony_ci snd_cs4236_ext_out(chip, CS4236_ADC_RATE, rate); 20162306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci#ifdef CONFIG_PM 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void snd_cs4236_suspend(struct snd_wss *chip) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci int reg; 20962306a36Sopenharmony_ci unsigned long flags; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 21262306a36Sopenharmony_ci for (reg = 0; reg < 32; reg++) 21362306a36Sopenharmony_ci chip->image[reg] = snd_wss_in(chip, reg); 21462306a36Sopenharmony_ci for (reg = 0; reg < 18; reg++) 21562306a36Sopenharmony_ci chip->eimage[reg] = snd_cs4236_ext_in(chip, CS4236_I23VAL(reg)); 21662306a36Sopenharmony_ci for (reg = 2; reg < 9; reg++) 21762306a36Sopenharmony_ci chip->cimage[reg] = snd_cs4236_ctrl_in(chip, reg); 21862306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic void snd_cs4236_resume(struct snd_wss *chip) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci int reg; 22462306a36Sopenharmony_ci unsigned long flags; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci snd_wss_mce_up(chip); 22762306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 22862306a36Sopenharmony_ci for (reg = 0; reg < 32; reg++) { 22962306a36Sopenharmony_ci switch (reg) { 23062306a36Sopenharmony_ci case CS4236_EXT_REG: 23162306a36Sopenharmony_ci case CS4231_VERSION: 23262306a36Sopenharmony_ci case 27: /* why? CS4235 - master left */ 23362306a36Sopenharmony_ci case 29: /* why? CS4235 - master right */ 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci default: 23662306a36Sopenharmony_ci snd_wss_out(chip, reg, chip->image[reg]); 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci for (reg = 0; reg < 18; reg++) 24162306a36Sopenharmony_ci snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), chip->eimage[reg]); 24262306a36Sopenharmony_ci for (reg = 2; reg < 9; reg++) { 24362306a36Sopenharmony_ci switch (reg) { 24462306a36Sopenharmony_ci case 7: 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci default: 24762306a36Sopenharmony_ci snd_cs4236_ctrl_out(chip, reg, chip->cimage[reg]); 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 25162306a36Sopenharmony_ci snd_wss_mce_down(chip); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci#endif /* CONFIG_PM */ 25562306a36Sopenharmony_ci/* 25662306a36Sopenharmony_ci * This function does no fail if the chip is not CS4236B or compatible. 25762306a36Sopenharmony_ci * It just an equivalent to the snd_wss_create() then. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ciint snd_cs4236_create(struct snd_card *card, 26062306a36Sopenharmony_ci unsigned long port, 26162306a36Sopenharmony_ci unsigned long cport, 26262306a36Sopenharmony_ci int irq, int dma1, int dma2, 26362306a36Sopenharmony_ci unsigned short hardware, 26462306a36Sopenharmony_ci unsigned short hwshare, 26562306a36Sopenharmony_ci struct snd_wss **rchip) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct snd_wss *chip; 26862306a36Sopenharmony_ci unsigned char ver1, ver2; 26962306a36Sopenharmony_ci unsigned int reg; 27062306a36Sopenharmony_ci int err; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci *rchip = NULL; 27362306a36Sopenharmony_ci if (hardware == WSS_HW_DETECT) 27462306a36Sopenharmony_ci hardware = WSS_HW_DETECT3; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci err = snd_wss_create(card, port, cport, 27762306a36Sopenharmony_ci irq, dma1, dma2, hardware, hwshare, &chip); 27862306a36Sopenharmony_ci if (err < 0) 27962306a36Sopenharmony_ci return err; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if ((chip->hardware & WSS_HW_CS4236B_MASK) == 0) { 28262306a36Sopenharmony_ci snd_printd("chip is not CS4236+, hardware=0x%x\n", 28362306a36Sopenharmony_ci chip->hardware); 28462306a36Sopenharmony_ci *rchip = chip; 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci#if 0 28862306a36Sopenharmony_ci { 28962306a36Sopenharmony_ci int idx; 29062306a36Sopenharmony_ci for (idx = 0; idx < 8; idx++) 29162306a36Sopenharmony_ci snd_printk(KERN_DEBUG "CD%i = 0x%x\n", 29262306a36Sopenharmony_ci idx, inb(chip->cport + idx)); 29362306a36Sopenharmony_ci for (idx = 0; idx < 9; idx++) 29462306a36Sopenharmony_ci snd_printk(KERN_DEBUG "C%i = 0x%x\n", 29562306a36Sopenharmony_ci idx, snd_cs4236_ctrl_in(chip, idx)); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci#endif 29862306a36Sopenharmony_ci if (cport < 0x100 || cport == SNDRV_AUTO_PORT) { 29962306a36Sopenharmony_ci snd_printk(KERN_ERR "please, specify control port " 30062306a36Sopenharmony_ci "for CS4236+ chips\n"); 30162306a36Sopenharmony_ci return -ENODEV; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci ver1 = snd_cs4236_ctrl_in(chip, 1); 30462306a36Sopenharmony_ci ver2 = snd_cs4236_ext_in(chip, CS4236_VERSION); 30562306a36Sopenharmony_ci snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n", 30662306a36Sopenharmony_ci cport, ver1, ver2); 30762306a36Sopenharmony_ci if (ver1 != ver2) { 30862306a36Sopenharmony_ci snd_printk(KERN_ERR "CS4236+ chip detected, but " 30962306a36Sopenharmony_ci "control port 0x%lx is not valid\n", cport); 31062306a36Sopenharmony_ci return -ENODEV; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci snd_cs4236_ctrl_out(chip, 0, 0x00); 31362306a36Sopenharmony_ci snd_cs4236_ctrl_out(chip, 2, 0xff); 31462306a36Sopenharmony_ci snd_cs4236_ctrl_out(chip, 3, 0x00); 31562306a36Sopenharmony_ci snd_cs4236_ctrl_out(chip, 4, 0x80); 31662306a36Sopenharmony_ci reg = ((IEC958_AES1_CON_PCM_CODER & 3) << 6) | 31762306a36Sopenharmony_ci IEC958_AES0_CON_EMPHASIS_NONE; 31862306a36Sopenharmony_ci snd_cs4236_ctrl_out(chip, 5, reg); 31962306a36Sopenharmony_ci snd_cs4236_ctrl_out(chip, 6, IEC958_AES1_CON_PCM_CODER >> 2); 32062306a36Sopenharmony_ci snd_cs4236_ctrl_out(chip, 7, 0x00); 32162306a36Sopenharmony_ci /* 32262306a36Sopenharmony_ci * 0x8c for C8 is valid for Turtle Beach Malibu - the IEC-958 32362306a36Sopenharmony_ci * output is working with this setup, other hardware should 32462306a36Sopenharmony_ci * have different signal paths and this value should be 32562306a36Sopenharmony_ci * selectable in the future 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_ci snd_cs4236_ctrl_out(chip, 8, 0x8c); 32862306a36Sopenharmony_ci chip->rate_constraint = snd_cs4236_xrate; 32962306a36Sopenharmony_ci chip->set_playback_format = snd_cs4236_playback_format; 33062306a36Sopenharmony_ci chip->set_capture_format = snd_cs4236_capture_format; 33162306a36Sopenharmony_ci#ifdef CONFIG_PM 33262306a36Sopenharmony_ci chip->suspend = snd_cs4236_suspend; 33362306a36Sopenharmony_ci chip->resume = snd_cs4236_resume; 33462306a36Sopenharmony_ci#endif 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* initialize extended registers */ 33762306a36Sopenharmony_ci for (reg = 0; reg < sizeof(snd_cs4236_ext_map); reg++) 33862306a36Sopenharmony_ci snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), 33962306a36Sopenharmony_ci snd_cs4236_ext_map[reg]); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* initialize compatible but more featured registers */ 34262306a36Sopenharmony_ci snd_wss_out(chip, CS4231_LEFT_INPUT, 0x40); 34362306a36Sopenharmony_ci snd_wss_out(chip, CS4231_RIGHT_INPUT, 0x40); 34462306a36Sopenharmony_ci snd_wss_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff); 34562306a36Sopenharmony_ci snd_wss_out(chip, CS4231_AUX1_RIGHT_INPUT, 0xff); 34662306a36Sopenharmony_ci snd_wss_out(chip, CS4231_AUX2_LEFT_INPUT, 0xdf); 34762306a36Sopenharmony_ci snd_wss_out(chip, CS4231_AUX2_RIGHT_INPUT, 0xdf); 34862306a36Sopenharmony_ci snd_wss_out(chip, CS4231_RIGHT_LINE_IN, 0xff); 34962306a36Sopenharmony_ci snd_wss_out(chip, CS4231_LEFT_LINE_IN, 0xff); 35062306a36Sopenharmony_ci snd_wss_out(chip, CS4231_RIGHT_LINE_IN, 0xff); 35162306a36Sopenharmony_ci switch (chip->hardware) { 35262306a36Sopenharmony_ci case WSS_HW_CS4235: 35362306a36Sopenharmony_ci case WSS_HW_CS4239: 35462306a36Sopenharmony_ci snd_wss_out(chip, CS4235_LEFT_MASTER, 0xff); 35562306a36Sopenharmony_ci snd_wss_out(chip, CS4235_RIGHT_MASTER, 0xff); 35662306a36Sopenharmony_ci break; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci *rchip = chip; 36062306a36Sopenharmony_ci return 0; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ciint snd_cs4236_pcm(struct snd_wss *chip, int device) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci int err; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci err = snd_wss_pcm(chip, device); 36862306a36Sopenharmony_ci if (err < 0) 36962306a36Sopenharmony_ci return err; 37062306a36Sopenharmony_ci chip->pcm->info_flags &= ~SNDRV_PCM_INFO_JOINT_DUPLEX; 37162306a36Sopenharmony_ci return 0; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci/* 37562306a36Sopenharmony_ci * MIXER 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci#define CS4236_SINGLE(xname, xindex, reg, shift, mask, invert) \ 37962306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 38062306a36Sopenharmony_ci .info = snd_cs4236_info_single, \ 38162306a36Sopenharmony_ci .get = snd_cs4236_get_single, .put = snd_cs4236_put_single, \ 38262306a36Sopenharmony_ci .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci#define CS4236_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \ 38562306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 38662306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 38762306a36Sopenharmony_ci .info = snd_cs4236_info_single, \ 38862306a36Sopenharmony_ci .get = snd_cs4236_get_single, .put = snd_cs4236_put_single, \ 38962306a36Sopenharmony_ci .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \ 39062306a36Sopenharmony_ci .tlv = { .p = (xtlv) } } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic int snd_cs4236_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 39762306a36Sopenharmony_ci uinfo->count = 1; 39862306a36Sopenharmony_ci uinfo->value.integer.min = 0; 39962306a36Sopenharmony_ci uinfo->value.integer.max = mask; 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic int snd_cs4236_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 40662306a36Sopenharmony_ci unsigned long flags; 40762306a36Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 40862306a36Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 40962306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 41062306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 41362306a36Sopenharmony_ci ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(reg)] >> shift) & mask; 41462306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 41562306a36Sopenharmony_ci if (invert) 41662306a36Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 41762306a36Sopenharmony_ci return 0; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic int snd_cs4236_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 42362306a36Sopenharmony_ci unsigned long flags; 42462306a36Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 42562306a36Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 42662306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 42762306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 42862306a36Sopenharmony_ci int change; 42962306a36Sopenharmony_ci unsigned short val; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci val = (ucontrol->value.integer.value[0] & mask); 43262306a36Sopenharmony_ci if (invert) 43362306a36Sopenharmony_ci val = mask - val; 43462306a36Sopenharmony_ci val <<= shift; 43562306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 43662306a36Sopenharmony_ci val = (chip->eimage[CS4236_REG(reg)] & ~(mask << shift)) | val; 43762306a36Sopenharmony_ci change = val != chip->eimage[CS4236_REG(reg)]; 43862306a36Sopenharmony_ci snd_cs4236_ext_out(chip, reg, val); 43962306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 44062306a36Sopenharmony_ci return change; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci#define CS4236_SINGLEC(xname, xindex, reg, shift, mask, invert) \ 44462306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 44562306a36Sopenharmony_ci .info = snd_cs4236_info_single, \ 44662306a36Sopenharmony_ci .get = snd_cs4236_get_singlec, .put = snd_cs4236_put_singlec, \ 44762306a36Sopenharmony_ci .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic int snd_cs4236_get_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 45262306a36Sopenharmony_ci unsigned long flags; 45362306a36Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 45462306a36Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 45562306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 45662306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 45962306a36Sopenharmony_ci ucontrol->value.integer.value[0] = (chip->cimage[reg] >> shift) & mask; 46062306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 46162306a36Sopenharmony_ci if (invert) 46262306a36Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic int snd_cs4236_put_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 46962306a36Sopenharmony_ci unsigned long flags; 47062306a36Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 47162306a36Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 47262306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 47362306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 47462306a36Sopenharmony_ci int change; 47562306a36Sopenharmony_ci unsigned short val; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci val = (ucontrol->value.integer.value[0] & mask); 47862306a36Sopenharmony_ci if (invert) 47962306a36Sopenharmony_ci val = mask - val; 48062306a36Sopenharmony_ci val <<= shift; 48162306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 48262306a36Sopenharmony_ci val = (chip->cimage[reg] & ~(mask << shift)) | val; 48362306a36Sopenharmony_ci change = val != chip->cimage[reg]; 48462306a36Sopenharmony_ci snd_cs4236_ctrl_out(chip, reg, val); 48562306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 48662306a36Sopenharmony_ci return change; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci#define CS4236_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ 49062306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 49162306a36Sopenharmony_ci .info = snd_cs4236_info_double, \ 49262306a36Sopenharmony_ci .get = snd_cs4236_get_double, .put = snd_cs4236_put_double, \ 49362306a36Sopenharmony_ci .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci#define CS4236_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, \ 49662306a36Sopenharmony_ci shift_right, mask, invert, xtlv) \ 49762306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 49862306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 49962306a36Sopenharmony_ci .info = snd_cs4236_info_double, \ 50062306a36Sopenharmony_ci .get = snd_cs4236_get_double, .put = snd_cs4236_put_double, \ 50162306a36Sopenharmony_ci .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | \ 50262306a36Sopenharmony_ci (shift_right << 19) | (mask << 24) | (invert << 22), \ 50362306a36Sopenharmony_ci .tlv = { .p = (xtlv) } } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic int snd_cs4236_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 51062306a36Sopenharmony_ci uinfo->count = 2; 51162306a36Sopenharmony_ci uinfo->value.integer.min = 0; 51262306a36Sopenharmony_ci uinfo->value.integer.max = mask; 51362306a36Sopenharmony_ci return 0; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic int snd_cs4236_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 51962306a36Sopenharmony_ci unsigned long flags; 52062306a36Sopenharmony_ci int left_reg = kcontrol->private_value & 0xff; 52162306a36Sopenharmony_ci int right_reg = (kcontrol->private_value >> 8) & 0xff; 52262306a36Sopenharmony_ci int shift_left = (kcontrol->private_value >> 16) & 0x07; 52362306a36Sopenharmony_ci int shift_right = (kcontrol->private_value >> 19) & 0x07; 52462306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 52562306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 22) & 1; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 52862306a36Sopenharmony_ci ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(left_reg)] >> shift_left) & mask; 52962306a36Sopenharmony_ci ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask; 53062306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 53162306a36Sopenharmony_ci if (invert) { 53262306a36Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 53362306a36Sopenharmony_ci ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci return 0; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 54162306a36Sopenharmony_ci unsigned long flags; 54262306a36Sopenharmony_ci int left_reg = kcontrol->private_value & 0xff; 54362306a36Sopenharmony_ci int right_reg = (kcontrol->private_value >> 8) & 0xff; 54462306a36Sopenharmony_ci int shift_left = (kcontrol->private_value >> 16) & 0x07; 54562306a36Sopenharmony_ci int shift_right = (kcontrol->private_value >> 19) & 0x07; 54662306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 54762306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 22) & 1; 54862306a36Sopenharmony_ci int change; 54962306a36Sopenharmony_ci unsigned short val1, val2; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci val1 = ucontrol->value.integer.value[0] & mask; 55262306a36Sopenharmony_ci val2 = ucontrol->value.integer.value[1] & mask; 55362306a36Sopenharmony_ci if (invert) { 55462306a36Sopenharmony_ci val1 = mask - val1; 55562306a36Sopenharmony_ci val2 = mask - val2; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci val1 <<= shift_left; 55862306a36Sopenharmony_ci val2 <<= shift_right; 55962306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 56062306a36Sopenharmony_ci if (left_reg != right_reg) { 56162306a36Sopenharmony_ci val1 = (chip->eimage[CS4236_REG(left_reg)] & ~(mask << shift_left)) | val1; 56262306a36Sopenharmony_ci val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2; 56362306a36Sopenharmony_ci change = val1 != chip->eimage[CS4236_REG(left_reg)] || val2 != chip->eimage[CS4236_REG(right_reg)]; 56462306a36Sopenharmony_ci snd_cs4236_ext_out(chip, left_reg, val1); 56562306a36Sopenharmony_ci snd_cs4236_ext_out(chip, right_reg, val2); 56662306a36Sopenharmony_ci } else { 56762306a36Sopenharmony_ci val1 = (chip->eimage[CS4236_REG(left_reg)] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; 56862306a36Sopenharmony_ci change = val1 != chip->eimage[CS4236_REG(left_reg)]; 56962306a36Sopenharmony_ci snd_cs4236_ext_out(chip, left_reg, val1); 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 57262306a36Sopenharmony_ci return change; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci#define CS4236_DOUBLE1(xname, xindex, left_reg, right_reg, shift_left, \ 57662306a36Sopenharmony_ci shift_right, mask, invert) \ 57762306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 57862306a36Sopenharmony_ci .info = snd_cs4236_info_double, \ 57962306a36Sopenharmony_ci .get = snd_cs4236_get_double1, .put = snd_cs4236_put_double1, \ 58062306a36Sopenharmony_ci .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci#define CS4236_DOUBLE1_TLV(xname, xindex, left_reg, right_reg, shift_left, \ 58362306a36Sopenharmony_ci shift_right, mask, invert, xtlv) \ 58462306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 58562306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 58662306a36Sopenharmony_ci .info = snd_cs4236_info_double, \ 58762306a36Sopenharmony_ci .get = snd_cs4236_get_double1, .put = snd_cs4236_put_double1, \ 58862306a36Sopenharmony_ci .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | \ 58962306a36Sopenharmony_ci (shift_right << 19) | (mask << 24) | (invert << 22), \ 59062306a36Sopenharmony_ci .tlv = { .p = (xtlv) } } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic int snd_cs4236_get_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 59562306a36Sopenharmony_ci unsigned long flags; 59662306a36Sopenharmony_ci int left_reg = kcontrol->private_value & 0xff; 59762306a36Sopenharmony_ci int right_reg = (kcontrol->private_value >> 8) & 0xff; 59862306a36Sopenharmony_ci int shift_left = (kcontrol->private_value >> 16) & 0x07; 59962306a36Sopenharmony_ci int shift_right = (kcontrol->private_value >> 19) & 0x07; 60062306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 60162306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 22) & 1; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 60462306a36Sopenharmony_ci ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; 60562306a36Sopenharmony_ci ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask; 60662306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 60762306a36Sopenharmony_ci if (invert) { 60862306a36Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 60962306a36Sopenharmony_ci ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci return 0; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic int snd_cs4236_put_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 61762306a36Sopenharmony_ci unsigned long flags; 61862306a36Sopenharmony_ci int left_reg = kcontrol->private_value & 0xff; 61962306a36Sopenharmony_ci int right_reg = (kcontrol->private_value >> 8) & 0xff; 62062306a36Sopenharmony_ci int shift_left = (kcontrol->private_value >> 16) & 0x07; 62162306a36Sopenharmony_ci int shift_right = (kcontrol->private_value >> 19) & 0x07; 62262306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 62362306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 22) & 1; 62462306a36Sopenharmony_ci int change; 62562306a36Sopenharmony_ci unsigned short val1, val2; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci val1 = ucontrol->value.integer.value[0] & mask; 62862306a36Sopenharmony_ci val2 = ucontrol->value.integer.value[1] & mask; 62962306a36Sopenharmony_ci if (invert) { 63062306a36Sopenharmony_ci val1 = mask - val1; 63162306a36Sopenharmony_ci val2 = mask - val2; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci val1 <<= shift_left; 63462306a36Sopenharmony_ci val2 <<= shift_right; 63562306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 63662306a36Sopenharmony_ci val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; 63762306a36Sopenharmony_ci val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2; 63862306a36Sopenharmony_ci change = val1 != chip->image[left_reg] || val2 != chip->eimage[CS4236_REG(right_reg)]; 63962306a36Sopenharmony_ci snd_wss_out(chip, left_reg, val1); 64062306a36Sopenharmony_ci snd_cs4236_ext_out(chip, right_reg, val2); 64162306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 64262306a36Sopenharmony_ci return change; 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci#define CS4236_MASTER_DIGITAL(xname, xindex, xtlv) \ 64662306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 64762306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 64862306a36Sopenharmony_ci .info = snd_cs4236_info_double, \ 64962306a36Sopenharmony_ci .get = snd_cs4236_get_master_digital, .put = snd_cs4236_put_master_digital, \ 65062306a36Sopenharmony_ci .private_value = 71 << 24, \ 65162306a36Sopenharmony_ci .tlv = { .p = (xtlv) } } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic inline int snd_cs4236_mixer_master_digital_invert_volume(int vol) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci return (vol < 64) ? 63 - vol : 64 + (71 - vol); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic int snd_cs4236_get_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 66162306a36Sopenharmony_ci unsigned long flags; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 66462306a36Sopenharmony_ci ucontrol->value.integer.value[0] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & 0x7f); 66562306a36Sopenharmony_ci ucontrol->value.integer.value[1] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & 0x7f); 66662306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 66762306a36Sopenharmony_ci return 0; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic int snd_cs4236_put_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 67362306a36Sopenharmony_ci unsigned long flags; 67462306a36Sopenharmony_ci int change; 67562306a36Sopenharmony_ci unsigned short val1, val2; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci val1 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[0] & 0x7f); 67862306a36Sopenharmony_ci val2 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[1] & 0x7f); 67962306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 68062306a36Sopenharmony_ci val1 = (chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & ~0x7f) | val1; 68162306a36Sopenharmony_ci val2 = (chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & ~0x7f) | val2; 68262306a36Sopenharmony_ci change = val1 != chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] || val2 != chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)]; 68362306a36Sopenharmony_ci snd_cs4236_ext_out(chip, CS4236_LEFT_MASTER, val1); 68462306a36Sopenharmony_ci snd_cs4236_ext_out(chip, CS4236_RIGHT_MASTER, val2); 68562306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 68662306a36Sopenharmony_ci return change; 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci#define CS4235_OUTPUT_ACCU(xname, xindex, xtlv) \ 69062306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 69162306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 69262306a36Sopenharmony_ci .info = snd_cs4236_info_double, \ 69362306a36Sopenharmony_ci .get = snd_cs4235_get_output_accu, .put = snd_cs4235_put_output_accu, \ 69462306a36Sopenharmony_ci .private_value = 3 << 24, \ 69562306a36Sopenharmony_ci .tlv = { .p = (xtlv) } } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic inline int snd_cs4235_mixer_output_accu_get_volume(int vol) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci switch ((vol >> 5) & 3) { 70062306a36Sopenharmony_ci case 0: return 1; 70162306a36Sopenharmony_ci case 1: return 3; 70262306a36Sopenharmony_ci case 2: return 2; 70362306a36Sopenharmony_ci case 3: return 0; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci return 3; 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic inline int snd_cs4235_mixer_output_accu_set_volume(int vol) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci switch (vol & 3) { 71162306a36Sopenharmony_ci case 0: return 3 << 5; 71262306a36Sopenharmony_ci case 1: return 0 << 5; 71362306a36Sopenharmony_ci case 2: return 2 << 5; 71462306a36Sopenharmony_ci case 3: return 1 << 5; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci return 1 << 5; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic int snd_cs4235_get_output_accu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 72262306a36Sopenharmony_ci unsigned long flags; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 72562306a36Sopenharmony_ci ucontrol->value.integer.value[0] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_LEFT_MASTER]); 72662306a36Sopenharmony_ci ucontrol->value.integer.value[1] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_RIGHT_MASTER]); 72762306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 72862306a36Sopenharmony_ci return 0; 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic int snd_cs4235_put_output_accu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 73462306a36Sopenharmony_ci unsigned long flags; 73562306a36Sopenharmony_ci int change; 73662306a36Sopenharmony_ci unsigned short val1, val2; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci val1 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[0]); 73962306a36Sopenharmony_ci val2 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[1]); 74062306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 74162306a36Sopenharmony_ci val1 = (chip->image[CS4235_LEFT_MASTER] & ~(3 << 5)) | val1; 74262306a36Sopenharmony_ci val2 = (chip->image[CS4235_RIGHT_MASTER] & ~(3 << 5)) | val2; 74362306a36Sopenharmony_ci change = val1 != chip->image[CS4235_LEFT_MASTER] || val2 != chip->image[CS4235_RIGHT_MASTER]; 74462306a36Sopenharmony_ci snd_wss_out(chip, CS4235_LEFT_MASTER, val1); 74562306a36Sopenharmony_ci snd_wss_out(chip, CS4235_RIGHT_MASTER, val2); 74662306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 74762306a36Sopenharmony_ci return change; 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_7bit, -9450, 150, 0); 75162306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0); 75262306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_6bit_12db_max, -8250, 150, 0); 75362306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0); 75462306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_5bit_22db_max, -2400, 150, 0); 75562306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0); 75662306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_2bit, -1800, 600, 0); 75762306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_cs4236_controls[] = { 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ciCS4236_DOUBLE("Master Digital Playback Switch", 0, 76262306a36Sopenharmony_ci CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1), 76362306a36Sopenharmony_ciCS4236_DOUBLE("Master Digital Capture Switch", 0, 76462306a36Sopenharmony_ci CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1), 76562306a36Sopenharmony_ciCS4236_MASTER_DIGITAL("Master Digital Volume", 0, db_scale_7bit), 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ciCS4236_DOUBLE_TLV("Capture Boost Volume", 0, 76862306a36Sopenharmony_ci CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1, 76962306a36Sopenharmony_ci db_scale_2bit), 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ciWSS_DOUBLE("PCM Playback Switch", 0, 77262306a36Sopenharmony_ci CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), 77362306a36Sopenharmony_ciWSS_DOUBLE_TLV("PCM Playback Volume", 0, 77462306a36Sopenharmony_ci CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1, 77562306a36Sopenharmony_ci db_scale_6bit), 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ciCS4236_DOUBLE("DSP Playback Switch", 0, 77862306a36Sopenharmony_ci CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1), 77962306a36Sopenharmony_ciCS4236_DOUBLE_TLV("DSP Playback Volume", 0, 78062306a36Sopenharmony_ci CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1, 78162306a36Sopenharmony_ci db_scale_6bit), 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ciCS4236_DOUBLE("FM Playback Switch", 0, 78462306a36Sopenharmony_ci CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1), 78562306a36Sopenharmony_ciCS4236_DOUBLE_TLV("FM Playback Volume", 0, 78662306a36Sopenharmony_ci CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1, 78762306a36Sopenharmony_ci db_scale_6bit), 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ciCS4236_DOUBLE("Wavetable Playback Switch", 0, 79062306a36Sopenharmony_ci CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1), 79162306a36Sopenharmony_ciCS4236_DOUBLE_TLV("Wavetable Playback Volume", 0, 79262306a36Sopenharmony_ci CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1, 79362306a36Sopenharmony_ci db_scale_6bit_12db_max), 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ciWSS_DOUBLE("Synth Playback Switch", 0, 79662306a36Sopenharmony_ci CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), 79762306a36Sopenharmony_ciWSS_DOUBLE_TLV("Synth Volume", 0, 79862306a36Sopenharmony_ci CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1, 79962306a36Sopenharmony_ci db_scale_5bit_12db_max), 80062306a36Sopenharmony_ciWSS_DOUBLE("Synth Capture Switch", 0, 80162306a36Sopenharmony_ci CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1), 80262306a36Sopenharmony_ciWSS_DOUBLE("Synth Capture Bypass", 0, 80362306a36Sopenharmony_ci CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 5, 5, 1, 1), 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ciCS4236_DOUBLE("Mic Playback Switch", 0, 80662306a36Sopenharmony_ci CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1), 80762306a36Sopenharmony_ciCS4236_DOUBLE("Mic Capture Switch", 0, 80862306a36Sopenharmony_ci CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1), 80962306a36Sopenharmony_ciCS4236_DOUBLE_TLV("Mic Volume", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 81062306a36Sopenharmony_ci 0, 0, 31, 1, db_scale_5bit_22db_max), 81162306a36Sopenharmony_ciCS4236_DOUBLE("Mic Playback Boost (+20dB)", 0, 81262306a36Sopenharmony_ci CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0), 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ciWSS_DOUBLE("Line Playback Switch", 0, 81562306a36Sopenharmony_ci CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), 81662306a36Sopenharmony_ciWSS_DOUBLE_TLV("Line Volume", 0, 81762306a36Sopenharmony_ci CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1, 81862306a36Sopenharmony_ci db_scale_5bit_12db_max), 81962306a36Sopenharmony_ciWSS_DOUBLE("Line Capture Switch", 0, 82062306a36Sopenharmony_ci CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1), 82162306a36Sopenharmony_ciWSS_DOUBLE("Line Capture Bypass", 0, 82262306a36Sopenharmony_ci CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 5, 5, 1, 1), 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ciWSS_DOUBLE("CD Playback Switch", 0, 82562306a36Sopenharmony_ci CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), 82662306a36Sopenharmony_ciWSS_DOUBLE_TLV("CD Volume", 0, 82762306a36Sopenharmony_ci CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1, 82862306a36Sopenharmony_ci db_scale_5bit_12db_max), 82962306a36Sopenharmony_ciWSS_DOUBLE("CD Capture Switch", 0, 83062306a36Sopenharmony_ci CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1), 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ciCS4236_DOUBLE1("Mono Output Playback Switch", 0, 83362306a36Sopenharmony_ci CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1), 83462306a36Sopenharmony_ciCS4236_DOUBLE1("Beep Playback Switch", 0, 83562306a36Sopenharmony_ci CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1), 83662306a36Sopenharmony_ciWSS_SINGLE_TLV("Beep Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1, 83762306a36Sopenharmony_ci db_scale_4bit), 83862306a36Sopenharmony_ciWSS_SINGLE("Beep Bypass Playback Switch", 0, CS4231_MONO_CTRL, 5, 1, 0), 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ciWSS_DOUBLE_TLV("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 84162306a36Sopenharmony_ci 0, 0, 15, 0, db_scale_rec_gain), 84262306a36Sopenharmony_ciWSS_DOUBLE("Analog Loopback Capture Switch", 0, 84362306a36Sopenharmony_ci CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0), 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ciWSS_SINGLE("Loopback Digital Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0), 84662306a36Sopenharmony_ciCS4236_DOUBLE1_TLV("Loopback Digital Playback Volume", 0, 84762306a36Sopenharmony_ci CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1, 84862306a36Sopenharmony_ci db_scale_6bit), 84962306a36Sopenharmony_ci}; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_5bit_6db_max, -5600, 200, 0); 85262306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_2bit_16db_max, -2400, 800, 0); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_cs4235_controls[] = { 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ciWSS_DOUBLE("Master Playback Switch", 0, 85762306a36Sopenharmony_ci CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1), 85862306a36Sopenharmony_ciWSS_DOUBLE_TLV("Master Playback Volume", 0, 85962306a36Sopenharmony_ci CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1, 86062306a36Sopenharmony_ci db_scale_5bit_6db_max), 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ciCS4235_OUTPUT_ACCU("Playback Volume", 0, db_scale_2bit_16db_max), 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ciWSS_DOUBLE("Synth Playback Switch", 1, 86562306a36Sopenharmony_ci CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), 86662306a36Sopenharmony_ciWSS_DOUBLE("Synth Capture Switch", 1, 86762306a36Sopenharmony_ci CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1), 86862306a36Sopenharmony_ciWSS_DOUBLE_TLV("Synth Volume", 1, 86962306a36Sopenharmony_ci CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1, 87062306a36Sopenharmony_ci db_scale_5bit_12db_max), 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ciCS4236_DOUBLE_TLV("Capture Volume", 0, 87362306a36Sopenharmony_ci CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1, 87462306a36Sopenharmony_ci db_scale_2bit), 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ciWSS_DOUBLE("PCM Playback Switch", 0, 87762306a36Sopenharmony_ci CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), 87862306a36Sopenharmony_ciWSS_DOUBLE("PCM Capture Switch", 0, 87962306a36Sopenharmony_ci CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1), 88062306a36Sopenharmony_ciWSS_DOUBLE_TLV("PCM Volume", 0, 88162306a36Sopenharmony_ci CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1, 88262306a36Sopenharmony_ci db_scale_6bit), 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ciCS4236_DOUBLE("DSP Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1), 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ciCS4236_DOUBLE("FM Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1), 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ciCS4236_DOUBLE("Wavetable Switch", 0, 88962306a36Sopenharmony_ci CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1), 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ciCS4236_DOUBLE("Mic Capture Switch", 0, 89262306a36Sopenharmony_ci CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1), 89362306a36Sopenharmony_ciCS4236_DOUBLE("Mic Playback Switch", 0, 89462306a36Sopenharmony_ci CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1), 89562306a36Sopenharmony_ciCS4236_SINGLE_TLV("Mic Volume", 0, CS4236_LEFT_MIC, 0, 31, 1, 89662306a36Sopenharmony_ci db_scale_5bit_22db_max), 89762306a36Sopenharmony_ciCS4236_SINGLE("Mic Boost (+20dB)", 0, CS4236_LEFT_MIC, 5, 1, 0), 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ciWSS_DOUBLE("Line Playback Switch", 0, 90062306a36Sopenharmony_ci CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), 90162306a36Sopenharmony_ciWSS_DOUBLE("Line Capture Switch", 0, 90262306a36Sopenharmony_ci CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1), 90362306a36Sopenharmony_ciWSS_DOUBLE_TLV("Line Volume", 0, 90462306a36Sopenharmony_ci CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1, 90562306a36Sopenharmony_ci db_scale_5bit_12db_max), 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ciWSS_DOUBLE("CD Playback Switch", 1, 90862306a36Sopenharmony_ci CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), 90962306a36Sopenharmony_ciWSS_DOUBLE("CD Capture Switch", 1, 91062306a36Sopenharmony_ci CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1), 91162306a36Sopenharmony_ciWSS_DOUBLE_TLV("CD Volume", 1, 91262306a36Sopenharmony_ci CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1, 91362306a36Sopenharmony_ci db_scale_5bit_12db_max), 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ciCS4236_DOUBLE1("Beep Playback Switch", 0, 91662306a36Sopenharmony_ci CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1), 91762306a36Sopenharmony_ciWSS_SINGLE("Beep Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ciWSS_DOUBLE("Analog Loopback Switch", 0, 92062306a36Sopenharmony_ci CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0), 92162306a36Sopenharmony_ci}; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci#define CS4236_IEC958_ENABLE(xname, xindex) \ 92462306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 92562306a36Sopenharmony_ci .info = snd_cs4236_info_single, \ 92662306a36Sopenharmony_ci .get = snd_cs4236_get_iec958_switch, .put = snd_cs4236_put_iec958_switch, \ 92762306a36Sopenharmony_ci .private_value = 1 << 16 } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_cistatic int snd_cs4236_get_iec958_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 93062306a36Sopenharmony_ci{ 93162306a36Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 93262306a36Sopenharmony_ci unsigned long flags; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 93562306a36Sopenharmony_ci ucontrol->value.integer.value[0] = chip->image[CS4231_ALT_FEATURE_1] & 0x02 ? 1 : 0; 93662306a36Sopenharmony_ci#if 0 93762306a36Sopenharmony_ci printk(KERN_DEBUG "get valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, " 93862306a36Sopenharmony_ci "C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n", 93962306a36Sopenharmony_ci snd_wss_in(chip, CS4231_ALT_FEATURE_1), 94062306a36Sopenharmony_ci snd_cs4236_ctrl_in(chip, 3), 94162306a36Sopenharmony_ci snd_cs4236_ctrl_in(chip, 4), 94262306a36Sopenharmony_ci snd_cs4236_ctrl_in(chip, 5), 94362306a36Sopenharmony_ci snd_cs4236_ctrl_in(chip, 6), 94462306a36Sopenharmony_ci snd_cs4236_ctrl_in(chip, 8)); 94562306a36Sopenharmony_ci#endif 94662306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 94762306a36Sopenharmony_ci return 0; 94862306a36Sopenharmony_ci} 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_cistatic int snd_cs4236_put_iec958_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci struct snd_wss *chip = snd_kcontrol_chip(kcontrol); 95362306a36Sopenharmony_ci unsigned long flags; 95462306a36Sopenharmony_ci int change; 95562306a36Sopenharmony_ci unsigned short enable, val; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci enable = ucontrol->value.integer.value[0] & 1; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci mutex_lock(&chip->mce_mutex); 96062306a36Sopenharmony_ci snd_wss_mce_up(chip); 96162306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 96262306a36Sopenharmony_ci val = (chip->image[CS4231_ALT_FEATURE_1] & ~0x0e) | (0<<2) | (enable << 1); 96362306a36Sopenharmony_ci change = val != chip->image[CS4231_ALT_FEATURE_1]; 96462306a36Sopenharmony_ci snd_wss_out(chip, CS4231_ALT_FEATURE_1, val); 96562306a36Sopenharmony_ci val = snd_cs4236_ctrl_in(chip, 4) | 0xc0; 96662306a36Sopenharmony_ci snd_cs4236_ctrl_out(chip, 4, val); 96762306a36Sopenharmony_ci udelay(100); 96862306a36Sopenharmony_ci val &= ~0x40; 96962306a36Sopenharmony_ci snd_cs4236_ctrl_out(chip, 4, val); 97062306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 97162306a36Sopenharmony_ci snd_wss_mce_down(chip); 97262306a36Sopenharmony_ci mutex_unlock(&chip->mce_mutex); 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci#if 0 97562306a36Sopenharmony_ci printk(KERN_DEBUG "set valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, " 97662306a36Sopenharmony_ci "C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n", 97762306a36Sopenharmony_ci snd_wss_in(chip, CS4231_ALT_FEATURE_1), 97862306a36Sopenharmony_ci snd_cs4236_ctrl_in(chip, 3), 97962306a36Sopenharmony_ci snd_cs4236_ctrl_in(chip, 4), 98062306a36Sopenharmony_ci snd_cs4236_ctrl_in(chip, 5), 98162306a36Sopenharmony_ci snd_cs4236_ctrl_in(chip, 6), 98262306a36Sopenharmony_ci snd_cs4236_ctrl_in(chip, 8)); 98362306a36Sopenharmony_ci#endif 98462306a36Sopenharmony_ci return change; 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_cs4236_iec958_controls[] = { 98862306a36Sopenharmony_ciCS4236_IEC958_ENABLE("IEC958 Output Enable", 0), 98962306a36Sopenharmony_ciCS4236_SINGLEC("IEC958 Output Validity", 0, 4, 4, 1, 0), 99062306a36Sopenharmony_ciCS4236_SINGLEC("IEC958 Output User", 0, 4, 5, 1, 0), 99162306a36Sopenharmony_ciCS4236_SINGLEC("IEC958 Output CSBR", 0, 4, 6, 1, 0), 99262306a36Sopenharmony_ciCS4236_SINGLEC("IEC958 Output Channel Status Low", 0, 5, 1, 127, 0), 99362306a36Sopenharmony_ciCS4236_SINGLEC("IEC958 Output Channel Status High", 0, 6, 0, 255, 0) 99462306a36Sopenharmony_ci}; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_cs4236_3d_controls_cs4235[] = { 99762306a36Sopenharmony_ciCS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0), 99862306a36Sopenharmony_ciCS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1) 99962306a36Sopenharmony_ci}; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_cs4236_3d_controls_cs4237[] = { 100262306a36Sopenharmony_ciCS4236_SINGLEC("3D Control - Switch", 0, 3, 7, 1, 0), 100362306a36Sopenharmony_ciCS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1), 100462306a36Sopenharmony_ciCS4236_SINGLEC("3D Control - Center", 0, 2, 0, 15, 1), 100562306a36Sopenharmony_ciCS4236_SINGLEC("3D Control - Mono", 0, 3, 6, 1, 0), 100662306a36Sopenharmony_ciCS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0) 100762306a36Sopenharmony_ci}; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_cs4236_3d_controls_cs4238[] = { 101062306a36Sopenharmony_ciCS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0), 101162306a36Sopenharmony_ciCS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1), 101262306a36Sopenharmony_ciCS4236_SINGLEC("3D Control - Volume", 0, 2, 0, 15, 1), 101362306a36Sopenharmony_ciCS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0) 101462306a36Sopenharmony_ci}; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ciint snd_cs4236_mixer(struct snd_wss *chip) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci struct snd_card *card; 101962306a36Sopenharmony_ci unsigned int idx, count; 102062306a36Sopenharmony_ci int err; 102162306a36Sopenharmony_ci const struct snd_kcontrol_new *kcontrol; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci if (snd_BUG_ON(!chip || !chip->card)) 102462306a36Sopenharmony_ci return -EINVAL; 102562306a36Sopenharmony_ci card = chip->card; 102662306a36Sopenharmony_ci strcpy(card->mixername, snd_wss_chip_id(chip)); 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci if (chip->hardware == WSS_HW_CS4235 || 102962306a36Sopenharmony_ci chip->hardware == WSS_HW_CS4239) { 103062306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_cs4235_controls); idx++) { 103162306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4235_controls[idx], chip)); 103262306a36Sopenharmony_ci if (err < 0) 103362306a36Sopenharmony_ci return err; 103462306a36Sopenharmony_ci } 103562306a36Sopenharmony_ci } else { 103662306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_cs4236_controls); idx++) { 103762306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_controls[idx], chip)); 103862306a36Sopenharmony_ci if (err < 0) 103962306a36Sopenharmony_ci return err; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci switch (chip->hardware) { 104362306a36Sopenharmony_ci case WSS_HW_CS4235: 104462306a36Sopenharmony_ci case WSS_HW_CS4239: 104562306a36Sopenharmony_ci count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4235); 104662306a36Sopenharmony_ci kcontrol = snd_cs4236_3d_controls_cs4235; 104762306a36Sopenharmony_ci break; 104862306a36Sopenharmony_ci case WSS_HW_CS4237B: 104962306a36Sopenharmony_ci count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4237); 105062306a36Sopenharmony_ci kcontrol = snd_cs4236_3d_controls_cs4237; 105162306a36Sopenharmony_ci break; 105262306a36Sopenharmony_ci case WSS_HW_CS4238B: 105362306a36Sopenharmony_ci count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4238); 105462306a36Sopenharmony_ci kcontrol = snd_cs4236_3d_controls_cs4238; 105562306a36Sopenharmony_ci break; 105662306a36Sopenharmony_ci default: 105762306a36Sopenharmony_ci count = 0; 105862306a36Sopenharmony_ci kcontrol = NULL; 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci for (idx = 0; idx < count; idx++, kcontrol++) { 106162306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(kcontrol, chip)); 106262306a36Sopenharmony_ci if (err < 0) 106362306a36Sopenharmony_ci return err; 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci if (chip->hardware == WSS_HW_CS4237B || 106662306a36Sopenharmony_ci chip->hardware == WSS_HW_CS4238B) { 106762306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_cs4236_iec958_controls); idx++) { 106862306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_iec958_controls[idx], chip)); 106962306a36Sopenharmony_ci if (err < 0) 107062306a36Sopenharmony_ci return err; 107162306a36Sopenharmony_ci } 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci return 0; 107462306a36Sopenharmony_ci} 1075