162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
462306a36Sopenharmony_ci *  Universal interface for Audio Codec '97
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  For more details look to AC '97 component specification revision 2.2
762306a36Sopenharmony_ci *  by Intel Corporation (http://developer.intel.com).
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/pci.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/mutex.h>
1662306a36Sopenharmony_ci#include <sound/core.h>
1762306a36Sopenharmony_ci#include <sound/pcm.h>
1862306a36Sopenharmony_ci#include <sound/tlv.h>
1962306a36Sopenharmony_ci#include <sound/ac97_codec.h>
2062306a36Sopenharmony_ci#include <sound/asoundef.h>
2162306a36Sopenharmony_ci#include <sound/initval.h>
2262306a36Sopenharmony_ci#include "ac97_id.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "ac97_patch.c"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
2762306a36Sopenharmony_ciMODULE_DESCRIPTION("Universal interface for Audio Codec '97");
2862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic bool enable_loopback;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cimodule_param(enable_loopback, bool, 0444);
3362306a36Sopenharmony_ciMODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control");
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE
3662306a36Sopenharmony_cistatic int power_save = CONFIG_SND_AC97_POWER_SAVE_DEFAULT;
3762306a36Sopenharmony_cimodule_param(power_save, int, 0644);
3862306a36Sopenharmony_ciMODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
3962306a36Sopenharmony_ci		 "(in second, 0 = disable).");
4062306a36Sopenharmony_ci#endif
4162306a36Sopenharmony_ci/*
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistruct ac97_codec_id {
4662306a36Sopenharmony_ci	unsigned int id;
4762306a36Sopenharmony_ci	unsigned int mask;
4862306a36Sopenharmony_ci	const char *name;
4962306a36Sopenharmony_ci	int (*patch)(struct snd_ac97 *ac97);
5062306a36Sopenharmony_ci	int (*mpatch)(struct snd_ac97 *ac97);
5162306a36Sopenharmony_ci	unsigned int flags;
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic const struct ac97_codec_id snd_ac97_codec_id_vendors[] = {
5562306a36Sopenharmony_ci{ 0x41445300, 0xffffff00, "Analog Devices",	NULL,	NULL },
5662306a36Sopenharmony_ci{ 0x414b4d00, 0xffffff00, "Asahi Kasei",	NULL,	NULL },
5762306a36Sopenharmony_ci{ 0x414c4300, 0xffffff00, "Realtek",		NULL,	NULL },
5862306a36Sopenharmony_ci{ 0x414c4700, 0xffffff00, "Realtek",		NULL,	NULL },
5962306a36Sopenharmony_ci/*
6062306a36Sopenharmony_ci * This is an _inofficial_ Aztech Labs entry
6162306a36Sopenharmony_ci * (value might differ from unknown official Aztech ID),
6262306a36Sopenharmony_ci * currently used by the AC97 emulation of the almost-AC97 PCI168 card.
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_ci{ 0x415a5400, 0xffffff00, "Aztech Labs (emulated)",	NULL,	NULL },
6562306a36Sopenharmony_ci{ 0x434d4900, 0xffffff00, "C-Media Electronics", NULL,	NULL },
6662306a36Sopenharmony_ci{ 0x43525900, 0xffffff00, "Cirrus Logic",	NULL,	NULL },
6762306a36Sopenharmony_ci{ 0x43585400, 0xffffff00, "Conexant",           NULL,	NULL },
6862306a36Sopenharmony_ci{ 0x44543000, 0xffffff00, "Diamond Technology", NULL,	NULL },
6962306a36Sopenharmony_ci{ 0x454d4300, 0xffffff00, "eMicro",		NULL,	NULL },
7062306a36Sopenharmony_ci{ 0x45838300, 0xffffff00, "ESS Technology",	NULL,	NULL },
7162306a36Sopenharmony_ci{ 0x48525300, 0xffffff00, "Intersil",		NULL,	NULL },
7262306a36Sopenharmony_ci{ 0x49434500, 0xffffff00, "ICEnsemble",		NULL,	NULL },
7362306a36Sopenharmony_ci{ 0x49544500, 0xffffff00, "ITE Tech.Inc",	NULL,	NULL },
7462306a36Sopenharmony_ci{ 0x4e534300, 0xffffff00, "National Semiconductor", NULL, NULL },
7562306a36Sopenharmony_ci{ 0x50534300, 0xffffff00, "Philips",		NULL,	NULL },
7662306a36Sopenharmony_ci{ 0x53494c00, 0xffffff00, "Silicon Laboratory",	NULL,	NULL },
7762306a36Sopenharmony_ci{ 0x53544d00, 0xffffff00, "STMicroelectronics",	NULL,	NULL },
7862306a36Sopenharmony_ci{ 0x54524100, 0xffffff00, "TriTech",		NULL,	NULL },
7962306a36Sopenharmony_ci{ 0x54584e00, 0xffffff00, "Texas Instruments",	NULL,	NULL },
8062306a36Sopenharmony_ci{ 0x56494100, 0xffffff00, "VIA Technologies",   NULL,	NULL },
8162306a36Sopenharmony_ci{ 0x57454300, 0xffffff00, "Winbond",		NULL,	NULL },
8262306a36Sopenharmony_ci{ 0x574d4c00, 0xffffff00, "Wolfson",		NULL,	NULL },
8362306a36Sopenharmony_ci{ 0x594d4800, 0xffffff00, "Yamaha",		NULL,	NULL },
8462306a36Sopenharmony_ci{ 0x83847600, 0xffffff00, "SigmaTel",		NULL,	NULL },
8562306a36Sopenharmony_ci{ 0,	      0, 	  NULL,			NULL,	NULL }
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic const struct ac97_codec_id snd_ac97_codec_ids[] = {
8962306a36Sopenharmony_ci{ 0x41445303, 0xffffffff, "AD1819",		patch_ad1819,	NULL },
9062306a36Sopenharmony_ci{ 0x41445340, 0xffffffff, "AD1881",		patch_ad1881,	NULL },
9162306a36Sopenharmony_ci{ 0x41445348, 0xffffffff, "AD1881A",		patch_ad1881,	NULL },
9262306a36Sopenharmony_ci{ 0x41445360, 0xffffffff, "AD1885",		patch_ad1885,	NULL },
9362306a36Sopenharmony_ci{ 0x41445361, 0xffffffff, "AD1886",		patch_ad1886,	NULL },
9462306a36Sopenharmony_ci{ 0x41445362, 0xffffffff, "AD1887",		patch_ad1881,	NULL },
9562306a36Sopenharmony_ci{ 0x41445363, 0xffffffff, "AD1886A",		patch_ad1881,	NULL },
9662306a36Sopenharmony_ci{ 0x41445368, 0xffffffff, "AD1888",		patch_ad1888,	NULL },
9762306a36Sopenharmony_ci{ 0x41445370, 0xffffffff, "AD1980",		patch_ad1980,	NULL },
9862306a36Sopenharmony_ci{ 0x41445372, 0xffffffff, "AD1981A",		patch_ad1981a,	NULL },
9962306a36Sopenharmony_ci{ 0x41445374, 0xffffffff, "AD1981B",		patch_ad1981b,	NULL },
10062306a36Sopenharmony_ci{ 0x41445375, 0xffffffff, "AD1985",		patch_ad1985,	NULL },
10162306a36Sopenharmony_ci{ 0x41445378, 0xffffffff, "AD1986",		patch_ad1986,	NULL },
10262306a36Sopenharmony_ci{ 0x414b4d00, 0xffffffff, "AK4540",		NULL,		NULL },
10362306a36Sopenharmony_ci{ 0x414b4d01, 0xffffffff, "AK4542",		NULL,		NULL },
10462306a36Sopenharmony_ci{ 0x414b4d02, 0xffffffff, "AK4543",		NULL,		NULL },
10562306a36Sopenharmony_ci{ 0x414b4d06, 0xffffffff, "AK4544A",		NULL,		NULL },
10662306a36Sopenharmony_ci{ 0x414b4d07, 0xffffffff, "AK4545",		NULL,		NULL },
10762306a36Sopenharmony_ci{ 0x414c4300, 0xffffff00, "ALC100,100P", 	NULL,		NULL },
10862306a36Sopenharmony_ci{ 0x414c4710, 0xfffffff0, "ALC200,200P",	NULL,		NULL },
10962306a36Sopenharmony_ci{ 0x414c4721, 0xffffffff, "ALC650D",		NULL,	NULL }, /* already patched */
11062306a36Sopenharmony_ci{ 0x414c4722, 0xffffffff, "ALC650E",		NULL,	NULL }, /* already patched */
11162306a36Sopenharmony_ci{ 0x414c4723, 0xffffffff, "ALC650F",		NULL,	NULL }, /* already patched */
11262306a36Sopenharmony_ci{ 0x414c4720, 0xfffffff0, "ALC650",		patch_alc650,	NULL },
11362306a36Sopenharmony_ci{ 0x414c4730, 0xffffffff, "ALC101",		NULL,		NULL },
11462306a36Sopenharmony_ci{ 0x414c4740, 0xfffffff0, "ALC202",		NULL,		NULL },
11562306a36Sopenharmony_ci{ 0x414c4750, 0xfffffff0, "ALC250",		NULL,		NULL },
11662306a36Sopenharmony_ci{ 0x414c4760, 0xfffffff0, "ALC655",		patch_alc655,	NULL },
11762306a36Sopenharmony_ci{ 0x414c4770, 0xfffffff0, "ALC203",		patch_alc203,	NULL },
11862306a36Sopenharmony_ci{ 0x414c4781, 0xffffffff, "ALC658D",		NULL,	NULL }, /* already patched */
11962306a36Sopenharmony_ci{ 0x414c4780, 0xfffffff0, "ALC658",		patch_alc655,	NULL },
12062306a36Sopenharmony_ci{ 0x414c4790, 0xfffffff0, "ALC850",		patch_alc850,	NULL },
12162306a36Sopenharmony_ci{ 0x415a5401, 0xffffffff, "AZF3328",		patch_aztech_azf3328,	NULL },
12262306a36Sopenharmony_ci{ 0x434d4941, 0xffffffff, "CMI9738",		patch_cm9738,	NULL },
12362306a36Sopenharmony_ci{ 0x434d4961, 0xffffffff, "CMI9739",		patch_cm9739,	NULL },
12462306a36Sopenharmony_ci{ 0x434d4969, 0xffffffff, "CMI9780",		patch_cm9780,	NULL },
12562306a36Sopenharmony_ci{ 0x434d4978, 0xffffffff, "CMI9761A",		patch_cm9761,	NULL },
12662306a36Sopenharmony_ci{ 0x434d4982, 0xffffffff, "CMI9761B",		patch_cm9761,	NULL },
12762306a36Sopenharmony_ci{ 0x434d4983, 0xffffffff, "CMI9761A+",		patch_cm9761,	NULL },
12862306a36Sopenharmony_ci{ 0x43525900, 0xfffffff8, "CS4297",		NULL,		NULL },
12962306a36Sopenharmony_ci{ 0x43525910, 0xfffffff8, "CS4297A",		patch_cirrus_spdif,	NULL },
13062306a36Sopenharmony_ci{ 0x43525920, 0xfffffff8, "CS4298",		patch_cirrus_spdif,		NULL },
13162306a36Sopenharmony_ci{ 0x43525928, 0xfffffff8, "CS4294",		NULL,		NULL },
13262306a36Sopenharmony_ci{ 0x43525930, 0xfffffff8, "CS4299",		patch_cirrus_cs4299,	NULL },
13362306a36Sopenharmony_ci{ 0x43525948, 0xfffffff8, "CS4201",		NULL,		NULL },
13462306a36Sopenharmony_ci{ 0x43525958, 0xfffffff8, "CS4205",		patch_cirrus_spdif,	NULL },
13562306a36Sopenharmony_ci{ 0x43525960, 0xfffffff8, "CS4291",		NULL,		NULL },
13662306a36Sopenharmony_ci{ 0x43525970, 0xfffffff8, "CS4202",		NULL,		NULL },
13762306a36Sopenharmony_ci{ 0x43585421, 0xffffffff, "HSD11246",		NULL,		NULL },	// SmartMC II
13862306a36Sopenharmony_ci{ 0x43585428, 0xfffffff8, "Cx20468",		patch_conexant,	NULL }, // SmartAMC fixme: the mask might be different
13962306a36Sopenharmony_ci{ 0x43585430, 0xffffffff, "Cx20468-31",		patch_conexant, NULL },
14062306a36Sopenharmony_ci{ 0x43585431, 0xffffffff, "Cx20551",           patch_cx20551,  NULL },
14162306a36Sopenharmony_ci{ 0x44543031, 0xfffffff0, "DT0398",		NULL,		NULL },
14262306a36Sopenharmony_ci{ 0x454d4328, 0xffffffff, "EM28028",		NULL,		NULL },  // same as TR28028?
14362306a36Sopenharmony_ci{ 0x45838308, 0xffffffff, "ESS1988",		NULL,		NULL },
14462306a36Sopenharmony_ci{ 0x48525300, 0xffffff00, "HMP9701",		NULL,		NULL },
14562306a36Sopenharmony_ci{ 0x49434501, 0xffffffff, "ICE1230",		NULL,		NULL },
14662306a36Sopenharmony_ci{ 0x49434511, 0xffffffff, "ICE1232",		NULL,		NULL }, // alias VIA VT1611A?
14762306a36Sopenharmony_ci{ 0x49434514, 0xffffffff, "ICE1232A",		NULL,		NULL },
14862306a36Sopenharmony_ci{ 0x49434551, 0xffffffff, "VT1616", 		patch_vt1616,	NULL },
14962306a36Sopenharmony_ci{ 0x49434552, 0xffffffff, "VT1616i",		patch_vt1616,	NULL }, // VT1616 compatible (chipset integrated)
15062306a36Sopenharmony_ci{ 0x49544520, 0xffffffff, "IT2226E",		NULL,		NULL },
15162306a36Sopenharmony_ci{ 0x49544561, 0xffffffff, "IT2646E",		patch_it2646,	NULL },
15262306a36Sopenharmony_ci{ 0x4e534300, 0xffffffff, "LM4540,43,45,46,48",	NULL,		NULL }, // only guess --jk
15362306a36Sopenharmony_ci{ 0x4e534331, 0xffffffff, "LM4549",		NULL,		NULL },
15462306a36Sopenharmony_ci{ 0x4e534350, 0xffffffff, "LM4550",		patch_lm4550,  	NULL }, // volume wrap fix
15562306a36Sopenharmony_ci{ 0x53494c20, 0xffffffe0, "Si3036,8",		mpatch_si3036,	mpatch_si3036, AC97_MODEM_PATCH },
15662306a36Sopenharmony_ci{ 0x53544d02, 0xffffffff, "ST7597",		NULL,		NULL },
15762306a36Sopenharmony_ci{ 0x54524102, 0xffffffff, "TR28022",		NULL,		NULL },
15862306a36Sopenharmony_ci{ 0x54524103, 0xffffffff, "TR28023",		NULL,		NULL },
15962306a36Sopenharmony_ci{ 0x54524106, 0xffffffff, "TR28026",		NULL,		NULL },
16062306a36Sopenharmony_ci{ 0x54524108, 0xffffffff, "TR28028",		patch_tritech_tr28028,	NULL }, // added by xin jin [07/09/99]
16162306a36Sopenharmony_ci{ 0x54524123, 0xffffffff, "TR28602",		NULL,		NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)]
16262306a36Sopenharmony_ci{ 0x54584e03, 0xffffffff, "TLV320AIC27",	NULL,		NULL },
16362306a36Sopenharmony_ci{ 0x54584e20, 0xffffffff, "TLC320AD9xC",	NULL,		NULL },
16462306a36Sopenharmony_ci{ 0x56494120, 0xfffffff0, "VIA1613",		patch_vt1613,	NULL },
16562306a36Sopenharmony_ci{ 0x56494161, 0xffffffff, "VIA1612A",		NULL,		NULL }, // modified ICE1232 with S/PDIF
16662306a36Sopenharmony_ci{ 0x56494170, 0xffffffff, "VIA1617A",		patch_vt1617a,	NULL }, // modified VT1616 with S/PDIF
16762306a36Sopenharmony_ci{ 0x56494182, 0xffffffff, "VIA1618",		patch_vt1618,   NULL },
16862306a36Sopenharmony_ci{ 0x57454301, 0xffffffff, "W83971D",		NULL,		NULL },
16962306a36Sopenharmony_ci{ 0x574d4c00, 0xffffffff, "WM9701,WM9701A",	NULL,		NULL },
17062306a36Sopenharmony_ci{ 0x574d4C03, 0xffffffff, "WM9703,WM9707,WM9708,WM9717", patch_wolfson03, NULL},
17162306a36Sopenharmony_ci{ 0x574d4C04, 0xffffffff, "WM9704M,WM9704Q",	patch_wolfson04, NULL},
17262306a36Sopenharmony_ci{ 0x574d4C05, 0xffffffff, "WM9705,WM9710",	patch_wolfson05, NULL},
17362306a36Sopenharmony_ci{ 0x574d4C09, 0xffffffff, "WM9709",		NULL,		NULL},
17462306a36Sopenharmony_ci{ 0x574d4C12, 0xffffffff, "WM9711,WM9712,WM9715",	patch_wolfson11, NULL},
17562306a36Sopenharmony_ci{ 0x574d4c13, 0xffffffff, "WM9713,WM9714",	patch_wolfson13, NULL, AC97_DEFAULT_POWER_OFF},
17662306a36Sopenharmony_ci{ 0x594d4800, 0xffffffff, "YMF743",		patch_yamaha_ymf743,	NULL },
17762306a36Sopenharmony_ci{ 0x594d4802, 0xffffffff, "YMF752",		NULL,		NULL },
17862306a36Sopenharmony_ci{ 0x594d4803, 0xffffffff, "YMF753",		patch_yamaha_ymf753,	NULL },
17962306a36Sopenharmony_ci{ 0x83847600, 0xffffffff, "STAC9700,83,84",	patch_sigmatel_stac9700,	NULL },
18062306a36Sopenharmony_ci{ 0x83847604, 0xffffffff, "STAC9701,3,4,5",	NULL,		NULL },
18162306a36Sopenharmony_ci{ 0x83847605, 0xffffffff, "STAC9704",		NULL,		NULL },
18262306a36Sopenharmony_ci{ 0x83847608, 0xffffffff, "STAC9708,11",	patch_sigmatel_stac9708,	NULL },
18362306a36Sopenharmony_ci{ 0x83847609, 0xffffffff, "STAC9721,23",	patch_sigmatel_stac9721,	NULL },
18462306a36Sopenharmony_ci{ 0x83847644, 0xffffffff, "STAC9744",		patch_sigmatel_stac9744,	NULL },
18562306a36Sopenharmony_ci{ 0x83847650, 0xffffffff, "STAC9750,51",	NULL,		NULL },	// patch?
18662306a36Sopenharmony_ci{ 0x83847652, 0xffffffff, "STAC9752,53",	NULL,		NULL }, // patch?
18762306a36Sopenharmony_ci{ 0x83847656, 0xffffffff, "STAC9756,57",	patch_sigmatel_stac9756,	NULL },
18862306a36Sopenharmony_ci{ 0x83847658, 0xffffffff, "STAC9758,59",	patch_sigmatel_stac9758,	NULL },
18962306a36Sopenharmony_ci{ 0x83847666, 0xffffffff, "STAC9766,67",	NULL,		NULL }, // patch?
19062306a36Sopenharmony_ci{ 0, 	      0,	  NULL,			NULL,		NULL }
19162306a36Sopenharmony_ci};
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic void update_power_regs(struct snd_ac97 *ac97);
19562306a36Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE
19662306a36Sopenharmony_ci#define ac97_is_power_save_mode(ac97) \
19762306a36Sopenharmony_ci	((ac97->scaps & AC97_SCAP_POWER_SAVE) && power_save)
19862306a36Sopenharmony_ci#else
19962306a36Sopenharmony_ci#define ac97_is_power_save_mode(ac97) 0
20062306a36Sopenharmony_ci#endif
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci#define ac97_err(ac97, fmt, args...)	\
20362306a36Sopenharmony_ci	dev_err((ac97)->bus->card->dev, fmt, ##args)
20462306a36Sopenharmony_ci#define ac97_warn(ac97, fmt, args...)	\
20562306a36Sopenharmony_ci	dev_warn((ac97)->bus->card->dev, fmt, ##args)
20662306a36Sopenharmony_ci#define ac97_dbg(ac97, fmt, args...)	\
20762306a36Sopenharmony_ci	dev_dbg((ac97)->bus->card->dev, fmt, ##args)
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci/*
21062306a36Sopenharmony_ci *  I/O routines
21162306a36Sopenharmony_ci */
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic int snd_ac97_valid_reg(struct snd_ac97 *ac97, unsigned short reg)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	/* filter some registers for buggy codecs */
21662306a36Sopenharmony_ci	switch (ac97->id) {
21762306a36Sopenharmony_ci	case AC97_ID_ST_AC97_ID4:
21862306a36Sopenharmony_ci		if (reg == 0x08)
21962306a36Sopenharmony_ci			return 0;
22062306a36Sopenharmony_ci		fallthrough;
22162306a36Sopenharmony_ci	case AC97_ID_ST7597:
22262306a36Sopenharmony_ci		if (reg == 0x22 || reg == 0x7a)
22362306a36Sopenharmony_ci			return 1;
22462306a36Sopenharmony_ci		fallthrough;
22562306a36Sopenharmony_ci	case AC97_ID_AK4540:
22662306a36Sopenharmony_ci	case AC97_ID_AK4542:
22762306a36Sopenharmony_ci		if (reg <= 0x1c || reg == 0x20 || reg == 0x26 || reg >= 0x7c)
22862306a36Sopenharmony_ci			return 1;
22962306a36Sopenharmony_ci		return 0;
23062306a36Sopenharmony_ci	case AC97_ID_AD1819:	/* AD1819 */
23162306a36Sopenharmony_ci	case AC97_ID_AD1881:	/* AD1881 */
23262306a36Sopenharmony_ci	case AC97_ID_AD1881A:	/* AD1881A */
23362306a36Sopenharmony_ci		if (reg >= 0x3a && reg <= 0x6e)	/* 0x59 */
23462306a36Sopenharmony_ci			return 0;
23562306a36Sopenharmony_ci		return 1;
23662306a36Sopenharmony_ci	case AC97_ID_AD1885:	/* AD1885 */
23762306a36Sopenharmony_ci	case AC97_ID_AD1886:	/* AD1886 */
23862306a36Sopenharmony_ci	case AC97_ID_AD1886A:	/* AD1886A - !!verify!! --jk */
23962306a36Sopenharmony_ci	case AC97_ID_AD1887:	/* AD1887 - !!verify!! --jk */
24062306a36Sopenharmony_ci		if (reg == 0x5a)
24162306a36Sopenharmony_ci			return 1;
24262306a36Sopenharmony_ci		if (reg >= 0x3c && reg <= 0x6e)	/* 0x59 */
24362306a36Sopenharmony_ci			return 0;
24462306a36Sopenharmony_ci		return 1;
24562306a36Sopenharmony_ci	case AC97_ID_STAC9700:
24662306a36Sopenharmony_ci	case AC97_ID_STAC9704:
24762306a36Sopenharmony_ci	case AC97_ID_STAC9705:
24862306a36Sopenharmony_ci	case AC97_ID_STAC9708:
24962306a36Sopenharmony_ci	case AC97_ID_STAC9721:
25062306a36Sopenharmony_ci	case AC97_ID_STAC9744:
25162306a36Sopenharmony_ci	case AC97_ID_STAC9756:
25262306a36Sopenharmony_ci		if (reg <= 0x3a || reg >= 0x5a)
25362306a36Sopenharmony_ci			return 1;
25462306a36Sopenharmony_ci		return 0;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci	return 1;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci/**
26062306a36Sopenharmony_ci * snd_ac97_write - write a value on the given register
26162306a36Sopenharmony_ci * @ac97: the ac97 instance
26262306a36Sopenharmony_ci * @reg: the register to change
26362306a36Sopenharmony_ci * @value: the value to set
26462306a36Sopenharmony_ci *
26562306a36Sopenharmony_ci * Writes a value on the given register.  This will invoke the write
26662306a36Sopenharmony_ci * callback directly after the register check.
26762306a36Sopenharmony_ci * This function doesn't change the register cache unlike
26862306a36Sopenharmony_ci * #snd_ca97_write_cache(), so use this only when you don't want to
26962306a36Sopenharmony_ci * reflect the change to the suspend/resume state.
27062306a36Sopenharmony_ci */
27162306a36Sopenharmony_civoid snd_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short value)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	if (!snd_ac97_valid_reg(ac97, reg))
27462306a36Sopenharmony_ci		return;
27562306a36Sopenharmony_ci	if ((ac97->id & 0xffffff00) == AC97_ID_ALC100) {
27662306a36Sopenharmony_ci		/* Fix H/W bug of ALC100/100P */
27762306a36Sopenharmony_ci		if (reg == AC97_MASTER || reg == AC97_HEADPHONE)
27862306a36Sopenharmony_ci			ac97->bus->ops->write(ac97, AC97_RESET, 0);	/* reset audio codec */
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci	ac97->bus->ops->write(ac97, reg, value);
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_write);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci/**
28662306a36Sopenharmony_ci * snd_ac97_read - read a value from the given register
28762306a36Sopenharmony_ci *
28862306a36Sopenharmony_ci * @ac97: the ac97 instance
28962306a36Sopenharmony_ci * @reg: the register to read
29062306a36Sopenharmony_ci *
29162306a36Sopenharmony_ci * Reads a value from the given register.  This will invoke the read
29262306a36Sopenharmony_ci * callback directly after the register check.
29362306a36Sopenharmony_ci *
29462306a36Sopenharmony_ci * Return: The read value.
29562306a36Sopenharmony_ci */
29662306a36Sopenharmony_ciunsigned short snd_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	if (!snd_ac97_valid_reg(ac97, reg))
29962306a36Sopenharmony_ci		return 0;
30062306a36Sopenharmony_ci	return ac97->bus->ops->read(ac97, reg);
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci/* read a register - return the cached value if already read */
30462306a36Sopenharmony_cistatic inline unsigned short snd_ac97_read_cache(struct snd_ac97 *ac97, unsigned short reg)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	if (! test_bit(reg, ac97->reg_accessed)) {
30762306a36Sopenharmony_ci		ac97->regs[reg] = ac97->bus->ops->read(ac97, reg);
30862306a36Sopenharmony_ci		// set_bit(reg, ac97->reg_accessed);
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci	return ac97->regs[reg];
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_read);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci/**
31662306a36Sopenharmony_ci * snd_ac97_write_cache - write a value on the given register and update the cache
31762306a36Sopenharmony_ci * @ac97: the ac97 instance
31862306a36Sopenharmony_ci * @reg: the register to change
31962306a36Sopenharmony_ci * @value: the value to set
32062306a36Sopenharmony_ci *
32162306a36Sopenharmony_ci * Writes a value on the given register and updates the register
32262306a36Sopenharmony_ci * cache.  The cached values are used for the cached-read and the
32362306a36Sopenharmony_ci * suspend/resume.
32462306a36Sopenharmony_ci */
32562306a36Sopenharmony_civoid snd_ac97_write_cache(struct snd_ac97 *ac97, unsigned short reg, unsigned short value)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	if (!snd_ac97_valid_reg(ac97, reg))
32862306a36Sopenharmony_ci		return;
32962306a36Sopenharmony_ci	mutex_lock(&ac97->reg_mutex);
33062306a36Sopenharmony_ci	ac97->regs[reg] = value;
33162306a36Sopenharmony_ci	ac97->bus->ops->write(ac97, reg, value);
33262306a36Sopenharmony_ci	set_bit(reg, ac97->reg_accessed);
33362306a36Sopenharmony_ci	mutex_unlock(&ac97->reg_mutex);
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_write_cache);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci/**
33962306a36Sopenharmony_ci * snd_ac97_update - update the value on the given register
34062306a36Sopenharmony_ci * @ac97: the ac97 instance
34162306a36Sopenharmony_ci * @reg: the register to change
34262306a36Sopenharmony_ci * @value: the value to set
34362306a36Sopenharmony_ci *
34462306a36Sopenharmony_ci * Compares the value with the register cache and updates the value
34562306a36Sopenharmony_ci * only when the value is changed.
34662306a36Sopenharmony_ci *
34762306a36Sopenharmony_ci * Return: 1 if the value is changed, 0 if no change, or a negative
34862306a36Sopenharmony_ci * code on failure.
34962306a36Sopenharmony_ci */
35062306a36Sopenharmony_ciint snd_ac97_update(struct snd_ac97 *ac97, unsigned short reg, unsigned short value)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	int change;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	if (!snd_ac97_valid_reg(ac97, reg))
35562306a36Sopenharmony_ci		return -EINVAL;
35662306a36Sopenharmony_ci	mutex_lock(&ac97->reg_mutex);
35762306a36Sopenharmony_ci	change = ac97->regs[reg] != value;
35862306a36Sopenharmony_ci	if (change) {
35962306a36Sopenharmony_ci		ac97->regs[reg] = value;
36062306a36Sopenharmony_ci		ac97->bus->ops->write(ac97, reg, value);
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci	set_bit(reg, ac97->reg_accessed);
36362306a36Sopenharmony_ci	mutex_unlock(&ac97->reg_mutex);
36462306a36Sopenharmony_ci	return change;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_update);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci/**
37062306a36Sopenharmony_ci * snd_ac97_update_bits - update the bits on the given register
37162306a36Sopenharmony_ci * @ac97: the ac97 instance
37262306a36Sopenharmony_ci * @reg: the register to change
37362306a36Sopenharmony_ci * @mask: the bit-mask to change
37462306a36Sopenharmony_ci * @value: the value to set
37562306a36Sopenharmony_ci *
37662306a36Sopenharmony_ci * Updates the masked-bits on the given register only when the value
37762306a36Sopenharmony_ci * is changed.
37862306a36Sopenharmony_ci *
37962306a36Sopenharmony_ci * Return: 1 if the bits are changed, 0 if no change, or a negative
38062306a36Sopenharmony_ci * code on failure.
38162306a36Sopenharmony_ci */
38262306a36Sopenharmony_ciint snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	int change;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (!snd_ac97_valid_reg(ac97, reg))
38762306a36Sopenharmony_ci		return -EINVAL;
38862306a36Sopenharmony_ci	mutex_lock(&ac97->reg_mutex);
38962306a36Sopenharmony_ci	change = snd_ac97_update_bits_nolock(ac97, reg, mask, value);
39062306a36Sopenharmony_ci	mutex_unlock(&ac97->reg_mutex);
39162306a36Sopenharmony_ci	return change;
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_update_bits);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci/* no lock version - see snd_ac97_update_bits() */
39762306a36Sopenharmony_ciint snd_ac97_update_bits_nolock(struct snd_ac97 *ac97, unsigned short reg,
39862306a36Sopenharmony_ci				unsigned short mask, unsigned short value)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	int change;
40162306a36Sopenharmony_ci	unsigned short old, new;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	old = snd_ac97_read_cache(ac97, reg);
40462306a36Sopenharmony_ci	new = (old & ~mask) | (value & mask);
40562306a36Sopenharmony_ci	change = old != new;
40662306a36Sopenharmony_ci	if (change) {
40762306a36Sopenharmony_ci		ac97->regs[reg] = new;
40862306a36Sopenharmony_ci		ac97->bus->ops->write(ac97, reg, new);
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci	set_bit(reg, ac97->reg_accessed);
41162306a36Sopenharmony_ci	return change;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic int snd_ac97_ad18xx_update_pcm_bits(struct snd_ac97 *ac97, int codec, unsigned short mask, unsigned short value)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	int change;
41762306a36Sopenharmony_ci	unsigned short old, new, cfg;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	mutex_lock(&ac97->page_mutex);
42062306a36Sopenharmony_ci	old = ac97->spec.ad18xx.pcmreg[codec];
42162306a36Sopenharmony_ci	new = (old & ~mask) | (value & mask);
42262306a36Sopenharmony_ci	change = old != new;
42362306a36Sopenharmony_ci	if (change) {
42462306a36Sopenharmony_ci		mutex_lock(&ac97->reg_mutex);
42562306a36Sopenharmony_ci		cfg = snd_ac97_read_cache(ac97, AC97_AD_SERIAL_CFG);
42662306a36Sopenharmony_ci		ac97->spec.ad18xx.pcmreg[codec] = new;
42762306a36Sopenharmony_ci		/* select single codec */
42862306a36Sopenharmony_ci		ac97->bus->ops->write(ac97, AC97_AD_SERIAL_CFG,
42962306a36Sopenharmony_ci				 (cfg & ~0x7000) |
43062306a36Sopenharmony_ci				 ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]);
43162306a36Sopenharmony_ci		/* update PCM bits */
43262306a36Sopenharmony_ci		ac97->bus->ops->write(ac97, AC97_PCM, new);
43362306a36Sopenharmony_ci		/* select all codecs */
43462306a36Sopenharmony_ci		ac97->bus->ops->write(ac97, AC97_AD_SERIAL_CFG,
43562306a36Sopenharmony_ci				 cfg | 0x7000);
43662306a36Sopenharmony_ci		mutex_unlock(&ac97->reg_mutex);
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci	mutex_unlock(&ac97->page_mutex);
43962306a36Sopenharmony_ci	return change;
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci/*
44362306a36Sopenharmony_ci * Controls
44462306a36Sopenharmony_ci */
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic int snd_ac97_info_enum_double(struct snd_kcontrol *kcontrol,
44762306a36Sopenharmony_ci				     struct snd_ctl_elem_info *uinfo)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	return snd_ctl_enum_info(uinfo, e->shift_l == e->shift_r ? 1 : 2,
45262306a36Sopenharmony_ci				 e->mask, e->texts);
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic int snd_ac97_get_enum_double(struct snd_kcontrol *kcontrol,
45662306a36Sopenharmony_ci				    struct snd_ctl_elem_value *ucontrol)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
45962306a36Sopenharmony_ci	struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
46062306a36Sopenharmony_ci	unsigned short val, bitmask;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
46362306a36Sopenharmony_ci		;
46462306a36Sopenharmony_ci	val = snd_ac97_read_cache(ac97, e->reg);
46562306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
46662306a36Sopenharmony_ci	if (e->shift_l != e->shift_r)
46762306a36Sopenharmony_ci		ucontrol->value.enumerated.item[1] = (val >> e->shift_r) & (bitmask - 1);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	return 0;
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic int snd_ac97_put_enum_double(struct snd_kcontrol *kcontrol,
47362306a36Sopenharmony_ci				    struct snd_ctl_elem_value *ucontrol)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
47662306a36Sopenharmony_ci	struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
47762306a36Sopenharmony_ci	unsigned short val;
47862306a36Sopenharmony_ci	unsigned short mask, bitmask;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
48162306a36Sopenharmony_ci		;
48262306a36Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] > e->mask - 1)
48362306a36Sopenharmony_ci		return -EINVAL;
48462306a36Sopenharmony_ci	val = ucontrol->value.enumerated.item[0] << e->shift_l;
48562306a36Sopenharmony_ci	mask = (bitmask - 1) << e->shift_l;
48662306a36Sopenharmony_ci	if (e->shift_l != e->shift_r) {
48762306a36Sopenharmony_ci		if (ucontrol->value.enumerated.item[1] > e->mask - 1)
48862306a36Sopenharmony_ci			return -EINVAL;
48962306a36Sopenharmony_ci		val |= ucontrol->value.enumerated.item[1] << e->shift_r;
49062306a36Sopenharmony_ci		mask |= (bitmask - 1) << e->shift_r;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci	return snd_ac97_update_bits(ac97, e->reg, mask, val);
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci/* save/restore ac97 v2.3 paging */
49662306a36Sopenharmony_cistatic int snd_ac97_page_save(struct snd_ac97 *ac97, int reg, struct snd_kcontrol *kcontrol)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	int page_save = -1;
49962306a36Sopenharmony_ci	if ((kcontrol->private_value & (1<<25)) &&
50062306a36Sopenharmony_ci	    (ac97->ext_id & AC97_EI_REV_MASK) >= AC97_EI_REV_23 &&
50162306a36Sopenharmony_ci	    (reg >= 0x60 && reg < 0x70)) {
50262306a36Sopenharmony_ci		unsigned short page = (kcontrol->private_value >> 26) & 0x0f;
50362306a36Sopenharmony_ci		mutex_lock(&ac97->page_mutex); /* lock paging */
50462306a36Sopenharmony_ci		page_save = snd_ac97_read(ac97, AC97_INT_PAGING) & AC97_PAGE_MASK;
50562306a36Sopenharmony_ci		snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page);
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci	return page_save;
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_cistatic void snd_ac97_page_restore(struct snd_ac97 *ac97, int page_save)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	if (page_save >= 0) {
51362306a36Sopenharmony_ci		snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page_save);
51462306a36Sopenharmony_ci		mutex_unlock(&ac97->page_mutex); /* unlock paging */
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci/* volume and switch controls */
51962306a36Sopenharmony_cistatic int snd_ac97_info_volsw(struct snd_kcontrol *kcontrol,
52062306a36Sopenharmony_ci			       struct snd_ctl_elem_info *uinfo)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
52362306a36Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0x0f;
52462306a36Sopenharmony_ci	int rshift = (kcontrol->private_value >> 12) & 0x0f;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
52762306a36Sopenharmony_ci	uinfo->count = shift == rshift ? 1 : 2;
52862306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
52962306a36Sopenharmony_ci	uinfo->value.integer.max = mask;
53062306a36Sopenharmony_ci	return 0;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic int snd_ac97_get_volsw(struct snd_kcontrol *kcontrol,
53462306a36Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
53762306a36Sopenharmony_ci	int reg = kcontrol->private_value & 0xff;
53862306a36Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0x0f;
53962306a36Sopenharmony_ci	int rshift = (kcontrol->private_value >> 12) & 0x0f;
54062306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
54162306a36Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & 0x01;
54262306a36Sopenharmony_ci	int page_save;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	page_save = snd_ac97_page_save(ac97, reg, kcontrol);
54562306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = (snd_ac97_read_cache(ac97, reg) >> shift) & mask;
54662306a36Sopenharmony_ci	if (shift != rshift)
54762306a36Sopenharmony_ci		ucontrol->value.integer.value[1] = (snd_ac97_read_cache(ac97, reg) >> rshift) & mask;
54862306a36Sopenharmony_ci	if (invert) {
54962306a36Sopenharmony_ci		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
55062306a36Sopenharmony_ci		if (shift != rshift)
55162306a36Sopenharmony_ci			ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci	snd_ac97_page_restore(ac97, page_save);
55462306a36Sopenharmony_ci	return 0;
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cistatic int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol,
55862306a36Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
56162306a36Sopenharmony_ci	int reg = kcontrol->private_value & 0xff;
56262306a36Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0x0f;
56362306a36Sopenharmony_ci	int rshift = (kcontrol->private_value >> 12) & 0x0f;
56462306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
56562306a36Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & 0x01;
56662306a36Sopenharmony_ci	int err, page_save;
56762306a36Sopenharmony_ci	unsigned short val, val2, val_mask;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	page_save = snd_ac97_page_save(ac97, reg, kcontrol);
57062306a36Sopenharmony_ci	val = (ucontrol->value.integer.value[0] & mask);
57162306a36Sopenharmony_ci	if (invert)
57262306a36Sopenharmony_ci		val = mask - val;
57362306a36Sopenharmony_ci	val_mask = mask << shift;
57462306a36Sopenharmony_ci	val = val << shift;
57562306a36Sopenharmony_ci	if (shift != rshift) {
57662306a36Sopenharmony_ci		val2 = (ucontrol->value.integer.value[1] & mask);
57762306a36Sopenharmony_ci		if (invert)
57862306a36Sopenharmony_ci			val2 = mask - val2;
57962306a36Sopenharmony_ci		val_mask |= mask << rshift;
58062306a36Sopenharmony_ci		val |= val2 << rshift;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci	err = snd_ac97_update_bits(ac97, reg, val_mask, val);
58362306a36Sopenharmony_ci	snd_ac97_page_restore(ac97, page_save);
58462306a36Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE
58562306a36Sopenharmony_ci	/* check analog mixer power-down */
58662306a36Sopenharmony_ci	if ((val_mask & AC97_PD_EAPD) &&
58762306a36Sopenharmony_ci	    (kcontrol->private_value & (1<<30))) {
58862306a36Sopenharmony_ci		if (val & AC97_PD_EAPD)
58962306a36Sopenharmony_ci			ac97->power_up &= ~(1 << (reg>>1));
59062306a36Sopenharmony_ci		else
59162306a36Sopenharmony_ci			ac97->power_up |= 1 << (reg>>1);
59262306a36Sopenharmony_ci		update_power_regs(ac97);
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci#endif
59562306a36Sopenharmony_ci	return err;
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_tone[2] = {
59962306a36Sopenharmony_ciAC97_SINGLE("Tone Control - Bass", AC97_MASTER_TONE, 8, 15, 1),
60062306a36Sopenharmony_ciAC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1)
60162306a36Sopenharmony_ci};
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_pc_beep[2] = {
60462306a36Sopenharmony_ciAC97_SINGLE("Beep Playback Switch", AC97_PC_BEEP, 15, 1, 1),
60562306a36Sopenharmony_ciAC97_SINGLE("Beep Playback Volume", AC97_PC_BEEP, 1, 15, 1)
60662306a36Sopenharmony_ci};
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_mic_boost =
60962306a36Sopenharmony_ci	AC97_SINGLE("Mic Boost (+20dB)", AC97_MIC, 6, 1, 0);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic const char* std_rec_sel[] = {"Mic", "CD", "Video", "Aux", "Line", "Mix", "Mix Mono", "Phone"};
61362306a36Sopenharmony_cistatic const char* std_3d_path[] = {"pre 3D", "post 3D"};
61462306a36Sopenharmony_cistatic const char* std_mix[] = {"Mix", "Mic"};
61562306a36Sopenharmony_cistatic const char* std_mic[] = {"Mic1", "Mic2"};
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_cistatic const struct ac97_enum std_enum[] = {
61862306a36Sopenharmony_ciAC97_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 8, std_rec_sel),
61962306a36Sopenharmony_ciAC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, std_3d_path),
62062306a36Sopenharmony_ciAC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 9, 2, std_mix),
62162306a36Sopenharmony_ciAC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 8, 2, std_mic),
62262306a36Sopenharmony_ci};
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_control_capture_src =
62562306a36Sopenharmony_ciAC97_ENUM("Capture Source", std_enum[0]);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_control_capture_vol =
62862306a36Sopenharmony_ciAC97_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 15, 0);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_mic_capture[2] = {
63162306a36Sopenharmony_ciAC97_SINGLE("Mic Capture Switch", AC97_REC_GAIN_MIC, 15, 1, 1),
63262306a36Sopenharmony_ciAC97_SINGLE("Mic Capture Volume", AC97_REC_GAIN_MIC, 0, 15, 0)
63362306a36Sopenharmony_ci};
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cienum {
63662306a36Sopenharmony_ci	AC97_GENERAL_PCM_OUT = 0,
63762306a36Sopenharmony_ci	AC97_GENERAL_STEREO_ENHANCEMENT,
63862306a36Sopenharmony_ci	AC97_GENERAL_3D,
63962306a36Sopenharmony_ci	AC97_GENERAL_LOUDNESS,
64062306a36Sopenharmony_ci	AC97_GENERAL_MONO,
64162306a36Sopenharmony_ci	AC97_GENERAL_MIC,
64262306a36Sopenharmony_ci	AC97_GENERAL_LOOPBACK
64362306a36Sopenharmony_ci};
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_general[7] = {
64662306a36Sopenharmony_ciAC97_ENUM("PCM Out Path & Mute", std_enum[1]),
64762306a36Sopenharmony_ciAC97_SINGLE("Simulated Stereo Enhancement", AC97_GENERAL_PURPOSE, 14, 1, 0),
64862306a36Sopenharmony_ciAC97_SINGLE("3D Control - Switch", AC97_GENERAL_PURPOSE, 13, 1, 0),
64962306a36Sopenharmony_ciAC97_SINGLE("Loudness (bass boost)", AC97_GENERAL_PURPOSE, 12, 1, 0),
65062306a36Sopenharmony_ciAC97_ENUM("Mono Output Select", std_enum[2]),
65162306a36Sopenharmony_ciAC97_ENUM("Mic Select", std_enum[3]),
65262306a36Sopenharmony_ciAC97_SINGLE("ADC/DAC Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0)
65362306a36Sopenharmony_ci};
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_3d[2] = {
65662306a36Sopenharmony_ciAC97_SINGLE("3D Control - Center", AC97_3D_CONTROL, 8, 15, 0),
65762306a36Sopenharmony_ciAC97_SINGLE("3D Control - Depth", AC97_3D_CONTROL, 0, 15, 0)
65862306a36Sopenharmony_ci};
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_center[2] = {
66162306a36Sopenharmony_ciAC97_SINGLE("Center Playback Switch", AC97_CENTER_LFE_MASTER, 7, 1, 1),
66262306a36Sopenharmony_ciAC97_SINGLE("Center Playback Volume", AC97_CENTER_LFE_MASTER, 0, 31, 1)
66362306a36Sopenharmony_ci};
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_lfe[2] = {
66662306a36Sopenharmony_ciAC97_SINGLE("LFE Playback Switch", AC97_CENTER_LFE_MASTER, 15, 1, 1),
66762306a36Sopenharmony_ciAC97_SINGLE("LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 31, 1)
66862306a36Sopenharmony_ci};
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_control_eapd =
67162306a36Sopenharmony_ciAC97_SINGLE("External Amplifier", AC97_POWERDOWN, 15, 1, 1);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_modem_switches[2] = {
67462306a36Sopenharmony_ciAC97_SINGLE("Off-hook Switch", AC97_GPIO_STATUS, 0, 1, 0),
67562306a36Sopenharmony_ciAC97_SINGLE("Caller ID Switch", AC97_GPIO_STATUS, 2, 1, 0)
67662306a36Sopenharmony_ci};
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci/* change the existing EAPD control as inverted */
67962306a36Sopenharmony_cistatic void set_inv_eapd(struct snd_ac97 *ac97, struct snd_kcontrol *kctl)
68062306a36Sopenharmony_ci{
68162306a36Sopenharmony_ci	kctl->private_value = AC97_SINGLE_VALUE(AC97_POWERDOWN, 15, 1, 0);
68262306a36Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_POWERDOWN, (1<<15), (1<<15)); /* EAPD up */
68362306a36Sopenharmony_ci	ac97->scaps |= AC97_SCAP_INV_EAPD;
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic int snd_ac97_spdif_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
68962306a36Sopenharmony_ci	uinfo->count = 1;
69062306a36Sopenharmony_ci	return 0;
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic int snd_ac97_spdif_cmask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
69662306a36Sopenharmony_ci					   IEC958_AES0_NONAUDIO |
69762306a36Sopenharmony_ci					   IEC958_AES0_CON_EMPHASIS_5015 |
69862306a36Sopenharmony_ci					   IEC958_AES0_CON_NOT_COPYRIGHT;
69962306a36Sopenharmony_ci	ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY |
70062306a36Sopenharmony_ci					   IEC958_AES1_CON_ORIGINAL;
70162306a36Sopenharmony_ci	ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS;
70262306a36Sopenharmony_ci	return 0;
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic int snd_ac97_spdif_pmask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
70662306a36Sopenharmony_ci{
70762306a36Sopenharmony_ci	/* FIXME: AC'97 spec doesn't say which bits are used for what */
70862306a36Sopenharmony_ci	ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
70962306a36Sopenharmony_ci					   IEC958_AES0_NONAUDIO |
71062306a36Sopenharmony_ci					   IEC958_AES0_PRO_FS |
71162306a36Sopenharmony_ci					   IEC958_AES0_PRO_EMPHASIS_5015;
71262306a36Sopenharmony_ci	return 0;
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic int snd_ac97_spdif_default_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	mutex_lock(&ac97->reg_mutex);
72062306a36Sopenharmony_ci	ucontrol->value.iec958.status[0] = ac97->spdif_status & 0xff;
72162306a36Sopenharmony_ci	ucontrol->value.iec958.status[1] = (ac97->spdif_status >> 8) & 0xff;
72262306a36Sopenharmony_ci	ucontrol->value.iec958.status[2] = (ac97->spdif_status >> 16) & 0xff;
72362306a36Sopenharmony_ci	ucontrol->value.iec958.status[3] = (ac97->spdif_status >> 24) & 0xff;
72462306a36Sopenharmony_ci	mutex_unlock(&ac97->reg_mutex);
72562306a36Sopenharmony_ci	return 0;
72662306a36Sopenharmony_ci}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_cistatic int snd_ac97_spdif_default_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
73162306a36Sopenharmony_ci	unsigned int new = 0;
73262306a36Sopenharmony_ci	unsigned short val = 0;
73362306a36Sopenharmony_ci	int change;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	new = val = ucontrol->value.iec958.status[0] & (IEC958_AES0_PROFESSIONAL|IEC958_AES0_NONAUDIO);
73662306a36Sopenharmony_ci	if (ucontrol->value.iec958.status[0] & IEC958_AES0_PROFESSIONAL) {
73762306a36Sopenharmony_ci		new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_PRO_FS|IEC958_AES0_PRO_EMPHASIS_5015);
73862306a36Sopenharmony_ci		switch (new & IEC958_AES0_PRO_FS) {
73962306a36Sopenharmony_ci		case IEC958_AES0_PRO_FS_44100: val |= 0<<12; break;
74062306a36Sopenharmony_ci		case IEC958_AES0_PRO_FS_48000: val |= 2<<12; break;
74162306a36Sopenharmony_ci		case IEC958_AES0_PRO_FS_32000: val |= 3<<12; break;
74262306a36Sopenharmony_ci		default:		       val |= 1<<12; break;
74362306a36Sopenharmony_ci		}
74462306a36Sopenharmony_ci		if ((new & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015)
74562306a36Sopenharmony_ci			val |= 1<<3;
74662306a36Sopenharmony_ci	} else {
74762306a36Sopenharmony_ci		new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT);
74862306a36Sopenharmony_ci		new |= ((ucontrol->value.iec958.status[1] & (IEC958_AES1_CON_CATEGORY|IEC958_AES1_CON_ORIGINAL)) << 8);
74962306a36Sopenharmony_ci		new |= ((ucontrol->value.iec958.status[3] & IEC958_AES3_CON_FS) << 24);
75062306a36Sopenharmony_ci		if ((new & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015)
75162306a36Sopenharmony_ci			val |= 1<<3;
75262306a36Sopenharmony_ci		if (!(new & IEC958_AES0_CON_NOT_COPYRIGHT))
75362306a36Sopenharmony_ci			val |= 1<<2;
75462306a36Sopenharmony_ci		val |= ((new >> 8) & 0xff) << 4;	// category + original
75562306a36Sopenharmony_ci		switch ((new >> 24) & 0xff) {
75662306a36Sopenharmony_ci		case IEC958_AES3_CON_FS_44100: val |= 0<<12; break;
75762306a36Sopenharmony_ci		case IEC958_AES3_CON_FS_48000: val |= 2<<12; break;
75862306a36Sopenharmony_ci		case IEC958_AES3_CON_FS_32000: val |= 3<<12; break;
75962306a36Sopenharmony_ci		default:		       val |= 1<<12; break;
76062306a36Sopenharmony_ci		}
76162306a36Sopenharmony_ci	}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	mutex_lock(&ac97->reg_mutex);
76462306a36Sopenharmony_ci	change = ac97->spdif_status != new;
76562306a36Sopenharmony_ci	ac97->spdif_status = new;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	if (ac97->flags & AC97_CS_SPDIF) {
76862306a36Sopenharmony_ci		int x = (val >> 12) & 0x03;
76962306a36Sopenharmony_ci		switch (x) {
77062306a36Sopenharmony_ci		case 0: x = 1; break;  // 44.1
77162306a36Sopenharmony_ci		case 2: x = 0; break;  // 48.0
77262306a36Sopenharmony_ci		default: x = 0; break; // illegal.
77362306a36Sopenharmony_ci		}
77462306a36Sopenharmony_ci		change |= snd_ac97_update_bits_nolock(ac97, AC97_CSR_SPDIF, 0x3fff, ((val & 0xcfff) | (x << 12)));
77562306a36Sopenharmony_ci	} else if (ac97->flags & AC97_CX_SPDIF) {
77662306a36Sopenharmony_ci		int v;
77762306a36Sopenharmony_ci		v = new & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT) ? 0 : AC97_CXR_COPYRGT;
77862306a36Sopenharmony_ci		v |= new & IEC958_AES0_NONAUDIO ? AC97_CXR_SPDIF_AC3 : AC97_CXR_SPDIF_PCM;
77962306a36Sopenharmony_ci		change |= snd_ac97_update_bits_nolock(ac97, AC97_CXR_AUDIO_MISC,
78062306a36Sopenharmony_ci						      AC97_CXR_SPDIF_MASK | AC97_CXR_COPYRGT,
78162306a36Sopenharmony_ci						      v);
78262306a36Sopenharmony_ci	} else if (ac97->id == AC97_ID_YMF743) {
78362306a36Sopenharmony_ci		change |= snd_ac97_update_bits_nolock(ac97,
78462306a36Sopenharmony_ci						      AC97_YMF7X3_DIT_CTRL,
78562306a36Sopenharmony_ci						      0xff38,
78662306a36Sopenharmony_ci						      ((val << 4) & 0xff00) |
78762306a36Sopenharmony_ci						      ((val << 2) & 0x0038));
78862306a36Sopenharmony_ci	} else {
78962306a36Sopenharmony_ci		unsigned short extst = snd_ac97_read_cache(ac97, AC97_EXTENDED_STATUS);
79062306a36Sopenharmony_ci		snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); /* turn off */
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci		change |= snd_ac97_update_bits_nolock(ac97, AC97_SPDIF, 0x3fff, val);
79362306a36Sopenharmony_ci		if (extst & AC97_EA_SPDIF) {
79462306a36Sopenharmony_ci			snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); /* turn on again */
79562306a36Sopenharmony_ci                }
79662306a36Sopenharmony_ci	}
79762306a36Sopenharmony_ci	mutex_unlock(&ac97->reg_mutex);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	return change;
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_cistatic int snd_ac97_put_spsa(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
80362306a36Sopenharmony_ci{
80462306a36Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
80562306a36Sopenharmony_ci	int reg = kcontrol->private_value & 0xff;
80662306a36Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0x0f;
80762306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
80862306a36Sopenharmony_ci	// int invert = (kcontrol->private_value >> 24) & 0xff;
80962306a36Sopenharmony_ci	unsigned short value, old, new;
81062306a36Sopenharmony_ci	int change;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	value = (ucontrol->value.integer.value[0] & mask);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	mutex_lock(&ac97->reg_mutex);
81562306a36Sopenharmony_ci	mask <<= shift;
81662306a36Sopenharmony_ci	value <<= shift;
81762306a36Sopenharmony_ci	old = snd_ac97_read_cache(ac97, reg);
81862306a36Sopenharmony_ci	new = (old & ~mask) | value;
81962306a36Sopenharmony_ci	change = old != new;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	if (change) {
82262306a36Sopenharmony_ci		unsigned short extst = snd_ac97_read_cache(ac97, AC97_EXTENDED_STATUS);
82362306a36Sopenharmony_ci		snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); /* turn off */
82462306a36Sopenharmony_ci		change = snd_ac97_update_bits_nolock(ac97, reg, mask, value);
82562306a36Sopenharmony_ci		if (extst & AC97_EA_SPDIF)
82662306a36Sopenharmony_ci			snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); /* turn on again */
82762306a36Sopenharmony_ci	}
82862306a36Sopenharmony_ci	mutex_unlock(&ac97->reg_mutex);
82962306a36Sopenharmony_ci	return change;
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_spdif[5] = {
83362306a36Sopenharmony_ci	{
83462306a36Sopenharmony_ci		.access = SNDRV_CTL_ELEM_ACCESS_READ,
83562306a36Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
83662306a36Sopenharmony_ci		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
83762306a36Sopenharmony_ci		.info = snd_ac97_spdif_mask_info,
83862306a36Sopenharmony_ci		.get = snd_ac97_spdif_cmask_get,
83962306a36Sopenharmony_ci	},
84062306a36Sopenharmony_ci	{
84162306a36Sopenharmony_ci		.access = SNDRV_CTL_ELEM_ACCESS_READ,
84262306a36Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
84362306a36Sopenharmony_ci		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
84462306a36Sopenharmony_ci		.info = snd_ac97_spdif_mask_info,
84562306a36Sopenharmony_ci		.get = snd_ac97_spdif_pmask_get,
84662306a36Sopenharmony_ci	},
84762306a36Sopenharmony_ci	{
84862306a36Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
84962306a36Sopenharmony_ci		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
85062306a36Sopenharmony_ci		.info = snd_ac97_spdif_mask_info,
85162306a36Sopenharmony_ci		.get = snd_ac97_spdif_default_get,
85262306a36Sopenharmony_ci		.put = snd_ac97_spdif_default_put,
85362306a36Sopenharmony_ci	},
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),AC97_EXTENDED_STATUS, 2, 1, 0),
85662306a36Sopenharmony_ci	{
85762306a36Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
85862306a36Sopenharmony_ci		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA",
85962306a36Sopenharmony_ci		.info = snd_ac97_info_volsw,
86062306a36Sopenharmony_ci		.get = snd_ac97_get_volsw,
86162306a36Sopenharmony_ci		.put = snd_ac97_put_spsa,
86262306a36Sopenharmony_ci		.private_value = AC97_SINGLE_VALUE(AC97_EXTENDED_STATUS, 4, 3, 0)
86362306a36Sopenharmony_ci	},
86462306a36Sopenharmony_ci};
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci#define AD18XX_PCM_BITS(xname, codec, lshift, rshift, mask) \
86762306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_ad18xx_pcm_info_bits, \
86862306a36Sopenharmony_ci  .get = snd_ac97_ad18xx_pcm_get_bits, .put = snd_ac97_ad18xx_pcm_put_bits, \
86962306a36Sopenharmony_ci  .private_value = (codec) | ((lshift) << 8) | ((rshift) << 12) | ((mask) << 16) }
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_cistatic int snd_ac97_ad18xx_pcm_info_bits(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
87262306a36Sopenharmony_ci{
87362306a36Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
87462306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0x0f;
87562306a36Sopenharmony_ci	int lshift = (kcontrol->private_value >> 8) & 0x0f;
87662306a36Sopenharmony_ci	int rshift = (kcontrol->private_value >> 12) & 0x0f;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
87962306a36Sopenharmony_ci	if (lshift != rshift && (ac97->flags & AC97_STEREO_MUTES))
88062306a36Sopenharmony_ci		uinfo->count = 2;
88162306a36Sopenharmony_ci	else
88262306a36Sopenharmony_ci		uinfo->count = 1;
88362306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
88462306a36Sopenharmony_ci	uinfo->value.integer.max = mask;
88562306a36Sopenharmony_ci	return 0;
88662306a36Sopenharmony_ci}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_cistatic int snd_ac97_ad18xx_pcm_get_bits(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
89162306a36Sopenharmony_ci	int codec = kcontrol->private_value & 3;
89262306a36Sopenharmony_ci	int lshift = (kcontrol->private_value >> 8) & 0x0f;
89362306a36Sopenharmony_ci	int rshift = (kcontrol->private_value >> 12) & 0x0f;
89462306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = mask - ((ac97->spec.ad18xx.pcmreg[codec] >> lshift) & mask);
89762306a36Sopenharmony_ci	if (lshift != rshift && (ac97->flags & AC97_STEREO_MUTES))
89862306a36Sopenharmony_ci		ucontrol->value.integer.value[1] = mask - ((ac97->spec.ad18xx.pcmreg[codec] >> rshift) & mask);
89962306a36Sopenharmony_ci	return 0;
90062306a36Sopenharmony_ci}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_cistatic int snd_ac97_ad18xx_pcm_put_bits(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
90362306a36Sopenharmony_ci{
90462306a36Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
90562306a36Sopenharmony_ci	int codec = kcontrol->private_value & 3;
90662306a36Sopenharmony_ci	int lshift = (kcontrol->private_value >> 8) & 0x0f;
90762306a36Sopenharmony_ci	int rshift = (kcontrol->private_value >> 12) & 0x0f;
90862306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
90962306a36Sopenharmony_ci	unsigned short val, valmask;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	val = (mask - (ucontrol->value.integer.value[0] & mask)) << lshift;
91262306a36Sopenharmony_ci	valmask = mask << lshift;
91362306a36Sopenharmony_ci	if (lshift != rshift && (ac97->flags & AC97_STEREO_MUTES)) {
91462306a36Sopenharmony_ci		val |= (mask - (ucontrol->value.integer.value[1] & mask)) << rshift;
91562306a36Sopenharmony_ci		valmask |= mask << rshift;
91662306a36Sopenharmony_ci	}
91762306a36Sopenharmony_ci	return snd_ac97_ad18xx_update_pcm_bits(ac97, codec, valmask, val);
91862306a36Sopenharmony_ci}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci#define AD18XX_PCM_VOLUME(xname, codec) \
92162306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_ad18xx_pcm_info_volume, \
92262306a36Sopenharmony_ci  .get = snd_ac97_ad18xx_pcm_get_volume, .put = snd_ac97_ad18xx_pcm_put_volume, \
92362306a36Sopenharmony_ci  .private_value = codec }
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_cistatic int snd_ac97_ad18xx_pcm_info_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
92662306a36Sopenharmony_ci{
92762306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
92862306a36Sopenharmony_ci	uinfo->count = 2;
92962306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
93062306a36Sopenharmony_ci	uinfo->value.integer.max = 31;
93162306a36Sopenharmony_ci	return 0;
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_cistatic int snd_ac97_ad18xx_pcm_get_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
93762306a36Sopenharmony_ci	int codec = kcontrol->private_value & 3;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	mutex_lock(&ac97->page_mutex);
94062306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 8) & 31);
94162306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 0) & 31);
94262306a36Sopenharmony_ci	mutex_unlock(&ac97->page_mutex);
94362306a36Sopenharmony_ci	return 0;
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_cistatic int snd_ac97_ad18xx_pcm_put_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
94962306a36Sopenharmony_ci	int codec = kcontrol->private_value & 3;
95062306a36Sopenharmony_ci	unsigned short val1, val2;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	val1 = 31 - (ucontrol->value.integer.value[0] & 31);
95362306a36Sopenharmony_ci	val2 = 31 - (ucontrol->value.integer.value[1] & 31);
95462306a36Sopenharmony_ci	return snd_ac97_ad18xx_update_pcm_bits(ac97, codec, 0x1f1f, (val1 << 8) | val2);
95562306a36Sopenharmony_ci}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_ad18xx_pcm[2] = {
95862306a36Sopenharmony_ciAD18XX_PCM_BITS("PCM Playback Switch", 0, 15, 7, 1),
95962306a36Sopenharmony_ciAD18XX_PCM_VOLUME("PCM Playback Volume", 0)
96062306a36Sopenharmony_ci};
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_ad18xx_surround[2] = {
96362306a36Sopenharmony_ciAD18XX_PCM_BITS("Surround Playback Switch", 1, 15, 7, 1),
96462306a36Sopenharmony_ciAD18XX_PCM_VOLUME("Surround Playback Volume", 1)
96562306a36Sopenharmony_ci};
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_ad18xx_center[2] = {
96862306a36Sopenharmony_ciAD18XX_PCM_BITS("Center Playback Switch", 2, 15, 15, 1),
96962306a36Sopenharmony_ciAD18XX_PCM_BITS("Center Playback Volume", 2, 8, 8, 31)
97062306a36Sopenharmony_ci};
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_ad18xx_lfe[2] = {
97362306a36Sopenharmony_ciAD18XX_PCM_BITS("LFE Playback Switch", 2, 7, 7, 1),
97462306a36Sopenharmony_ciAD18XX_PCM_BITS("LFE Playback Volume", 2, 0, 0, 31)
97562306a36Sopenharmony_ci};
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci/*
97862306a36Sopenharmony_ci *
97962306a36Sopenharmony_ci */
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_cistatic void snd_ac97_powerdown(struct snd_ac97 *ac97);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_cistatic int snd_ac97_bus_free(struct snd_ac97_bus *bus)
98462306a36Sopenharmony_ci{
98562306a36Sopenharmony_ci	if (bus) {
98662306a36Sopenharmony_ci		snd_ac97_bus_proc_done(bus);
98762306a36Sopenharmony_ci		kfree(bus->pcms);
98862306a36Sopenharmony_ci		if (bus->private_free)
98962306a36Sopenharmony_ci			bus->private_free(bus);
99062306a36Sopenharmony_ci		kfree(bus);
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci	return 0;
99362306a36Sopenharmony_ci}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_cistatic int snd_ac97_bus_dev_free(struct snd_device *device)
99662306a36Sopenharmony_ci{
99762306a36Sopenharmony_ci	struct snd_ac97_bus *bus = device->device_data;
99862306a36Sopenharmony_ci	return snd_ac97_bus_free(bus);
99962306a36Sopenharmony_ci}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_cistatic int snd_ac97_free(struct snd_ac97 *ac97)
100262306a36Sopenharmony_ci{
100362306a36Sopenharmony_ci	if (ac97) {
100462306a36Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE
100562306a36Sopenharmony_ci		cancel_delayed_work_sync(&ac97->power_work);
100662306a36Sopenharmony_ci#endif
100762306a36Sopenharmony_ci		snd_ac97_proc_done(ac97);
100862306a36Sopenharmony_ci		if (ac97->bus)
100962306a36Sopenharmony_ci			ac97->bus->codec[ac97->num] = NULL;
101062306a36Sopenharmony_ci		if (ac97->private_free)
101162306a36Sopenharmony_ci			ac97->private_free(ac97);
101262306a36Sopenharmony_ci		kfree(ac97);
101362306a36Sopenharmony_ci	}
101462306a36Sopenharmony_ci	return 0;
101562306a36Sopenharmony_ci}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_cistatic int snd_ac97_dev_free(struct snd_device *device)
101862306a36Sopenharmony_ci{
101962306a36Sopenharmony_ci	struct snd_ac97 *ac97 = device->device_data;
102062306a36Sopenharmony_ci	snd_ac97_powerdown(ac97); /* for avoiding click noises during shut down */
102162306a36Sopenharmony_ci	return snd_ac97_free(ac97);
102262306a36Sopenharmony_ci}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_cistatic int snd_ac97_try_volume_mix(struct snd_ac97 * ac97, int reg)
102562306a36Sopenharmony_ci{
102662306a36Sopenharmony_ci	unsigned short val, mask = AC97_MUTE_MASK_MONO;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	if (! snd_ac97_valid_reg(ac97, reg))
102962306a36Sopenharmony_ci		return 0;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	switch (reg) {
103262306a36Sopenharmony_ci	case AC97_MASTER_TONE:
103362306a36Sopenharmony_ci		return ac97->caps & AC97_BC_BASS_TREBLE ? 1 : 0;
103462306a36Sopenharmony_ci	case AC97_HEADPHONE:
103562306a36Sopenharmony_ci		return ac97->caps & AC97_BC_HEADPHONE ? 1 : 0;
103662306a36Sopenharmony_ci	case AC97_REC_GAIN_MIC:
103762306a36Sopenharmony_ci		return ac97->caps & AC97_BC_DEDICATED_MIC ? 1 : 0;
103862306a36Sopenharmony_ci	case AC97_3D_CONTROL:
103962306a36Sopenharmony_ci		if (ac97->caps & AC97_BC_3D_TECH_ID_MASK) {
104062306a36Sopenharmony_ci			val = snd_ac97_read(ac97, reg);
104162306a36Sopenharmony_ci			/* if nonzero - fixed and we can't set it */
104262306a36Sopenharmony_ci			return val == 0;
104362306a36Sopenharmony_ci		}
104462306a36Sopenharmony_ci		return 0;
104562306a36Sopenharmony_ci	case AC97_CENTER_LFE_MASTER:	/* center */
104662306a36Sopenharmony_ci		if ((ac97->ext_id & AC97_EI_CDAC) == 0)
104762306a36Sopenharmony_ci			return 0;
104862306a36Sopenharmony_ci		break;
104962306a36Sopenharmony_ci	case AC97_CENTER_LFE_MASTER+1:	/* lfe */
105062306a36Sopenharmony_ci		if ((ac97->ext_id & AC97_EI_LDAC) == 0)
105162306a36Sopenharmony_ci			return 0;
105262306a36Sopenharmony_ci		reg = AC97_CENTER_LFE_MASTER;
105362306a36Sopenharmony_ci		mask = 0x0080;
105462306a36Sopenharmony_ci		break;
105562306a36Sopenharmony_ci	case AC97_SURROUND_MASTER:
105662306a36Sopenharmony_ci		if ((ac97->ext_id & AC97_EI_SDAC) == 0)
105762306a36Sopenharmony_ci			return 0;
105862306a36Sopenharmony_ci		break;
105962306a36Sopenharmony_ci	}
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	val = snd_ac97_read(ac97, reg);
106262306a36Sopenharmony_ci	if (!(val & mask)) {
106362306a36Sopenharmony_ci		/* nothing seems to be here - mute flag is not set */
106462306a36Sopenharmony_ci		/* try another test */
106562306a36Sopenharmony_ci		snd_ac97_write_cache(ac97, reg, val | mask);
106662306a36Sopenharmony_ci		val = snd_ac97_read(ac97, reg);
106762306a36Sopenharmony_ci		val = snd_ac97_read(ac97, reg);
106862306a36Sopenharmony_ci		if (!(val & mask))
106962306a36Sopenharmony_ci			return 0;	/* nothing here */
107062306a36Sopenharmony_ci	}
107162306a36Sopenharmony_ci	return 1;		/* success, useable */
107262306a36Sopenharmony_ci}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_cistatic void check_volume_resolution(struct snd_ac97 *ac97, int reg, unsigned char *lo_max, unsigned char *hi_max)
107562306a36Sopenharmony_ci{
107662306a36Sopenharmony_ci	unsigned short cbit[3] = { 0x20, 0x10, 0x01 };
107762306a36Sopenharmony_ci	unsigned char max[3] = { 63, 31, 15 };
107862306a36Sopenharmony_ci	int i;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	/* first look up the static resolution table */
108162306a36Sopenharmony_ci	if (ac97->res_table) {
108262306a36Sopenharmony_ci		const struct snd_ac97_res_table *tbl;
108362306a36Sopenharmony_ci		for (tbl = ac97->res_table; tbl->reg; tbl++) {
108462306a36Sopenharmony_ci			if (tbl->reg == reg) {
108562306a36Sopenharmony_ci				*lo_max = tbl->bits & 0xff;
108662306a36Sopenharmony_ci				*hi_max = (tbl->bits >> 8) & 0xff;
108762306a36Sopenharmony_ci				return;
108862306a36Sopenharmony_ci			}
108962306a36Sopenharmony_ci		}
109062306a36Sopenharmony_ci	}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	*lo_max = *hi_max = 0;
109362306a36Sopenharmony_ci	for (i = 0 ; i < ARRAY_SIZE(cbit); i++) {
109462306a36Sopenharmony_ci		unsigned short val;
109562306a36Sopenharmony_ci		snd_ac97_write(
109662306a36Sopenharmony_ci			ac97, reg,
109762306a36Sopenharmony_ci			AC97_MUTE_MASK_STEREO | cbit[i] | (cbit[i] << 8)
109862306a36Sopenharmony_ci		);
109962306a36Sopenharmony_ci		/* Do the read twice due to buffers on some ac97 codecs.
110062306a36Sopenharmony_ci		 * e.g. The STAC9704 returns exactly what you wrote to the register
110162306a36Sopenharmony_ci		 * if you read it immediately. This causes the detect routine to fail.
110262306a36Sopenharmony_ci		 */
110362306a36Sopenharmony_ci		val = snd_ac97_read(ac97, reg);
110462306a36Sopenharmony_ci		val = snd_ac97_read(ac97, reg);
110562306a36Sopenharmony_ci		if (! *lo_max && (val & 0x7f) == cbit[i])
110662306a36Sopenharmony_ci			*lo_max = max[i];
110762306a36Sopenharmony_ci		if (! *hi_max && ((val >> 8) & 0x7f) == cbit[i])
110862306a36Sopenharmony_ci			*hi_max = max[i];
110962306a36Sopenharmony_ci		if (*lo_max && *hi_max)
111062306a36Sopenharmony_ci			break;
111162306a36Sopenharmony_ci	}
111262306a36Sopenharmony_ci}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_cistatic int snd_ac97_try_bit(struct snd_ac97 * ac97, int reg, int bit)
111562306a36Sopenharmony_ci{
111662306a36Sopenharmony_ci	unsigned short mask, val, orig, res;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	mask = 1 << bit;
111962306a36Sopenharmony_ci	orig = snd_ac97_read(ac97, reg);
112062306a36Sopenharmony_ci	val = orig ^ mask;
112162306a36Sopenharmony_ci	snd_ac97_write(ac97, reg, val);
112262306a36Sopenharmony_ci	res = snd_ac97_read(ac97, reg);
112362306a36Sopenharmony_ci	snd_ac97_write_cache(ac97, reg, orig);
112462306a36Sopenharmony_ci	return res == val;
112562306a36Sopenharmony_ci}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci/* check the volume resolution of center/lfe */
112862306a36Sopenharmony_cistatic void snd_ac97_change_volume_params2(struct snd_ac97 * ac97, int reg, int shift, unsigned char *max)
112962306a36Sopenharmony_ci{
113062306a36Sopenharmony_ci	unsigned short val, val1;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	*max = 63;
113362306a36Sopenharmony_ci	val = AC97_MUTE_MASK_STEREO | (0x20 << shift);
113462306a36Sopenharmony_ci	snd_ac97_write(ac97, reg, val);
113562306a36Sopenharmony_ci	val1 = snd_ac97_read(ac97, reg);
113662306a36Sopenharmony_ci	if (val != val1) {
113762306a36Sopenharmony_ci		*max = 31;
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci	/* reset volume to zero */
114062306a36Sopenharmony_ci	snd_ac97_write_cache(ac97, reg, AC97_MUTE_MASK_STEREO);
114162306a36Sopenharmony_ci}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_cistatic inline int printable(unsigned int x)
114462306a36Sopenharmony_ci{
114562306a36Sopenharmony_ci	x &= 0xff;
114662306a36Sopenharmony_ci	if (x < ' ' || x >= 0x71) {
114762306a36Sopenharmony_ci		if (x <= 0x89)
114862306a36Sopenharmony_ci			return x - 0x71 + 'A';
114962306a36Sopenharmony_ci		return '?';
115062306a36Sopenharmony_ci	}
115162306a36Sopenharmony_ci	return x;
115262306a36Sopenharmony_ci}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_cistatic struct snd_kcontrol *snd_ac97_cnew(const struct snd_kcontrol_new *_template,
115562306a36Sopenharmony_ci					  struct snd_ac97 * ac97)
115662306a36Sopenharmony_ci{
115762306a36Sopenharmony_ci	struct snd_kcontrol_new template;
115862306a36Sopenharmony_ci	memcpy(&template, _template, sizeof(template));
115962306a36Sopenharmony_ci	template.index = ac97->num;
116062306a36Sopenharmony_ci	return snd_ctl_new1(&template, ac97);
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci/*
116462306a36Sopenharmony_ci * create mute switch(es) for normal stereo controls
116562306a36Sopenharmony_ci */
116662306a36Sopenharmony_cistatic int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg,
116762306a36Sopenharmony_ci				     int check_stereo, int check_amix,
116862306a36Sopenharmony_ci				     struct snd_ac97 *ac97)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
117162306a36Sopenharmony_ci	int err;
117262306a36Sopenharmony_ci	unsigned short val, val1, mute_mask;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	if (! snd_ac97_valid_reg(ac97, reg))
117562306a36Sopenharmony_ci		return 0;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	mute_mask = AC97_MUTE_MASK_MONO;
117862306a36Sopenharmony_ci	val = snd_ac97_read(ac97, reg);
117962306a36Sopenharmony_ci	if (check_stereo || (ac97->flags & AC97_STEREO_MUTES)) {
118062306a36Sopenharmony_ci		/* check whether both mute bits work */
118162306a36Sopenharmony_ci		val1 = val | AC97_MUTE_MASK_STEREO;
118262306a36Sopenharmony_ci		snd_ac97_write(ac97, reg, val1);
118362306a36Sopenharmony_ci		if (val1 == snd_ac97_read(ac97, reg))
118462306a36Sopenharmony_ci			mute_mask = AC97_MUTE_MASK_STEREO;
118562306a36Sopenharmony_ci	}
118662306a36Sopenharmony_ci	if (mute_mask == AC97_MUTE_MASK_STEREO) {
118762306a36Sopenharmony_ci		struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 15, 7, 1, 1);
118862306a36Sopenharmony_ci		if (check_amix)
118962306a36Sopenharmony_ci			tmp.private_value |= (1 << 30);
119062306a36Sopenharmony_ci		tmp.index = ac97->num;
119162306a36Sopenharmony_ci		kctl = snd_ctl_new1(&tmp, ac97);
119262306a36Sopenharmony_ci	} else {
119362306a36Sopenharmony_ci		struct snd_kcontrol_new tmp = AC97_SINGLE(name, reg, 15, 1, 1);
119462306a36Sopenharmony_ci		if (check_amix)
119562306a36Sopenharmony_ci			tmp.private_value |= (1 << 30);
119662306a36Sopenharmony_ci		tmp.index = ac97->num;
119762306a36Sopenharmony_ci		kctl = snd_ctl_new1(&tmp, ac97);
119862306a36Sopenharmony_ci	}
119962306a36Sopenharmony_ci	err = snd_ctl_add(card, kctl);
120062306a36Sopenharmony_ci	if (err < 0)
120162306a36Sopenharmony_ci		return err;
120262306a36Sopenharmony_ci	/* mute as default */
120362306a36Sopenharmony_ci	snd_ac97_write_cache(ac97, reg, val | mute_mask);
120462306a36Sopenharmony_ci	return 0;
120562306a36Sopenharmony_ci}
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci/*
120862306a36Sopenharmony_ci * set dB information
120962306a36Sopenharmony_ci */
121062306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
121162306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
121262306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
121362306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
121462306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_cistatic const unsigned int *find_db_scale(unsigned int maxval)
121762306a36Sopenharmony_ci{
121862306a36Sopenharmony_ci	switch (maxval) {
121962306a36Sopenharmony_ci	case 0x0f: return db_scale_4bit;
122062306a36Sopenharmony_ci	case 0x1f: return db_scale_5bit;
122162306a36Sopenharmony_ci	case 0x3f: return db_scale_6bit;
122262306a36Sopenharmony_ci	}
122362306a36Sopenharmony_ci	return NULL;
122462306a36Sopenharmony_ci}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_cistatic void set_tlv_db_scale(struct snd_kcontrol *kctl, const unsigned int *tlv)
122762306a36Sopenharmony_ci{
122862306a36Sopenharmony_ci	kctl->tlv.p = tlv;
122962306a36Sopenharmony_ci	if (tlv)
123062306a36Sopenharmony_ci		kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
123162306a36Sopenharmony_ci}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci/*
123462306a36Sopenharmony_ci * create a volume for normal stereo/mono controls
123562306a36Sopenharmony_ci */
123662306a36Sopenharmony_cistatic int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigned int lo_max,
123762306a36Sopenharmony_ci			     unsigned int hi_max, struct snd_ac97 *ac97)
123862306a36Sopenharmony_ci{
123962306a36Sopenharmony_ci	int err;
124062306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	if (! snd_ac97_valid_reg(ac97, reg))
124362306a36Sopenharmony_ci		return 0;
124462306a36Sopenharmony_ci	if (hi_max) {
124562306a36Sopenharmony_ci		/* invert */
124662306a36Sopenharmony_ci		struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 8, 0, lo_max, 1);
124762306a36Sopenharmony_ci		tmp.index = ac97->num;
124862306a36Sopenharmony_ci		kctl = snd_ctl_new1(&tmp, ac97);
124962306a36Sopenharmony_ci	} else {
125062306a36Sopenharmony_ci		/* invert */
125162306a36Sopenharmony_ci		struct snd_kcontrol_new tmp = AC97_SINGLE(name, reg, 0, lo_max, 1);
125262306a36Sopenharmony_ci		tmp.index = ac97->num;
125362306a36Sopenharmony_ci		kctl = snd_ctl_new1(&tmp, ac97);
125462306a36Sopenharmony_ci	}
125562306a36Sopenharmony_ci	if (!kctl)
125662306a36Sopenharmony_ci		return -ENOMEM;
125762306a36Sopenharmony_ci	if (reg >= AC97_PHONE && reg <= AC97_PCM)
125862306a36Sopenharmony_ci		set_tlv_db_scale(kctl, db_scale_5bit_12db_max);
125962306a36Sopenharmony_ci	else
126062306a36Sopenharmony_ci		set_tlv_db_scale(kctl, find_db_scale(lo_max));
126162306a36Sopenharmony_ci	err = snd_ctl_add(card, kctl);
126262306a36Sopenharmony_ci	if (err < 0)
126362306a36Sopenharmony_ci		return err;
126462306a36Sopenharmony_ci	snd_ac97_write_cache(
126562306a36Sopenharmony_ci		ac97, reg,
126662306a36Sopenharmony_ci		(snd_ac97_read(ac97, reg) & AC97_MUTE_MASK_STEREO)
126762306a36Sopenharmony_ci		| lo_max | (hi_max << 8)
126862306a36Sopenharmony_ci	);
126962306a36Sopenharmony_ci	return 0;
127062306a36Sopenharmony_ci}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci/*
127362306a36Sopenharmony_ci * create a mute-switch and a volume for normal stereo/mono controls
127462306a36Sopenharmony_ci */
127562306a36Sopenharmony_cistatic int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx,
127662306a36Sopenharmony_ci				    int reg, int check_stereo, int check_amix,
127762306a36Sopenharmony_ci				    struct snd_ac97 *ac97)
127862306a36Sopenharmony_ci{
127962306a36Sopenharmony_ci	int err;
128062306a36Sopenharmony_ci	char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
128162306a36Sopenharmony_ci	unsigned char lo_max, hi_max;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	if (! snd_ac97_valid_reg(ac97, reg))
128462306a36Sopenharmony_ci		return 0;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	if (snd_ac97_try_bit(ac97, reg, 15)) {
128762306a36Sopenharmony_ci		sprintf(name, "%s Switch", pfx);
128862306a36Sopenharmony_ci		err = snd_ac97_cmute_new_stereo(card, name, reg,
128962306a36Sopenharmony_ci						check_stereo, check_amix,
129062306a36Sopenharmony_ci						ac97);
129162306a36Sopenharmony_ci		if (err < 0)
129262306a36Sopenharmony_ci			return err;
129362306a36Sopenharmony_ci	}
129462306a36Sopenharmony_ci	check_volume_resolution(ac97, reg, &lo_max, &hi_max);
129562306a36Sopenharmony_ci	if (lo_max) {
129662306a36Sopenharmony_ci		sprintf(name, "%s Volume", pfx);
129762306a36Sopenharmony_ci		err = snd_ac97_cvol_new(card, name, reg, lo_max, hi_max, ac97);
129862306a36Sopenharmony_ci		if (err < 0)
129962306a36Sopenharmony_ci			return err;
130062306a36Sopenharmony_ci	}
130162306a36Sopenharmony_ci	return 0;
130262306a36Sopenharmony_ci}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci#define snd_ac97_cmix_new(card, pfx, reg, acheck, ac97) \
130562306a36Sopenharmony_ci	snd_ac97_cmix_new_stereo(card, pfx, reg, 0, acheck, ac97)
130662306a36Sopenharmony_ci#define snd_ac97_cmute_new(card, name, reg, acheck, ac97) \
130762306a36Sopenharmony_ci	snd_ac97_cmute_new_stereo(card, name, reg, 0, acheck, ac97)
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_cistatic unsigned int snd_ac97_determine_spdif_rates(struct snd_ac97 *ac97);
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_cistatic int snd_ac97_mixer_build(struct snd_ac97 * ac97)
131262306a36Sopenharmony_ci{
131362306a36Sopenharmony_ci	struct snd_card *card = ac97->bus->card;
131462306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
131562306a36Sopenharmony_ci	int err;
131662306a36Sopenharmony_ci	unsigned int idx;
131762306a36Sopenharmony_ci	unsigned char max;
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	/* build master controls */
132062306a36Sopenharmony_ci	/* AD claims to remove this control from AD1887, although spec v2.2 does not allow this */
132162306a36Sopenharmony_ci	if (snd_ac97_try_volume_mix(ac97, AC97_MASTER)) {
132262306a36Sopenharmony_ci		if (ac97->flags & AC97_HAS_NO_MASTER_VOL)
132362306a36Sopenharmony_ci			err = snd_ac97_cmute_new(card, "Master Playback Switch",
132462306a36Sopenharmony_ci						 AC97_MASTER, 0, ac97);
132562306a36Sopenharmony_ci		else
132662306a36Sopenharmony_ci			err = snd_ac97_cmix_new(card, "Master Playback",
132762306a36Sopenharmony_ci						AC97_MASTER, 0, ac97);
132862306a36Sopenharmony_ci		if (err < 0)
132962306a36Sopenharmony_ci			return err;
133062306a36Sopenharmony_ci	}
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	ac97->regs[AC97_CENTER_LFE_MASTER] = AC97_MUTE_MASK_STEREO;
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	/* build center controls */
133562306a36Sopenharmony_ci	if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER))
133662306a36Sopenharmony_ci		&& !(ac97->flags & AC97_AD_MULTI)) {
133762306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_center[0], ac97));
133862306a36Sopenharmony_ci		if (err < 0)
133962306a36Sopenharmony_ci			return err;
134062306a36Sopenharmony_ci		err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_center[1], ac97));
134162306a36Sopenharmony_ci		if (err < 0)
134262306a36Sopenharmony_ci			return err;
134362306a36Sopenharmony_ci		snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 0, &max);
134462306a36Sopenharmony_ci		kctl->private_value &= ~(0xff << 16);
134562306a36Sopenharmony_ci		kctl->private_value |= (int)max << 16;
134662306a36Sopenharmony_ci		set_tlv_db_scale(kctl, find_db_scale(max));
134762306a36Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max);
134862306a36Sopenharmony_ci	}
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	/* build LFE controls */
135162306a36Sopenharmony_ci	if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER+1))
135262306a36Sopenharmony_ci		&& !(ac97->flags & AC97_AD_MULTI)) {
135362306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_lfe[0], ac97));
135462306a36Sopenharmony_ci		if (err < 0)
135562306a36Sopenharmony_ci			return err;
135662306a36Sopenharmony_ci		err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_lfe[1], ac97));
135762306a36Sopenharmony_ci		if (err < 0)
135862306a36Sopenharmony_ci			return err;
135962306a36Sopenharmony_ci		snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 8, &max);
136062306a36Sopenharmony_ci		kctl->private_value &= ~(0xff << 16);
136162306a36Sopenharmony_ci		kctl->private_value |= (int)max << 16;
136262306a36Sopenharmony_ci		set_tlv_db_scale(kctl, find_db_scale(max));
136362306a36Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max << 8);
136462306a36Sopenharmony_ci	}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	/* build surround controls */
136762306a36Sopenharmony_ci	if ((snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER))
136862306a36Sopenharmony_ci		&& !(ac97->flags & AC97_AD_MULTI)) {
136962306a36Sopenharmony_ci		/* Surround Master (0x38) is with stereo mutes */
137062306a36Sopenharmony_ci		err = snd_ac97_cmix_new_stereo(card, "Surround Playback",
137162306a36Sopenharmony_ci					       AC97_SURROUND_MASTER, 1, 0,
137262306a36Sopenharmony_ci					       ac97);
137362306a36Sopenharmony_ci		if (err < 0)
137462306a36Sopenharmony_ci			return err;
137562306a36Sopenharmony_ci	}
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	/* build headphone controls */
137862306a36Sopenharmony_ci	if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE)) {
137962306a36Sopenharmony_ci		err = snd_ac97_cmix_new(card, "Headphone Playback",
138062306a36Sopenharmony_ci					AC97_HEADPHONE, 0, ac97);
138162306a36Sopenharmony_ci		if (err < 0)
138262306a36Sopenharmony_ci			return err;
138362306a36Sopenharmony_ci	}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	/* build master mono controls */
138662306a36Sopenharmony_ci	if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_MONO)) {
138762306a36Sopenharmony_ci		err = snd_ac97_cmix_new(card, "Master Mono Playback",
138862306a36Sopenharmony_ci					AC97_MASTER_MONO, 0, ac97);
138962306a36Sopenharmony_ci		if (err < 0)
139062306a36Sopenharmony_ci			return err;
139162306a36Sopenharmony_ci	}
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	/* build master tone controls */
139462306a36Sopenharmony_ci	if (!(ac97->flags & AC97_HAS_NO_TONE)) {
139562306a36Sopenharmony_ci		if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_TONE)) {
139662306a36Sopenharmony_ci			for (idx = 0; idx < 2; idx++) {
139762306a36Sopenharmony_ci				kctl = snd_ac97_cnew(&snd_ac97_controls_tone[idx], ac97);
139862306a36Sopenharmony_ci				err = snd_ctl_add(card, kctl);
139962306a36Sopenharmony_ci				if (err < 0)
140062306a36Sopenharmony_ci					return err;
140162306a36Sopenharmony_ci				if (ac97->id == AC97_ID_YMF743 ||
140262306a36Sopenharmony_ci				    ac97->id == AC97_ID_YMF753) {
140362306a36Sopenharmony_ci					kctl->private_value &= ~(0xff << 16);
140462306a36Sopenharmony_ci					kctl->private_value |= 7 << 16;
140562306a36Sopenharmony_ci				}
140662306a36Sopenharmony_ci			}
140762306a36Sopenharmony_ci			snd_ac97_write_cache(ac97, AC97_MASTER_TONE, 0x0f0f);
140862306a36Sopenharmony_ci		}
140962306a36Sopenharmony_ci	}
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	/* build Beep controls */
141262306a36Sopenharmony_ci	if (!(ac97->flags & AC97_HAS_NO_PC_BEEP) &&
141362306a36Sopenharmony_ci		((ac97->flags & AC97_HAS_PC_BEEP) ||
141462306a36Sopenharmony_ci	    snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) {
141562306a36Sopenharmony_ci		for (idx = 0; idx < 2; idx++) {
141662306a36Sopenharmony_ci			kctl = snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97);
141762306a36Sopenharmony_ci			err = snd_ctl_add(card, kctl);
141862306a36Sopenharmony_ci			if (err < 0)
141962306a36Sopenharmony_ci				return err;
142062306a36Sopenharmony_ci		}
142162306a36Sopenharmony_ci		set_tlv_db_scale(kctl, db_scale_4bit);
142262306a36Sopenharmony_ci		snd_ac97_write_cache(
142362306a36Sopenharmony_ci			ac97,
142462306a36Sopenharmony_ci			AC97_PC_BEEP,
142562306a36Sopenharmony_ci			(snd_ac97_read(ac97, AC97_PC_BEEP)
142662306a36Sopenharmony_ci				| AC97_MUTE_MASK_MONO | 0x001e)
142762306a36Sopenharmony_ci		);
142862306a36Sopenharmony_ci	}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	/* build Phone controls */
143162306a36Sopenharmony_ci	if (!(ac97->flags & AC97_HAS_NO_PHONE)) {
143262306a36Sopenharmony_ci		if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) {
143362306a36Sopenharmony_ci			err = snd_ac97_cmix_new(card, "Phone Playback",
143462306a36Sopenharmony_ci						AC97_PHONE, 1, ac97);
143562306a36Sopenharmony_ci			if (err < 0)
143662306a36Sopenharmony_ci				return err;
143762306a36Sopenharmony_ci		}
143862306a36Sopenharmony_ci	}
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	/* build MIC controls */
144162306a36Sopenharmony_ci	if (!(ac97->flags & AC97_HAS_NO_MIC)) {
144262306a36Sopenharmony_ci		if (snd_ac97_try_volume_mix(ac97, AC97_MIC)) {
144362306a36Sopenharmony_ci			err = snd_ac97_cmix_new(card, "Mic Playback",
144462306a36Sopenharmony_ci						AC97_MIC, 1, ac97);
144562306a36Sopenharmony_ci			if (err < 0)
144662306a36Sopenharmony_ci				return err;
144762306a36Sopenharmony_ci			err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_boost, ac97));
144862306a36Sopenharmony_ci			if (err < 0)
144962306a36Sopenharmony_ci				return err;
145062306a36Sopenharmony_ci		}
145162306a36Sopenharmony_ci	}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	/* build Line controls */
145462306a36Sopenharmony_ci	if (snd_ac97_try_volume_mix(ac97, AC97_LINE)) {
145562306a36Sopenharmony_ci		err = snd_ac97_cmix_new(card, "Line Playback",
145662306a36Sopenharmony_ci					AC97_LINE, 1, ac97);
145762306a36Sopenharmony_ci		if (err < 0)
145862306a36Sopenharmony_ci			return err;
145962306a36Sopenharmony_ci	}
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	/* build CD controls */
146262306a36Sopenharmony_ci	if (!(ac97->flags & AC97_HAS_NO_CD)) {
146362306a36Sopenharmony_ci		if (snd_ac97_try_volume_mix(ac97, AC97_CD)) {
146462306a36Sopenharmony_ci			err = snd_ac97_cmix_new(card, "CD Playback",
146562306a36Sopenharmony_ci						AC97_CD, 1, ac97);
146662306a36Sopenharmony_ci			if (err < 0)
146762306a36Sopenharmony_ci				return err;
146862306a36Sopenharmony_ci		}
146962306a36Sopenharmony_ci	}
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	/* build Video controls */
147262306a36Sopenharmony_ci	if (!(ac97->flags & AC97_HAS_NO_VIDEO)) {
147362306a36Sopenharmony_ci		if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) {
147462306a36Sopenharmony_ci			err = snd_ac97_cmix_new(card, "Video Playback",
147562306a36Sopenharmony_ci						AC97_VIDEO, 1, ac97);
147662306a36Sopenharmony_ci			if (err < 0)
147762306a36Sopenharmony_ci				return err;
147862306a36Sopenharmony_ci		}
147962306a36Sopenharmony_ci	}
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	/* build Aux controls */
148262306a36Sopenharmony_ci	if (!(ac97->flags & AC97_HAS_NO_AUX)) {
148362306a36Sopenharmony_ci		if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) {
148462306a36Sopenharmony_ci			err = snd_ac97_cmix_new(card, "Aux Playback",
148562306a36Sopenharmony_ci						AC97_AUX, 1, ac97);
148662306a36Sopenharmony_ci			if (err < 0)
148762306a36Sopenharmony_ci				return err;
148862306a36Sopenharmony_ci		}
148962306a36Sopenharmony_ci	}
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	/* build PCM controls */
149262306a36Sopenharmony_ci	if (ac97->flags & AC97_AD_MULTI) {
149362306a36Sopenharmony_ci		unsigned short init_val;
149462306a36Sopenharmony_ci		if (ac97->flags & AC97_STEREO_MUTES)
149562306a36Sopenharmony_ci			init_val = 0x9f9f;
149662306a36Sopenharmony_ci		else
149762306a36Sopenharmony_ci			init_val = 0x9f1f;
149862306a36Sopenharmony_ci		for (idx = 0; idx < 2; idx++) {
149962306a36Sopenharmony_ci			kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97);
150062306a36Sopenharmony_ci			err = snd_ctl_add(card, kctl);
150162306a36Sopenharmony_ci			if (err < 0)
150262306a36Sopenharmony_ci				return err;
150362306a36Sopenharmony_ci		}
150462306a36Sopenharmony_ci		set_tlv_db_scale(kctl, db_scale_5bit);
150562306a36Sopenharmony_ci		ac97->spec.ad18xx.pcmreg[0] = init_val;
150662306a36Sopenharmony_ci		if (ac97->scaps & AC97_SCAP_SURROUND_DAC) {
150762306a36Sopenharmony_ci			for (idx = 0; idx < 2; idx++) {
150862306a36Sopenharmony_ci				kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97);
150962306a36Sopenharmony_ci				err = snd_ctl_add(card, kctl);
151062306a36Sopenharmony_ci				if (err < 0)
151162306a36Sopenharmony_ci					return err;
151262306a36Sopenharmony_ci			}
151362306a36Sopenharmony_ci			set_tlv_db_scale(kctl, db_scale_5bit);
151462306a36Sopenharmony_ci			ac97->spec.ad18xx.pcmreg[1] = init_val;
151562306a36Sopenharmony_ci		}
151662306a36Sopenharmony_ci		if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) {
151762306a36Sopenharmony_ci			for (idx = 0; idx < 2; idx++) {
151862306a36Sopenharmony_ci				kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97);
151962306a36Sopenharmony_ci				err = snd_ctl_add(card, kctl);
152062306a36Sopenharmony_ci				if (err < 0)
152162306a36Sopenharmony_ci					return err;
152262306a36Sopenharmony_ci			}
152362306a36Sopenharmony_ci			set_tlv_db_scale(kctl, db_scale_5bit);
152462306a36Sopenharmony_ci			for (idx = 0; idx < 2; idx++) {
152562306a36Sopenharmony_ci				kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[idx], ac97);
152662306a36Sopenharmony_ci				err = snd_ctl_add(card, kctl);
152762306a36Sopenharmony_ci				if (err < 0)
152862306a36Sopenharmony_ci					return err;
152962306a36Sopenharmony_ci			}
153062306a36Sopenharmony_ci			set_tlv_db_scale(kctl, db_scale_5bit);
153162306a36Sopenharmony_ci			ac97->spec.ad18xx.pcmreg[2] = init_val;
153262306a36Sopenharmony_ci		}
153362306a36Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_PCM, init_val);
153462306a36Sopenharmony_ci	} else {
153562306a36Sopenharmony_ci		if (!(ac97->flags & AC97_HAS_NO_STD_PCM)) {
153662306a36Sopenharmony_ci			if (ac97->flags & AC97_HAS_NO_PCM_VOL)
153762306a36Sopenharmony_ci				err = snd_ac97_cmute_new(card,
153862306a36Sopenharmony_ci							 "PCM Playback Switch",
153962306a36Sopenharmony_ci							 AC97_PCM, 0, ac97);
154062306a36Sopenharmony_ci			else
154162306a36Sopenharmony_ci				err = snd_ac97_cmix_new(card, "PCM Playback",
154262306a36Sopenharmony_ci							AC97_PCM, 0, ac97);
154362306a36Sopenharmony_ci			if (err < 0)
154462306a36Sopenharmony_ci				return err;
154562306a36Sopenharmony_ci		}
154662306a36Sopenharmony_ci	}
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	/* build Capture controls */
154962306a36Sopenharmony_ci	if (!(ac97->flags & AC97_HAS_NO_REC_GAIN)) {
155062306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97));
155162306a36Sopenharmony_ci		if (err < 0)
155262306a36Sopenharmony_ci			return err;
155362306a36Sopenharmony_ci		if (snd_ac97_try_bit(ac97, AC97_REC_GAIN, 15)) {
155462306a36Sopenharmony_ci			err = snd_ac97_cmute_new(card, "Capture Switch",
155562306a36Sopenharmony_ci						 AC97_REC_GAIN, 0, ac97);
155662306a36Sopenharmony_ci			if (err < 0)
155762306a36Sopenharmony_ci				return err;
155862306a36Sopenharmony_ci		}
155962306a36Sopenharmony_ci		kctl = snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97);
156062306a36Sopenharmony_ci		err = snd_ctl_add(card, kctl);
156162306a36Sopenharmony_ci		if (err < 0)
156262306a36Sopenharmony_ci			return err;
156362306a36Sopenharmony_ci		set_tlv_db_scale(kctl, db_scale_rec_gain);
156462306a36Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000);
156562306a36Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x0000);
156662306a36Sopenharmony_ci	}
156762306a36Sopenharmony_ci	/* build MIC Capture controls */
156862306a36Sopenharmony_ci	if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) {
156962306a36Sopenharmony_ci		for (idx = 0; idx < 2; idx++) {
157062306a36Sopenharmony_ci			kctl = snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97);
157162306a36Sopenharmony_ci			err = snd_ctl_add(card, kctl);
157262306a36Sopenharmony_ci			if (err < 0)
157362306a36Sopenharmony_ci				return err;
157462306a36Sopenharmony_ci		}
157562306a36Sopenharmony_ci		set_tlv_db_scale(kctl, db_scale_rec_gain);
157662306a36Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_REC_GAIN_MIC, 0x0000);
157762306a36Sopenharmony_ci	}
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	/* build PCM out path & mute control */
158062306a36Sopenharmony_ci	if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 15)) {
158162306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_PCM_OUT], ac97));
158262306a36Sopenharmony_ci		if (err < 0)
158362306a36Sopenharmony_ci			return err;
158462306a36Sopenharmony_ci	}
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	/* build Simulated Stereo Enhancement control */
158762306a36Sopenharmony_ci	if (ac97->caps & AC97_BC_SIM_STEREO) {
158862306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_STEREO_ENHANCEMENT], ac97));
158962306a36Sopenharmony_ci		if (err < 0)
159062306a36Sopenharmony_ci			return err;
159162306a36Sopenharmony_ci	}
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	/* build 3D Stereo Enhancement control */
159462306a36Sopenharmony_ci	if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 13)) {
159562306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_3D], ac97));
159662306a36Sopenharmony_ci		if (err < 0)
159762306a36Sopenharmony_ci			return err;
159862306a36Sopenharmony_ci	}
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	/* build Loudness control */
160162306a36Sopenharmony_ci	if (ac97->caps & AC97_BC_LOUDNESS) {
160262306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOUDNESS], ac97));
160362306a36Sopenharmony_ci		if (err < 0)
160462306a36Sopenharmony_ci			return err;
160562306a36Sopenharmony_ci	}
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	/* build Mono output select control */
160862306a36Sopenharmony_ci	if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 9)) {
160962306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MONO], ac97));
161062306a36Sopenharmony_ci		if (err < 0)
161162306a36Sopenharmony_ci			return err;
161262306a36Sopenharmony_ci	}
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	/* build Mic select control */
161562306a36Sopenharmony_ci	if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 8)) {
161662306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MIC], ac97));
161762306a36Sopenharmony_ci		if (err < 0)
161862306a36Sopenharmony_ci			return err;
161962306a36Sopenharmony_ci	}
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	/* build ADC/DAC loopback control */
162262306a36Sopenharmony_ci	if (enable_loopback && snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 7)) {
162362306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOOPBACK], ac97));
162462306a36Sopenharmony_ci		if (err < 0)
162562306a36Sopenharmony_ci			return err;
162662306a36Sopenharmony_ci	}
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_GENERAL_PURPOSE, ~AC97_GP_DRSS_MASK, 0x0000);
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	/* build 3D controls */
163162306a36Sopenharmony_ci	if (ac97->build_ops->build_3d) {
163262306a36Sopenharmony_ci		ac97->build_ops->build_3d(ac97);
163362306a36Sopenharmony_ci	} else {
163462306a36Sopenharmony_ci		if (snd_ac97_try_volume_mix(ac97, AC97_3D_CONTROL)) {
163562306a36Sopenharmony_ci			unsigned short val;
163662306a36Sopenharmony_ci			val = 0x0707;
163762306a36Sopenharmony_ci			snd_ac97_write(ac97, AC97_3D_CONTROL, val);
163862306a36Sopenharmony_ci			val = snd_ac97_read(ac97, AC97_3D_CONTROL);
163962306a36Sopenharmony_ci			val = val == 0x0606;
164062306a36Sopenharmony_ci			kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97);
164162306a36Sopenharmony_ci			err = snd_ctl_add(card, kctl);
164262306a36Sopenharmony_ci			if (err < 0)
164362306a36Sopenharmony_ci				return err;
164462306a36Sopenharmony_ci			if (val)
164562306a36Sopenharmony_ci				kctl->private_value = AC97_3D_CONTROL | (9 << 8) | (7 << 16);
164662306a36Sopenharmony_ci			kctl = snd_ac97_cnew(&snd_ac97_controls_3d[1], ac97);
164762306a36Sopenharmony_ci			err = snd_ctl_add(card, kctl);
164862306a36Sopenharmony_ci			if (err < 0)
164962306a36Sopenharmony_ci				return err;
165062306a36Sopenharmony_ci			if (val)
165162306a36Sopenharmony_ci				kctl->private_value = AC97_3D_CONTROL | (1 << 8) | (7 << 16);
165262306a36Sopenharmony_ci			snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
165362306a36Sopenharmony_ci		}
165462306a36Sopenharmony_ci	}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	/* build S/PDIF controls */
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci	/* Hack for ASUS P5P800-VM, which does not indicate S/PDIF capability */
165962306a36Sopenharmony_ci	if (ac97->subsystem_vendor == 0x1043 &&
166062306a36Sopenharmony_ci	    ac97->subsystem_device == 0x810f)
166162306a36Sopenharmony_ci		ac97->ext_id |= AC97_EI_SPDIF;
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	if ((ac97->ext_id & AC97_EI_SPDIF) && !(ac97->scaps & AC97_SCAP_NO_SPDIF)) {
166462306a36Sopenharmony_ci		if (ac97->build_ops->build_spdif) {
166562306a36Sopenharmony_ci			err = ac97->build_ops->build_spdif(ac97);
166662306a36Sopenharmony_ci			if (err < 0)
166762306a36Sopenharmony_ci				return err;
166862306a36Sopenharmony_ci		} else {
166962306a36Sopenharmony_ci			for (idx = 0; idx < 5; idx++) {
167062306a36Sopenharmony_ci				err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97));
167162306a36Sopenharmony_ci				if (err < 0)
167262306a36Sopenharmony_ci					return err;
167362306a36Sopenharmony_ci			}
167462306a36Sopenharmony_ci			if (ac97->build_ops->build_post_spdif) {
167562306a36Sopenharmony_ci				err = ac97->build_ops->build_post_spdif(ac97);
167662306a36Sopenharmony_ci				if (err < 0)
167762306a36Sopenharmony_ci					return err;
167862306a36Sopenharmony_ci			}
167962306a36Sopenharmony_ci			/* set default PCM S/PDIF params */
168062306a36Sopenharmony_ci			/* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */
168162306a36Sopenharmony_ci			snd_ac97_write_cache(ac97, AC97_SPDIF, 0x2a20);
168262306a36Sopenharmony_ci			ac97->rates[AC97_RATES_SPDIF] = snd_ac97_determine_spdif_rates(ac97);
168362306a36Sopenharmony_ci		}
168462306a36Sopenharmony_ci		ac97->spdif_status = SNDRV_PCM_DEFAULT_CON_SPDIF;
168562306a36Sopenharmony_ci	}
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	/* build chip specific controls */
168862306a36Sopenharmony_ci	if (ac97->build_ops->build_specific) {
168962306a36Sopenharmony_ci		err = ac97->build_ops->build_specific(ac97);
169062306a36Sopenharmony_ci		if (err < 0)
169162306a36Sopenharmony_ci			return err;
169262306a36Sopenharmony_ci	}
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	if (snd_ac97_try_bit(ac97, AC97_POWERDOWN, 15)) {
169562306a36Sopenharmony_ci		kctl = snd_ac97_cnew(&snd_ac97_control_eapd, ac97);
169662306a36Sopenharmony_ci		if (! kctl)
169762306a36Sopenharmony_ci			return -ENOMEM;
169862306a36Sopenharmony_ci		if (ac97->scaps & AC97_SCAP_INV_EAPD)
169962306a36Sopenharmony_ci			set_inv_eapd(ac97, kctl);
170062306a36Sopenharmony_ci		err = snd_ctl_add(card, kctl);
170162306a36Sopenharmony_ci		if (err < 0)
170262306a36Sopenharmony_ci			return err;
170362306a36Sopenharmony_ci	}
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	return 0;
170662306a36Sopenharmony_ci}
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_cistatic int snd_ac97_modem_build(struct snd_card *card, struct snd_ac97 * ac97)
170962306a36Sopenharmony_ci{
171062306a36Sopenharmony_ci	int err, idx;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	/*
171362306a36Sopenharmony_ci	ac97_dbg(ac97, "AC97_GPIO_CFG = %x\n",
171462306a36Sopenharmony_ci	       snd_ac97_read(ac97,AC97_GPIO_CFG));
171562306a36Sopenharmony_ci	*/
171662306a36Sopenharmony_ci	snd_ac97_write(ac97, AC97_GPIO_CFG, 0xffff & ~(AC97_GPIO_LINE1_OH));
171762306a36Sopenharmony_ci	snd_ac97_write(ac97, AC97_GPIO_POLARITY, 0xffff & ~(AC97_GPIO_LINE1_OH));
171862306a36Sopenharmony_ci	snd_ac97_write(ac97, AC97_GPIO_STICKY, 0xffff);
171962306a36Sopenharmony_ci	snd_ac97_write(ac97, AC97_GPIO_WAKEUP, 0x0);
172062306a36Sopenharmony_ci	snd_ac97_write(ac97, AC97_MISC_AFE, 0x0);
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	/* build modem switches */
172362306a36Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_modem_switches); idx++) {
172462306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&snd_ac97_controls_modem_switches[idx], ac97));
172562306a36Sopenharmony_ci		if (err < 0)
172662306a36Sopenharmony_ci			return err;
172762306a36Sopenharmony_ci	}
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	/* build chip specific controls */
173062306a36Sopenharmony_ci	if (ac97->build_ops->build_specific) {
173162306a36Sopenharmony_ci		err = ac97->build_ops->build_specific(ac97);
173262306a36Sopenharmony_ci		if (err < 0)
173362306a36Sopenharmony_ci			return err;
173462306a36Sopenharmony_ci	}
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	return 0;
173762306a36Sopenharmony_ci}
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_cistatic int snd_ac97_test_rate(struct snd_ac97 *ac97, int reg, int shadow_reg, int rate)
174062306a36Sopenharmony_ci{
174162306a36Sopenharmony_ci	unsigned short val;
174262306a36Sopenharmony_ci	unsigned int tmp;
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	tmp = ((unsigned int)rate * ac97->bus->clock) / 48000;
174562306a36Sopenharmony_ci	snd_ac97_write_cache(ac97, reg, tmp & 0xffff);
174662306a36Sopenharmony_ci	if (shadow_reg)
174762306a36Sopenharmony_ci		snd_ac97_write_cache(ac97, shadow_reg, tmp & 0xffff);
174862306a36Sopenharmony_ci	val = snd_ac97_read(ac97, reg);
174962306a36Sopenharmony_ci	return val == (tmp & 0xffff);
175062306a36Sopenharmony_ci}
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_cistatic void snd_ac97_determine_rates(struct snd_ac97 *ac97, int reg, int shadow_reg, unsigned int *r_result)
175362306a36Sopenharmony_ci{
175462306a36Sopenharmony_ci	unsigned int result = 0;
175562306a36Sopenharmony_ci	unsigned short saved;
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	if (ac97->bus->no_vra) {
175862306a36Sopenharmony_ci		*r_result = SNDRV_PCM_RATE_48000;
175962306a36Sopenharmony_ci		if ((ac97->flags & AC97_DOUBLE_RATE) &&
176062306a36Sopenharmony_ci		    reg == AC97_PCM_FRONT_DAC_RATE)
176162306a36Sopenharmony_ci			*r_result |= SNDRV_PCM_RATE_96000;
176262306a36Sopenharmony_ci		return;
176362306a36Sopenharmony_ci	}
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	saved = snd_ac97_read(ac97, reg);
176662306a36Sopenharmony_ci	if ((ac97->ext_id & AC97_EI_DRA) && reg == AC97_PCM_FRONT_DAC_RATE)
176762306a36Sopenharmony_ci		snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,
176862306a36Sopenharmony_ci				     AC97_EA_DRA, 0);
176962306a36Sopenharmony_ci	/* test a non-standard rate */
177062306a36Sopenharmony_ci	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11000))
177162306a36Sopenharmony_ci		result |= SNDRV_PCM_RATE_CONTINUOUS;
177262306a36Sopenharmony_ci	/* let's try to obtain standard rates */
177362306a36Sopenharmony_ci	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 8000))
177462306a36Sopenharmony_ci		result |= SNDRV_PCM_RATE_8000;
177562306a36Sopenharmony_ci	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11025))
177662306a36Sopenharmony_ci		result |= SNDRV_PCM_RATE_11025;
177762306a36Sopenharmony_ci	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 16000))
177862306a36Sopenharmony_ci		result |= SNDRV_PCM_RATE_16000;
177962306a36Sopenharmony_ci	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 22050))
178062306a36Sopenharmony_ci		result |= SNDRV_PCM_RATE_22050;
178162306a36Sopenharmony_ci	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 32000))
178262306a36Sopenharmony_ci		result |= SNDRV_PCM_RATE_32000;
178362306a36Sopenharmony_ci	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 44100))
178462306a36Sopenharmony_ci		result |= SNDRV_PCM_RATE_44100;
178562306a36Sopenharmony_ci	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 48000))
178662306a36Sopenharmony_ci		result |= SNDRV_PCM_RATE_48000;
178762306a36Sopenharmony_ci	if ((ac97->flags & AC97_DOUBLE_RATE) &&
178862306a36Sopenharmony_ci	    reg == AC97_PCM_FRONT_DAC_RATE) {
178962306a36Sopenharmony_ci		/* test standard double rates */
179062306a36Sopenharmony_ci		snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,
179162306a36Sopenharmony_ci				     AC97_EA_DRA, AC97_EA_DRA);
179262306a36Sopenharmony_ci		if (snd_ac97_test_rate(ac97, reg, shadow_reg, 64000 / 2))
179362306a36Sopenharmony_ci			result |= SNDRV_PCM_RATE_64000;
179462306a36Sopenharmony_ci		if (snd_ac97_test_rate(ac97, reg, shadow_reg, 88200 / 2))
179562306a36Sopenharmony_ci			result |= SNDRV_PCM_RATE_88200;
179662306a36Sopenharmony_ci		if (snd_ac97_test_rate(ac97, reg, shadow_reg, 96000 / 2))
179762306a36Sopenharmony_ci			result |= SNDRV_PCM_RATE_96000;
179862306a36Sopenharmony_ci		/* some codecs don't support variable double rates */
179962306a36Sopenharmony_ci		if (!snd_ac97_test_rate(ac97, reg, shadow_reg, 76100 / 2))
180062306a36Sopenharmony_ci			result &= ~SNDRV_PCM_RATE_CONTINUOUS;
180162306a36Sopenharmony_ci		snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,
180262306a36Sopenharmony_ci				     AC97_EA_DRA, 0);
180362306a36Sopenharmony_ci	}
180462306a36Sopenharmony_ci	/* restore the default value */
180562306a36Sopenharmony_ci	snd_ac97_write_cache(ac97, reg, saved);
180662306a36Sopenharmony_ci	if (shadow_reg)
180762306a36Sopenharmony_ci		snd_ac97_write_cache(ac97, shadow_reg, saved);
180862306a36Sopenharmony_ci	*r_result = result;
180962306a36Sopenharmony_ci}
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci/* check AC97_SPDIF register to accept which sample rates */
181262306a36Sopenharmony_cistatic unsigned int snd_ac97_determine_spdif_rates(struct snd_ac97 *ac97)
181362306a36Sopenharmony_ci{
181462306a36Sopenharmony_ci	unsigned int result = 0;
181562306a36Sopenharmony_ci	int i;
181662306a36Sopenharmony_ci	static const unsigned short ctl_bits[] = {
181762306a36Sopenharmony_ci		AC97_SC_SPSR_44K, AC97_SC_SPSR_32K, AC97_SC_SPSR_48K
181862306a36Sopenharmony_ci	};
181962306a36Sopenharmony_ci	static const unsigned int rate_bits[] = {
182062306a36Sopenharmony_ci		SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_32000, SNDRV_PCM_RATE_48000
182162306a36Sopenharmony_ci	};
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci	for (i = 0; i < (int)ARRAY_SIZE(ctl_bits); i++) {
182462306a36Sopenharmony_ci		snd_ac97_update_bits(ac97, AC97_SPDIF, AC97_SC_SPSR_MASK, ctl_bits[i]);
182562306a36Sopenharmony_ci		if ((snd_ac97_read(ac97, AC97_SPDIF) & AC97_SC_SPSR_MASK) == ctl_bits[i])
182662306a36Sopenharmony_ci			result |= rate_bits[i];
182762306a36Sopenharmony_ci	}
182862306a36Sopenharmony_ci	return result;
182962306a36Sopenharmony_ci}
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci/* look for the codec id table matching with the given id */
183262306a36Sopenharmony_cistatic const struct ac97_codec_id *look_for_codec_id(const struct ac97_codec_id *table,
183362306a36Sopenharmony_ci						     unsigned int id)
183462306a36Sopenharmony_ci{
183562306a36Sopenharmony_ci	const struct ac97_codec_id *pid;
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	for (pid = table; pid->id; pid++)
183862306a36Sopenharmony_ci		if (pid->id == (id & pid->mask))
183962306a36Sopenharmony_ci			return pid;
184062306a36Sopenharmony_ci	return NULL;
184162306a36Sopenharmony_ci}
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_civoid snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int modem)
184462306a36Sopenharmony_ci{
184562306a36Sopenharmony_ci	const struct ac97_codec_id *pid;
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci	sprintf(name, "0x%x %c%c%c", id,
184862306a36Sopenharmony_ci		printable(id >> 24),
184962306a36Sopenharmony_ci		printable(id >> 16),
185062306a36Sopenharmony_ci		printable(id >> 8));
185162306a36Sopenharmony_ci	pid = look_for_codec_id(snd_ac97_codec_id_vendors, id);
185262306a36Sopenharmony_ci	if (! pid)
185362306a36Sopenharmony_ci		return;
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	strcpy(name, pid->name);
185662306a36Sopenharmony_ci	if (ac97 && pid->patch) {
185762306a36Sopenharmony_ci		if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
185862306a36Sopenharmony_ci		    (! modem && ! (pid->flags & AC97_MODEM_PATCH)))
185962306a36Sopenharmony_ci			pid->patch(ac97);
186062306a36Sopenharmony_ci	}
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	pid = look_for_codec_id(snd_ac97_codec_ids, id);
186362306a36Sopenharmony_ci	if (pid) {
186462306a36Sopenharmony_ci		strcat(name, " ");
186562306a36Sopenharmony_ci		strcat(name, pid->name);
186662306a36Sopenharmony_ci		if (pid->mask != 0xffffffff)
186762306a36Sopenharmony_ci			sprintf(name + strlen(name), " rev %d", id & ~pid->mask);
186862306a36Sopenharmony_ci		if (ac97 && pid->patch) {
186962306a36Sopenharmony_ci			if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
187062306a36Sopenharmony_ci			    (! modem && ! (pid->flags & AC97_MODEM_PATCH)))
187162306a36Sopenharmony_ci				pid->patch(ac97);
187262306a36Sopenharmony_ci		}
187362306a36Sopenharmony_ci	} else
187462306a36Sopenharmony_ci		sprintf(name + strlen(name), " id %x", id & 0xff);
187562306a36Sopenharmony_ci}
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ci/**
187862306a36Sopenharmony_ci * snd_ac97_get_short_name - retrieve codec name
187962306a36Sopenharmony_ci * @ac97: the codec instance
188062306a36Sopenharmony_ci *
188162306a36Sopenharmony_ci * Return: The short identifying name of the codec.
188262306a36Sopenharmony_ci */
188362306a36Sopenharmony_ciconst char *snd_ac97_get_short_name(struct snd_ac97 *ac97)
188462306a36Sopenharmony_ci{
188562306a36Sopenharmony_ci	const struct ac97_codec_id *pid;
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	for (pid = snd_ac97_codec_ids; pid->id; pid++)
188862306a36Sopenharmony_ci		if (pid->id == (ac97->id & pid->mask))
188962306a36Sopenharmony_ci			return pid->name;
189062306a36Sopenharmony_ci	return "unknown codec";
189162306a36Sopenharmony_ci}
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_get_short_name);
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci/* wait for a while until registers are accessible after RESET
189662306a36Sopenharmony_ci * return 0 if ok, negative not ready
189762306a36Sopenharmony_ci */
189862306a36Sopenharmony_cistatic int ac97_reset_wait(struct snd_ac97 *ac97, int timeout, int with_modem)
189962306a36Sopenharmony_ci{
190062306a36Sopenharmony_ci	unsigned long end_time;
190162306a36Sopenharmony_ci	unsigned short val;
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci	end_time = jiffies + timeout;
190462306a36Sopenharmony_ci	do {
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci		/* use preliminary reads to settle the communication */
190762306a36Sopenharmony_ci		snd_ac97_read(ac97, AC97_RESET);
190862306a36Sopenharmony_ci		snd_ac97_read(ac97, AC97_VENDOR_ID1);
190962306a36Sopenharmony_ci		snd_ac97_read(ac97, AC97_VENDOR_ID2);
191062306a36Sopenharmony_ci		/* modem? */
191162306a36Sopenharmony_ci		if (with_modem) {
191262306a36Sopenharmony_ci			val = snd_ac97_read(ac97, AC97_EXTENDED_MID);
191362306a36Sopenharmony_ci			if (val != 0xffff && (val & 1) != 0)
191462306a36Sopenharmony_ci				return 0;
191562306a36Sopenharmony_ci		}
191662306a36Sopenharmony_ci		if (ac97->scaps & AC97_SCAP_DETECT_BY_VENDOR) {
191762306a36Sopenharmony_ci			/* probably only Xbox issue - all registers are read as zero */
191862306a36Sopenharmony_ci			val = snd_ac97_read(ac97, AC97_VENDOR_ID1);
191962306a36Sopenharmony_ci			if (val != 0 && val != 0xffff)
192062306a36Sopenharmony_ci				return 0;
192162306a36Sopenharmony_ci		} else {
192262306a36Sopenharmony_ci			/* because the PCM or MASTER volume registers can be modified,
192362306a36Sopenharmony_ci			 * the REC_GAIN register is used for tests
192462306a36Sopenharmony_ci			 */
192562306a36Sopenharmony_ci			/* test if we can write to the record gain volume register */
192662306a36Sopenharmony_ci			snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05);
192762306a36Sopenharmony_ci			if ((snd_ac97_read(ac97, AC97_REC_GAIN) & 0x7fff) == 0x0a05)
192862306a36Sopenharmony_ci				return 0;
192962306a36Sopenharmony_ci		}
193062306a36Sopenharmony_ci		schedule_timeout_uninterruptible(1);
193162306a36Sopenharmony_ci	} while (time_after_eq(end_time, jiffies));
193262306a36Sopenharmony_ci	return -ENODEV;
193362306a36Sopenharmony_ci}
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci/**
193662306a36Sopenharmony_ci * snd_ac97_bus - create an AC97 bus component
193762306a36Sopenharmony_ci * @card: the card instance
193862306a36Sopenharmony_ci * @num: the bus number
193962306a36Sopenharmony_ci * @ops: the bus callbacks table
194062306a36Sopenharmony_ci * @private_data: private data pointer for the new instance
194162306a36Sopenharmony_ci * @rbus: the pointer to store the new AC97 bus instance.
194262306a36Sopenharmony_ci *
194362306a36Sopenharmony_ci * Creates an AC97 bus component.  An struct snd_ac97_bus instance is newly
194462306a36Sopenharmony_ci * allocated and initialized.
194562306a36Sopenharmony_ci *
194662306a36Sopenharmony_ci * The ops table must include valid callbacks (at least read and
194762306a36Sopenharmony_ci * write).  The other callbacks, wait and reset, are not mandatory.
194862306a36Sopenharmony_ci *
194962306a36Sopenharmony_ci * The clock is set to 48000.  If another clock is needed, set
195062306a36Sopenharmony_ci * ``(*rbus)->clock`` manually.
195162306a36Sopenharmony_ci *
195262306a36Sopenharmony_ci * The AC97 bus instance is registered as a low-level device, so you don't
195362306a36Sopenharmony_ci * have to release it manually.
195462306a36Sopenharmony_ci *
195562306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure.
195662306a36Sopenharmony_ci */
195762306a36Sopenharmony_ciint snd_ac97_bus(struct snd_card *card, int num,
195862306a36Sopenharmony_ci		 const struct snd_ac97_bus_ops *ops,
195962306a36Sopenharmony_ci		 void *private_data, struct snd_ac97_bus **rbus)
196062306a36Sopenharmony_ci{
196162306a36Sopenharmony_ci	int err;
196262306a36Sopenharmony_ci	struct snd_ac97_bus *bus;
196362306a36Sopenharmony_ci	static const struct snd_device_ops dev_ops = {
196462306a36Sopenharmony_ci		.dev_free =	snd_ac97_bus_dev_free,
196562306a36Sopenharmony_ci	};
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	if (snd_BUG_ON(!card))
196862306a36Sopenharmony_ci		return -EINVAL;
196962306a36Sopenharmony_ci	bus = kzalloc(sizeof(*bus), GFP_KERNEL);
197062306a36Sopenharmony_ci	if (bus == NULL)
197162306a36Sopenharmony_ci		return -ENOMEM;
197262306a36Sopenharmony_ci	bus->card = card;
197362306a36Sopenharmony_ci	bus->num = num;
197462306a36Sopenharmony_ci	bus->ops = ops;
197562306a36Sopenharmony_ci	bus->private_data = private_data;
197662306a36Sopenharmony_ci	bus->clock = 48000;
197762306a36Sopenharmony_ci	spin_lock_init(&bus->bus_lock);
197862306a36Sopenharmony_ci	snd_ac97_bus_proc_init(bus);
197962306a36Sopenharmony_ci	err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops);
198062306a36Sopenharmony_ci	if (err < 0) {
198162306a36Sopenharmony_ci		snd_ac97_bus_free(bus);
198262306a36Sopenharmony_ci		return err;
198362306a36Sopenharmony_ci	}
198462306a36Sopenharmony_ci	if (rbus)
198562306a36Sopenharmony_ci		*rbus = bus;
198662306a36Sopenharmony_ci	return 0;
198762306a36Sopenharmony_ci}
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_bus);
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci/* stop no dev release warning */
199262306a36Sopenharmony_cistatic void ac97_device_release(struct device * dev)
199362306a36Sopenharmony_ci{
199462306a36Sopenharmony_ci}
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci/* register ac97 codec to bus */
199762306a36Sopenharmony_cistatic int snd_ac97_dev_register(struct snd_device *device)
199862306a36Sopenharmony_ci{
199962306a36Sopenharmony_ci	struct snd_ac97 *ac97 = device->device_data;
200062306a36Sopenharmony_ci	int err;
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	ac97->dev.bus = &ac97_bus_type;
200362306a36Sopenharmony_ci	ac97->dev.parent = ac97->bus->card->dev;
200462306a36Sopenharmony_ci	ac97->dev.release = ac97_device_release;
200562306a36Sopenharmony_ci	dev_set_name(&ac97->dev, "%d-%d:%s",
200662306a36Sopenharmony_ci		     ac97->bus->card->number, ac97->num,
200762306a36Sopenharmony_ci		     snd_ac97_get_short_name(ac97));
200862306a36Sopenharmony_ci	err = device_register(&ac97->dev);
200962306a36Sopenharmony_ci	if (err < 0) {
201062306a36Sopenharmony_ci		ac97_err(ac97, "Can't register ac97 bus\n");
201162306a36Sopenharmony_ci		put_device(&ac97->dev);
201262306a36Sopenharmony_ci		ac97->dev.bus = NULL;
201362306a36Sopenharmony_ci		return err;
201462306a36Sopenharmony_ci	}
201562306a36Sopenharmony_ci	return 0;
201662306a36Sopenharmony_ci}
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci/* disconnect ac97 codec */
201962306a36Sopenharmony_cistatic int snd_ac97_dev_disconnect(struct snd_device *device)
202062306a36Sopenharmony_ci{
202162306a36Sopenharmony_ci	struct snd_ac97 *ac97 = device->device_data;
202262306a36Sopenharmony_ci	if (ac97->dev.bus)
202362306a36Sopenharmony_ci		device_unregister(&ac97->dev);
202462306a36Sopenharmony_ci	return 0;
202562306a36Sopenharmony_ci}
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci/* build_ops to do nothing */
202862306a36Sopenharmony_cistatic const struct snd_ac97_build_ops null_build_ops;
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE
203162306a36Sopenharmony_cistatic void do_update_power(struct work_struct *work)
203262306a36Sopenharmony_ci{
203362306a36Sopenharmony_ci	update_power_regs(
203462306a36Sopenharmony_ci		container_of(work, struct snd_ac97, power_work.work));
203562306a36Sopenharmony_ci}
203662306a36Sopenharmony_ci#endif
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci/**
203962306a36Sopenharmony_ci * snd_ac97_mixer - create an Codec97 component
204062306a36Sopenharmony_ci * @bus: the AC97 bus which codec is attached to
204162306a36Sopenharmony_ci * @template: the template of ac97, including index, callbacks and
204262306a36Sopenharmony_ci *         the private data.
204362306a36Sopenharmony_ci * @rac97: the pointer to store the new ac97 instance.
204462306a36Sopenharmony_ci *
204562306a36Sopenharmony_ci * Creates an Codec97 component.  An struct snd_ac97 instance is newly
204662306a36Sopenharmony_ci * allocated and initialized from the template.  The codec
204762306a36Sopenharmony_ci * is then initialized by the standard procedure.
204862306a36Sopenharmony_ci *
204962306a36Sopenharmony_ci * The template must include the codec number (num) and address (addr),
205062306a36Sopenharmony_ci * and the private data (private_data).
205162306a36Sopenharmony_ci *
205262306a36Sopenharmony_ci * The ac97 instance is registered as a low-level device, so you don't
205362306a36Sopenharmony_ci * have to release it manually.
205462306a36Sopenharmony_ci *
205562306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure.
205662306a36Sopenharmony_ci */
205762306a36Sopenharmony_ciint snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, struct snd_ac97 **rac97)
205862306a36Sopenharmony_ci{
205962306a36Sopenharmony_ci	int err;
206062306a36Sopenharmony_ci	struct snd_ac97 *ac97;
206162306a36Sopenharmony_ci	struct snd_card *card;
206262306a36Sopenharmony_ci	char name[64];
206362306a36Sopenharmony_ci	unsigned long end_time;
206462306a36Sopenharmony_ci	unsigned int reg;
206562306a36Sopenharmony_ci	const struct ac97_codec_id *pid;
206662306a36Sopenharmony_ci	static const struct snd_device_ops ops = {
206762306a36Sopenharmony_ci		.dev_free =	snd_ac97_dev_free,
206862306a36Sopenharmony_ci		.dev_register =	snd_ac97_dev_register,
206962306a36Sopenharmony_ci		.dev_disconnect =	snd_ac97_dev_disconnect,
207062306a36Sopenharmony_ci	};
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci	if (snd_BUG_ON(!bus || !template || !rac97))
207362306a36Sopenharmony_ci		return -EINVAL;
207462306a36Sopenharmony_ci	*rac97 = NULL;
207562306a36Sopenharmony_ci	if (snd_BUG_ON(template->num >= 4))
207662306a36Sopenharmony_ci		return -EINVAL;
207762306a36Sopenharmony_ci	if (bus->codec[template->num])
207862306a36Sopenharmony_ci		return -EBUSY;
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	card = bus->card;
208162306a36Sopenharmony_ci	ac97 = kzalloc(sizeof(*ac97), GFP_KERNEL);
208262306a36Sopenharmony_ci	if (ac97 == NULL)
208362306a36Sopenharmony_ci		return -ENOMEM;
208462306a36Sopenharmony_ci	ac97->private_data = template->private_data;
208562306a36Sopenharmony_ci	ac97->private_free = template->private_free;
208662306a36Sopenharmony_ci	ac97->bus = bus;
208762306a36Sopenharmony_ci	ac97->pci = template->pci;
208862306a36Sopenharmony_ci	ac97->num = template->num;
208962306a36Sopenharmony_ci	ac97->addr = template->addr;
209062306a36Sopenharmony_ci	ac97->scaps = template->scaps;
209162306a36Sopenharmony_ci	ac97->res_table = template->res_table;
209262306a36Sopenharmony_ci	bus->codec[ac97->num] = ac97;
209362306a36Sopenharmony_ci	mutex_init(&ac97->reg_mutex);
209462306a36Sopenharmony_ci	mutex_init(&ac97->page_mutex);
209562306a36Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE
209662306a36Sopenharmony_ci	INIT_DELAYED_WORK(&ac97->power_work, do_update_power);
209762306a36Sopenharmony_ci#endif
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci#ifdef CONFIG_PCI
210062306a36Sopenharmony_ci	if (ac97->pci) {
210162306a36Sopenharmony_ci		pci_read_config_word(ac97->pci, PCI_SUBSYSTEM_VENDOR_ID, &ac97->subsystem_vendor);
210262306a36Sopenharmony_ci		pci_read_config_word(ac97->pci, PCI_SUBSYSTEM_ID, &ac97->subsystem_device);
210362306a36Sopenharmony_ci	}
210462306a36Sopenharmony_ci#endif
210562306a36Sopenharmony_ci	if (bus->ops->reset) {
210662306a36Sopenharmony_ci		bus->ops->reset(ac97);
210762306a36Sopenharmony_ci		goto __access_ok;
210862306a36Sopenharmony_ci	}
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16;
211162306a36Sopenharmony_ci	ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2);
211262306a36Sopenharmony_ci	if (ac97->id && ac97->id != (unsigned int)-1) {
211362306a36Sopenharmony_ci		pid = look_for_codec_id(snd_ac97_codec_ids, ac97->id);
211462306a36Sopenharmony_ci		if (pid && (pid->flags & AC97_DEFAULT_POWER_OFF))
211562306a36Sopenharmony_ci			goto __access_ok;
211662306a36Sopenharmony_ci	}
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_ci	/* reset to defaults */
211962306a36Sopenharmony_ci	if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO))
212062306a36Sopenharmony_ci		snd_ac97_write(ac97, AC97_RESET, 0);
212162306a36Sopenharmony_ci	if (!(ac97->scaps & AC97_SCAP_SKIP_MODEM))
212262306a36Sopenharmony_ci		snd_ac97_write(ac97, AC97_EXTENDED_MID, 0);
212362306a36Sopenharmony_ci	if (bus->ops->wait)
212462306a36Sopenharmony_ci		bus->ops->wait(ac97);
212562306a36Sopenharmony_ci	else {
212662306a36Sopenharmony_ci		udelay(50);
212762306a36Sopenharmony_ci		if (ac97->scaps & AC97_SCAP_SKIP_AUDIO)
212862306a36Sopenharmony_ci			err = ac97_reset_wait(ac97, msecs_to_jiffies(500), 1);
212962306a36Sopenharmony_ci		else {
213062306a36Sopenharmony_ci			err = ac97_reset_wait(ac97, msecs_to_jiffies(500), 0);
213162306a36Sopenharmony_ci			if (err < 0)
213262306a36Sopenharmony_ci				err = ac97_reset_wait(ac97,
213362306a36Sopenharmony_ci						      msecs_to_jiffies(500), 1);
213462306a36Sopenharmony_ci		}
213562306a36Sopenharmony_ci		if (err < 0) {
213662306a36Sopenharmony_ci			ac97_warn(ac97, "AC'97 %d does not respond - RESET\n",
213762306a36Sopenharmony_ci				 ac97->num);
213862306a36Sopenharmony_ci			/* proceed anyway - it's often non-critical */
213962306a36Sopenharmony_ci		}
214062306a36Sopenharmony_ci	}
214162306a36Sopenharmony_ci      __access_ok:
214262306a36Sopenharmony_ci	ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16;
214362306a36Sopenharmony_ci	ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2);
214462306a36Sopenharmony_ci	if (! (ac97->scaps & AC97_SCAP_DETECT_BY_VENDOR) &&
214562306a36Sopenharmony_ci	    (ac97->id == 0x00000000 || ac97->id == 0xffffffff)) {
214662306a36Sopenharmony_ci		ac97_err(ac97,
214762306a36Sopenharmony_ci			 "AC'97 %d access is not valid [0x%x], removing mixer.\n",
214862306a36Sopenharmony_ci			 ac97->num, ac97->id);
214962306a36Sopenharmony_ci		snd_ac97_free(ac97);
215062306a36Sopenharmony_ci		return -EIO;
215162306a36Sopenharmony_ci	}
215262306a36Sopenharmony_ci	pid = look_for_codec_id(snd_ac97_codec_ids, ac97->id);
215362306a36Sopenharmony_ci	if (pid)
215462306a36Sopenharmony_ci		ac97->flags |= pid->flags;
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	/* test for AC'97 */
215762306a36Sopenharmony_ci	if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO) && !(ac97->scaps & AC97_SCAP_AUDIO)) {
215862306a36Sopenharmony_ci		/* test if we can write to the record gain volume register */
215962306a36Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a06);
216062306a36Sopenharmony_ci		err = snd_ac97_read(ac97, AC97_REC_GAIN);
216162306a36Sopenharmony_ci		if ((err & 0x7fff) == 0x0a06)
216262306a36Sopenharmony_ci			ac97->scaps |= AC97_SCAP_AUDIO;
216362306a36Sopenharmony_ci	}
216462306a36Sopenharmony_ci	if (ac97->scaps & AC97_SCAP_AUDIO) {
216562306a36Sopenharmony_ci		ac97->caps = snd_ac97_read(ac97, AC97_RESET);
216662306a36Sopenharmony_ci		ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID);
216762306a36Sopenharmony_ci		if (ac97->ext_id == 0xffff)	/* invalid combination */
216862306a36Sopenharmony_ci			ac97->ext_id = 0;
216962306a36Sopenharmony_ci	}
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	/* test for MC'97 */
217262306a36Sopenharmony_ci	if (!(ac97->scaps & AC97_SCAP_SKIP_MODEM) && !(ac97->scaps & AC97_SCAP_MODEM)) {
217362306a36Sopenharmony_ci		ac97->ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID);
217462306a36Sopenharmony_ci		if (ac97->ext_mid == 0xffff)	/* invalid combination */
217562306a36Sopenharmony_ci			ac97->ext_mid = 0;
217662306a36Sopenharmony_ci		if (ac97->ext_mid & 1)
217762306a36Sopenharmony_ci			ac97->scaps |= AC97_SCAP_MODEM;
217862306a36Sopenharmony_ci	}
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci	if (!ac97_is_audio(ac97) && !ac97_is_modem(ac97)) {
218162306a36Sopenharmony_ci		if (!(ac97->scaps & (AC97_SCAP_SKIP_AUDIO|AC97_SCAP_SKIP_MODEM)))
218262306a36Sopenharmony_ci			ac97_err(ac97,
218362306a36Sopenharmony_ci				 "AC'97 %d access error (not audio or modem codec)\n",
218462306a36Sopenharmony_ci				 ac97->num);
218562306a36Sopenharmony_ci		snd_ac97_free(ac97);
218662306a36Sopenharmony_ci		return -EACCES;
218762306a36Sopenharmony_ci	}
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_ci	if (bus->ops->reset) // FIXME: always skipping?
219062306a36Sopenharmony_ci		goto __ready_ok;
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci	/* FIXME: add powerdown control */
219362306a36Sopenharmony_ci	if (ac97_is_audio(ac97)) {
219462306a36Sopenharmony_ci		/* nothing should be in powerdown mode */
219562306a36Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0);
219662306a36Sopenharmony_ci		if (! (ac97->flags & AC97_DEFAULT_POWER_OFF)) {
219762306a36Sopenharmony_ci			snd_ac97_write_cache(ac97, AC97_RESET, 0); /* reset to defaults */
219862306a36Sopenharmony_ci			udelay(100);
219962306a36Sopenharmony_ci			snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0);
220062306a36Sopenharmony_ci		}
220162306a36Sopenharmony_ci		/* nothing should be in powerdown mode */
220262306a36Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0);
220362306a36Sopenharmony_ci		end_time = jiffies + msecs_to_jiffies(5000);
220462306a36Sopenharmony_ci		do {
220562306a36Sopenharmony_ci			if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f)
220662306a36Sopenharmony_ci				goto __ready_ok;
220762306a36Sopenharmony_ci			schedule_timeout_uninterruptible(1);
220862306a36Sopenharmony_ci		} while (time_after_eq(end_time, jiffies));
220962306a36Sopenharmony_ci		ac97_warn(ac97,
221062306a36Sopenharmony_ci			  "AC'97 %d analog subsections not ready\n", ac97->num);
221162306a36Sopenharmony_ci	}
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	/* FIXME: add powerdown control */
221462306a36Sopenharmony_ci	if (ac97_is_modem(ac97)) {
221562306a36Sopenharmony_ci		unsigned char tmp;
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci		/* nothing should be in powerdown mode */
221862306a36Sopenharmony_ci		/* note: it's important to set the rate at first */
221962306a36Sopenharmony_ci		tmp = AC97_MEA_GPIO;
222062306a36Sopenharmony_ci		if (ac97->ext_mid & AC97_MEI_LINE1) {
222162306a36Sopenharmony_ci			snd_ac97_write_cache(ac97, AC97_LINE1_RATE, 8000);
222262306a36Sopenharmony_ci			tmp |= AC97_MEA_ADC1 | AC97_MEA_DAC1;
222362306a36Sopenharmony_ci		}
222462306a36Sopenharmony_ci		if (ac97->ext_mid & AC97_MEI_LINE2) {
222562306a36Sopenharmony_ci			snd_ac97_write_cache(ac97, AC97_LINE2_RATE, 8000);
222662306a36Sopenharmony_ci			tmp |= AC97_MEA_ADC2 | AC97_MEA_DAC2;
222762306a36Sopenharmony_ci		}
222862306a36Sopenharmony_ci		if (ac97->ext_mid & AC97_MEI_HANDSET) {
222962306a36Sopenharmony_ci			snd_ac97_write_cache(ac97, AC97_HANDSET_RATE, 8000);
223062306a36Sopenharmony_ci			tmp |= AC97_MEA_HADC | AC97_MEA_HDAC;
223162306a36Sopenharmony_ci		}
223262306a36Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0);
223362306a36Sopenharmony_ci		udelay(100);
223462306a36Sopenharmony_ci		/* nothing should be in powerdown mode */
223562306a36Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0);
223662306a36Sopenharmony_ci		end_time = jiffies + msecs_to_jiffies(100);
223762306a36Sopenharmony_ci		do {
223862306a36Sopenharmony_ci			if ((snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS) & tmp) == tmp)
223962306a36Sopenharmony_ci				goto __ready_ok;
224062306a36Sopenharmony_ci			schedule_timeout_uninterruptible(1);
224162306a36Sopenharmony_ci		} while (time_after_eq(end_time, jiffies));
224262306a36Sopenharmony_ci		ac97_warn(ac97,
224362306a36Sopenharmony_ci			  "MC'97 %d converters and GPIO not ready (0x%x)\n",
224462306a36Sopenharmony_ci			  ac97->num,
224562306a36Sopenharmony_ci			  snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS));
224662306a36Sopenharmony_ci	}
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci      __ready_ok:
224962306a36Sopenharmony_ci	if (ac97_is_audio(ac97))
225062306a36Sopenharmony_ci		ac97->addr = (ac97->ext_id & AC97_EI_ADDR_MASK) >> AC97_EI_ADDR_SHIFT;
225162306a36Sopenharmony_ci	else
225262306a36Sopenharmony_ci		ac97->addr = (ac97->ext_mid & AC97_MEI_ADDR_MASK) >> AC97_MEI_ADDR_SHIFT;
225362306a36Sopenharmony_ci	if (ac97->ext_id & 0x01c9) {	/* L/R, MIC, SDAC, LDAC VRA support */
225462306a36Sopenharmony_ci		reg = snd_ac97_read(ac97, AC97_EXTENDED_STATUS);
225562306a36Sopenharmony_ci		reg |= ac97->ext_id & 0x01c0; /* LDAC/SDAC/CDAC */
225662306a36Sopenharmony_ci		if (! bus->no_vra)
225762306a36Sopenharmony_ci			reg |= ac97->ext_id & 0x0009; /* VRA/VRM */
225862306a36Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, reg);
225962306a36Sopenharmony_ci	}
226062306a36Sopenharmony_ci	if ((ac97->ext_id & AC97_EI_DRA) && bus->dra) {
226162306a36Sopenharmony_ci		/* Intel controllers require double rate data to be put in
226262306a36Sopenharmony_ci		 * slots 7+8, so let's hope the codec supports it. */
226362306a36Sopenharmony_ci		snd_ac97_update_bits(ac97, AC97_GENERAL_PURPOSE, AC97_GP_DRSS_MASK, AC97_GP_DRSS_78);
226462306a36Sopenharmony_ci		if ((snd_ac97_read(ac97, AC97_GENERAL_PURPOSE) & AC97_GP_DRSS_MASK) == AC97_GP_DRSS_78)
226562306a36Sopenharmony_ci			ac97->flags |= AC97_DOUBLE_RATE;
226662306a36Sopenharmony_ci		/* restore to slots 10/11 to avoid the confliction with surrounds */
226762306a36Sopenharmony_ci		snd_ac97_update_bits(ac97, AC97_GENERAL_PURPOSE, AC97_GP_DRSS_MASK, 0);
226862306a36Sopenharmony_ci	}
226962306a36Sopenharmony_ci	if (ac97->ext_id & AC97_EI_VRA) {	/* VRA support */
227062306a36Sopenharmony_ci		snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, 0, &ac97->rates[AC97_RATES_FRONT_DAC]);
227162306a36Sopenharmony_ci		snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, 0, &ac97->rates[AC97_RATES_ADC]);
227262306a36Sopenharmony_ci	} else {
227362306a36Sopenharmony_ci		ac97->rates[AC97_RATES_FRONT_DAC] = SNDRV_PCM_RATE_48000;
227462306a36Sopenharmony_ci		if (ac97->flags & AC97_DOUBLE_RATE)
227562306a36Sopenharmony_ci			ac97->rates[AC97_RATES_FRONT_DAC] |= SNDRV_PCM_RATE_96000;
227662306a36Sopenharmony_ci		ac97->rates[AC97_RATES_ADC] = SNDRV_PCM_RATE_48000;
227762306a36Sopenharmony_ci	}
227862306a36Sopenharmony_ci	if (ac97->ext_id & AC97_EI_SPDIF) {
227962306a36Sopenharmony_ci		/* codec specific code (patch) should override these values */
228062306a36Sopenharmony_ci		ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_32000;
228162306a36Sopenharmony_ci	}
228262306a36Sopenharmony_ci	if (ac97->ext_id & AC97_EI_VRM) {	/* MIC VRA support */
228362306a36Sopenharmony_ci		snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, 0, &ac97->rates[AC97_RATES_MIC_ADC]);
228462306a36Sopenharmony_ci	} else {
228562306a36Sopenharmony_ci		ac97->rates[AC97_RATES_MIC_ADC] = SNDRV_PCM_RATE_48000;
228662306a36Sopenharmony_ci	}
228762306a36Sopenharmony_ci	if (ac97->ext_id & AC97_EI_SDAC) {	/* SDAC support */
228862306a36Sopenharmony_ci		snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_SURR_DAC]);
228962306a36Sopenharmony_ci		ac97->scaps |= AC97_SCAP_SURROUND_DAC;
229062306a36Sopenharmony_ci	}
229162306a36Sopenharmony_ci	if (ac97->ext_id & AC97_EI_LDAC) {	/* LDAC support */
229262306a36Sopenharmony_ci		snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_LFE_DAC]);
229362306a36Sopenharmony_ci		ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC;
229462306a36Sopenharmony_ci	}
229562306a36Sopenharmony_ci	/* additional initializations */
229662306a36Sopenharmony_ci	if (bus->ops->init)
229762306a36Sopenharmony_ci		bus->ops->init(ac97);
229862306a36Sopenharmony_ci	snd_ac97_get_name(ac97, ac97->id, name, !ac97_is_audio(ac97));
229962306a36Sopenharmony_ci	snd_ac97_get_name(NULL, ac97->id, name, !ac97_is_audio(ac97));  // ac97->id might be changed in the special setup code
230062306a36Sopenharmony_ci	if (! ac97->build_ops)
230162306a36Sopenharmony_ci		ac97->build_ops = &null_build_ops;
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ci	if (ac97_is_audio(ac97)) {
230462306a36Sopenharmony_ci		char comp[16];
230562306a36Sopenharmony_ci		if (card->mixername[0] == '\0') {
230662306a36Sopenharmony_ci			strcpy(card->mixername, name);
230762306a36Sopenharmony_ci		} else {
230862306a36Sopenharmony_ci			if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) {
230962306a36Sopenharmony_ci				strcat(card->mixername, ",");
231062306a36Sopenharmony_ci				strcat(card->mixername, name);
231162306a36Sopenharmony_ci			}
231262306a36Sopenharmony_ci		}
231362306a36Sopenharmony_ci		sprintf(comp, "AC97a:%08x", ac97->id);
231462306a36Sopenharmony_ci		err = snd_component_add(card, comp);
231562306a36Sopenharmony_ci		if (err < 0) {
231662306a36Sopenharmony_ci			snd_ac97_free(ac97);
231762306a36Sopenharmony_ci			return err;
231862306a36Sopenharmony_ci		}
231962306a36Sopenharmony_ci		if (snd_ac97_mixer_build(ac97) < 0) {
232062306a36Sopenharmony_ci			snd_ac97_free(ac97);
232162306a36Sopenharmony_ci			return -ENOMEM;
232262306a36Sopenharmony_ci		}
232362306a36Sopenharmony_ci	}
232462306a36Sopenharmony_ci	if (ac97_is_modem(ac97)) {
232562306a36Sopenharmony_ci		char comp[16];
232662306a36Sopenharmony_ci		if (card->mixername[0] == '\0') {
232762306a36Sopenharmony_ci			strcpy(card->mixername, name);
232862306a36Sopenharmony_ci		} else {
232962306a36Sopenharmony_ci			if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) {
233062306a36Sopenharmony_ci				strcat(card->mixername, ",");
233162306a36Sopenharmony_ci				strcat(card->mixername, name);
233262306a36Sopenharmony_ci			}
233362306a36Sopenharmony_ci		}
233462306a36Sopenharmony_ci		sprintf(comp, "AC97m:%08x", ac97->id);
233562306a36Sopenharmony_ci		err = snd_component_add(card, comp);
233662306a36Sopenharmony_ci		if (err < 0) {
233762306a36Sopenharmony_ci			snd_ac97_free(ac97);
233862306a36Sopenharmony_ci			return err;
233962306a36Sopenharmony_ci		}
234062306a36Sopenharmony_ci		if (snd_ac97_modem_build(card, ac97) < 0) {
234162306a36Sopenharmony_ci			snd_ac97_free(ac97);
234262306a36Sopenharmony_ci			return -ENOMEM;
234362306a36Sopenharmony_ci		}
234462306a36Sopenharmony_ci	}
234562306a36Sopenharmony_ci	if (ac97_is_audio(ac97))
234662306a36Sopenharmony_ci		update_power_regs(ac97);
234762306a36Sopenharmony_ci	snd_ac97_proc_init(ac97);
234862306a36Sopenharmony_ci	err = snd_device_new(card, SNDRV_DEV_CODEC, ac97, &ops);
234962306a36Sopenharmony_ci	if (err < 0) {
235062306a36Sopenharmony_ci		snd_ac97_free(ac97);
235162306a36Sopenharmony_ci		return err;
235262306a36Sopenharmony_ci	}
235362306a36Sopenharmony_ci	*rac97 = ac97;
235462306a36Sopenharmony_ci	return 0;
235562306a36Sopenharmony_ci}
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_mixer);
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci/*
236062306a36Sopenharmony_ci * Power down the chip.
236162306a36Sopenharmony_ci *
236262306a36Sopenharmony_ci * MASTER and HEADPHONE registers are muted but the register cache values
236362306a36Sopenharmony_ci * are not changed, so that the values can be restored in snd_ac97_resume().
236462306a36Sopenharmony_ci */
236562306a36Sopenharmony_cistatic void snd_ac97_powerdown(struct snd_ac97 *ac97)
236662306a36Sopenharmony_ci{
236762306a36Sopenharmony_ci	unsigned short power;
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci	if (ac97_is_audio(ac97)) {
237062306a36Sopenharmony_ci		/* some codecs have stereo mute bits */
237162306a36Sopenharmony_ci		snd_ac97_write(ac97, AC97_MASTER, 0x9f9f);
237262306a36Sopenharmony_ci		snd_ac97_write(ac97, AC97_HEADPHONE, 0x9f9f);
237362306a36Sopenharmony_ci	}
237462306a36Sopenharmony_ci
237562306a36Sopenharmony_ci	/* surround, CLFE, mic powerdown */
237662306a36Sopenharmony_ci	power = ac97->regs[AC97_EXTENDED_STATUS];
237762306a36Sopenharmony_ci	if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
237862306a36Sopenharmony_ci		power |= AC97_EA_PRJ;
237962306a36Sopenharmony_ci	if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
238062306a36Sopenharmony_ci		power |= AC97_EA_PRI | AC97_EA_PRK;
238162306a36Sopenharmony_ci	power |= AC97_EA_PRL;
238262306a36Sopenharmony_ci	snd_ac97_write(ac97, AC97_EXTENDED_STATUS, power);
238362306a36Sopenharmony_ci
238462306a36Sopenharmony_ci	/* powerdown external amplifier */
238562306a36Sopenharmony_ci	if (ac97->scaps & AC97_SCAP_INV_EAPD)
238662306a36Sopenharmony_ci		power = ac97->regs[AC97_POWERDOWN] & ~AC97_PD_EAPD;
238762306a36Sopenharmony_ci	else if (! (ac97->scaps & AC97_SCAP_EAPD_LED))
238862306a36Sopenharmony_ci		power = ac97->regs[AC97_POWERDOWN] | AC97_PD_EAPD;
238962306a36Sopenharmony_ci	power |= AC97_PD_PR6;	/* Headphone amplifier powerdown */
239062306a36Sopenharmony_ci	power |= AC97_PD_PR0 | AC97_PD_PR1;	/* ADC & DAC powerdown */
239162306a36Sopenharmony_ci	snd_ac97_write(ac97, AC97_POWERDOWN, power);
239262306a36Sopenharmony_ci	udelay(100);
239362306a36Sopenharmony_ci	power |= AC97_PD_PR2;	/* Analog Mixer powerdown (Vref on) */
239462306a36Sopenharmony_ci	snd_ac97_write(ac97, AC97_POWERDOWN, power);
239562306a36Sopenharmony_ci	if (ac97_is_power_save_mode(ac97)) {
239662306a36Sopenharmony_ci		power |= AC97_PD_PR3;	/* Analog Mixer powerdown */
239762306a36Sopenharmony_ci		snd_ac97_write(ac97, AC97_POWERDOWN, power);
239862306a36Sopenharmony_ci		udelay(100);
239962306a36Sopenharmony_ci		/* AC-link powerdown, internal Clk disable */
240062306a36Sopenharmony_ci		/* FIXME: this may cause click noises on some boards */
240162306a36Sopenharmony_ci		power |= AC97_PD_PR4 | AC97_PD_PR5;
240262306a36Sopenharmony_ci		snd_ac97_write(ac97, AC97_POWERDOWN, power);
240362306a36Sopenharmony_ci	}
240462306a36Sopenharmony_ci}
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_cistruct ac97_power_reg {
240862306a36Sopenharmony_ci	unsigned short reg;
240962306a36Sopenharmony_ci	unsigned short power_reg;
241062306a36Sopenharmony_ci	unsigned short mask;
241162306a36Sopenharmony_ci};
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_cienum { PWIDX_ADC, PWIDX_FRONT, PWIDX_CLFE, PWIDX_SURR, PWIDX_MIC, PWIDX_SIZE };
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_cistatic const struct ac97_power_reg power_regs[PWIDX_SIZE] = {
241662306a36Sopenharmony_ci	[PWIDX_ADC] = { AC97_PCM_LR_ADC_RATE, AC97_POWERDOWN, AC97_PD_PR0},
241762306a36Sopenharmony_ci	[PWIDX_FRONT] = { AC97_PCM_FRONT_DAC_RATE, AC97_POWERDOWN, AC97_PD_PR1},
241862306a36Sopenharmony_ci	[PWIDX_CLFE] = { AC97_PCM_LFE_DAC_RATE, AC97_EXTENDED_STATUS,
241962306a36Sopenharmony_ci			 AC97_EA_PRI | AC97_EA_PRK},
242062306a36Sopenharmony_ci	[PWIDX_SURR] = { AC97_PCM_SURR_DAC_RATE, AC97_EXTENDED_STATUS,
242162306a36Sopenharmony_ci			 AC97_EA_PRJ},
242262306a36Sopenharmony_ci	[PWIDX_MIC] = { AC97_PCM_MIC_ADC_RATE, AC97_EXTENDED_STATUS,
242362306a36Sopenharmony_ci			AC97_EA_PRL},
242462306a36Sopenharmony_ci};
242562306a36Sopenharmony_ci
242662306a36Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE
242762306a36Sopenharmony_ci/**
242862306a36Sopenharmony_ci * snd_ac97_update_power - update the powerdown register
242962306a36Sopenharmony_ci * @ac97: the codec instance
243062306a36Sopenharmony_ci * @reg: the rate register, e.g. AC97_PCM_FRONT_DAC_RATE
243162306a36Sopenharmony_ci * @powerup: non-zero when power up the part
243262306a36Sopenharmony_ci *
243362306a36Sopenharmony_ci * Update the AC97 powerdown register bits of the given part.
243462306a36Sopenharmony_ci *
243562306a36Sopenharmony_ci * Return: Zero.
243662306a36Sopenharmony_ci */
243762306a36Sopenharmony_ciint snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup)
243862306a36Sopenharmony_ci{
243962306a36Sopenharmony_ci	int i;
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci	if (! ac97)
244262306a36Sopenharmony_ci		return 0;
244362306a36Sopenharmony_ci
244462306a36Sopenharmony_ci	if (reg) {
244562306a36Sopenharmony_ci		/* SPDIF requires DAC power, too */
244662306a36Sopenharmony_ci		if (reg == AC97_SPDIF)
244762306a36Sopenharmony_ci			reg = AC97_PCM_FRONT_DAC_RATE;
244862306a36Sopenharmony_ci		for (i = 0; i < PWIDX_SIZE; i++) {
244962306a36Sopenharmony_ci			if (power_regs[i].reg == reg) {
245062306a36Sopenharmony_ci				if (powerup)
245162306a36Sopenharmony_ci					ac97->power_up |= (1 << i);
245262306a36Sopenharmony_ci				else
245362306a36Sopenharmony_ci					ac97->power_up &= ~(1 << i);
245462306a36Sopenharmony_ci				break;
245562306a36Sopenharmony_ci			}
245662306a36Sopenharmony_ci		}
245762306a36Sopenharmony_ci	}
245862306a36Sopenharmony_ci
245962306a36Sopenharmony_ci	if (ac97_is_power_save_mode(ac97) && !powerup)
246062306a36Sopenharmony_ci		/* adjust power-down bits after two seconds delay
246162306a36Sopenharmony_ci		 * (for avoiding loud click noises for many (OSS) apps
246262306a36Sopenharmony_ci		 *  that open/close frequently)
246362306a36Sopenharmony_ci		 */
246462306a36Sopenharmony_ci		schedule_delayed_work(&ac97->power_work,
246562306a36Sopenharmony_ci				      msecs_to_jiffies(power_save * 1000));
246662306a36Sopenharmony_ci	else {
246762306a36Sopenharmony_ci		cancel_delayed_work(&ac97->power_work);
246862306a36Sopenharmony_ci		update_power_regs(ac97);
246962306a36Sopenharmony_ci	}
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci	return 0;
247262306a36Sopenharmony_ci}
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_update_power);
247562306a36Sopenharmony_ci#endif /* CONFIG_SND_AC97_POWER_SAVE */
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_cistatic void update_power_regs(struct snd_ac97 *ac97)
247862306a36Sopenharmony_ci{
247962306a36Sopenharmony_ci	unsigned int power_up, bits;
248062306a36Sopenharmony_ci	int i;
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci	power_up = (1 << PWIDX_FRONT) | (1 << PWIDX_ADC);
248362306a36Sopenharmony_ci	power_up |= (1 << PWIDX_MIC);
248462306a36Sopenharmony_ci	if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
248562306a36Sopenharmony_ci		power_up |= (1 << PWIDX_SURR);
248662306a36Sopenharmony_ci	if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
248762306a36Sopenharmony_ci		power_up |= (1 << PWIDX_CLFE);
248862306a36Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE
248962306a36Sopenharmony_ci	if (ac97_is_power_save_mode(ac97))
249062306a36Sopenharmony_ci		power_up = ac97->power_up;
249162306a36Sopenharmony_ci#endif
249262306a36Sopenharmony_ci	if (power_up) {
249362306a36Sopenharmony_ci		if (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2) {
249462306a36Sopenharmony_ci			/* needs power-up analog mix and vref */
249562306a36Sopenharmony_ci			snd_ac97_update_bits(ac97, AC97_POWERDOWN,
249662306a36Sopenharmony_ci					     AC97_PD_PR3, 0);
249762306a36Sopenharmony_ci			msleep(1);
249862306a36Sopenharmony_ci			snd_ac97_update_bits(ac97, AC97_POWERDOWN,
249962306a36Sopenharmony_ci					     AC97_PD_PR2, 0);
250062306a36Sopenharmony_ci		}
250162306a36Sopenharmony_ci	}
250262306a36Sopenharmony_ci	for (i = 0; i < PWIDX_SIZE; i++) {
250362306a36Sopenharmony_ci		if (power_up & (1 << i))
250462306a36Sopenharmony_ci			bits = 0;
250562306a36Sopenharmony_ci		else
250662306a36Sopenharmony_ci			bits = power_regs[i].mask;
250762306a36Sopenharmony_ci		snd_ac97_update_bits(ac97, power_regs[i].power_reg,
250862306a36Sopenharmony_ci				     power_regs[i].mask, bits);
250962306a36Sopenharmony_ci	}
251062306a36Sopenharmony_ci	if (! power_up) {
251162306a36Sopenharmony_ci		if (! (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2)) {
251262306a36Sopenharmony_ci			/* power down analog mix and vref */
251362306a36Sopenharmony_ci			snd_ac97_update_bits(ac97, AC97_POWERDOWN,
251462306a36Sopenharmony_ci					     AC97_PD_PR2, AC97_PD_PR2);
251562306a36Sopenharmony_ci			snd_ac97_update_bits(ac97, AC97_POWERDOWN,
251662306a36Sopenharmony_ci					     AC97_PD_PR3, AC97_PD_PR3);
251762306a36Sopenharmony_ci		}
251862306a36Sopenharmony_ci	}
251962306a36Sopenharmony_ci}
252062306a36Sopenharmony_ci
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci#ifdef CONFIG_PM
252362306a36Sopenharmony_ci/**
252462306a36Sopenharmony_ci * snd_ac97_suspend - General suspend function for AC97 codec
252562306a36Sopenharmony_ci * @ac97: the ac97 instance
252662306a36Sopenharmony_ci *
252762306a36Sopenharmony_ci * Suspends the codec, power down the chip.
252862306a36Sopenharmony_ci */
252962306a36Sopenharmony_civoid snd_ac97_suspend(struct snd_ac97 *ac97)
253062306a36Sopenharmony_ci{
253162306a36Sopenharmony_ci	if (! ac97)
253262306a36Sopenharmony_ci		return;
253362306a36Sopenharmony_ci	if (ac97->build_ops->suspend)
253462306a36Sopenharmony_ci		ac97->build_ops->suspend(ac97);
253562306a36Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE
253662306a36Sopenharmony_ci	cancel_delayed_work_sync(&ac97->power_work);
253762306a36Sopenharmony_ci#endif
253862306a36Sopenharmony_ci	snd_ac97_powerdown(ac97);
253962306a36Sopenharmony_ci}
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_suspend);
254262306a36Sopenharmony_ci
254362306a36Sopenharmony_ci/*
254462306a36Sopenharmony_ci * restore ac97 status
254562306a36Sopenharmony_ci */
254662306a36Sopenharmony_cistatic void snd_ac97_restore_status(struct snd_ac97 *ac97)
254762306a36Sopenharmony_ci{
254862306a36Sopenharmony_ci	int i;
254962306a36Sopenharmony_ci
255062306a36Sopenharmony_ci	for (i = 2; i < 0x7c ; i += 2) {
255162306a36Sopenharmony_ci		if (i == AC97_POWERDOWN || i == AC97_EXTENDED_ID)
255262306a36Sopenharmony_ci			continue;
255362306a36Sopenharmony_ci		/* restore only accessible registers
255462306a36Sopenharmony_ci		 * some chip (e.g. nm256) may hang up when unsupported registers
255562306a36Sopenharmony_ci		 * are accessed..!
255662306a36Sopenharmony_ci		 */
255762306a36Sopenharmony_ci		if (test_bit(i, ac97->reg_accessed)) {
255862306a36Sopenharmony_ci			snd_ac97_write(ac97, i, ac97->regs[i]);
255962306a36Sopenharmony_ci			snd_ac97_read(ac97, i);
256062306a36Sopenharmony_ci		}
256162306a36Sopenharmony_ci	}
256262306a36Sopenharmony_ci}
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci/*
256562306a36Sopenharmony_ci * restore IEC958 status
256662306a36Sopenharmony_ci */
256762306a36Sopenharmony_cistatic void snd_ac97_restore_iec958(struct snd_ac97 *ac97)
256862306a36Sopenharmony_ci{
256962306a36Sopenharmony_ci	if (ac97->ext_id & AC97_EI_SPDIF) {
257062306a36Sopenharmony_ci		if (ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_SPDIF) {
257162306a36Sopenharmony_ci			/* reset spdif status */
257262306a36Sopenharmony_ci			snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
257362306a36Sopenharmony_ci			snd_ac97_write(ac97, AC97_EXTENDED_STATUS, ac97->regs[AC97_EXTENDED_STATUS]);
257462306a36Sopenharmony_ci			if (ac97->flags & AC97_CS_SPDIF)
257562306a36Sopenharmony_ci				snd_ac97_write(ac97, AC97_CSR_SPDIF, ac97->regs[AC97_CSR_SPDIF]);
257662306a36Sopenharmony_ci			else
257762306a36Sopenharmony_ci				snd_ac97_write(ac97, AC97_SPDIF, ac97->regs[AC97_SPDIF]);
257862306a36Sopenharmony_ci			snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); /* turn on again */
257962306a36Sopenharmony_ci		}
258062306a36Sopenharmony_ci	}
258162306a36Sopenharmony_ci}
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci/**
258462306a36Sopenharmony_ci * snd_ac97_resume - General resume function for AC97 codec
258562306a36Sopenharmony_ci * @ac97: the ac97 instance
258662306a36Sopenharmony_ci *
258762306a36Sopenharmony_ci * Do the standard resume procedure, power up and restoring the
258862306a36Sopenharmony_ci * old register values.
258962306a36Sopenharmony_ci */
259062306a36Sopenharmony_civoid snd_ac97_resume(struct snd_ac97 *ac97)
259162306a36Sopenharmony_ci{
259262306a36Sopenharmony_ci	unsigned long end_time;
259362306a36Sopenharmony_ci
259462306a36Sopenharmony_ci	if (! ac97)
259562306a36Sopenharmony_ci		return;
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci	if (ac97->bus->ops->reset) {
259862306a36Sopenharmony_ci		ac97->bus->ops->reset(ac97);
259962306a36Sopenharmony_ci		goto  __reset_ready;
260062306a36Sopenharmony_ci	}
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci	snd_ac97_write(ac97, AC97_POWERDOWN, 0);
260362306a36Sopenharmony_ci	if (! (ac97->flags & AC97_DEFAULT_POWER_OFF)) {
260462306a36Sopenharmony_ci		if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO))
260562306a36Sopenharmony_ci			snd_ac97_write(ac97, AC97_RESET, 0);
260662306a36Sopenharmony_ci		else if (!(ac97->scaps & AC97_SCAP_SKIP_MODEM))
260762306a36Sopenharmony_ci			snd_ac97_write(ac97, AC97_EXTENDED_MID, 0);
260862306a36Sopenharmony_ci		udelay(100);
260962306a36Sopenharmony_ci		snd_ac97_write(ac97, AC97_POWERDOWN, 0);
261062306a36Sopenharmony_ci	}
261162306a36Sopenharmony_ci	snd_ac97_write(ac97, AC97_GENERAL_PURPOSE, 0);
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci	snd_ac97_write(ac97, AC97_POWERDOWN, ac97->regs[AC97_POWERDOWN]);
261462306a36Sopenharmony_ci	if (ac97_is_audio(ac97)) {
261562306a36Sopenharmony_ci		ac97->bus->ops->write(ac97, AC97_MASTER, 0x8101);
261662306a36Sopenharmony_ci		end_time = jiffies + msecs_to_jiffies(100);
261762306a36Sopenharmony_ci		do {
261862306a36Sopenharmony_ci			if (snd_ac97_read(ac97, AC97_MASTER) == 0x8101)
261962306a36Sopenharmony_ci				break;
262062306a36Sopenharmony_ci			schedule_timeout_uninterruptible(1);
262162306a36Sopenharmony_ci		} while (time_after_eq(end_time, jiffies));
262262306a36Sopenharmony_ci		/* FIXME: extra delay */
262362306a36Sopenharmony_ci		ac97->bus->ops->write(ac97, AC97_MASTER, AC97_MUTE_MASK_MONO);
262462306a36Sopenharmony_ci		if (snd_ac97_read(ac97, AC97_MASTER) != AC97_MUTE_MASK_MONO)
262562306a36Sopenharmony_ci			msleep(250);
262662306a36Sopenharmony_ci	} else {
262762306a36Sopenharmony_ci		end_time = jiffies + msecs_to_jiffies(100);
262862306a36Sopenharmony_ci		do {
262962306a36Sopenharmony_ci			unsigned short val = snd_ac97_read(ac97, AC97_EXTENDED_MID);
263062306a36Sopenharmony_ci			if (val != 0xffff && (val & 1) != 0)
263162306a36Sopenharmony_ci				break;
263262306a36Sopenharmony_ci			schedule_timeout_uninterruptible(1);
263362306a36Sopenharmony_ci		} while (time_after_eq(end_time, jiffies));
263462306a36Sopenharmony_ci	}
263562306a36Sopenharmony_ci__reset_ready:
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci	if (ac97->bus->ops->init)
263862306a36Sopenharmony_ci		ac97->bus->ops->init(ac97);
263962306a36Sopenharmony_ci
264062306a36Sopenharmony_ci	if (ac97->build_ops->resume)
264162306a36Sopenharmony_ci		ac97->build_ops->resume(ac97);
264262306a36Sopenharmony_ci	else {
264362306a36Sopenharmony_ci		snd_ac97_restore_status(ac97);
264462306a36Sopenharmony_ci		snd_ac97_restore_iec958(ac97);
264562306a36Sopenharmony_ci	}
264662306a36Sopenharmony_ci}
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_resume);
264962306a36Sopenharmony_ci#endif
265062306a36Sopenharmony_ci
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_ci/*
265362306a36Sopenharmony_ci * Hardware tuning
265462306a36Sopenharmony_ci */
265562306a36Sopenharmony_cistatic void set_ctl_name(char *dst, const char *src, const char *suffix)
265662306a36Sopenharmony_ci{
265762306a36Sopenharmony_ci	const size_t msize = SNDRV_CTL_ELEM_ID_NAME_MAXLEN;
265862306a36Sopenharmony_ci
265962306a36Sopenharmony_ci	if (suffix) {
266062306a36Sopenharmony_ci		if (snprintf(dst, msize, "%s %s", src, suffix) >= msize)
266162306a36Sopenharmony_ci			pr_warn("ALSA: AC97 control name '%s %s' truncated to '%s'\n",
266262306a36Sopenharmony_ci				src, suffix, dst);
266362306a36Sopenharmony_ci	} else {
266462306a36Sopenharmony_ci		if (strscpy(dst, src, msize) < 0)
266562306a36Sopenharmony_ci			pr_warn("ALSA: AC97 control name '%s' truncated to '%s'\n",
266662306a36Sopenharmony_ci				src, dst);
266762306a36Sopenharmony_ci	}
266862306a36Sopenharmony_ci}
266962306a36Sopenharmony_ci
267062306a36Sopenharmony_ci/* remove the control with the given name and optional suffix */
267162306a36Sopenharmony_cistatic int snd_ac97_remove_ctl(struct snd_ac97 *ac97, const char *name,
267262306a36Sopenharmony_ci			       const char *suffix)
267362306a36Sopenharmony_ci{
267462306a36Sopenharmony_ci	struct snd_ctl_elem_id id;
267562306a36Sopenharmony_ci	memset(&id, 0, sizeof(id));
267662306a36Sopenharmony_ci	set_ctl_name(id.name, name, suffix);
267762306a36Sopenharmony_ci	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
267862306a36Sopenharmony_ci	return snd_ctl_remove_id(ac97->bus->card, &id);
267962306a36Sopenharmony_ci}
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_cistatic struct snd_kcontrol *ctl_find(struct snd_ac97 *ac97, const char *name, const char *suffix)
268262306a36Sopenharmony_ci{
268362306a36Sopenharmony_ci	struct snd_ctl_elem_id sid;
268462306a36Sopenharmony_ci	memset(&sid, 0, sizeof(sid));
268562306a36Sopenharmony_ci	set_ctl_name(sid.name, name, suffix);
268662306a36Sopenharmony_ci	sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
268762306a36Sopenharmony_ci	return snd_ctl_find_id(ac97->bus->card, &sid);
268862306a36Sopenharmony_ci}
268962306a36Sopenharmony_ci
269062306a36Sopenharmony_ci/* rename the control with the given name and optional suffix */
269162306a36Sopenharmony_cistatic int snd_ac97_rename_ctl(struct snd_ac97 *ac97, const char *src,
269262306a36Sopenharmony_ci			       const char *dst, const char *suffix)
269362306a36Sopenharmony_ci{
269462306a36Sopenharmony_ci	struct snd_kcontrol *kctl = ctl_find(ac97, src, suffix);
269562306a36Sopenharmony_ci	char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_ci	if (kctl) {
269862306a36Sopenharmony_ci		set_ctl_name(name, dst, suffix);
269962306a36Sopenharmony_ci		snd_ctl_rename(ac97->bus->card, kctl, name);
270062306a36Sopenharmony_ci		return 0;
270162306a36Sopenharmony_ci	}
270262306a36Sopenharmony_ci	return -ENOENT;
270362306a36Sopenharmony_ci}
270462306a36Sopenharmony_ci
270562306a36Sopenharmony_ci/* rename both Volume and Switch controls - don't check the return value */
270662306a36Sopenharmony_cistatic void snd_ac97_rename_vol_ctl(struct snd_ac97 *ac97, const char *src,
270762306a36Sopenharmony_ci				    const char *dst)
270862306a36Sopenharmony_ci{
270962306a36Sopenharmony_ci	snd_ac97_rename_ctl(ac97, src, dst, "Switch");
271062306a36Sopenharmony_ci	snd_ac97_rename_ctl(ac97, src, dst, "Volume");
271162306a36Sopenharmony_ci}
271262306a36Sopenharmony_ci
271362306a36Sopenharmony_ci/* swap controls */
271462306a36Sopenharmony_cistatic int snd_ac97_swap_ctl(struct snd_ac97 *ac97, const char *s1,
271562306a36Sopenharmony_ci			     const char *s2, const char *suffix)
271662306a36Sopenharmony_ci{
271762306a36Sopenharmony_ci	struct snd_kcontrol *kctl1, *kctl2;
271862306a36Sopenharmony_ci	char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
271962306a36Sopenharmony_ci
272062306a36Sopenharmony_ci	kctl1 = ctl_find(ac97, s1, suffix);
272162306a36Sopenharmony_ci	kctl2 = ctl_find(ac97, s2, suffix);
272262306a36Sopenharmony_ci	if (kctl1 && kctl2) {
272362306a36Sopenharmony_ci		set_ctl_name(name, s2, suffix);
272462306a36Sopenharmony_ci		snd_ctl_rename(ac97->bus->card, kctl1, name);
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_ci		set_ctl_name(name, s1, suffix);
272762306a36Sopenharmony_ci		snd_ctl_rename(ac97->bus->card, kctl2, name);
272862306a36Sopenharmony_ci
272962306a36Sopenharmony_ci		return 0;
273062306a36Sopenharmony_ci	}
273162306a36Sopenharmony_ci	return -ENOENT;
273262306a36Sopenharmony_ci}
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_ci#if 1
273562306a36Sopenharmony_ci/* bind hp and master controls instead of using only hp control */
273662306a36Sopenharmony_cistatic int bind_hp_volsw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
273762306a36Sopenharmony_ci{
273862306a36Sopenharmony_ci	int err = snd_ac97_put_volsw(kcontrol, ucontrol);
273962306a36Sopenharmony_ci	if (err > 0) {
274062306a36Sopenharmony_ci		unsigned long priv_saved = kcontrol->private_value;
274162306a36Sopenharmony_ci		kcontrol->private_value = (kcontrol->private_value & ~0xff) | AC97_HEADPHONE;
274262306a36Sopenharmony_ci		snd_ac97_put_volsw(kcontrol, ucontrol);
274362306a36Sopenharmony_ci		kcontrol->private_value = priv_saved;
274462306a36Sopenharmony_ci	}
274562306a36Sopenharmony_ci	return err;
274662306a36Sopenharmony_ci}
274762306a36Sopenharmony_ci
274862306a36Sopenharmony_ci/* ac97 tune: bind Master and Headphone controls */
274962306a36Sopenharmony_cistatic int tune_hp_only(struct snd_ac97 *ac97)
275062306a36Sopenharmony_ci{
275162306a36Sopenharmony_ci	struct snd_kcontrol *msw = ctl_find(ac97, "Master Playback Switch", NULL);
275262306a36Sopenharmony_ci	struct snd_kcontrol *mvol = ctl_find(ac97, "Master Playback Volume", NULL);
275362306a36Sopenharmony_ci	if (! msw || ! mvol)
275462306a36Sopenharmony_ci		return -ENOENT;
275562306a36Sopenharmony_ci	msw->put = bind_hp_volsw_put;
275662306a36Sopenharmony_ci	mvol->put = bind_hp_volsw_put;
275762306a36Sopenharmony_ci	snd_ac97_remove_ctl(ac97, "Headphone Playback", "Switch");
275862306a36Sopenharmony_ci	snd_ac97_remove_ctl(ac97, "Headphone Playback", "Volume");
275962306a36Sopenharmony_ci	return 0;
276062306a36Sopenharmony_ci}
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_ci#else
276362306a36Sopenharmony_ci/* ac97 tune: use Headphone control as master */
276462306a36Sopenharmony_cistatic int tune_hp_only(struct snd_ac97 *ac97)
276562306a36Sopenharmony_ci{
276662306a36Sopenharmony_ci	if (ctl_find(ac97, "Headphone Playback Switch", NULL) == NULL)
276762306a36Sopenharmony_ci		return -ENOENT;
276862306a36Sopenharmony_ci	snd_ac97_remove_ctl(ac97, "Master Playback", "Switch");
276962306a36Sopenharmony_ci	snd_ac97_remove_ctl(ac97, "Master Playback", "Volume");
277062306a36Sopenharmony_ci	snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback");
277162306a36Sopenharmony_ci	return 0;
277262306a36Sopenharmony_ci}
277362306a36Sopenharmony_ci#endif
277462306a36Sopenharmony_ci
277562306a36Sopenharmony_ci/* ac97 tune: swap Headphone and Master controls */
277662306a36Sopenharmony_cistatic int tune_swap_hp(struct snd_ac97 *ac97)
277762306a36Sopenharmony_ci{
277862306a36Sopenharmony_ci	if (ctl_find(ac97, "Headphone Playback Switch", NULL) == NULL)
277962306a36Sopenharmony_ci		return -ENOENT;
278062306a36Sopenharmony_ci	snd_ac97_rename_vol_ctl(ac97, "Master Playback", "Line-Out Playback");
278162306a36Sopenharmony_ci	snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback");
278262306a36Sopenharmony_ci	return 0;
278362306a36Sopenharmony_ci}
278462306a36Sopenharmony_ci
278562306a36Sopenharmony_ci/* ac97 tune: swap Surround and Master controls */
278662306a36Sopenharmony_cistatic int tune_swap_surround(struct snd_ac97 *ac97)
278762306a36Sopenharmony_ci{
278862306a36Sopenharmony_ci	if (snd_ac97_swap_ctl(ac97, "Master Playback", "Surround Playback", "Switch") ||
278962306a36Sopenharmony_ci	    snd_ac97_swap_ctl(ac97, "Master Playback", "Surround Playback", "Volume"))
279062306a36Sopenharmony_ci		return -ENOENT;
279162306a36Sopenharmony_ci	return 0;
279262306a36Sopenharmony_ci}
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_ci/* ac97 tune: set up mic sharing for AD codecs */
279562306a36Sopenharmony_cistatic int tune_ad_sharing(struct snd_ac97 *ac97)
279662306a36Sopenharmony_ci{
279762306a36Sopenharmony_ci	unsigned short scfg;
279862306a36Sopenharmony_ci	if ((ac97->id & 0xffffff00) != 0x41445300) {
279962306a36Sopenharmony_ci		ac97_err(ac97, "ac97_quirk AD_SHARING is only for AD codecs\n");
280062306a36Sopenharmony_ci		return -EINVAL;
280162306a36Sopenharmony_ci	}
280262306a36Sopenharmony_ci	/* Turn on OMS bit to route microphone to back panel */
280362306a36Sopenharmony_ci	scfg = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG);
280462306a36Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, scfg | 0x0200);
280562306a36Sopenharmony_ci	return 0;
280662306a36Sopenharmony_ci}
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_alc_jack_detect =
280962306a36Sopenharmony_ciAC97_SINGLE("Jack Detect", AC97_ALC650_CLOCK, 5, 1, 0);
281062306a36Sopenharmony_ci
281162306a36Sopenharmony_ci/* ac97 tune: set up ALC jack-select */
281262306a36Sopenharmony_cistatic int tune_alc_jack(struct snd_ac97 *ac97)
281362306a36Sopenharmony_ci{
281462306a36Sopenharmony_ci	if ((ac97->id & 0xffffff00) != 0x414c4700) {
281562306a36Sopenharmony_ci		ac97_err(ac97,
281662306a36Sopenharmony_ci			 "ac97_quirk ALC_JACK is only for Realtek codecs\n");
281762306a36Sopenharmony_ci		return -EINVAL;
281862306a36Sopenharmony_ci	}
281962306a36Sopenharmony_ci	snd_ac97_update_bits(ac97, 0x7a, 0x20, 0x20); /* select jack detect function */
282062306a36Sopenharmony_ci	snd_ac97_update_bits(ac97, 0x7a, 0x01, 0x01); /* Line-out auto mute */
282162306a36Sopenharmony_ci	if (ac97->id == AC97_ID_ALC658D)
282262306a36Sopenharmony_ci		snd_ac97_update_bits(ac97, 0x74, 0x0800, 0x0800);
282362306a36Sopenharmony_ci	return snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&snd_ac97_alc_jack_detect, ac97));
282462306a36Sopenharmony_ci}
282562306a36Sopenharmony_ci
282662306a36Sopenharmony_ci/* ac97 tune: inversed EAPD bit */
282762306a36Sopenharmony_cistatic int tune_inv_eapd(struct snd_ac97 *ac97)
282862306a36Sopenharmony_ci{
282962306a36Sopenharmony_ci	struct snd_kcontrol *kctl = ctl_find(ac97, "External Amplifier", NULL);
283062306a36Sopenharmony_ci	if (! kctl)
283162306a36Sopenharmony_ci		return -ENOENT;
283262306a36Sopenharmony_ci	set_inv_eapd(ac97, kctl);
283362306a36Sopenharmony_ci	return 0;
283462306a36Sopenharmony_ci}
283562306a36Sopenharmony_ci
283662306a36Sopenharmony_cistatic int master_mute_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
283762306a36Sopenharmony_ci{
283862306a36Sopenharmony_ci	int err = snd_ac97_put_volsw(kcontrol, ucontrol);
283962306a36Sopenharmony_ci	if (err > 0) {
284062306a36Sopenharmony_ci		struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
284162306a36Sopenharmony_ci		int shift = (kcontrol->private_value >> 8) & 0x0f;
284262306a36Sopenharmony_ci		int rshift = (kcontrol->private_value >> 12) & 0x0f;
284362306a36Sopenharmony_ci		unsigned short mask;
284462306a36Sopenharmony_ci		if (shift != rshift)
284562306a36Sopenharmony_ci			mask = AC97_MUTE_MASK_STEREO;
284662306a36Sopenharmony_ci		else
284762306a36Sopenharmony_ci			mask = AC97_MUTE_MASK_MONO;
284862306a36Sopenharmony_ci		snd_ac97_update_bits(ac97, AC97_POWERDOWN, AC97_PD_EAPD,
284962306a36Sopenharmony_ci				     (ac97->regs[AC97_MASTER] & mask) == mask ?
285062306a36Sopenharmony_ci				     AC97_PD_EAPD : 0);
285162306a36Sopenharmony_ci	}
285262306a36Sopenharmony_ci	return err;
285362306a36Sopenharmony_ci}
285462306a36Sopenharmony_ci
285562306a36Sopenharmony_ci/* ac97 tune: EAPD controls mute LED bound with the master mute */
285662306a36Sopenharmony_cistatic int tune_mute_led(struct snd_ac97 *ac97)
285762306a36Sopenharmony_ci{
285862306a36Sopenharmony_ci	struct snd_kcontrol *msw = ctl_find(ac97, "Master Playback Switch", NULL);
285962306a36Sopenharmony_ci	if (! msw)
286062306a36Sopenharmony_ci		return -ENOENT;
286162306a36Sopenharmony_ci	msw->put = master_mute_sw_put;
286262306a36Sopenharmony_ci	snd_ac97_remove_ctl(ac97, "External Amplifier", NULL);
286362306a36Sopenharmony_ci	snd_ac97_update_bits(
286462306a36Sopenharmony_ci		ac97, AC97_POWERDOWN,
286562306a36Sopenharmony_ci		AC97_PD_EAPD, AC97_PD_EAPD /* mute LED on */
286662306a36Sopenharmony_ci	);
286762306a36Sopenharmony_ci	ac97->scaps |= AC97_SCAP_EAPD_LED;
286862306a36Sopenharmony_ci	return 0;
286962306a36Sopenharmony_ci}
287062306a36Sopenharmony_ci
287162306a36Sopenharmony_cistatic int hp_master_mute_sw_put(struct snd_kcontrol *kcontrol,
287262306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
287362306a36Sopenharmony_ci{
287462306a36Sopenharmony_ci	int err = bind_hp_volsw_put(kcontrol, ucontrol);
287562306a36Sopenharmony_ci	if (err > 0) {
287662306a36Sopenharmony_ci		struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
287762306a36Sopenharmony_ci		int shift = (kcontrol->private_value >> 8) & 0x0f;
287862306a36Sopenharmony_ci		int rshift = (kcontrol->private_value >> 12) & 0x0f;
287962306a36Sopenharmony_ci		unsigned short mask;
288062306a36Sopenharmony_ci		if (shift != rshift)
288162306a36Sopenharmony_ci			mask = AC97_MUTE_MASK_STEREO;
288262306a36Sopenharmony_ci		else
288362306a36Sopenharmony_ci			mask = AC97_MUTE_MASK_MONO;
288462306a36Sopenharmony_ci		snd_ac97_update_bits(ac97, AC97_POWERDOWN, AC97_PD_EAPD,
288562306a36Sopenharmony_ci				     (ac97->regs[AC97_MASTER] & mask) == mask ?
288662306a36Sopenharmony_ci				     AC97_PD_EAPD : 0);
288762306a36Sopenharmony_ci	}
288862306a36Sopenharmony_ci	return err;
288962306a36Sopenharmony_ci}
289062306a36Sopenharmony_ci
289162306a36Sopenharmony_cistatic int tune_hp_mute_led(struct snd_ac97 *ac97)
289262306a36Sopenharmony_ci{
289362306a36Sopenharmony_ci	struct snd_kcontrol *msw = ctl_find(ac97, "Master Playback Switch", NULL);
289462306a36Sopenharmony_ci	struct snd_kcontrol *mvol = ctl_find(ac97, "Master Playback Volume", NULL);
289562306a36Sopenharmony_ci	if (! msw || ! mvol)
289662306a36Sopenharmony_ci		return -ENOENT;
289762306a36Sopenharmony_ci	msw->put = hp_master_mute_sw_put;
289862306a36Sopenharmony_ci	mvol->put = bind_hp_volsw_put;
289962306a36Sopenharmony_ci	snd_ac97_remove_ctl(ac97, "External Amplifier", NULL);
290062306a36Sopenharmony_ci	snd_ac97_remove_ctl(ac97, "Headphone Playback", "Switch");
290162306a36Sopenharmony_ci	snd_ac97_remove_ctl(ac97, "Headphone Playback", "Volume");
290262306a36Sopenharmony_ci	snd_ac97_update_bits(
290362306a36Sopenharmony_ci		ac97, AC97_POWERDOWN,
290462306a36Sopenharmony_ci		AC97_PD_EAPD, AC97_PD_EAPD /* mute LED on */
290562306a36Sopenharmony_ci	);
290662306a36Sopenharmony_ci	return 0;
290762306a36Sopenharmony_ci}
290862306a36Sopenharmony_ci
290962306a36Sopenharmony_cistruct quirk_table {
291062306a36Sopenharmony_ci	const char *name;
291162306a36Sopenharmony_ci	int (*func)(struct snd_ac97 *);
291262306a36Sopenharmony_ci};
291362306a36Sopenharmony_ci
291462306a36Sopenharmony_cistatic const struct quirk_table applicable_quirks[] = {
291562306a36Sopenharmony_ci	{ "none", NULL },
291662306a36Sopenharmony_ci	{ "hp_only", tune_hp_only },
291762306a36Sopenharmony_ci	{ "swap_hp", tune_swap_hp },
291862306a36Sopenharmony_ci	{ "swap_surround", tune_swap_surround },
291962306a36Sopenharmony_ci	{ "ad_sharing", tune_ad_sharing },
292062306a36Sopenharmony_ci	{ "alc_jack", tune_alc_jack },
292162306a36Sopenharmony_ci	{ "inv_eapd", tune_inv_eapd },
292262306a36Sopenharmony_ci	{ "mute_led", tune_mute_led },
292362306a36Sopenharmony_ci	{ "hp_mute_led", tune_hp_mute_led },
292462306a36Sopenharmony_ci};
292562306a36Sopenharmony_ci
292662306a36Sopenharmony_ci/* apply the quirk with the given type */
292762306a36Sopenharmony_cistatic int apply_quirk(struct snd_ac97 *ac97, int type)
292862306a36Sopenharmony_ci{
292962306a36Sopenharmony_ci	if (type <= 0)
293062306a36Sopenharmony_ci		return 0;
293162306a36Sopenharmony_ci	else if (type >= ARRAY_SIZE(applicable_quirks))
293262306a36Sopenharmony_ci		return -EINVAL;
293362306a36Sopenharmony_ci	if (applicable_quirks[type].func)
293462306a36Sopenharmony_ci		return applicable_quirks[type].func(ac97);
293562306a36Sopenharmony_ci	return 0;
293662306a36Sopenharmony_ci}
293762306a36Sopenharmony_ci
293862306a36Sopenharmony_ci/* apply the quirk with the given name */
293962306a36Sopenharmony_cistatic int apply_quirk_str(struct snd_ac97 *ac97, const char *typestr)
294062306a36Sopenharmony_ci{
294162306a36Sopenharmony_ci	int i;
294262306a36Sopenharmony_ci	const struct quirk_table *q;
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(applicable_quirks); i++) {
294562306a36Sopenharmony_ci		q = &applicable_quirks[i];
294662306a36Sopenharmony_ci		if (q->name && ! strcmp(typestr, q->name))
294762306a36Sopenharmony_ci			return apply_quirk(ac97, i);
294862306a36Sopenharmony_ci	}
294962306a36Sopenharmony_ci	/* for compatibility, accept the numbers, too */
295062306a36Sopenharmony_ci	if (*typestr >= '0' && *typestr <= '9')
295162306a36Sopenharmony_ci		return apply_quirk(ac97, (int)simple_strtoul(typestr, NULL, 10));
295262306a36Sopenharmony_ci	return -EINVAL;
295362306a36Sopenharmony_ci}
295462306a36Sopenharmony_ci
295562306a36Sopenharmony_ci/**
295662306a36Sopenharmony_ci * snd_ac97_tune_hardware - tune up the hardware
295762306a36Sopenharmony_ci * @ac97: the ac97 instance
295862306a36Sopenharmony_ci * @quirk: quirk list
295962306a36Sopenharmony_ci * @override: explicit quirk value (overrides the list if non-NULL)
296062306a36Sopenharmony_ci *
296162306a36Sopenharmony_ci * Do some workaround for each pci device, such as renaming of the
296262306a36Sopenharmony_ci * headphone (true line-out) control as "Master".
296362306a36Sopenharmony_ci * The quirk-list must be terminated with a zero-filled entry.
296462306a36Sopenharmony_ci *
296562306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure.
296662306a36Sopenharmony_ci */
296762306a36Sopenharmony_ci
296862306a36Sopenharmony_ciint snd_ac97_tune_hardware(struct snd_ac97 *ac97,
296962306a36Sopenharmony_ci			   const struct ac97_quirk *quirk, const char *override)
297062306a36Sopenharmony_ci{
297162306a36Sopenharmony_ci	int result;
297262306a36Sopenharmony_ci
297362306a36Sopenharmony_ci	/* quirk overriden? */
297462306a36Sopenharmony_ci	if (override && strcmp(override, "-1") && strcmp(override, "default")) {
297562306a36Sopenharmony_ci		result = apply_quirk_str(ac97, override);
297662306a36Sopenharmony_ci		if (result < 0)
297762306a36Sopenharmony_ci			ac97_err(ac97, "applying quirk type %s failed (%d)\n",
297862306a36Sopenharmony_ci				 override, result);
297962306a36Sopenharmony_ci		return result;
298062306a36Sopenharmony_ci	}
298162306a36Sopenharmony_ci
298262306a36Sopenharmony_ci	if (! quirk)
298362306a36Sopenharmony_ci		return -EINVAL;
298462306a36Sopenharmony_ci
298562306a36Sopenharmony_ci	for (; quirk->subvendor; quirk++) {
298662306a36Sopenharmony_ci		if (quirk->subvendor != ac97->subsystem_vendor)
298762306a36Sopenharmony_ci			continue;
298862306a36Sopenharmony_ci		if ((! quirk->mask && quirk->subdevice == ac97->subsystem_device) ||
298962306a36Sopenharmony_ci		    quirk->subdevice == (quirk->mask & ac97->subsystem_device)) {
299062306a36Sopenharmony_ci			if (quirk->codec_id && quirk->codec_id != ac97->id)
299162306a36Sopenharmony_ci				continue;
299262306a36Sopenharmony_ci			ac97_dbg(ac97, "ac97 quirk for %s (%04x:%04x)\n",
299362306a36Sopenharmony_ci				 quirk->name, ac97->subsystem_vendor,
299462306a36Sopenharmony_ci				 ac97->subsystem_device);
299562306a36Sopenharmony_ci			result = apply_quirk(ac97, quirk->type);
299662306a36Sopenharmony_ci			if (result < 0)
299762306a36Sopenharmony_ci				ac97_err(ac97,
299862306a36Sopenharmony_ci					 "applying quirk type %d for %s failed (%d)\n",
299962306a36Sopenharmony_ci					 quirk->type, quirk->name, result);
300062306a36Sopenharmony_ci			return result;
300162306a36Sopenharmony_ci		}
300262306a36Sopenharmony_ci	}
300362306a36Sopenharmony_ci	return 0;
300462306a36Sopenharmony_ci}
300562306a36Sopenharmony_ci
300662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_tune_hardware);
3007