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