18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
48c2ecf20Sopenharmony_ci *  Universal interface for Audio Codec '97
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *  For more details look to AC '97 component specification revision 2.2
78c2ecf20Sopenharmony_ci *  by Intel Corporation (http://developer.intel.com) and to datasheets
88c2ecf20Sopenharmony_ci *  for specific codecs.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "ac97_local.h"
128c2ecf20Sopenharmony_ci#include "ac97_patch.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/*
158c2ecf20Sopenharmony_ci *  Forward declarations
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97,
198c2ecf20Sopenharmony_ci						    const char *name);
208c2ecf20Sopenharmony_cistatic int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
218c2ecf20Sopenharmony_ci				const unsigned int *tlv,
228c2ecf20Sopenharmony_ci				const char * const *followers);
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/*
258c2ecf20Sopenharmony_ci *  Chip specific initialization
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic int patch_build_controls(struct snd_ac97 * ac97, const struct snd_kcontrol_new *controls, int count)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	int idx, err;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	for (idx = 0; idx < count; idx++)
338c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&controls[idx], ac97))) < 0)
348c2ecf20Sopenharmony_ci			return err;
358c2ecf20Sopenharmony_ci	return 0;
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* replace with a new TLV */
398c2ecf20Sopenharmony_cistatic void reset_tlv(struct snd_ac97 *ac97, const char *name,
408c2ecf20Sopenharmony_ci		      const unsigned int *tlv)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	struct snd_ctl_elem_id sid;
438c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
448c2ecf20Sopenharmony_ci	memset(&sid, 0, sizeof(sid));
458c2ecf20Sopenharmony_ci	strcpy(sid.name, name);
468c2ecf20Sopenharmony_ci	sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
478c2ecf20Sopenharmony_ci	kctl = snd_ctl_find_id(ac97->bus->card, &sid);
488c2ecf20Sopenharmony_ci	if (kctl && kctl->tlv.p)
498c2ecf20Sopenharmony_ci		kctl->tlv.p = tlv;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/* set to the page, update bits and restore the page */
538c2ecf20Sopenharmony_cistatic int ac97_update_bits_page(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value, unsigned short page)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	unsigned short page_save;
568c2ecf20Sopenharmony_ci	int ret;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	mutex_lock(&ac97->page_mutex);
598c2ecf20Sopenharmony_ci	page_save = snd_ac97_read(ac97, AC97_INT_PAGING) & AC97_PAGE_MASK;
608c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page);
618c2ecf20Sopenharmony_ci	ret = snd_ac97_update_bits(ac97, reg, mask, value);
628c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page_save);
638c2ecf20Sopenharmony_ci	mutex_unlock(&ac97->page_mutex); /* unlock paging */
648c2ecf20Sopenharmony_ci	return ret;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/*
688c2ecf20Sopenharmony_ci * shared line-in/mic controls
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_cistatic int ac97_surround_jack_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	static const char * const texts[] = { "Shared", "Independent" };
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 2, texts);
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int ac97_surround_jack_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = ac97->indep_surround;
828c2ecf20Sopenharmony_ci	return 0;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int ac97_surround_jack_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
888c2ecf20Sopenharmony_ci	unsigned char indep = !!ucontrol->value.enumerated.item[0];
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (indep != ac97->indep_surround) {
918c2ecf20Sopenharmony_ci		ac97->indep_surround = indep;
928c2ecf20Sopenharmony_ci		if (ac97->build_ops->update_jacks)
938c2ecf20Sopenharmony_ci			ac97->build_ops->update_jacks(ac97);
948c2ecf20Sopenharmony_ci		return 1;
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci	return 0;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic int ac97_channel_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	static const char * const texts[] = { "2ch", "4ch", "6ch", "8ch" };
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, kcontrol->private_value, texts);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic int ac97_channel_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = ac97->channel_mode;
1118c2ecf20Sopenharmony_ci	return 0;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
1178c2ecf20Sopenharmony_ci	unsigned char mode = ucontrol->value.enumerated.item[0];
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	if (mode >= kcontrol->private_value)
1208c2ecf20Sopenharmony_ci		return -EINVAL;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	if (mode != ac97->channel_mode) {
1238c2ecf20Sopenharmony_ci		ac97->channel_mode = mode;
1248c2ecf20Sopenharmony_ci		if (ac97->build_ops->update_jacks)
1258c2ecf20Sopenharmony_ci			ac97->build_ops->update_jacks(ac97);
1268c2ecf20Sopenharmony_ci		return 1;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci	return 0;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci#define AC97_SURROUND_JACK_MODE_CTL \
1328c2ecf20Sopenharmony_ci	{ \
1338c2ecf20Sopenharmony_ci		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER, \
1348c2ecf20Sopenharmony_ci		.name	= "Surround Jack Mode", \
1358c2ecf20Sopenharmony_ci		.info = ac97_surround_jack_mode_info, \
1368c2ecf20Sopenharmony_ci		.get = ac97_surround_jack_mode_get, \
1378c2ecf20Sopenharmony_ci		.put = ac97_surround_jack_mode_put, \
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci/* 6ch */
1408c2ecf20Sopenharmony_ci#define AC97_CHANNEL_MODE_CTL \
1418c2ecf20Sopenharmony_ci	{ \
1428c2ecf20Sopenharmony_ci		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER, \
1438c2ecf20Sopenharmony_ci		.name	= "Channel Mode", \
1448c2ecf20Sopenharmony_ci		.info = ac97_channel_mode_info, \
1458c2ecf20Sopenharmony_ci		.get = ac97_channel_mode_get, \
1468c2ecf20Sopenharmony_ci		.put = ac97_channel_mode_put, \
1478c2ecf20Sopenharmony_ci		.private_value = 3, \
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci/* 4ch */
1508c2ecf20Sopenharmony_ci#define AC97_CHANNEL_MODE_4CH_CTL \
1518c2ecf20Sopenharmony_ci	{ \
1528c2ecf20Sopenharmony_ci		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER, \
1538c2ecf20Sopenharmony_ci		.name	= "Channel Mode", \
1548c2ecf20Sopenharmony_ci		.info = ac97_channel_mode_info, \
1558c2ecf20Sopenharmony_ci		.get = ac97_channel_mode_get, \
1568c2ecf20Sopenharmony_ci		.put = ac97_channel_mode_put, \
1578c2ecf20Sopenharmony_ci		.private_value = 2, \
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci/* 8ch */
1608c2ecf20Sopenharmony_ci#define AC97_CHANNEL_MODE_8CH_CTL \
1618c2ecf20Sopenharmony_ci	{ \
1628c2ecf20Sopenharmony_ci		.iface  = SNDRV_CTL_ELEM_IFACE_MIXER, \
1638c2ecf20Sopenharmony_ci		.name   = "Channel Mode", \
1648c2ecf20Sopenharmony_ci		.info = ac97_channel_mode_info, \
1658c2ecf20Sopenharmony_ci		.get = ac97_channel_mode_get, \
1668c2ecf20Sopenharmony_ci		.put = ac97_channel_mode_put, \
1678c2ecf20Sopenharmony_ci		.private_value = 4, \
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic inline int is_surround_on(struct snd_ac97 *ac97)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	return ac97->channel_mode >= 1;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic inline int is_clfe_on(struct snd_ac97 *ac97)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	return ac97->channel_mode >= 2;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci/* system has shared jacks with surround out enabled */
1818c2ecf20Sopenharmony_cistatic inline int is_shared_surrout(struct snd_ac97 *ac97)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	return !ac97->indep_surround && is_surround_on(ac97);
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci/* system has shared jacks with center/lfe out enabled */
1878c2ecf20Sopenharmony_cistatic inline int is_shared_clfeout(struct snd_ac97 *ac97)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	return !ac97->indep_surround && is_clfe_on(ac97);
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/* system has shared jacks with line in enabled */
1938c2ecf20Sopenharmony_cistatic inline int is_shared_linein(struct snd_ac97 *ac97)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	return !ac97->indep_surround && !is_surround_on(ac97);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci/* system has shared jacks with mic in enabled */
1998c2ecf20Sopenharmony_cistatic inline int is_shared_micin(struct snd_ac97 *ac97)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	return !ac97->indep_surround && !is_clfe_on(ac97);
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic inline int alc850_is_aux_back_surround(struct snd_ac97 *ac97)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	return is_surround_on(ac97);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci/* The following snd_ac97_ymf753_... items added by David Shust (dshust@shustring.com) */
2108c2ecf20Sopenharmony_ci/* Modified for YMF743 by Keita Maehara <maehara@debian.org> */
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci/* It is possible to indicate to the Yamaha YMF7x3 the type of
2138c2ecf20Sopenharmony_ci   speakers being used. */
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic int snd_ac97_ymf7x3_info_speaker(struct snd_kcontrol *kcontrol,
2168c2ecf20Sopenharmony_ci					struct snd_ctl_elem_info *uinfo)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	static const char * const texts[3] = {
2198c2ecf20Sopenharmony_ci		"Standard", "Small", "Smaller"
2208c2ecf20Sopenharmony_ci	};
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 3, texts);
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic int snd_ac97_ymf7x3_get_speaker(struct snd_kcontrol *kcontrol,
2268c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
2298c2ecf20Sopenharmony_ci	unsigned short val;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	val = ac97->regs[AC97_YMF7X3_3D_MODE_SEL];
2328c2ecf20Sopenharmony_ci	val = (val >> 10) & 3;
2338c2ecf20Sopenharmony_ci	if (val > 0)    /* 0 = invalid */
2348c2ecf20Sopenharmony_ci		val--;
2358c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = val;
2368c2ecf20Sopenharmony_ci	return 0;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic int snd_ac97_ymf7x3_put_speaker(struct snd_kcontrol *kcontrol,
2408c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
2438c2ecf20Sopenharmony_ci	unsigned short val;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] > 2)
2468c2ecf20Sopenharmony_ci		return -EINVAL;
2478c2ecf20Sopenharmony_ci	val = (ucontrol->value.enumerated.item[0] + 1) << 10;
2488c2ecf20Sopenharmony_ci	return snd_ac97_update(ac97, AC97_YMF7X3_3D_MODE_SEL, val);
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_ymf7x3_controls_speaker =
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	.iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
2548c2ecf20Sopenharmony_ci	.name   = "3D Control - Speaker",
2558c2ecf20Sopenharmony_ci	.info   = snd_ac97_ymf7x3_info_speaker,
2568c2ecf20Sopenharmony_ci	.get    = snd_ac97_ymf7x3_get_speaker,
2578c2ecf20Sopenharmony_ci	.put    = snd_ac97_ymf7x3_put_speaker,
2588c2ecf20Sopenharmony_ci};
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci/* It is possible to indicate to the Yamaha YMF7x3 the source to
2618c2ecf20Sopenharmony_ci   direct to the S/PDIF output. */
2628c2ecf20Sopenharmony_cistatic int snd_ac97_ymf7x3_spdif_source_info(struct snd_kcontrol *kcontrol,
2638c2ecf20Sopenharmony_ci					     struct snd_ctl_elem_info *uinfo)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	static const char * const texts[2] = { "AC-Link", "A/D Converter" };
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 2, texts);
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic int snd_ac97_ymf7x3_spdif_source_get(struct snd_kcontrol *kcontrol,
2718c2ecf20Sopenharmony_ci					    struct snd_ctl_elem_value *ucontrol)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
2748c2ecf20Sopenharmony_ci	unsigned short val;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	val = ac97->regs[AC97_YMF7X3_DIT_CTRL];
2778c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = (val >> 1) & 1;
2788c2ecf20Sopenharmony_ci	return 0;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic int snd_ac97_ymf7x3_spdif_source_put(struct snd_kcontrol *kcontrol,
2828c2ecf20Sopenharmony_ci					    struct snd_ctl_elem_value *ucontrol)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
2858c2ecf20Sopenharmony_ci	unsigned short val;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] > 1)
2888c2ecf20Sopenharmony_ci		return -EINVAL;
2898c2ecf20Sopenharmony_ci	val = ucontrol->value.enumerated.item[0] << 1;
2908c2ecf20Sopenharmony_ci	return snd_ac97_update_bits(ac97, AC97_YMF7X3_DIT_CTRL, 0x0002, val);
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic int patch_yamaha_ymf7x3_3d(struct snd_ac97 *ac97)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
2968c2ecf20Sopenharmony_ci	int err;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97);
2998c2ecf20Sopenharmony_ci	err = snd_ctl_add(ac97->bus->card, kctl);
3008c2ecf20Sopenharmony_ci	if (err < 0)
3018c2ecf20Sopenharmony_ci		return err;
3028c2ecf20Sopenharmony_ci	strcpy(kctl->id.name, "3D Control - Wide");
3038c2ecf20Sopenharmony_ci	kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 9, 7, 0);
3048c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
3058c2ecf20Sopenharmony_ci	err = snd_ctl_add(ac97->bus->card,
3068c2ecf20Sopenharmony_ci			  snd_ac97_cnew(&snd_ac97_ymf7x3_controls_speaker,
3078c2ecf20Sopenharmony_ci					ac97));
3088c2ecf20Sopenharmony_ci	if (err < 0)
3098c2ecf20Sopenharmony_ci		return err;
3108c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_YMF7X3_3D_MODE_SEL, 0x0c00);
3118c2ecf20Sopenharmony_ci	return 0;
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_yamaha_ymf743_controls_spdif[3] =
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
3178c2ecf20Sopenharmony_ci		    AC97_YMF7X3_DIT_CTRL, 0, 1, 0),
3188c2ecf20Sopenharmony_ci	{
3198c2ecf20Sopenharmony_ci		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
3208c2ecf20Sopenharmony_ci		.name	= SNDRV_CTL_NAME_IEC958("", PLAYBACK, NONE) "Source",
3218c2ecf20Sopenharmony_ci		.info	= snd_ac97_ymf7x3_spdif_source_info,
3228c2ecf20Sopenharmony_ci		.get	= snd_ac97_ymf7x3_spdif_source_get,
3238c2ecf20Sopenharmony_ci		.put	= snd_ac97_ymf7x3_spdif_source_put,
3248c2ecf20Sopenharmony_ci	},
3258c2ecf20Sopenharmony_ci	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("", NONE, NONE) "Mute",
3268c2ecf20Sopenharmony_ci		    AC97_YMF7X3_DIT_CTRL, 2, 1, 1)
3278c2ecf20Sopenharmony_ci};
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic int patch_yamaha_ymf743_build_spdif(struct snd_ac97 *ac97)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	int err;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3);
3348c2ecf20Sopenharmony_ci	if (err < 0)
3358c2ecf20Sopenharmony_ci		return err;
3368c2ecf20Sopenharmony_ci	err = patch_build_controls(ac97,
3378c2ecf20Sopenharmony_ci				   snd_ac97_yamaha_ymf743_controls_spdif, 3);
3388c2ecf20Sopenharmony_ci	if (err < 0)
3398c2ecf20Sopenharmony_ci		return err;
3408c2ecf20Sopenharmony_ci	/* set default PCM S/PDIF params */
3418c2ecf20Sopenharmony_ci	/* PCM audio,no copyright,no preemphasis,PCM coder,original */
3428c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_YMF7X3_DIT_CTRL, 0xa201);
3438c2ecf20Sopenharmony_ci	return 0;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_yamaha_ymf743_ops = {
3478c2ecf20Sopenharmony_ci	.build_spdif	= patch_yamaha_ymf743_build_spdif,
3488c2ecf20Sopenharmony_ci	.build_3d	= patch_yamaha_ymf7x3_3d,
3498c2ecf20Sopenharmony_ci};
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic int patch_yamaha_ymf743(struct snd_ac97 *ac97)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_yamaha_ymf743_ops;
3548c2ecf20Sopenharmony_ci	ac97->caps |= AC97_BC_BASS_TREBLE;
3558c2ecf20Sopenharmony_ci	ac97->caps |= 0x04 << 10; /* Yamaha 3D enhancement */
3568c2ecf20Sopenharmony_ci	ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */
3578c2ecf20Sopenharmony_ci	ac97->ext_id |= AC97_EI_SPDIF; /* force the detection of spdif */
3588c2ecf20Sopenharmony_ci	return 0;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci/* The AC'97 spec states that the S/PDIF signal is to be output at pin 48.
3628c2ecf20Sopenharmony_ci   The YMF753 will output the S/PDIF signal to pin 43, 47 (EAPD), or 48.
3638c2ecf20Sopenharmony_ci   By default, no output pin is selected, and the S/PDIF signal is not output.
3648c2ecf20Sopenharmony_ci   There is also a bit to mute S/PDIF output in a vendor-specific register. */
3658c2ecf20Sopenharmony_cistatic int snd_ac97_ymf753_spdif_output_pin_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	static const char * const texts[3] = { "Disabled", "Pin 43", "Pin 48" };
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 3, texts);
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic int snd_ac97_ymf753_spdif_output_pin_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
3758c2ecf20Sopenharmony_ci	unsigned short val;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	val = ac97->regs[AC97_YMF7X3_DIT_CTRL];
3788c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = (val & 0x0008) ? 2 : (val & 0x0020) ? 1 : 0;
3798c2ecf20Sopenharmony_ci	return 0;
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic int snd_ac97_ymf753_spdif_output_pin_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
3858c2ecf20Sopenharmony_ci	unsigned short val;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] > 2)
3888c2ecf20Sopenharmony_ci		return -EINVAL;
3898c2ecf20Sopenharmony_ci	val = (ucontrol->value.enumerated.item[0] == 2) ? 0x0008 :
3908c2ecf20Sopenharmony_ci	      (ucontrol->value.enumerated.item[0] == 1) ? 0x0020 : 0;
3918c2ecf20Sopenharmony_ci	return snd_ac97_update_bits(ac97, AC97_YMF7X3_DIT_CTRL, 0x0028, val);
3928c2ecf20Sopenharmony_ci	/* The following can be used to direct S/PDIF output to pin 47 (EAPD).
3938c2ecf20Sopenharmony_ci	   snd_ac97_write_cache(ac97, 0x62, snd_ac97_read(ac97, 0x62) | 0x0008); */
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_ymf753_controls_spdif[3] = {
3978c2ecf20Sopenharmony_ci	{
3988c2ecf20Sopenharmony_ci		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
3998c2ecf20Sopenharmony_ci		.name	= SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4008c2ecf20Sopenharmony_ci		.info	= snd_ac97_ymf7x3_spdif_source_info,
4018c2ecf20Sopenharmony_ci		.get	= snd_ac97_ymf7x3_spdif_source_get,
4028c2ecf20Sopenharmony_ci		.put	= snd_ac97_ymf7x3_spdif_source_put,
4038c2ecf20Sopenharmony_ci	},
4048c2ecf20Sopenharmony_ci	{
4058c2ecf20Sopenharmony_ci		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
4068c2ecf20Sopenharmony_ci		.name	= SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Output Pin",
4078c2ecf20Sopenharmony_ci		.info	= snd_ac97_ymf753_spdif_output_pin_info,
4088c2ecf20Sopenharmony_ci		.get	= snd_ac97_ymf753_spdif_output_pin_get,
4098c2ecf20Sopenharmony_ci		.put	= snd_ac97_ymf753_spdif_output_pin_put,
4108c2ecf20Sopenharmony_ci	},
4118c2ecf20Sopenharmony_ci	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("", NONE, NONE) "Mute",
4128c2ecf20Sopenharmony_ci		    AC97_YMF7X3_DIT_CTRL, 2, 1, 1)
4138c2ecf20Sopenharmony_ci};
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic int patch_yamaha_ymf753_post_spdif(struct snd_ac97 * ac97)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	int err;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	if ((err = patch_build_controls(ac97, snd_ac97_ymf753_controls_spdif, ARRAY_SIZE(snd_ac97_ymf753_controls_spdif))) < 0)
4208c2ecf20Sopenharmony_ci		return err;
4218c2ecf20Sopenharmony_ci	return 0;
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_yamaha_ymf753_ops = {
4258c2ecf20Sopenharmony_ci	.build_3d	= patch_yamaha_ymf7x3_3d,
4268c2ecf20Sopenharmony_ci	.build_post_spdif = patch_yamaha_ymf753_post_spdif
4278c2ecf20Sopenharmony_ci};
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic int patch_yamaha_ymf753(struct snd_ac97 * ac97)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	/* Patch for Yamaha YMF753, Copyright (c) by David Shust, dshust@shustring.com.
4328c2ecf20Sopenharmony_ci	   This chip has nonstandard and extended behaviour with regard to its S/PDIF output.
4338c2ecf20Sopenharmony_ci	   The AC'97 spec states that the S/PDIF signal is to be output at pin 48.
4348c2ecf20Sopenharmony_ci	   The YMF753 will ouput the S/PDIF signal to pin 43, 47 (EAPD), or 48.
4358c2ecf20Sopenharmony_ci	   By default, no output pin is selected, and the S/PDIF signal is not output.
4368c2ecf20Sopenharmony_ci	   There is also a bit to mute S/PDIF output in a vendor-specific register.
4378c2ecf20Sopenharmony_ci	*/
4388c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_yamaha_ymf753_ops;
4398c2ecf20Sopenharmony_ci	ac97->caps |= AC97_BC_BASS_TREBLE;
4408c2ecf20Sopenharmony_ci	ac97->caps |= 0x04 << 10; /* Yamaha 3D enhancement */
4418c2ecf20Sopenharmony_ci	return 0;
4428c2ecf20Sopenharmony_ci}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci/*
4458c2ecf20Sopenharmony_ci * May 2, 2003 Liam Girdwood <lrg@slimlogic.co.uk>
4468c2ecf20Sopenharmony_ci *  removed broken wolfson00 patch.
4478c2ecf20Sopenharmony_ci *  added support for WM9705,WM9708,WM9709,WM9710,WM9711,WM9712 and WM9717.
4488c2ecf20Sopenharmony_ci */
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm97xx_snd_ac97_controls[] = {
4518c2ecf20Sopenharmony_ciAC97_DOUBLE("Front Playback Volume", AC97_WM97XX_FMIXER_VOL, 8, 0, 31, 1),
4528c2ecf20Sopenharmony_ciAC97_SINGLE("Front Playback Switch", AC97_WM97XX_FMIXER_VOL, 15, 1, 1),
4538c2ecf20Sopenharmony_ci};
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic int patch_wolfson_wm9703_specific(struct snd_ac97 * ac97)
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	/* This is known to work for the ViewSonic ViewPad 1000
4588c2ecf20Sopenharmony_ci	 * Randolph Bentson <bentson@holmsjoen.com>
4598c2ecf20Sopenharmony_ci	 * WM9703/9707/9708/9717
4608c2ecf20Sopenharmony_ci	 */
4618c2ecf20Sopenharmony_ci	int err, i;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(wm97xx_snd_ac97_controls); i++) {
4648c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm97xx_snd_ac97_controls[i], ac97))) < 0)
4658c2ecf20Sopenharmony_ci			return err;
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97,  AC97_WM97XX_FMIXER_VOL, 0x0808);
4688c2ecf20Sopenharmony_ci	return 0;
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_wolfson_wm9703_ops = {
4728c2ecf20Sopenharmony_ci	.build_specific = patch_wolfson_wm9703_specific,
4738c2ecf20Sopenharmony_ci};
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic int patch_wolfson03(struct snd_ac97 * ac97)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_wolfson_wm9703_ops;
4788c2ecf20Sopenharmony_ci	return 0;
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9704_snd_ac97_controls[] = {
4828c2ecf20Sopenharmony_ciAC97_DOUBLE("Front Playback Volume", AC97_WM97XX_FMIXER_VOL, 8, 0, 31, 1),
4838c2ecf20Sopenharmony_ciAC97_SINGLE("Front Playback Switch", AC97_WM97XX_FMIXER_VOL, 15, 1, 1),
4848c2ecf20Sopenharmony_ciAC97_DOUBLE("Rear Playback Volume", AC97_WM9704_RMIXER_VOL, 8, 0, 31, 1),
4858c2ecf20Sopenharmony_ciAC97_SINGLE("Rear Playback Switch", AC97_WM9704_RMIXER_VOL, 15, 1, 1),
4868c2ecf20Sopenharmony_ciAC97_DOUBLE("Rear DAC Volume", AC97_WM9704_RPCM_VOL, 8, 0, 31, 1),
4878c2ecf20Sopenharmony_ciAC97_DOUBLE("Surround Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1),
4888c2ecf20Sopenharmony_ci};
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_cistatic int patch_wolfson_wm9704_specific(struct snd_ac97 * ac97)
4918c2ecf20Sopenharmony_ci{
4928c2ecf20Sopenharmony_ci	int err, i;
4938c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(wm9704_snd_ac97_controls); i++) {
4948c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm9704_snd_ac97_controls[i], ac97))) < 0)
4958c2ecf20Sopenharmony_ci			return err;
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci	/* patch for DVD noise */
4988c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_WM9704_TEST, 0x0200);
4998c2ecf20Sopenharmony_ci	return 0;
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_wolfson_wm9704_ops = {
5038c2ecf20Sopenharmony_ci	.build_specific = patch_wolfson_wm9704_specific,
5048c2ecf20Sopenharmony_ci};
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_cistatic int patch_wolfson04(struct snd_ac97 * ac97)
5078c2ecf20Sopenharmony_ci{
5088c2ecf20Sopenharmony_ci	/* WM9704M/9704Q */
5098c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_wolfson_wm9704_ops;
5108c2ecf20Sopenharmony_ci	return 0;
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cistatic int patch_wolfson05(struct snd_ac97 * ac97)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	/* WM9705, WM9710 */
5168c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_wolfson_wm9703_ops;
5178c2ecf20Sopenharmony_ci#ifdef CONFIG_TOUCHSCREEN_WM9705
5188c2ecf20Sopenharmony_ci	/* WM9705 touchscreen uses AUX and VIDEO for touch */
5198c2ecf20Sopenharmony_ci	ac97->flags |= AC97_HAS_NO_VIDEO | AC97_HAS_NO_AUX;
5208c2ecf20Sopenharmony_ci#endif
5218c2ecf20Sopenharmony_ci	return 0;
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_cistatic const char* wm9711_alc_select[] = {"None", "Left", "Right", "Stereo"};
5258c2ecf20Sopenharmony_cistatic const char* wm9711_alc_mix[] = {"Stereo", "Right", "Left", "None"};
5268c2ecf20Sopenharmony_cistatic const char* wm9711_out3_src[] = {"Left", "VREF", "Left + Right", "Mono"};
5278c2ecf20Sopenharmony_cistatic const char* wm9711_out3_lrsrc[] = {"Master Mix", "Headphone Mix"};
5288c2ecf20Sopenharmony_cistatic const char* wm9711_rec_adc[] = {"Stereo", "Left", "Right", "Mute"};
5298c2ecf20Sopenharmony_cistatic const char* wm9711_base[] = {"Linear Control", "Adaptive Boost"};
5308c2ecf20Sopenharmony_cistatic const char* wm9711_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"};
5318c2ecf20Sopenharmony_cistatic const char* wm9711_mic[] = {"Mic 1", "Differential", "Mic 2", "Stereo"};
5328c2ecf20Sopenharmony_cistatic const char* wm9711_rec_sel[] =
5338c2ecf20Sopenharmony_ci	{"Mic 1", "NC", "NC", "Master Mix", "Line", "Headphone Mix", "Phone Mix", "Phone"};
5348c2ecf20Sopenharmony_cistatic const char* wm9711_ng_type[] = {"Constant Gain", "Mute"};
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic const struct ac97_enum wm9711_enum[] = {
5378c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9711_alc_select),
5388c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_VIDEO, 10, 4, wm9711_alc_mix),
5398c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_AUX, 9, 4, wm9711_out3_src),
5408c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_AUX, 8, 2, wm9711_out3_lrsrc),
5418c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_REC_SEL, 12, 4, wm9711_rec_adc),
5428c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_MASTER_TONE, 15, 2, wm9711_base),
5438c2ecf20Sopenharmony_ciAC97_ENUM_DOUBLE(AC97_REC_GAIN, 14, 6, 2, wm9711_rec_gain),
5448c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_MIC, 5, 4, wm9711_mic),
5458c2ecf20Sopenharmony_ciAC97_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 8, wm9711_rec_sel),
5468c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9711_ng_type),
5478c2ecf20Sopenharmony_ci};
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm9711_snd_ac97_controls[] = {
5508c2ecf20Sopenharmony_ciAC97_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0),
5518c2ecf20Sopenharmony_ciAC97_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0),
5528c2ecf20Sopenharmony_ciAC97_SINGLE("ALC Decay Time", AC97_CODEC_CLASS_REV, 4, 15, 0),
5538c2ecf20Sopenharmony_ciAC97_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0),
5548c2ecf20Sopenharmony_ciAC97_ENUM("ALC Function", wm9711_enum[0]),
5558c2ecf20Sopenharmony_ciAC97_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 1),
5568c2ecf20Sopenharmony_ciAC97_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 1),
5578c2ecf20Sopenharmony_ciAC97_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0),
5588c2ecf20Sopenharmony_ciAC97_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0),
5598c2ecf20Sopenharmony_ciAC97_ENUM("ALC NG Type", wm9711_enum[9]),
5608c2ecf20Sopenharmony_ciAC97_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 1),
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ciAC97_SINGLE("Side Tone Switch", AC97_VIDEO, 15, 1, 1),
5638c2ecf20Sopenharmony_ciAC97_SINGLE("Side Tone Volume", AC97_VIDEO, 12, 7, 1),
5648c2ecf20Sopenharmony_ciAC97_ENUM("ALC Headphone Mux", wm9711_enum[1]),
5658c2ecf20Sopenharmony_ciAC97_SINGLE("ALC Headphone Volume", AC97_VIDEO, 7, 7, 1),
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ciAC97_SINGLE("Out3 Switch", AC97_AUX, 15, 1, 1),
5688c2ecf20Sopenharmony_ciAC97_SINGLE("Out3 ZC Switch", AC97_AUX, 7, 1, 0),
5698c2ecf20Sopenharmony_ciAC97_ENUM("Out3 Mux", wm9711_enum[2]),
5708c2ecf20Sopenharmony_ciAC97_ENUM("Out3 LR Mux", wm9711_enum[3]),
5718c2ecf20Sopenharmony_ciAC97_SINGLE("Out3 Volume", AC97_AUX, 0, 31, 1),
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ciAC97_SINGLE("Beep to Headphone Switch", AC97_PC_BEEP, 15, 1, 1),
5748c2ecf20Sopenharmony_ciAC97_SINGLE("Beep to Headphone Volume", AC97_PC_BEEP, 12, 7, 1),
5758c2ecf20Sopenharmony_ciAC97_SINGLE("Beep to Side Tone Switch", AC97_PC_BEEP, 11, 1, 1),
5768c2ecf20Sopenharmony_ciAC97_SINGLE("Beep to Side Tone Volume", AC97_PC_BEEP, 8, 7, 1),
5778c2ecf20Sopenharmony_ciAC97_SINGLE("Beep to Phone Switch", AC97_PC_BEEP, 7, 1, 1),
5788c2ecf20Sopenharmony_ciAC97_SINGLE("Beep to Phone Volume", AC97_PC_BEEP, 4, 7, 1),
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ciAC97_SINGLE("Aux to Headphone Switch", AC97_CD, 15, 1, 1),
5818c2ecf20Sopenharmony_ciAC97_SINGLE("Aux to Headphone Volume", AC97_CD, 12, 7, 1),
5828c2ecf20Sopenharmony_ciAC97_SINGLE("Aux to Side Tone Switch", AC97_CD, 11, 1, 1),
5838c2ecf20Sopenharmony_ciAC97_SINGLE("Aux to Side Tone Volume", AC97_CD, 8, 7, 1),
5848c2ecf20Sopenharmony_ciAC97_SINGLE("Aux to Phone Switch", AC97_CD, 7, 1, 1),
5858c2ecf20Sopenharmony_ciAC97_SINGLE("Aux to Phone Volume", AC97_CD, 4, 7, 1),
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ciAC97_SINGLE("Phone to Headphone Switch", AC97_PHONE, 15, 1, 1),
5888c2ecf20Sopenharmony_ciAC97_SINGLE("Phone to Master Switch", AC97_PHONE, 14, 1, 1),
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ciAC97_SINGLE("Line to Headphone Switch", AC97_LINE, 15, 1, 1),
5918c2ecf20Sopenharmony_ciAC97_SINGLE("Line to Master Switch", AC97_LINE, 14, 1, 1),
5928c2ecf20Sopenharmony_ciAC97_SINGLE("Line to Phone Switch", AC97_LINE, 13, 1, 1),
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ciAC97_SINGLE("PCM Playback to Headphone Switch", AC97_PCM, 15, 1, 1),
5958c2ecf20Sopenharmony_ciAC97_SINGLE("PCM Playback to Master Switch", AC97_PCM, 14, 1, 1),
5968c2ecf20Sopenharmony_ciAC97_SINGLE("PCM Playback to Phone Switch", AC97_PCM, 13, 1, 1),
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ciAC97_SINGLE("Capture 20dB Boost Switch", AC97_REC_SEL, 14, 1, 0),
5998c2ecf20Sopenharmony_ciAC97_ENUM("Capture to Phone Mux", wm9711_enum[4]),
6008c2ecf20Sopenharmony_ciAC97_SINGLE("Capture to Phone 20dB Boost Switch", AC97_REC_SEL, 11, 1, 1),
6018c2ecf20Sopenharmony_ciAC97_ENUM("Capture Select", wm9711_enum[8]),
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ciAC97_SINGLE("3D Upper Cut-off Switch", AC97_3D_CONTROL, 5, 1, 1),
6048c2ecf20Sopenharmony_ciAC97_SINGLE("3D Lower Cut-off Switch", AC97_3D_CONTROL, 4, 1, 1),
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ciAC97_ENUM("Bass Control", wm9711_enum[5]),
6078c2ecf20Sopenharmony_ciAC97_SINGLE("Bass Cut-off Switch", AC97_MASTER_TONE, 12, 1, 1),
6088c2ecf20Sopenharmony_ciAC97_SINGLE("Tone Cut-off Switch", AC97_MASTER_TONE, 4, 1, 1),
6098c2ecf20Sopenharmony_ciAC97_SINGLE("Playback Attenuate (-6dB) Switch", AC97_MASTER_TONE, 6, 1, 0),
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ciAC97_SINGLE("ADC Switch", AC97_REC_GAIN, 15, 1, 1),
6128c2ecf20Sopenharmony_ciAC97_ENUM("Capture Volume Steps", wm9711_enum[6]),
6138c2ecf20Sopenharmony_ciAC97_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 63, 1),
6148c2ecf20Sopenharmony_ciAC97_SINGLE("Capture ZC Switch", AC97_REC_GAIN, 7, 1, 0),
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ciAC97_SINGLE("Mic 1 to Phone Switch", AC97_MIC, 14, 1, 1),
6178c2ecf20Sopenharmony_ciAC97_SINGLE("Mic 2 to Phone Switch", AC97_MIC, 13, 1, 1),
6188c2ecf20Sopenharmony_ciAC97_ENUM("Mic Select Source", wm9711_enum[7]),
6198c2ecf20Sopenharmony_ciAC97_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1),
6208c2ecf20Sopenharmony_ciAC97_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
6218c2ecf20Sopenharmony_ciAC97_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 7, 1, 0),
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ciAC97_SINGLE("Master Left Inv Switch", AC97_MASTER, 6, 1, 0),
6248c2ecf20Sopenharmony_ciAC97_SINGLE("Master ZC Switch", AC97_MASTER, 7, 1, 0),
6258c2ecf20Sopenharmony_ciAC97_SINGLE("Headphone ZC Switch", AC97_HEADPHONE, 7, 1, 0),
6268c2ecf20Sopenharmony_ciAC97_SINGLE("Mono ZC Switch", AC97_MASTER_MONO, 7, 1, 0),
6278c2ecf20Sopenharmony_ci};
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_cistatic int patch_wolfson_wm9711_specific(struct snd_ac97 * ac97)
6308c2ecf20Sopenharmony_ci{
6318c2ecf20Sopenharmony_ci	int err, i;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(wm9711_snd_ac97_controls); i++) {
6348c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm9711_snd_ac97_controls[i], ac97))) < 0)
6358c2ecf20Sopenharmony_ci			return err;
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97,  AC97_CODEC_CLASS_REV, 0x0808);
6388c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97,  AC97_PCI_SVID, 0x0808);
6398c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97,  AC97_VIDEO, 0x0808);
6408c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97,  AC97_AUX, 0x0808);
6418c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97,  AC97_PC_BEEP, 0x0808);
6428c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97,  AC97_CD, 0x0000);
6438c2ecf20Sopenharmony_ci	return 0;
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_wolfson_wm9711_ops = {
6478c2ecf20Sopenharmony_ci	.build_specific = patch_wolfson_wm9711_specific,
6488c2ecf20Sopenharmony_ci};
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_cistatic int patch_wolfson11(struct snd_ac97 * ac97)
6518c2ecf20Sopenharmony_ci{
6528c2ecf20Sopenharmony_ci	/* WM9711, WM9712 */
6538c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_wolfson_wm9711_ops;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	ac97->flags |= AC97_HAS_NO_REC_GAIN | AC97_STEREO_MUTES | AC97_HAS_NO_MIC |
6568c2ecf20Sopenharmony_ci		AC97_HAS_NO_PC_BEEP | AC97_HAS_NO_VIDEO | AC97_HAS_NO_CD;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	return 0;
6598c2ecf20Sopenharmony_ci}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_cistatic const char* wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"};
6628c2ecf20Sopenharmony_cistatic const char* wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"};
6638c2ecf20Sopenharmony_cistatic const char* wm9713_rec_src[] =
6648c2ecf20Sopenharmony_ci	{"Mic 1", "Mic 2", "Line", "Mono In", "Headphone Mix", "Master Mix",
6658c2ecf20Sopenharmony_ci	"Mono Mix", "Zh"};
6668c2ecf20Sopenharmony_cistatic const char* wm9713_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"};
6678c2ecf20Sopenharmony_cistatic const char* wm9713_alc_select[] = {"None", "Left", "Right", "Stereo"};
6688c2ecf20Sopenharmony_cistatic const char* wm9713_mono_pga[] = {"Vmid", "Zh", "Mono Mix", "Inv 1"};
6698c2ecf20Sopenharmony_cistatic const char* wm9713_spk_pga[] =
6708c2ecf20Sopenharmony_ci	{"Vmid", "Zh", "Headphone Mix", "Master Mix", "Inv", "NC", "NC", "NC"};
6718c2ecf20Sopenharmony_cistatic const char* wm9713_hp_pga[] = {"Vmid", "Zh", "Headphone Mix", "NC"};
6728c2ecf20Sopenharmony_cistatic const char* wm9713_out3_pga[] = {"Vmid", "Zh", "Inv 1", "NC"};
6738c2ecf20Sopenharmony_cistatic const char* wm9713_out4_pga[] = {"Vmid", "Zh", "Inv 2", "NC"};
6748c2ecf20Sopenharmony_cistatic const char* wm9713_dac_inv[] =
6758c2ecf20Sopenharmony_ci	{"Off", "Mono Mix", "Master Mix", "Headphone Mix L", "Headphone Mix R",
6768c2ecf20Sopenharmony_ci	"Headphone Mix Mono", "NC", "Vmid"};
6778c2ecf20Sopenharmony_cistatic const char* wm9713_base[] = {"Linear Control", "Adaptive Boost"};
6788c2ecf20Sopenharmony_cistatic const char* wm9713_ng_type[] = {"Constant Gain", "Mute"};
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_cistatic const struct ac97_enum wm9713_enum[] = {
6818c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_LINE, 3, 4, wm9713_mic_mixer),
6828c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_VIDEO, 14, 4, wm9713_rec_mux),
6838c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_VIDEO, 9, 4, wm9713_rec_mux),
6848c2ecf20Sopenharmony_ciAC97_ENUM_DOUBLE(AC97_VIDEO, 3, 0, 8, wm9713_rec_src),
6858c2ecf20Sopenharmony_ciAC97_ENUM_DOUBLE(AC97_CD, 14, 6, 2, wm9713_rec_gain),
6868c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9713_alc_select),
6878c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_REC_GAIN, 14, 4, wm9713_mono_pga),
6888c2ecf20Sopenharmony_ciAC97_ENUM_DOUBLE(AC97_REC_GAIN, 11, 8, 8, wm9713_spk_pga),
6898c2ecf20Sopenharmony_ciAC97_ENUM_DOUBLE(AC97_REC_GAIN, 6, 4, 4, wm9713_hp_pga),
6908c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_REC_GAIN, 2, 4, wm9713_out3_pga),
6918c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_REC_GAIN, 0, 4, wm9713_out4_pga),
6928c2ecf20Sopenharmony_ciAC97_ENUM_DOUBLE(AC97_REC_GAIN_MIC, 13, 10, 8, wm9713_dac_inv),
6938c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_base),
6948c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type),
6958c2ecf20Sopenharmony_ci};
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm13_snd_ac97_controls[] = {
6988c2ecf20Sopenharmony_ciAC97_DOUBLE("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1),
6998c2ecf20Sopenharmony_ciAC97_SINGLE("Line In to Headphone Switch", AC97_PC_BEEP, 15, 1, 1),
7008c2ecf20Sopenharmony_ciAC97_SINGLE("Line In to Master Switch", AC97_PC_BEEP, 14, 1, 1),
7018c2ecf20Sopenharmony_ciAC97_SINGLE("Line In to Mono Switch", AC97_PC_BEEP, 13, 1, 1),
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ciAC97_DOUBLE("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1),
7048c2ecf20Sopenharmony_ciAC97_SINGLE("PCM Playback to Headphone Switch", AC97_PHONE, 15, 1, 1),
7058c2ecf20Sopenharmony_ciAC97_SINGLE("PCM Playback to Master Switch", AC97_PHONE, 14, 1, 1),
7068c2ecf20Sopenharmony_ciAC97_SINGLE("PCM Playback to Mono Switch", AC97_PHONE, 13, 1, 1),
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ciAC97_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1),
7098c2ecf20Sopenharmony_ciAC97_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
7108c2ecf20Sopenharmony_ciAC97_SINGLE("Mic 1 to Mono Switch", AC97_LINE, 7, 1, 1),
7118c2ecf20Sopenharmony_ciAC97_SINGLE("Mic 2 to Mono Switch", AC97_LINE, 6, 1, 1),
7128c2ecf20Sopenharmony_ciAC97_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0),
7138c2ecf20Sopenharmony_ciAC97_ENUM("Mic to Headphone Mux", wm9713_enum[0]),
7148c2ecf20Sopenharmony_ciAC97_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1),
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ciAC97_SINGLE("Capture Switch", AC97_CD, 15, 1, 1),
7178c2ecf20Sopenharmony_ciAC97_ENUM("Capture Volume Steps", wm9713_enum[4]),
7188c2ecf20Sopenharmony_ciAC97_DOUBLE("Capture Volume", AC97_CD, 8, 0, 15, 0),
7198c2ecf20Sopenharmony_ciAC97_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0),
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ciAC97_ENUM("Capture to Headphone Mux", wm9713_enum[1]),
7228c2ecf20Sopenharmony_ciAC97_SINGLE("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1),
7238c2ecf20Sopenharmony_ciAC97_ENUM("Capture to Mono Mux", wm9713_enum[2]),
7248c2ecf20Sopenharmony_ciAC97_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0),
7258c2ecf20Sopenharmony_ciAC97_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0),
7268c2ecf20Sopenharmony_ciAC97_ENUM("Capture Select", wm9713_enum[3]),
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ciAC97_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0),
7298c2ecf20Sopenharmony_ciAC97_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0),
7308c2ecf20Sopenharmony_ciAC97_SINGLE("ALC Decay Time ", AC97_CODEC_CLASS_REV, 4, 15, 0),
7318c2ecf20Sopenharmony_ciAC97_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0),
7328c2ecf20Sopenharmony_ciAC97_ENUM("ALC Function", wm9713_enum[5]),
7338c2ecf20Sopenharmony_ciAC97_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0),
7348c2ecf20Sopenharmony_ciAC97_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 0),
7358c2ecf20Sopenharmony_ciAC97_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0),
7368c2ecf20Sopenharmony_ciAC97_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0),
7378c2ecf20Sopenharmony_ciAC97_ENUM("ALC NG Type", wm9713_enum[13]),
7388c2ecf20Sopenharmony_ciAC97_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 0),
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ciAC97_DOUBLE("Master ZC Switch", AC97_MASTER, 14, 6, 1, 0),
7418c2ecf20Sopenharmony_ciAC97_DOUBLE("Headphone ZC Switch", AC97_HEADPHONE, 14, 6, 1, 0),
7428c2ecf20Sopenharmony_ciAC97_DOUBLE("Out3/4 ZC Switch", AC97_MASTER_MONO, 14, 6, 1, 0),
7438c2ecf20Sopenharmony_ciAC97_SINGLE("Master Right Switch", AC97_MASTER, 7, 1, 1),
7448c2ecf20Sopenharmony_ciAC97_SINGLE("Headphone Right Switch", AC97_HEADPHONE, 7, 1, 1),
7458c2ecf20Sopenharmony_ciAC97_SINGLE("Out3/4 Right Switch", AC97_MASTER_MONO, 7, 1, 1),
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ciAC97_SINGLE("Mono In to Headphone Switch", AC97_MASTER_TONE, 15, 1, 1),
7488c2ecf20Sopenharmony_ciAC97_SINGLE("Mono In to Master Switch", AC97_MASTER_TONE, 14, 1, 1),
7498c2ecf20Sopenharmony_ciAC97_SINGLE("Mono In Volume", AC97_MASTER_TONE, 8, 31, 1),
7508c2ecf20Sopenharmony_ciAC97_SINGLE("Mono Switch", AC97_MASTER_TONE, 7, 1, 1),
7518c2ecf20Sopenharmony_ciAC97_SINGLE("Mono ZC Switch", AC97_MASTER_TONE, 6, 1, 0),
7528c2ecf20Sopenharmony_ciAC97_SINGLE("Mono Volume", AC97_MASTER_TONE, 0, 31, 1),
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ciAC97_SINGLE("Beep to Headphone Switch", AC97_AUX, 15, 1, 1),
7558c2ecf20Sopenharmony_ciAC97_SINGLE("Beep to Headphone Volume", AC97_AUX, 12, 7, 1),
7568c2ecf20Sopenharmony_ciAC97_SINGLE("Beep to Master Switch", AC97_AUX, 11, 1, 1),
7578c2ecf20Sopenharmony_ciAC97_SINGLE("Beep to Master Volume", AC97_AUX, 8, 7, 1),
7588c2ecf20Sopenharmony_ciAC97_SINGLE("Beep to Mono Switch", AC97_AUX, 7, 1, 1),
7598c2ecf20Sopenharmony_ciAC97_SINGLE("Beep to Mono Volume", AC97_AUX, 4, 7, 1),
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ciAC97_SINGLE("Voice to Headphone Switch", AC97_PCM, 15, 1, 1),
7628c2ecf20Sopenharmony_ciAC97_SINGLE("Voice to Headphone Volume", AC97_PCM, 12, 7, 1),
7638c2ecf20Sopenharmony_ciAC97_SINGLE("Voice to Master Switch", AC97_PCM, 11, 1, 1),
7648c2ecf20Sopenharmony_ciAC97_SINGLE("Voice to Master Volume", AC97_PCM, 8, 7, 1),
7658c2ecf20Sopenharmony_ciAC97_SINGLE("Voice to Mono Switch", AC97_PCM, 7, 1, 1),
7668c2ecf20Sopenharmony_ciAC97_SINGLE("Voice to Mono Volume", AC97_PCM, 4, 7, 1),
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ciAC97_SINGLE("Aux to Headphone Switch", AC97_REC_SEL, 15, 1, 1),
7698c2ecf20Sopenharmony_ciAC97_SINGLE("Aux to Headphone Volume", AC97_REC_SEL, 12, 7, 1),
7708c2ecf20Sopenharmony_ciAC97_SINGLE("Aux to Master Switch", AC97_REC_SEL, 11, 1, 1),
7718c2ecf20Sopenharmony_ciAC97_SINGLE("Aux to Master Volume", AC97_REC_SEL, 8, 7, 1),
7728c2ecf20Sopenharmony_ciAC97_SINGLE("Aux to Mono Switch", AC97_REC_SEL, 7, 1, 1),
7738c2ecf20Sopenharmony_ciAC97_SINGLE("Aux to Mono Volume", AC97_REC_SEL, 4, 7, 1),
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ciAC97_ENUM("Mono Input Mux", wm9713_enum[6]),
7768c2ecf20Sopenharmony_ciAC97_ENUM("Master Input Mux", wm9713_enum[7]),
7778c2ecf20Sopenharmony_ciAC97_ENUM("Headphone Input Mux", wm9713_enum[8]),
7788c2ecf20Sopenharmony_ciAC97_ENUM("Out 3 Input Mux", wm9713_enum[9]),
7798c2ecf20Sopenharmony_ciAC97_ENUM("Out 4 Input Mux", wm9713_enum[10]),
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ciAC97_ENUM("Bass Control", wm9713_enum[12]),
7828c2ecf20Sopenharmony_ciAC97_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1),
7838c2ecf20Sopenharmony_ciAC97_SINGLE("Tone Cut-off Switch", AC97_GENERAL_PURPOSE, 4, 1, 1),
7848c2ecf20Sopenharmony_ciAC97_SINGLE("Playback Attenuate (-6dB) Switch", AC97_GENERAL_PURPOSE, 6, 1, 0),
7858c2ecf20Sopenharmony_ciAC97_SINGLE("Bass Volume", AC97_GENERAL_PURPOSE, 8, 15, 1),
7868c2ecf20Sopenharmony_ciAC97_SINGLE("Tone Volume", AC97_GENERAL_PURPOSE, 0, 15, 1),
7878c2ecf20Sopenharmony_ci};
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm13_snd_ac97_controls_3d[] = {
7908c2ecf20Sopenharmony_ciAC97_ENUM("Inv Input Mux", wm9713_enum[11]),
7918c2ecf20Sopenharmony_ciAC97_SINGLE("3D Upper Cut-off Switch", AC97_REC_GAIN_MIC, 5, 1, 0),
7928c2ecf20Sopenharmony_ciAC97_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0),
7938c2ecf20Sopenharmony_ciAC97_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1),
7948c2ecf20Sopenharmony_ci};
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_cistatic int patch_wolfson_wm9713_3d (struct snd_ac97 * ac97)
7978c2ecf20Sopenharmony_ci{
7988c2ecf20Sopenharmony_ci	int err, i;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_3d); i++) {
8018c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_3d[i], ac97))) < 0)
8028c2ecf20Sopenharmony_ci			return err;
8038c2ecf20Sopenharmony_ci	}
8048c2ecf20Sopenharmony_ci	return 0;
8058c2ecf20Sopenharmony_ci}
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_cistatic int patch_wolfson_wm9713_specific(struct snd_ac97 * ac97)
8088c2ecf20Sopenharmony_ci{
8098c2ecf20Sopenharmony_ci	int err, i;
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls); i++) {
8128c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls[i], ac97))) < 0)
8138c2ecf20Sopenharmony_ci			return err;
8148c2ecf20Sopenharmony_ci	}
8158c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x0808);
8168c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_PHONE, 0x0808);
8178c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_MIC, 0x0808);
8188c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_LINE, 0x00da);
8198c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_CD, 0x0808);
8208c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_VIDEO, 0xd612);
8218c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x1ba0);
8228c2ecf20Sopenharmony_ci	return 0;
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
8268c2ecf20Sopenharmony_cistatic void patch_wolfson_wm9713_suspend (struct snd_ac97 * ac97)
8278c2ecf20Sopenharmony_ci{
8288c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_EXTENDED_MID, 0xfeff);
8298c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0xffff);
8308c2ecf20Sopenharmony_ci}
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_cistatic void patch_wolfson_wm9713_resume (struct snd_ac97 * ac97)
8338c2ecf20Sopenharmony_ci{
8348c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_EXTENDED_MID, 0xda00);
8358c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0x3810);
8368c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0x0);
8378c2ecf20Sopenharmony_ci}
8388c2ecf20Sopenharmony_ci#endif
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_wolfson_wm9713_ops = {
8418c2ecf20Sopenharmony_ci	.build_specific = patch_wolfson_wm9713_specific,
8428c2ecf20Sopenharmony_ci	.build_3d = patch_wolfson_wm9713_3d,
8438c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
8448c2ecf20Sopenharmony_ci	.suspend = patch_wolfson_wm9713_suspend,
8458c2ecf20Sopenharmony_ci	.resume = patch_wolfson_wm9713_resume
8468c2ecf20Sopenharmony_ci#endif
8478c2ecf20Sopenharmony_ci};
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_cistatic int patch_wolfson13(struct snd_ac97 * ac97)
8508c2ecf20Sopenharmony_ci{
8518c2ecf20Sopenharmony_ci	/* WM9713, WM9714 */
8528c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_wolfson_wm9713_ops;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	ac97->flags |= AC97_HAS_NO_REC_GAIN | AC97_STEREO_MUTES | AC97_HAS_NO_PHONE |
8558c2ecf20Sopenharmony_ci		AC97_HAS_NO_PC_BEEP | AC97_HAS_NO_VIDEO | AC97_HAS_NO_CD | AC97_HAS_NO_TONE |
8568c2ecf20Sopenharmony_ci		AC97_HAS_NO_STD_PCM;
8578c2ecf20Sopenharmony_ci    	ac97->scaps &= ~AC97_SCAP_MODEM;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_EXTENDED_MID, 0xda00);
8608c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0x3810);
8618c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0x0);
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	return 0;
8648c2ecf20Sopenharmony_ci}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci/*
8678c2ecf20Sopenharmony_ci * Tritech codec
8688c2ecf20Sopenharmony_ci */
8698c2ecf20Sopenharmony_cistatic int patch_tritech_tr28028(struct snd_ac97 * ac97)
8708c2ecf20Sopenharmony_ci{
8718c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, 0x26, 0x0300);
8728c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, 0x26, 0x0000);
8738c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x0000);
8748c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_SPDIF, 0x0000);
8758c2ecf20Sopenharmony_ci	return 0;
8768c2ecf20Sopenharmony_ci}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci/*
8798c2ecf20Sopenharmony_ci * Sigmatel STAC97xx codecs
8808c2ecf20Sopenharmony_ci */
8818c2ecf20Sopenharmony_cistatic int patch_sigmatel_stac9700_3d(struct snd_ac97 * ac97)
8828c2ecf20Sopenharmony_ci{
8838c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
8848c2ecf20Sopenharmony_ci	int err;
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
8878c2ecf20Sopenharmony_ci		return err;
8888c2ecf20Sopenharmony_ci	strcpy(kctl->id.name, "3D Control Sigmatel - Depth");
8898c2ecf20Sopenharmony_ci	kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0);
8908c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
8918c2ecf20Sopenharmony_ci	return 0;
8928c2ecf20Sopenharmony_ci}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_cistatic int patch_sigmatel_stac9708_3d(struct snd_ac97 * ac97)
8958c2ecf20Sopenharmony_ci{
8968c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
8978c2ecf20Sopenharmony_ci	int err;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
9008c2ecf20Sopenharmony_ci		return err;
9018c2ecf20Sopenharmony_ci	strcpy(kctl->id.name, "3D Control Sigmatel - Depth");
9028c2ecf20Sopenharmony_ci	kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 0, 3, 0);
9038c2ecf20Sopenharmony_ci	if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
9048c2ecf20Sopenharmony_ci		return err;
9058c2ecf20Sopenharmony_ci	strcpy(kctl->id.name, "3D Control Sigmatel - Rear Depth");
9068c2ecf20Sopenharmony_ci	kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0);
9078c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
9088c2ecf20Sopenharmony_ci	return 0;
9098c2ecf20Sopenharmony_ci}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_sigmatel_4speaker =
9128c2ecf20Sopenharmony_ciAC97_SINGLE("Sigmatel 4-Speaker Stereo Playback Switch",
9138c2ecf20Sopenharmony_ci		AC97_SIGMATEL_DAC2INVERT, 2, 1, 0);
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci/* "Sigmatel " removed due to excessive name length: */
9168c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_sigmatel_phaseinvert =
9178c2ecf20Sopenharmony_ciAC97_SINGLE("Surround Phase Inversion Playback Switch",
9188c2ecf20Sopenharmony_ci		AC97_SIGMATEL_DAC2INVERT, 3, 1, 0);
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_sigmatel_controls[] = {
9218c2ecf20Sopenharmony_ciAC97_SINGLE("Sigmatel DAC 6dB Attenuate", AC97_SIGMATEL_ANALOG, 1, 1, 0),
9228c2ecf20Sopenharmony_ciAC97_SINGLE("Sigmatel ADC 6dB Attenuate", AC97_SIGMATEL_ANALOG, 0, 1, 0)
9238c2ecf20Sopenharmony_ci};
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_cistatic int patch_sigmatel_stac97xx_specific(struct snd_ac97 * ac97)
9268c2ecf20Sopenharmony_ci{
9278c2ecf20Sopenharmony_ci	int err;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_SIGMATEL_ANALOG, snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG) & ~0x0003);
9308c2ecf20Sopenharmony_ci	if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 1))
9318c2ecf20Sopenharmony_ci		if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_controls[0], 1)) < 0)
9328c2ecf20Sopenharmony_ci			return err;
9338c2ecf20Sopenharmony_ci	if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 0))
9348c2ecf20Sopenharmony_ci		if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_controls[1], 1)) < 0)
9358c2ecf20Sopenharmony_ci			return err;
9368c2ecf20Sopenharmony_ci	if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_DAC2INVERT, 2))
9378c2ecf20Sopenharmony_ci		if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_4speaker, 1)) < 0)
9388c2ecf20Sopenharmony_ci			return err;
9398c2ecf20Sopenharmony_ci	if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_DAC2INVERT, 3))
9408c2ecf20Sopenharmony_ci		if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_phaseinvert, 1)) < 0)
9418c2ecf20Sopenharmony_ci			return err;
9428c2ecf20Sopenharmony_ci	return 0;
9438c2ecf20Sopenharmony_ci}
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_sigmatel_stac9700_ops = {
9468c2ecf20Sopenharmony_ci	.build_3d	= patch_sigmatel_stac9700_3d,
9478c2ecf20Sopenharmony_ci	.build_specific	= patch_sigmatel_stac97xx_specific
9488c2ecf20Sopenharmony_ci};
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_cistatic int patch_sigmatel_stac9700(struct snd_ac97 * ac97)
9518c2ecf20Sopenharmony_ci{
9528c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_sigmatel_stac9700_ops;
9538c2ecf20Sopenharmony_ci	return 0;
9548c2ecf20Sopenharmony_ci}
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_cistatic int snd_ac97_stac9708_put_bias(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
9578c2ecf20Sopenharmony_ci{
9588c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
9598c2ecf20Sopenharmony_ci	int err;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	mutex_lock(&ac97->page_mutex);
9628c2ecf20Sopenharmony_ci	snd_ac97_write(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
9638c2ecf20Sopenharmony_ci	err = snd_ac97_update_bits(ac97, AC97_SIGMATEL_BIAS2, 0x0010,
9648c2ecf20Sopenharmony_ci				   (ucontrol->value.integer.value[0] & 1) << 4);
9658c2ecf20Sopenharmony_ci	snd_ac97_write(ac97, AC97_SIGMATEL_BIAS1, 0);
9668c2ecf20Sopenharmony_ci	mutex_unlock(&ac97->page_mutex);
9678c2ecf20Sopenharmony_ci	return err;
9688c2ecf20Sopenharmony_ci}
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_stac9708_bias_control = {
9718c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
9728c2ecf20Sopenharmony_ci	.name = "Sigmatel Output Bias Switch",
9738c2ecf20Sopenharmony_ci	.info = snd_ac97_info_volsw,
9748c2ecf20Sopenharmony_ci	.get = snd_ac97_get_volsw,
9758c2ecf20Sopenharmony_ci	.put = snd_ac97_stac9708_put_bias,
9768c2ecf20Sopenharmony_ci	.private_value = AC97_SINGLE_VALUE(AC97_SIGMATEL_BIAS2, 4, 1, 0),
9778c2ecf20Sopenharmony_ci};
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_cistatic int patch_sigmatel_stac9708_specific(struct snd_ac97 *ac97)
9808c2ecf20Sopenharmony_ci{
9818c2ecf20Sopenharmony_ci	int err;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	/* the register bit is writable, but the function is not implemented: */
9848c2ecf20Sopenharmony_ci	snd_ac97_remove_ctl(ac97, "PCM Out Path & Mute", NULL);
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Sigmatel Surround Playback");
9878c2ecf20Sopenharmony_ci	if ((err = patch_build_controls(ac97, &snd_ac97_stac9708_bias_control, 1)) < 0)
9888c2ecf20Sopenharmony_ci		return err;
9898c2ecf20Sopenharmony_ci	return patch_sigmatel_stac97xx_specific(ac97);
9908c2ecf20Sopenharmony_ci}
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_sigmatel_stac9708_ops = {
9938c2ecf20Sopenharmony_ci	.build_3d	= patch_sigmatel_stac9708_3d,
9948c2ecf20Sopenharmony_ci	.build_specific	= patch_sigmatel_stac9708_specific
9958c2ecf20Sopenharmony_ci};
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_cistatic int patch_sigmatel_stac9708(struct snd_ac97 * ac97)
9988c2ecf20Sopenharmony_ci{
9998c2ecf20Sopenharmony_ci	unsigned int codec72, codec6c;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_sigmatel_stac9708_ops;
10028c2ecf20Sopenharmony_ci	ac97->caps |= 0x10;	/* HP (sigmatel surround) support */
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	codec72 = snd_ac97_read(ac97, AC97_SIGMATEL_BIAS2) & 0x8000;
10058c2ecf20Sopenharmony_ci	codec6c = snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	if ((codec72==0) && (codec6c==0)) {
10088c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
10098c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x1000);
10108c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
10118c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0007);
10128c2ecf20Sopenharmony_ci	} else if ((codec72==0x8000) && (codec6c==0)) {
10138c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
10148c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x1001);
10158c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_SIGMATEL_DAC2INVERT, 0x0008);
10168c2ecf20Sopenharmony_ci	} else if ((codec72==0x8000) && (codec6c==0x0080)) {
10178c2ecf20Sopenharmony_ci		/* nothing */
10188c2ecf20Sopenharmony_ci	}
10198c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000);
10208c2ecf20Sopenharmony_ci	return 0;
10218c2ecf20Sopenharmony_ci}
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_cistatic int patch_sigmatel_stac9721(struct snd_ac97 * ac97)
10248c2ecf20Sopenharmony_ci{
10258c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_sigmatel_stac9700_ops;
10268c2ecf20Sopenharmony_ci	if (snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG) == 0) {
10278c2ecf20Sopenharmony_ci		// patch for SigmaTel
10288c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
10298c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x4000);
10308c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
10318c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002);
10328c2ecf20Sopenharmony_ci	}
10338c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000);
10348c2ecf20Sopenharmony_ci	return 0;
10358c2ecf20Sopenharmony_ci}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_cistatic int patch_sigmatel_stac9744(struct snd_ac97 * ac97)
10388c2ecf20Sopenharmony_ci{
10398c2ecf20Sopenharmony_ci	// patch for SigmaTel
10408c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_sigmatel_stac9700_ops;
10418c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
10428c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x0000);	/* is this correct? --jk */
10438c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
10448c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002);
10458c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000);
10468c2ecf20Sopenharmony_ci	return 0;
10478c2ecf20Sopenharmony_ci}
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_cistatic int patch_sigmatel_stac9756(struct snd_ac97 * ac97)
10508c2ecf20Sopenharmony_ci{
10518c2ecf20Sopenharmony_ci	// patch for SigmaTel
10528c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_sigmatel_stac9700_ops;
10538c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
10548c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x0000);	/* is this correct? --jk */
10558c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
10568c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002);
10578c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000);
10588c2ecf20Sopenharmony_ci	return 0;
10598c2ecf20Sopenharmony_ci}
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_cistatic int snd_ac97_stac9758_output_jack_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
10628c2ecf20Sopenharmony_ci{
10638c2ecf20Sopenharmony_ci	static const char * const texts[5] = {
10648c2ecf20Sopenharmony_ci		"Input/Disabled", "Front Output",
10658c2ecf20Sopenharmony_ci		"Rear Output", "Center/LFE Output", "Mixer Output" };
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 5, texts);
10688c2ecf20Sopenharmony_ci}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_cistatic int snd_ac97_stac9758_output_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
10718c2ecf20Sopenharmony_ci{
10728c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
10738c2ecf20Sopenharmony_ci	int shift = kcontrol->private_value;
10748c2ecf20Sopenharmony_ci	unsigned short val;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	val = ac97->regs[AC97_SIGMATEL_OUTSEL] >> shift;
10778c2ecf20Sopenharmony_ci	if (!(val & 4))
10788c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = 0;
10798c2ecf20Sopenharmony_ci	else
10808c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = 1 + (val & 3);
10818c2ecf20Sopenharmony_ci	return 0;
10828c2ecf20Sopenharmony_ci}
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_cistatic int snd_ac97_stac9758_output_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
10858c2ecf20Sopenharmony_ci{
10868c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
10878c2ecf20Sopenharmony_ci	int shift = kcontrol->private_value;
10888c2ecf20Sopenharmony_ci	unsigned short val;
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] > 4)
10918c2ecf20Sopenharmony_ci		return -EINVAL;
10928c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] == 0)
10938c2ecf20Sopenharmony_ci		val = 0;
10948c2ecf20Sopenharmony_ci	else
10958c2ecf20Sopenharmony_ci		val = 4 | (ucontrol->value.enumerated.item[0] - 1);
10968c2ecf20Sopenharmony_ci	return ac97_update_bits_page(ac97, AC97_SIGMATEL_OUTSEL,
10978c2ecf20Sopenharmony_ci				     7 << shift, val << shift, 0);
10988c2ecf20Sopenharmony_ci}
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_cistatic int snd_ac97_stac9758_input_jack_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
11018c2ecf20Sopenharmony_ci{
11028c2ecf20Sopenharmony_ci	static const char * const texts[7] = {
11038c2ecf20Sopenharmony_ci		"Mic2 Jack", "Mic1 Jack", "Line In Jack",
11048c2ecf20Sopenharmony_ci		"Front Jack", "Rear Jack", "Center/LFE Jack", "Mute" };
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 7, texts);
11078c2ecf20Sopenharmony_ci}
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_cistatic int snd_ac97_stac9758_input_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
11108c2ecf20Sopenharmony_ci{
11118c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
11128c2ecf20Sopenharmony_ci	int shift = kcontrol->private_value;
11138c2ecf20Sopenharmony_ci	unsigned short val;
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	val = ac97->regs[AC97_SIGMATEL_INSEL];
11168c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = (val >> shift) & 7;
11178c2ecf20Sopenharmony_ci	return 0;
11188c2ecf20Sopenharmony_ci}
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_cistatic int snd_ac97_stac9758_input_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
11218c2ecf20Sopenharmony_ci{
11228c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
11238c2ecf20Sopenharmony_ci	int shift = kcontrol->private_value;
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	return ac97_update_bits_page(ac97, AC97_SIGMATEL_INSEL, 7 << shift,
11268c2ecf20Sopenharmony_ci				     ucontrol->value.enumerated.item[0] << shift, 0);
11278c2ecf20Sopenharmony_ci}
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_cistatic int snd_ac97_stac9758_phonesel_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
11308c2ecf20Sopenharmony_ci{
11318c2ecf20Sopenharmony_ci	static const char * const texts[3] = {
11328c2ecf20Sopenharmony_ci		"None", "Front Jack", "Rear Jack"
11338c2ecf20Sopenharmony_ci	};
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 3, texts);
11368c2ecf20Sopenharmony_ci}
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_cistatic int snd_ac97_stac9758_phonesel_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
11398c2ecf20Sopenharmony_ci{
11408c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = ac97->regs[AC97_SIGMATEL_IOMISC] & 3;
11438c2ecf20Sopenharmony_ci	return 0;
11448c2ecf20Sopenharmony_ci}
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_cistatic int snd_ac97_stac9758_phonesel_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
11478c2ecf20Sopenharmony_ci{
11488c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	return ac97_update_bits_page(ac97, AC97_SIGMATEL_IOMISC, 3,
11518c2ecf20Sopenharmony_ci				     ucontrol->value.enumerated.item[0], 0);
11528c2ecf20Sopenharmony_ci}
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci#define STAC9758_OUTPUT_JACK(xname, shift) \
11558c2ecf20Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
11568c2ecf20Sopenharmony_ci	.info = snd_ac97_stac9758_output_jack_info, \
11578c2ecf20Sopenharmony_ci	.get = snd_ac97_stac9758_output_jack_get, \
11588c2ecf20Sopenharmony_ci	.put = snd_ac97_stac9758_output_jack_put, \
11598c2ecf20Sopenharmony_ci	.private_value = shift }
11608c2ecf20Sopenharmony_ci#define STAC9758_INPUT_JACK(xname, shift) \
11618c2ecf20Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
11628c2ecf20Sopenharmony_ci	.info = snd_ac97_stac9758_input_jack_info, \
11638c2ecf20Sopenharmony_ci	.get = snd_ac97_stac9758_input_jack_get, \
11648c2ecf20Sopenharmony_ci	.put = snd_ac97_stac9758_input_jack_put, \
11658c2ecf20Sopenharmony_ci	.private_value = shift }
11668c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_sigmatel_stac9758_controls[] = {
11678c2ecf20Sopenharmony_ci	STAC9758_OUTPUT_JACK("Mic1 Jack", 1),
11688c2ecf20Sopenharmony_ci	STAC9758_OUTPUT_JACK("LineIn Jack", 4),
11698c2ecf20Sopenharmony_ci	STAC9758_OUTPUT_JACK("Front Jack", 7),
11708c2ecf20Sopenharmony_ci	STAC9758_OUTPUT_JACK("Rear Jack", 10),
11718c2ecf20Sopenharmony_ci	STAC9758_OUTPUT_JACK("Center/LFE Jack", 13),
11728c2ecf20Sopenharmony_ci	STAC9758_INPUT_JACK("Mic Input Source", 0),
11738c2ecf20Sopenharmony_ci	STAC9758_INPUT_JACK("Line Input Source", 8),
11748c2ecf20Sopenharmony_ci	{
11758c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
11768c2ecf20Sopenharmony_ci		.name = "Headphone Amp",
11778c2ecf20Sopenharmony_ci		.info = snd_ac97_stac9758_phonesel_info,
11788c2ecf20Sopenharmony_ci		.get = snd_ac97_stac9758_phonesel_get,
11798c2ecf20Sopenharmony_ci		.put = snd_ac97_stac9758_phonesel_put
11808c2ecf20Sopenharmony_ci	},
11818c2ecf20Sopenharmony_ci	AC97_SINGLE("Exchange Center/LFE", AC97_SIGMATEL_IOMISC, 4, 1, 0),
11828c2ecf20Sopenharmony_ci	AC97_SINGLE("Headphone +3dB Boost", AC97_SIGMATEL_IOMISC, 8, 1, 0)
11838c2ecf20Sopenharmony_ci};
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_cistatic int patch_sigmatel_stac9758_specific(struct snd_ac97 *ac97)
11868c2ecf20Sopenharmony_ci{
11878c2ecf20Sopenharmony_ci	int err;
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	err = patch_sigmatel_stac97xx_specific(ac97);
11908c2ecf20Sopenharmony_ci	if (err < 0)
11918c2ecf20Sopenharmony_ci		return err;
11928c2ecf20Sopenharmony_ci	err = patch_build_controls(ac97, snd_ac97_sigmatel_stac9758_controls,
11938c2ecf20Sopenharmony_ci				   ARRAY_SIZE(snd_ac97_sigmatel_stac9758_controls));
11948c2ecf20Sopenharmony_ci	if (err < 0)
11958c2ecf20Sopenharmony_ci		return err;
11968c2ecf20Sopenharmony_ci	/* DAC-A direct */
11978c2ecf20Sopenharmony_ci	snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Front Playback");
11988c2ecf20Sopenharmony_ci	/* DAC-A to Mix = PCM */
11998c2ecf20Sopenharmony_ci	/* DAC-B direct = Surround */
12008c2ecf20Sopenharmony_ci	/* DAC-B to Mix */
12018c2ecf20Sopenharmony_ci	snd_ac97_rename_vol_ctl(ac97, "Video Playback", "Surround Mix Playback");
12028c2ecf20Sopenharmony_ci	/* DAC-C direct = Center/LFE */
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	return 0;
12058c2ecf20Sopenharmony_ci}
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_sigmatel_stac9758_ops = {
12088c2ecf20Sopenharmony_ci	.build_3d	= patch_sigmatel_stac9700_3d,
12098c2ecf20Sopenharmony_ci	.build_specific	= patch_sigmatel_stac9758_specific
12108c2ecf20Sopenharmony_ci};
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_cistatic int patch_sigmatel_stac9758(struct snd_ac97 * ac97)
12138c2ecf20Sopenharmony_ci{
12148c2ecf20Sopenharmony_ci	static const unsigned short regs[4] = {
12158c2ecf20Sopenharmony_ci		AC97_SIGMATEL_OUTSEL,
12168c2ecf20Sopenharmony_ci		AC97_SIGMATEL_IOMISC,
12178c2ecf20Sopenharmony_ci		AC97_SIGMATEL_INSEL,
12188c2ecf20Sopenharmony_ci		AC97_SIGMATEL_VARIOUS
12198c2ecf20Sopenharmony_ci	};
12208c2ecf20Sopenharmony_ci	static const unsigned short def_regs[4] = {
12218c2ecf20Sopenharmony_ci		/* OUTSEL */ 0xd794, /* CL:CL, SR:SR, LO:MX, LI:DS, MI:DS */
12228c2ecf20Sopenharmony_ci		/* IOMISC */ 0x2001,
12238c2ecf20Sopenharmony_ci		/* INSEL */ 0x0201, /* LI:LI, MI:M1 */
12248c2ecf20Sopenharmony_ci		/* VARIOUS */ 0x0040
12258c2ecf20Sopenharmony_ci	};
12268c2ecf20Sopenharmony_ci	static const unsigned short m675_regs[4] = {
12278c2ecf20Sopenharmony_ci		/* OUTSEL */ 0xfc70, /* CL:MX, SR:MX, LO:DS, LI:MX, MI:DS */
12288c2ecf20Sopenharmony_ci		/* IOMISC */ 0x2102, /* HP amp on */
12298c2ecf20Sopenharmony_ci		/* INSEL */ 0x0203, /* LI:LI, MI:FR */
12308c2ecf20Sopenharmony_ci		/* VARIOUS */ 0x0041 /* stereo mic */
12318c2ecf20Sopenharmony_ci	};
12328c2ecf20Sopenharmony_ci	const unsigned short *pregs = def_regs;
12338c2ecf20Sopenharmony_ci	int i;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	/* Gateway M675 notebook */
12368c2ecf20Sopenharmony_ci	if (ac97->pci &&
12378c2ecf20Sopenharmony_ci	    ac97->subsystem_vendor == 0x107b &&
12388c2ecf20Sopenharmony_ci	    ac97->subsystem_device == 0x0601)
12398c2ecf20Sopenharmony_ci	    	pregs = m675_regs;
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	// patch for SigmaTel
12428c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_sigmatel_stac9758_ops;
12438c2ecf20Sopenharmony_ci	/* FIXME: assume only page 0 for writing cache */
12448c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, AC97_PAGE_VENDOR);
12458c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++)
12468c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, regs[i], pregs[i]);
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	ac97->flags |= AC97_STEREO_MUTES;
12498c2ecf20Sopenharmony_ci	return 0;
12508c2ecf20Sopenharmony_ci}
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci/*
12538c2ecf20Sopenharmony_ci * Cirrus Logic CS42xx codecs
12548c2ecf20Sopenharmony_ci */
12558c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_cirrus_controls_spdif[2] = {
12568c2ecf20Sopenharmony_ci	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), AC97_CSR_SPDIF, 15, 1, 0),
12578c2ecf20Sopenharmony_ci	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA", AC97_CSR_ACMODE, 0, 3, 0)
12588c2ecf20Sopenharmony_ci};
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_cistatic int patch_cirrus_build_spdif(struct snd_ac97 * ac97)
12618c2ecf20Sopenharmony_ci{
12628c2ecf20Sopenharmony_ci	int err;
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	/* con mask, pro mask, default */
12658c2ecf20Sopenharmony_ci	if ((err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3)) < 0)
12668c2ecf20Sopenharmony_ci		return err;
12678c2ecf20Sopenharmony_ci	/* switch, spsa */
12688c2ecf20Sopenharmony_ci	if ((err = patch_build_controls(ac97, &snd_ac97_cirrus_controls_spdif[0], 1)) < 0)
12698c2ecf20Sopenharmony_ci		return err;
12708c2ecf20Sopenharmony_ci	switch (ac97->id & AC97_ID_CS_MASK) {
12718c2ecf20Sopenharmony_ci	case AC97_ID_CS4205:
12728c2ecf20Sopenharmony_ci		if ((err = patch_build_controls(ac97, &snd_ac97_cirrus_controls_spdif[1], 1)) < 0)
12738c2ecf20Sopenharmony_ci			return err;
12748c2ecf20Sopenharmony_ci		break;
12758c2ecf20Sopenharmony_ci	}
12768c2ecf20Sopenharmony_ci	/* set default PCM S/PDIF params */
12778c2ecf20Sopenharmony_ci	/* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */
12788c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_CSR_SPDIF, 0x0a20);
12798c2ecf20Sopenharmony_ci	return 0;
12808c2ecf20Sopenharmony_ci}
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_cirrus_ops = {
12838c2ecf20Sopenharmony_ci	.build_spdif = patch_cirrus_build_spdif
12848c2ecf20Sopenharmony_ci};
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_cistatic int patch_cirrus_spdif(struct snd_ac97 * ac97)
12878c2ecf20Sopenharmony_ci{
12888c2ecf20Sopenharmony_ci	/* Basically, the cs4201/cs4205/cs4297a has non-standard sp/dif registers.
12898c2ecf20Sopenharmony_ci	   WHY CAN'T ANYONE FOLLOW THE BLOODY SPEC?  *sigh*
12908c2ecf20Sopenharmony_ci	   - sp/dif EA ID is not set, but sp/dif is always present.
12918c2ecf20Sopenharmony_ci	   - enable/disable is spdif register bit 15.
12928c2ecf20Sopenharmony_ci	   - sp/dif control register is 0x68.  differs from AC97:
12938c2ecf20Sopenharmony_ci	   - valid is bit 14 (vs 15)
12948c2ecf20Sopenharmony_ci	   - no DRS
12958c2ecf20Sopenharmony_ci	   - only 44.1/48k [00 = 48, 01=44,1] (AC97 is 00=44.1, 10=48)
12968c2ecf20Sopenharmony_ci	   - sp/dif ssource select is in 0x5e bits 0,1.
12978c2ecf20Sopenharmony_ci	*/
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_cirrus_ops;
13008c2ecf20Sopenharmony_ci	ac97->flags |= AC97_CS_SPDIF;
13018c2ecf20Sopenharmony_ci	ac97->rates[AC97_RATES_SPDIF] &= ~SNDRV_PCM_RATE_32000;
13028c2ecf20Sopenharmony_ci        ac97->ext_id |= AC97_EI_SPDIF;	/* force the detection of spdif */
13038c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_CSR_ACMODE, 0x0080);
13048c2ecf20Sopenharmony_ci	return 0;
13058c2ecf20Sopenharmony_ci}
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_cistatic int patch_cirrus_cs4299(struct snd_ac97 * ac97)
13088c2ecf20Sopenharmony_ci{
13098c2ecf20Sopenharmony_ci	/* force the detection of PC Beep */
13108c2ecf20Sopenharmony_ci	ac97->flags |= AC97_HAS_PC_BEEP;
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	return patch_cirrus_spdif(ac97);
13138c2ecf20Sopenharmony_ci}
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci/*
13168c2ecf20Sopenharmony_ci * Conexant codecs
13178c2ecf20Sopenharmony_ci */
13188c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_conexant_controls_spdif[1] = {
13198c2ecf20Sopenharmony_ci	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), AC97_CXR_AUDIO_MISC, 3, 1, 0),
13208c2ecf20Sopenharmony_ci};
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_cistatic int patch_conexant_build_spdif(struct snd_ac97 * ac97)
13238c2ecf20Sopenharmony_ci{
13248c2ecf20Sopenharmony_ci	int err;
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	/* con mask, pro mask, default */
13278c2ecf20Sopenharmony_ci	if ((err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3)) < 0)
13288c2ecf20Sopenharmony_ci		return err;
13298c2ecf20Sopenharmony_ci	/* switch */
13308c2ecf20Sopenharmony_ci	if ((err = patch_build_controls(ac97, &snd_ac97_conexant_controls_spdif[0], 1)) < 0)
13318c2ecf20Sopenharmony_ci		return err;
13328c2ecf20Sopenharmony_ci	/* set default PCM S/PDIF params */
13338c2ecf20Sopenharmony_ci	/* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */
13348c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_CXR_AUDIO_MISC,
13358c2ecf20Sopenharmony_ci			     snd_ac97_read(ac97, AC97_CXR_AUDIO_MISC) & ~(AC97_CXR_SPDIFEN|AC97_CXR_COPYRGT|AC97_CXR_SPDIF_MASK));
13368c2ecf20Sopenharmony_ci	return 0;
13378c2ecf20Sopenharmony_ci}
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_conexant_ops = {
13408c2ecf20Sopenharmony_ci	.build_spdif = patch_conexant_build_spdif
13418c2ecf20Sopenharmony_ci};
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_cistatic int patch_conexant(struct snd_ac97 * ac97)
13448c2ecf20Sopenharmony_ci{
13458c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_conexant_ops;
13468c2ecf20Sopenharmony_ci	ac97->flags |= AC97_CX_SPDIF;
13478c2ecf20Sopenharmony_ci        ac97->ext_id |= AC97_EI_SPDIF;	/* force the detection of spdif */
13488c2ecf20Sopenharmony_ci	ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */
13498c2ecf20Sopenharmony_ci	return 0;
13508c2ecf20Sopenharmony_ci}
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_cistatic int patch_cx20551(struct snd_ac97 *ac97)
13538c2ecf20Sopenharmony_ci{
13548c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, 0x5c, 0x01, 0x01);
13558c2ecf20Sopenharmony_ci	return 0;
13568c2ecf20Sopenharmony_ci}
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci/*
13598c2ecf20Sopenharmony_ci * Analog Devices AD18xx, AD19xx codecs
13608c2ecf20Sopenharmony_ci */
13618c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
13628c2ecf20Sopenharmony_cistatic void ad18xx_resume(struct snd_ac97 *ac97)
13638c2ecf20Sopenharmony_ci{
13648c2ecf20Sopenharmony_ci	static const unsigned short setup_regs[] = {
13658c2ecf20Sopenharmony_ci		AC97_AD_MISC, AC97_AD_SERIAL_CFG, AC97_AD_JACK_SPDIF,
13668c2ecf20Sopenharmony_ci	};
13678c2ecf20Sopenharmony_ci	int i, codec;
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	for (i = 0; i < (int)ARRAY_SIZE(setup_regs); i++) {
13708c2ecf20Sopenharmony_ci		unsigned short reg = setup_regs[i];
13718c2ecf20Sopenharmony_ci		if (test_bit(reg, ac97->reg_accessed)) {
13728c2ecf20Sopenharmony_ci			snd_ac97_write(ac97, reg, ac97->regs[reg]);
13738c2ecf20Sopenharmony_ci			snd_ac97_read(ac97, reg);
13748c2ecf20Sopenharmony_ci		}
13758c2ecf20Sopenharmony_ci	}
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	if (! (ac97->flags & AC97_AD_MULTI))
13788c2ecf20Sopenharmony_ci		/* normal restore */
13798c2ecf20Sopenharmony_ci		snd_ac97_restore_status(ac97);
13808c2ecf20Sopenharmony_ci	else {
13818c2ecf20Sopenharmony_ci		/* restore the AD18xx codec configurations */
13828c2ecf20Sopenharmony_ci		for (codec = 0; codec < 3; codec++) {
13838c2ecf20Sopenharmony_ci			if (! ac97->spec.ad18xx.id[codec])
13848c2ecf20Sopenharmony_ci				continue;
13858c2ecf20Sopenharmony_ci			/* select single codec */
13868c2ecf20Sopenharmony_ci			snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000,
13878c2ecf20Sopenharmony_ci					     ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]);
13888c2ecf20Sopenharmony_ci			ac97->bus->ops->write(ac97, AC97_AD_CODEC_CFG, ac97->spec.ad18xx.codec_cfg[codec]);
13898c2ecf20Sopenharmony_ci		}
13908c2ecf20Sopenharmony_ci		/* select all codecs */
13918c2ecf20Sopenharmony_ci		snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000);
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci		/* restore status */
13948c2ecf20Sopenharmony_ci		for (i = 2; i < 0x7c ; i += 2) {
13958c2ecf20Sopenharmony_ci			if (i == AC97_POWERDOWN || i == AC97_EXTENDED_ID)
13968c2ecf20Sopenharmony_ci				continue;
13978c2ecf20Sopenharmony_ci			if (test_bit(i, ac97->reg_accessed)) {
13988c2ecf20Sopenharmony_ci				/* handle multi codecs for AD18xx */
13998c2ecf20Sopenharmony_ci				if (i == AC97_PCM) {
14008c2ecf20Sopenharmony_ci					for (codec = 0; codec < 3; codec++) {
14018c2ecf20Sopenharmony_ci						if (! ac97->spec.ad18xx.id[codec])
14028c2ecf20Sopenharmony_ci							continue;
14038c2ecf20Sopenharmony_ci						/* select single codec */
14048c2ecf20Sopenharmony_ci						snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000,
14058c2ecf20Sopenharmony_ci								     ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]);
14068c2ecf20Sopenharmony_ci						/* update PCM bits */
14078c2ecf20Sopenharmony_ci						ac97->bus->ops->write(ac97, AC97_PCM, ac97->spec.ad18xx.pcmreg[codec]);
14088c2ecf20Sopenharmony_ci					}
14098c2ecf20Sopenharmony_ci					/* select all codecs */
14108c2ecf20Sopenharmony_ci					snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000);
14118c2ecf20Sopenharmony_ci					continue;
14128c2ecf20Sopenharmony_ci				} else if (i == AC97_AD_TEST ||
14138c2ecf20Sopenharmony_ci					   i == AC97_AD_CODEC_CFG ||
14148c2ecf20Sopenharmony_ci					   i == AC97_AD_SERIAL_CFG)
14158c2ecf20Sopenharmony_ci					continue; /* ignore */
14168c2ecf20Sopenharmony_ci			}
14178c2ecf20Sopenharmony_ci			snd_ac97_write(ac97, i, ac97->regs[i]);
14188c2ecf20Sopenharmony_ci			snd_ac97_read(ac97, i);
14198c2ecf20Sopenharmony_ci		}
14208c2ecf20Sopenharmony_ci	}
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_ci	snd_ac97_restore_iec958(ac97);
14238c2ecf20Sopenharmony_ci}
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_cistatic void ad1888_resume(struct snd_ac97 *ac97)
14268c2ecf20Sopenharmony_ci{
14278c2ecf20Sopenharmony_ci	ad18xx_resume(ac97);
14288c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_CODEC_CLASS_REV, 0x8080);
14298c2ecf20Sopenharmony_ci}
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci#endif
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_cistatic const struct snd_ac97_res_table ad1819_restbl[] = {
14348c2ecf20Sopenharmony_ci	{ AC97_PHONE, 0x9f1f },
14358c2ecf20Sopenharmony_ci	{ AC97_MIC, 0x9f1f },
14368c2ecf20Sopenharmony_ci	{ AC97_LINE, 0x9f1f },
14378c2ecf20Sopenharmony_ci	{ AC97_CD, 0x9f1f },
14388c2ecf20Sopenharmony_ci	{ AC97_VIDEO, 0x9f1f },
14398c2ecf20Sopenharmony_ci	{ AC97_AUX, 0x9f1f },
14408c2ecf20Sopenharmony_ci	{ AC97_PCM, 0x9f1f },
14418c2ecf20Sopenharmony_ci	{ } /* terminator */
14428c2ecf20Sopenharmony_ci};
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_cistatic int patch_ad1819(struct snd_ac97 * ac97)
14458c2ecf20Sopenharmony_ci{
14468c2ecf20Sopenharmony_ci	unsigned short scfg;
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	// patch for Analog Devices
14498c2ecf20Sopenharmony_ci	scfg = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG);
14508c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, scfg | 0x7000); /* select all codecs */
14518c2ecf20Sopenharmony_ci	ac97->res_table = ad1819_restbl;
14528c2ecf20Sopenharmony_ci	return 0;
14538c2ecf20Sopenharmony_ci}
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_cistatic unsigned short patch_ad1881_unchained(struct snd_ac97 * ac97, int idx, unsigned short mask)
14568c2ecf20Sopenharmony_ci{
14578c2ecf20Sopenharmony_ci	unsigned short val;
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	// test for unchained codec
14608c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, mask);
14618c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0000);	/* ID0C, ID1C, SDIE = off */
14628c2ecf20Sopenharmony_ci	val = snd_ac97_read(ac97, AC97_VENDOR_ID2);
14638c2ecf20Sopenharmony_ci	if ((val & 0xff40) != 0x5340)
14648c2ecf20Sopenharmony_ci		return 0;
14658c2ecf20Sopenharmony_ci	ac97->spec.ad18xx.unchained[idx] = mask;
14668c2ecf20Sopenharmony_ci	ac97->spec.ad18xx.id[idx] = val;
14678c2ecf20Sopenharmony_ci	ac97->spec.ad18xx.codec_cfg[idx] = 0x0000;
14688c2ecf20Sopenharmony_ci	return mask;
14698c2ecf20Sopenharmony_ci}
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_cistatic int patch_ad1881_chained1(struct snd_ac97 * ac97, int idx, unsigned short codec_bits)
14728c2ecf20Sopenharmony_ci{
14738c2ecf20Sopenharmony_ci	static const int cfg_bits[3] = { 1<<12, 1<<14, 1<<13 };
14748c2ecf20Sopenharmony_ci	unsigned short val;
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, cfg_bits[idx]);
14778c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0004);	// SDIE
14788c2ecf20Sopenharmony_ci	val = snd_ac97_read(ac97, AC97_VENDOR_ID2);
14798c2ecf20Sopenharmony_ci	if ((val & 0xff40) != 0x5340)
14808c2ecf20Sopenharmony_ci		return 0;
14818c2ecf20Sopenharmony_ci	if (codec_bits)
14828c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, codec_bits);
14838c2ecf20Sopenharmony_ci	ac97->spec.ad18xx.chained[idx] = cfg_bits[idx];
14848c2ecf20Sopenharmony_ci	ac97->spec.ad18xx.id[idx] = val;
14858c2ecf20Sopenharmony_ci	ac97->spec.ad18xx.codec_cfg[idx] = codec_bits ? codec_bits : 0x0004;
14868c2ecf20Sopenharmony_ci	return 1;
14878c2ecf20Sopenharmony_ci}
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_cistatic void patch_ad1881_chained(struct snd_ac97 * ac97, int unchained_idx, int cidx1, int cidx2)
14908c2ecf20Sopenharmony_ci{
14918c2ecf20Sopenharmony_ci	// already detected?
14928c2ecf20Sopenharmony_ci	if (ac97->spec.ad18xx.unchained[cidx1] || ac97->spec.ad18xx.chained[cidx1])
14938c2ecf20Sopenharmony_ci		cidx1 = -1;
14948c2ecf20Sopenharmony_ci	if (ac97->spec.ad18xx.unchained[cidx2] || ac97->spec.ad18xx.chained[cidx2])
14958c2ecf20Sopenharmony_ci		cidx2 = -1;
14968c2ecf20Sopenharmony_ci	if (cidx1 < 0 && cidx2 < 0)
14978c2ecf20Sopenharmony_ci		return;
14988c2ecf20Sopenharmony_ci	// test for chained codecs
14998c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000,
15008c2ecf20Sopenharmony_ci			     ac97->spec.ad18xx.unchained[unchained_idx]);
15018c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0002);		// ID1C
15028c2ecf20Sopenharmony_ci	ac97->spec.ad18xx.codec_cfg[unchained_idx] = 0x0002;
15038c2ecf20Sopenharmony_ci	if (cidx1 >= 0) {
15048c2ecf20Sopenharmony_ci		if (cidx2 < 0)
15058c2ecf20Sopenharmony_ci			patch_ad1881_chained1(ac97, cidx1, 0);
15068c2ecf20Sopenharmony_ci		else if (patch_ad1881_chained1(ac97, cidx1, 0x0006))	// SDIE | ID1C
15078c2ecf20Sopenharmony_ci			patch_ad1881_chained1(ac97, cidx2, 0);
15088c2ecf20Sopenharmony_ci		else if (patch_ad1881_chained1(ac97, cidx2, 0x0006))	// SDIE | ID1C
15098c2ecf20Sopenharmony_ci			patch_ad1881_chained1(ac97, cidx1, 0);
15108c2ecf20Sopenharmony_ci	} else if (cidx2 >= 0) {
15118c2ecf20Sopenharmony_ci		patch_ad1881_chained1(ac97, cidx2, 0);
15128c2ecf20Sopenharmony_ci	}
15138c2ecf20Sopenharmony_ci}
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_ad1881_build_ops = {
15168c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
15178c2ecf20Sopenharmony_ci	.resume = ad18xx_resume
15188c2ecf20Sopenharmony_ci#endif
15198c2ecf20Sopenharmony_ci};
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_cistatic int patch_ad1881(struct snd_ac97 * ac97)
15228c2ecf20Sopenharmony_ci{
15238c2ecf20Sopenharmony_ci	static const char cfg_idxs[3][2] = {
15248c2ecf20Sopenharmony_ci		{2, 1},
15258c2ecf20Sopenharmony_ci		{0, 2},
15268c2ecf20Sopenharmony_ci		{0, 1}
15278c2ecf20Sopenharmony_ci	};
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci	// patch for Analog Devices
15308c2ecf20Sopenharmony_ci	unsigned short codecs[3];
15318c2ecf20Sopenharmony_ci	unsigned short val;
15328c2ecf20Sopenharmony_ci	int idx, num;
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci	val = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG);
15358c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, val);
15368c2ecf20Sopenharmony_ci	codecs[0] = patch_ad1881_unchained(ac97, 0, (1<<12));
15378c2ecf20Sopenharmony_ci	codecs[1] = patch_ad1881_unchained(ac97, 1, (1<<14));
15388c2ecf20Sopenharmony_ci	codecs[2] = patch_ad1881_unchained(ac97, 2, (1<<13));
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci	if (! (codecs[0] || codecs[1] || codecs[2]))
15418c2ecf20Sopenharmony_ci		goto __end;
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci	for (idx = 0; idx < 3; idx++)
15448c2ecf20Sopenharmony_ci		if (ac97->spec.ad18xx.unchained[idx])
15458c2ecf20Sopenharmony_ci			patch_ad1881_chained(ac97, idx, cfg_idxs[idx][0], cfg_idxs[idx][1]);
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_ci	if (ac97->spec.ad18xx.id[1]) {
15488c2ecf20Sopenharmony_ci		ac97->flags |= AC97_AD_MULTI;
15498c2ecf20Sopenharmony_ci		ac97->scaps |= AC97_SCAP_SURROUND_DAC;
15508c2ecf20Sopenharmony_ci	}
15518c2ecf20Sopenharmony_ci	if (ac97->spec.ad18xx.id[2]) {
15528c2ecf20Sopenharmony_ci		ac97->flags |= AC97_AD_MULTI;
15538c2ecf20Sopenharmony_ci		ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC;
15548c2ecf20Sopenharmony_ci	}
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci      __end:
15578c2ecf20Sopenharmony_ci	/* select all codecs */
15588c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000);
15598c2ecf20Sopenharmony_ci	/* check if only one codec is present */
15608c2ecf20Sopenharmony_ci	for (idx = num = 0; idx < 3; idx++)
15618c2ecf20Sopenharmony_ci		if (ac97->spec.ad18xx.id[idx])
15628c2ecf20Sopenharmony_ci			num++;
15638c2ecf20Sopenharmony_ci	if (num == 1) {
15648c2ecf20Sopenharmony_ci		/* ok, deselect all ID bits */
15658c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0000);
15668c2ecf20Sopenharmony_ci		ac97->spec.ad18xx.codec_cfg[0] =
15678c2ecf20Sopenharmony_ci			ac97->spec.ad18xx.codec_cfg[1] =
15688c2ecf20Sopenharmony_ci			ac97->spec.ad18xx.codec_cfg[2] = 0x0000;
15698c2ecf20Sopenharmony_ci	}
15708c2ecf20Sopenharmony_ci	/* required for AD1886/AD1885 combination */
15718c2ecf20Sopenharmony_ci	ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID);
15728c2ecf20Sopenharmony_ci	if (ac97->spec.ad18xx.id[0]) {
15738c2ecf20Sopenharmony_ci		ac97->id &= 0xffff0000;
15748c2ecf20Sopenharmony_ci		ac97->id |= ac97->spec.ad18xx.id[0];
15758c2ecf20Sopenharmony_ci	}
15768c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_ad1881_build_ops;
15778c2ecf20Sopenharmony_ci	return 0;
15788c2ecf20Sopenharmony_ci}
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_ad1885[] = {
15818c2ecf20Sopenharmony_ci	AC97_SINGLE("Digital Mono Direct", AC97_AD_MISC, 11, 1, 0),
15828c2ecf20Sopenharmony_ci	/* AC97_SINGLE("Digital Audio Mode", AC97_AD_MISC, 12, 1, 0), */ /* seems problematic */
15838c2ecf20Sopenharmony_ci	AC97_SINGLE("Low Power Mixer", AC97_AD_MISC, 14, 1, 0),
15848c2ecf20Sopenharmony_ci	AC97_SINGLE("Zero Fill DAC", AC97_AD_MISC, 15, 1, 0),
15858c2ecf20Sopenharmony_ci	AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 9, 1, 1), /* inverted */
15868c2ecf20Sopenharmony_ci	AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 8, 1, 1), /* inverted */
15878c2ecf20Sopenharmony_ci};
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_6bit_6db_max, -8850, 150, 0);
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_cistatic int patch_ad1885_specific(struct snd_ac97 * ac97)
15928c2ecf20Sopenharmony_ci{
15938c2ecf20Sopenharmony_ci	int err;
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci	if ((err = patch_build_controls(ac97, snd_ac97_controls_ad1885, ARRAY_SIZE(snd_ac97_controls_ad1885))) < 0)
15968c2ecf20Sopenharmony_ci		return err;
15978c2ecf20Sopenharmony_ci	reset_tlv(ac97, "Headphone Playback Volume",
15988c2ecf20Sopenharmony_ci		  db_scale_6bit_6db_max);
15998c2ecf20Sopenharmony_ci	return 0;
16008c2ecf20Sopenharmony_ci}
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_ad1885_build_ops = {
16038c2ecf20Sopenharmony_ci	.build_specific = &patch_ad1885_specific,
16048c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
16058c2ecf20Sopenharmony_ci	.resume = ad18xx_resume
16068c2ecf20Sopenharmony_ci#endif
16078c2ecf20Sopenharmony_ci};
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_cistatic int patch_ad1885(struct snd_ac97 * ac97)
16108c2ecf20Sopenharmony_ci{
16118c2ecf20Sopenharmony_ci	patch_ad1881(ac97);
16128c2ecf20Sopenharmony_ci	/* This is required to deal with the Intel D815EEAL2 */
16138c2ecf20Sopenharmony_ci	/* i.e. Line out is actually headphone out from codec */
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	/* set default */
16168c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_AD_MISC, 0x0404);
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_ad1885_build_ops;
16198c2ecf20Sopenharmony_ci	return 0;
16208c2ecf20Sopenharmony_ci}
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_cistatic int patch_ad1886_specific(struct snd_ac97 * ac97)
16238c2ecf20Sopenharmony_ci{
16248c2ecf20Sopenharmony_ci	reset_tlv(ac97, "Headphone Playback Volume",
16258c2ecf20Sopenharmony_ci		  db_scale_6bit_6db_max);
16268c2ecf20Sopenharmony_ci	return 0;
16278c2ecf20Sopenharmony_ci}
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_ad1886_build_ops = {
16308c2ecf20Sopenharmony_ci	.build_specific = &patch_ad1886_specific,
16318c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
16328c2ecf20Sopenharmony_ci	.resume = ad18xx_resume
16338c2ecf20Sopenharmony_ci#endif
16348c2ecf20Sopenharmony_ci};
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_cistatic int patch_ad1886(struct snd_ac97 * ac97)
16378c2ecf20Sopenharmony_ci{
16388c2ecf20Sopenharmony_ci	patch_ad1881(ac97);
16398c2ecf20Sopenharmony_ci	/* Presario700 workaround */
16408c2ecf20Sopenharmony_ci	/* for Jack Sense/SPDIF Register misetting causing */
16418c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, 0x0010);
16428c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_ad1886_build_ops;
16438c2ecf20Sopenharmony_ci	return 0;
16448c2ecf20Sopenharmony_ci}
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci/* MISC bits (AD1888/AD1980/AD1985 register 0x76) */
16478c2ecf20Sopenharmony_ci#define AC97_AD198X_MBC		0x0003	/* mic boost */
16488c2ecf20Sopenharmony_ci#define AC97_AD198X_MBC_20	0x0000	/* +20dB */
16498c2ecf20Sopenharmony_ci#define AC97_AD198X_MBC_10	0x0001	/* +10dB */
16508c2ecf20Sopenharmony_ci#define AC97_AD198X_MBC_30	0x0002	/* +30dB */
16518c2ecf20Sopenharmony_ci#define AC97_AD198X_VREFD	0x0004	/* VREF high-Z */
16528c2ecf20Sopenharmony_ci#define AC97_AD198X_VREFH	0x0008	/* 0=2.25V, 1=3.7V */
16538c2ecf20Sopenharmony_ci#define AC97_AD198X_VREF_0	0x000c	/* 0V (AD1985 only) */
16548c2ecf20Sopenharmony_ci#define AC97_AD198X_VREF_MASK	(AC97_AD198X_VREFH | AC97_AD198X_VREFD)
16558c2ecf20Sopenharmony_ci#define AC97_AD198X_VREF_SHIFT	2
16568c2ecf20Sopenharmony_ci#define AC97_AD198X_SRU		0x0010	/* sample rate unlock */
16578c2ecf20Sopenharmony_ci#define AC97_AD198X_LOSEL	0x0020	/* LINE_OUT amplifiers input select */
16588c2ecf20Sopenharmony_ci#define AC97_AD198X_2MIC	0x0040	/* 2-channel mic select */
16598c2ecf20Sopenharmony_ci#define AC97_AD198X_SPRD	0x0080	/* SPREAD enable */
16608c2ecf20Sopenharmony_ci#define AC97_AD198X_DMIX0	0x0100	/* downmix mode: */
16618c2ecf20Sopenharmony_ci					/*  0 = 6-to-4, 1 = 6-to-2 downmix */
16628c2ecf20Sopenharmony_ci#define AC97_AD198X_DMIX1	0x0200	/* downmix mode: 1 = enabled */
16638c2ecf20Sopenharmony_ci#define AC97_AD198X_HPSEL	0x0400	/* headphone amplifier input select */
16648c2ecf20Sopenharmony_ci#define AC97_AD198X_CLDIS	0x0800	/* center/lfe disable */
16658c2ecf20Sopenharmony_ci#define AC97_AD198X_LODIS	0x1000	/* LINE_OUT disable */
16668c2ecf20Sopenharmony_ci#define AC97_AD198X_MSPLT	0x2000	/* mute split */
16678c2ecf20Sopenharmony_ci#define AC97_AD198X_AC97NC	0x4000	/* AC97 no compatible mode */
16688c2ecf20Sopenharmony_ci#define AC97_AD198X_DACZ	0x8000	/* DAC zero-fill mode */
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci/* MISC 1 bits (AD1986 register 0x76) */
16718c2ecf20Sopenharmony_ci#define AC97_AD1986_MBC		0x0003	/* mic boost */
16728c2ecf20Sopenharmony_ci#define AC97_AD1986_MBC_20	0x0000	/* +20dB */
16738c2ecf20Sopenharmony_ci#define AC97_AD1986_MBC_10	0x0001	/* +10dB */
16748c2ecf20Sopenharmony_ci#define AC97_AD1986_MBC_30	0x0002	/* +30dB */
16758c2ecf20Sopenharmony_ci#define AC97_AD1986_LISEL0	0x0004	/* LINE_IN select bit 0 */
16768c2ecf20Sopenharmony_ci#define AC97_AD1986_LISEL1	0x0008	/* LINE_IN select bit 1 */
16778c2ecf20Sopenharmony_ci#define AC97_AD1986_LISEL_MASK	(AC97_AD1986_LISEL1 | AC97_AD1986_LISEL0)
16788c2ecf20Sopenharmony_ci#define AC97_AD1986_LISEL_LI	0x0000  /* LINE_IN pins as LINE_IN source */
16798c2ecf20Sopenharmony_ci#define AC97_AD1986_LISEL_SURR	0x0004  /* SURROUND pins as LINE_IN source */
16808c2ecf20Sopenharmony_ci#define AC97_AD1986_LISEL_MIC	0x0008  /* MIC_1/2 pins as LINE_IN source */
16818c2ecf20Sopenharmony_ci#define AC97_AD1986_SRU		0x0010	/* sample rate unlock */
16828c2ecf20Sopenharmony_ci#define AC97_AD1986_SOSEL	0x0020	/* SURROUND_OUT amplifiers input sel */
16838c2ecf20Sopenharmony_ci#define AC97_AD1986_2MIC	0x0040	/* 2-channel mic select */
16848c2ecf20Sopenharmony_ci#define AC97_AD1986_SPRD	0x0080	/* SPREAD enable */
16858c2ecf20Sopenharmony_ci#define AC97_AD1986_DMIX0	0x0100	/* downmix mode: */
16868c2ecf20Sopenharmony_ci					/*  0 = 6-to-4, 1 = 6-to-2 downmix */
16878c2ecf20Sopenharmony_ci#define AC97_AD1986_DMIX1	0x0200	/* downmix mode: 1 = enabled */
16888c2ecf20Sopenharmony_ci#define AC97_AD1986_CLDIS	0x0800	/* center/lfe disable */
16898c2ecf20Sopenharmony_ci#define AC97_AD1986_SODIS	0x1000	/* SURROUND_OUT disable */
16908c2ecf20Sopenharmony_ci#define AC97_AD1986_MSPLT	0x2000	/* mute split (read only 1) */
16918c2ecf20Sopenharmony_ci#define AC97_AD1986_AC97NC	0x4000	/* AC97 no compatible mode (r/o 1) */
16928c2ecf20Sopenharmony_ci#define AC97_AD1986_DACZ	0x8000	/* DAC zero-fill mode */
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_ci/* MISC 2 bits (AD1986 register 0x70) */
16958c2ecf20Sopenharmony_ci#define AC97_AD_MISC2		0x70	/* Misc Control Bits 2 (AD1986) */
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci#define AC97_AD1986_CVREF0	0x0004	/* C/LFE VREF_OUT 2.25V */
16988c2ecf20Sopenharmony_ci#define AC97_AD1986_CVREF1	0x0008	/* C/LFE VREF_OUT 0V */
16998c2ecf20Sopenharmony_ci#define AC97_AD1986_CVREF2	0x0010	/* C/LFE VREF_OUT 3.7V */
17008c2ecf20Sopenharmony_ci#define AC97_AD1986_CVREF_MASK \
17018c2ecf20Sopenharmony_ci	(AC97_AD1986_CVREF2 | AC97_AD1986_CVREF1 | AC97_AD1986_CVREF0)
17028c2ecf20Sopenharmony_ci#define AC97_AD1986_JSMAP	0x0020	/* Jack Sense Mapping 1 = alternate */
17038c2ecf20Sopenharmony_ci#define AC97_AD1986_MMDIS	0x0080	/* Mono Mute Disable */
17048c2ecf20Sopenharmony_ci#define AC97_AD1986_MVREF0	0x0400	/* MIC VREF_OUT 2.25V */
17058c2ecf20Sopenharmony_ci#define AC97_AD1986_MVREF1	0x0800	/* MIC VREF_OUT 0V */
17068c2ecf20Sopenharmony_ci#define AC97_AD1986_MVREF2	0x1000	/* MIC VREF_OUT 3.7V */
17078c2ecf20Sopenharmony_ci#define AC97_AD1986_MVREF_MASK \
17088c2ecf20Sopenharmony_ci	(AC97_AD1986_MVREF2 | AC97_AD1986_MVREF1 | AC97_AD1986_MVREF0)
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci/* MISC 3 bits (AD1986 register 0x7a) */
17118c2ecf20Sopenharmony_ci#define AC97_AD_MISC3		0x7a	/* Misc Control Bits 3 (AD1986) */
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_ci#define AC97_AD1986_MMIX	0x0004	/* Mic Mix, left/right */
17148c2ecf20Sopenharmony_ci#define AC97_AD1986_GPO		0x0008	/* General Purpose Out */
17158c2ecf20Sopenharmony_ci#define AC97_AD1986_LOHPEN	0x0010	/* LINE_OUT headphone drive */
17168c2ecf20Sopenharmony_ci#define AC97_AD1986_LVREF0	0x0100	/* LINE_OUT VREF_OUT 2.25V */
17178c2ecf20Sopenharmony_ci#define AC97_AD1986_LVREF1	0x0200	/* LINE_OUT VREF_OUT 0V */
17188c2ecf20Sopenharmony_ci#define AC97_AD1986_LVREF2	0x0400	/* LINE_OUT VREF_OUT 3.7V */
17198c2ecf20Sopenharmony_ci#define AC97_AD1986_LVREF_MASK \
17208c2ecf20Sopenharmony_ci	(AC97_AD1986_LVREF2 | AC97_AD1986_LVREF1 | AC97_AD1986_LVREF0)
17218c2ecf20Sopenharmony_ci#define AC97_AD1986_JSINVA	0x0800	/* Jack Sense Invert SENSE_A */
17228c2ecf20Sopenharmony_ci#define AC97_AD1986_LOSEL	0x1000	/* LINE_OUT amplifiers input select */
17238c2ecf20Sopenharmony_ci#define AC97_AD1986_HPSEL0	0x2000	/* Headphone amplifiers */
17248c2ecf20Sopenharmony_ci					/*   input select Surround DACs */
17258c2ecf20Sopenharmony_ci#define AC97_AD1986_HPSEL1	0x4000	/* Headphone amplifiers input */
17268c2ecf20Sopenharmony_ci					/*   select C/LFE DACs */
17278c2ecf20Sopenharmony_ci#define AC97_AD1986_JSINVB	0x8000	/* Jack Sense Invert SENSE_B */
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci/* Serial Config bits (AD1986 register 0x74) (incomplete) */
17308c2ecf20Sopenharmony_ci#define AC97_AD1986_OMS0	0x0100	/* Optional Mic Selector bit 0 */
17318c2ecf20Sopenharmony_ci#define AC97_AD1986_OMS1	0x0200	/* Optional Mic Selector bit 1 */
17328c2ecf20Sopenharmony_ci#define AC97_AD1986_OMS2	0x0400	/* Optional Mic Selector bit 2 */
17338c2ecf20Sopenharmony_ci#define AC97_AD1986_OMS_MASK \
17348c2ecf20Sopenharmony_ci	(AC97_AD1986_OMS2 | AC97_AD1986_OMS1 | AC97_AD1986_OMS0)
17358c2ecf20Sopenharmony_ci#define AC97_AD1986_OMS_M	0x0000  /* MIC_1/2 pins are MIC sources */
17368c2ecf20Sopenharmony_ci#define AC97_AD1986_OMS_L	0x0100  /* LINE_IN pins are MIC sources */
17378c2ecf20Sopenharmony_ci#define AC97_AD1986_OMS_C	0x0200  /* Center/LFE pins are MCI sources */
17388c2ecf20Sopenharmony_ci#define AC97_AD1986_OMS_MC	0x0400  /* Mix of MIC and C/LFE pins */
17398c2ecf20Sopenharmony_ci					/*   are MIC sources */
17408c2ecf20Sopenharmony_ci#define AC97_AD1986_OMS_ML	0x0500  /* MIX of MIC and LINE_IN pins */
17418c2ecf20Sopenharmony_ci					/*   are MIC sources */
17428c2ecf20Sopenharmony_ci#define AC97_AD1986_OMS_LC	0x0600  /* MIX of LINE_IN and C/LFE pins */
17438c2ecf20Sopenharmony_ci					/*   are MIC sources */
17448c2ecf20Sopenharmony_ci#define AC97_AD1986_OMS_MLC	0x0700  /* MIX of MIC, LINE_IN, C/LFE pins */
17458c2ecf20Sopenharmony_ci					/*   are MIC sources */
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_cistatic int snd_ac97_ad198x_spdif_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
17498c2ecf20Sopenharmony_ci{
17508c2ecf20Sopenharmony_ci	static const char * const texts[2] = { "AC-Link", "A/D Converter" };
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 2, texts);
17538c2ecf20Sopenharmony_ci}
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_cistatic int snd_ac97_ad198x_spdif_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
17568c2ecf20Sopenharmony_ci{
17578c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
17588c2ecf20Sopenharmony_ci	unsigned short val;
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_ci	val = ac97->regs[AC97_AD_SERIAL_CFG];
17618c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = (val >> 2) & 1;
17628c2ecf20Sopenharmony_ci	return 0;
17638c2ecf20Sopenharmony_ci}
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_cistatic int snd_ac97_ad198x_spdif_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
17668c2ecf20Sopenharmony_ci{
17678c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
17688c2ecf20Sopenharmony_ci	unsigned short val;
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] > 1)
17718c2ecf20Sopenharmony_ci		return -EINVAL;
17728c2ecf20Sopenharmony_ci	val = ucontrol->value.enumerated.item[0] << 2;
17738c2ecf20Sopenharmony_ci	return snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x0004, val);
17748c2ecf20Sopenharmony_ci}
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_ad198x_spdif_source = {
17778c2ecf20Sopenharmony_ci	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
17788c2ecf20Sopenharmony_ci	.name	= SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
17798c2ecf20Sopenharmony_ci	.info	= snd_ac97_ad198x_spdif_source_info,
17808c2ecf20Sopenharmony_ci	.get	= snd_ac97_ad198x_spdif_source_get,
17818c2ecf20Sopenharmony_ci	.put	= snd_ac97_ad198x_spdif_source_put,
17828c2ecf20Sopenharmony_ci};
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_cistatic int patch_ad198x_post_spdif(struct snd_ac97 * ac97)
17858c2ecf20Sopenharmony_ci{
17868c2ecf20Sopenharmony_ci 	return patch_build_controls(ac97, &snd_ac97_ad198x_spdif_source, 1);
17878c2ecf20Sopenharmony_ci}
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_ad1981x_jack_sense[] = {
17908c2ecf20Sopenharmony_ci	AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 11, 1, 0),
17918c2ecf20Sopenharmony_ci	AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0),
17928c2ecf20Sopenharmony_ci};
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci/* deny list to avoid HP/Line jack-sense controls
17958c2ecf20Sopenharmony_ci * (SS vendor << 16 | device)
17968c2ecf20Sopenharmony_ci */
17978c2ecf20Sopenharmony_cistatic const unsigned int ad1981_jacks_denylist[] = {
17988c2ecf20Sopenharmony_ci	0x10140523, /* Thinkpad R40 */
17998c2ecf20Sopenharmony_ci	0x10140534, /* Thinkpad X31 */
18008c2ecf20Sopenharmony_ci	0x10140537, /* Thinkpad T41p */
18018c2ecf20Sopenharmony_ci	0x1014053e, /* Thinkpad R40e */
18028c2ecf20Sopenharmony_ci	0x10140554, /* Thinkpad T42p/R50p */
18038c2ecf20Sopenharmony_ci	0x10140567, /* Thinkpad T43p 2668-G7U */
18048c2ecf20Sopenharmony_ci	0x10140581, /* Thinkpad X41-2527 */
18058c2ecf20Sopenharmony_ci	0x10280160, /* Dell Dimension 2400 */
18068c2ecf20Sopenharmony_ci	0x104380b0, /* Asus A7V8X-MX */
18078c2ecf20Sopenharmony_ci	0x11790241, /* Toshiba Satellite A-15 S127 */
18088c2ecf20Sopenharmony_ci	0x1179ff10, /* Toshiba P500 */
18098c2ecf20Sopenharmony_ci	0x144dc01a, /* Samsung NP-X20C004/SEG */
18108c2ecf20Sopenharmony_ci	0 /* end */
18118c2ecf20Sopenharmony_ci};
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_cistatic int check_list(struct snd_ac97 *ac97, const unsigned int *list)
18148c2ecf20Sopenharmony_ci{
18158c2ecf20Sopenharmony_ci	u32 subid = ((u32)ac97->subsystem_vendor << 16) | ac97->subsystem_device;
18168c2ecf20Sopenharmony_ci	for (; *list; list++)
18178c2ecf20Sopenharmony_ci		if (*list == subid)
18188c2ecf20Sopenharmony_ci			return 1;
18198c2ecf20Sopenharmony_ci	return 0;
18208c2ecf20Sopenharmony_ci}
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_cistatic int patch_ad1981a_specific(struct snd_ac97 * ac97)
18238c2ecf20Sopenharmony_ci{
18248c2ecf20Sopenharmony_ci	if (check_list(ac97, ad1981_jacks_denylist))
18258c2ecf20Sopenharmony_ci		return 0;
18268c2ecf20Sopenharmony_ci	return patch_build_controls(ac97, snd_ac97_ad1981x_jack_sense,
18278c2ecf20Sopenharmony_ci				    ARRAY_SIZE(snd_ac97_ad1981x_jack_sense));
18288c2ecf20Sopenharmony_ci}
18298c2ecf20Sopenharmony_ci
18308c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_ad1981a_build_ops = {
18318c2ecf20Sopenharmony_ci	.build_post_spdif = patch_ad198x_post_spdif,
18328c2ecf20Sopenharmony_ci	.build_specific = patch_ad1981a_specific,
18338c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
18348c2ecf20Sopenharmony_ci	.resume = ad18xx_resume
18358c2ecf20Sopenharmony_ci#endif
18368c2ecf20Sopenharmony_ci};
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci/* allow list to enable HP jack-sense bits
18398c2ecf20Sopenharmony_ci * (SS vendor << 16 | device)
18408c2ecf20Sopenharmony_ci */
18418c2ecf20Sopenharmony_cistatic const unsigned int ad1981_jacks_allowlist[] = {
18428c2ecf20Sopenharmony_ci	0x0e11005a, /* HP nc4000/4010 */
18438c2ecf20Sopenharmony_ci	0x103c0890, /* HP nc6000 */
18448c2ecf20Sopenharmony_ci	0x103c0938, /* HP nc4220 */
18458c2ecf20Sopenharmony_ci	0x103c099c, /* HP nx6110 */
18468c2ecf20Sopenharmony_ci	0x103c0944, /* HP nc6220 */
18478c2ecf20Sopenharmony_ci	0x103c0934, /* HP nc8220 */
18488c2ecf20Sopenharmony_ci	0x103c006d, /* HP nx9105 */
18498c2ecf20Sopenharmony_ci	0x103c300d, /* HP Compaq dc5100 SFF(PT003AW) */
18508c2ecf20Sopenharmony_ci	0x17340088, /* FSC Scenic-W */
18518c2ecf20Sopenharmony_ci	0 /* end */
18528c2ecf20Sopenharmony_ci};
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_cistatic void check_ad1981_hp_jack_sense(struct snd_ac97 *ac97)
18558c2ecf20Sopenharmony_ci{
18568c2ecf20Sopenharmony_ci	if (check_list(ac97, ad1981_jacks_allowlist))
18578c2ecf20Sopenharmony_ci		/* enable headphone jack sense */
18588c2ecf20Sopenharmony_ci		snd_ac97_update_bits(ac97, AC97_AD_JACK_SPDIF, 1<<11, 1<<11);
18598c2ecf20Sopenharmony_ci}
18608c2ecf20Sopenharmony_ci
18618c2ecf20Sopenharmony_cistatic int patch_ad1981a(struct snd_ac97 *ac97)
18628c2ecf20Sopenharmony_ci{
18638c2ecf20Sopenharmony_ci	patch_ad1881(ac97);
18648c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_ad1981a_build_ops;
18658c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD198X_MSPLT, AC97_AD198X_MSPLT);
18668c2ecf20Sopenharmony_ci	ac97->flags |= AC97_STEREO_MUTES;
18678c2ecf20Sopenharmony_ci	check_ad1981_hp_jack_sense(ac97);
18688c2ecf20Sopenharmony_ci	return 0;
18698c2ecf20Sopenharmony_ci}
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_ad198x_2cmic =
18728c2ecf20Sopenharmony_ciAC97_SINGLE("Stereo Mic", AC97_AD_MISC, 6, 1, 0);
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_cistatic int patch_ad1981b_specific(struct snd_ac97 *ac97)
18758c2ecf20Sopenharmony_ci{
18768c2ecf20Sopenharmony_ci	int err;
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_ci	if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
18798c2ecf20Sopenharmony_ci		return err;
18808c2ecf20Sopenharmony_ci	if (check_list(ac97, ad1981_jacks_denylist))
18818c2ecf20Sopenharmony_ci		return 0;
18828c2ecf20Sopenharmony_ci	return patch_build_controls(ac97, snd_ac97_ad1981x_jack_sense,
18838c2ecf20Sopenharmony_ci				    ARRAY_SIZE(snd_ac97_ad1981x_jack_sense));
18848c2ecf20Sopenharmony_ci}
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_ad1981b_build_ops = {
18878c2ecf20Sopenharmony_ci	.build_post_spdif = patch_ad198x_post_spdif,
18888c2ecf20Sopenharmony_ci	.build_specific = patch_ad1981b_specific,
18898c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
18908c2ecf20Sopenharmony_ci	.resume = ad18xx_resume
18918c2ecf20Sopenharmony_ci#endif
18928c2ecf20Sopenharmony_ci};
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_cistatic int patch_ad1981b(struct snd_ac97 *ac97)
18958c2ecf20Sopenharmony_ci{
18968c2ecf20Sopenharmony_ci	patch_ad1881(ac97);
18978c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_ad1981b_build_ops;
18988c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD198X_MSPLT, AC97_AD198X_MSPLT);
18998c2ecf20Sopenharmony_ci	ac97->flags |= AC97_STEREO_MUTES;
19008c2ecf20Sopenharmony_ci	check_ad1981_hp_jack_sense(ac97);
19018c2ecf20Sopenharmony_ci	return 0;
19028c2ecf20Sopenharmony_ci}
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ci#define snd_ac97_ad1888_lohpsel_info	snd_ctl_boolean_mono_info
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_cistatic int snd_ac97_ad1888_lohpsel_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
19078c2ecf20Sopenharmony_ci{
19088c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
19098c2ecf20Sopenharmony_ci	unsigned short val;
19108c2ecf20Sopenharmony_ci
19118c2ecf20Sopenharmony_ci	val = ac97->regs[AC97_AD_MISC];
19128c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = !(val & AC97_AD198X_LOSEL);
19138c2ecf20Sopenharmony_ci	if (ac97->spec.ad18xx.lo_as_master)
19148c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[0] =
19158c2ecf20Sopenharmony_ci			!ucontrol->value.integer.value[0];
19168c2ecf20Sopenharmony_ci	return 0;
19178c2ecf20Sopenharmony_ci}
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_cistatic int snd_ac97_ad1888_lohpsel_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
19208c2ecf20Sopenharmony_ci{
19218c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
19228c2ecf20Sopenharmony_ci	unsigned short val;
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci	val = !ucontrol->value.integer.value[0];
19258c2ecf20Sopenharmony_ci	if (ac97->spec.ad18xx.lo_as_master)
19268c2ecf20Sopenharmony_ci		val = !val;
19278c2ecf20Sopenharmony_ci	val = val ? (AC97_AD198X_LOSEL | AC97_AD198X_HPSEL) : 0;
19288c2ecf20Sopenharmony_ci	return snd_ac97_update_bits(ac97, AC97_AD_MISC,
19298c2ecf20Sopenharmony_ci				    AC97_AD198X_LOSEL | AC97_AD198X_HPSEL, val);
19308c2ecf20Sopenharmony_ci}
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_cistatic int snd_ac97_ad1888_downmix_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
19338c2ecf20Sopenharmony_ci{
19348c2ecf20Sopenharmony_ci	static const char * const texts[3] = {"Off", "6 -> 4", "6 -> 2"};
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 3, texts);
19378c2ecf20Sopenharmony_ci}
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_cistatic int snd_ac97_ad1888_downmix_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
19408c2ecf20Sopenharmony_ci{
19418c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
19428c2ecf20Sopenharmony_ci	unsigned short val;
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_ci	val = ac97->regs[AC97_AD_MISC];
19458c2ecf20Sopenharmony_ci	if (!(val & AC97_AD198X_DMIX1))
19468c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = 0;
19478c2ecf20Sopenharmony_ci	else
19488c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = 1 + ((val >> 8) & 1);
19498c2ecf20Sopenharmony_ci	return 0;
19508c2ecf20Sopenharmony_ci}
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_cistatic int snd_ac97_ad1888_downmix_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
19538c2ecf20Sopenharmony_ci{
19548c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
19558c2ecf20Sopenharmony_ci	unsigned short val;
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] > 2)
19588c2ecf20Sopenharmony_ci		return -EINVAL;
19598c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] == 0)
19608c2ecf20Sopenharmony_ci		val = 0;
19618c2ecf20Sopenharmony_ci	else
19628c2ecf20Sopenharmony_ci		val = AC97_AD198X_DMIX1 |
19638c2ecf20Sopenharmony_ci			((ucontrol->value.enumerated.item[0] - 1) << 8);
19648c2ecf20Sopenharmony_ci	return snd_ac97_update_bits(ac97, AC97_AD_MISC,
19658c2ecf20Sopenharmony_ci				    AC97_AD198X_DMIX0 | AC97_AD198X_DMIX1, val);
19668c2ecf20Sopenharmony_ci}
19678c2ecf20Sopenharmony_ci
19688c2ecf20Sopenharmony_cistatic void ad1888_update_jacks(struct snd_ac97 *ac97)
19698c2ecf20Sopenharmony_ci{
19708c2ecf20Sopenharmony_ci	unsigned short val = 0;
19718c2ecf20Sopenharmony_ci	/* clear LODIS if shared jack is to be used for Surround out */
19728c2ecf20Sopenharmony_ci	if (!ac97->spec.ad18xx.lo_as_master && is_shared_linein(ac97))
19738c2ecf20Sopenharmony_ci		val |= (1 << 12);
19748c2ecf20Sopenharmony_ci	/* clear CLDIS if shared jack is to be used for C/LFE out */
19758c2ecf20Sopenharmony_ci	if (is_shared_micin(ac97))
19768c2ecf20Sopenharmony_ci		val |= (1 << 11);
19778c2ecf20Sopenharmony_ci	/* shared Line-In */
19788c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_AD_MISC, (1 << 11) | (1 << 12), val);
19798c2ecf20Sopenharmony_ci}
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_ad1888_controls[] = {
19828c2ecf20Sopenharmony_ci	{
19838c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
19848c2ecf20Sopenharmony_ci		.name = "Exchange Front/Surround",
19858c2ecf20Sopenharmony_ci		.info = snd_ac97_ad1888_lohpsel_info,
19868c2ecf20Sopenharmony_ci		.get = snd_ac97_ad1888_lohpsel_get,
19878c2ecf20Sopenharmony_ci		.put = snd_ac97_ad1888_lohpsel_put
19888c2ecf20Sopenharmony_ci	},
19898c2ecf20Sopenharmony_ci	AC97_SINGLE("V_REFOUT Enable", AC97_AD_MISC, AC97_AD_VREFD_SHIFT, 1, 1),
19908c2ecf20Sopenharmony_ci	AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2,
19918c2ecf20Sopenharmony_ci			AC97_AD_HPFD_SHIFT, 1, 1),
19928c2ecf20Sopenharmony_ci	AC97_SINGLE("Spread Front to Surround and Center/LFE", AC97_AD_MISC, 7, 1, 0),
19938c2ecf20Sopenharmony_ci	{
19948c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
19958c2ecf20Sopenharmony_ci		.name = "Downmix",
19968c2ecf20Sopenharmony_ci		.info = snd_ac97_ad1888_downmix_info,
19978c2ecf20Sopenharmony_ci		.get = snd_ac97_ad1888_downmix_get,
19988c2ecf20Sopenharmony_ci		.put = snd_ac97_ad1888_downmix_put
19998c2ecf20Sopenharmony_ci	},
20008c2ecf20Sopenharmony_ci	AC97_SURROUND_JACK_MODE_CTL,
20018c2ecf20Sopenharmony_ci	AC97_CHANNEL_MODE_CTL,
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci	AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 10, 1, 0),
20048c2ecf20Sopenharmony_ci	AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0),
20058c2ecf20Sopenharmony_ci};
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_cistatic int patch_ad1888_specific(struct snd_ac97 *ac97)
20088c2ecf20Sopenharmony_ci{
20098c2ecf20Sopenharmony_ci	if (!ac97->spec.ad18xx.lo_as_master) {
20108c2ecf20Sopenharmony_ci		/* rename 0x04 as "Master" and 0x02 as "Master Surround" */
20118c2ecf20Sopenharmony_ci		snd_ac97_rename_vol_ctl(ac97, "Master Playback",
20128c2ecf20Sopenharmony_ci					"Master Surround Playback");
20138c2ecf20Sopenharmony_ci		snd_ac97_rename_vol_ctl(ac97, "Headphone Playback",
20148c2ecf20Sopenharmony_ci					"Master Playback");
20158c2ecf20Sopenharmony_ci	}
20168c2ecf20Sopenharmony_ci	return patch_build_controls(ac97, snd_ac97_ad1888_controls, ARRAY_SIZE(snd_ac97_ad1888_controls));
20178c2ecf20Sopenharmony_ci}
20188c2ecf20Sopenharmony_ci
20198c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_ad1888_build_ops = {
20208c2ecf20Sopenharmony_ci	.build_post_spdif = patch_ad198x_post_spdif,
20218c2ecf20Sopenharmony_ci	.build_specific = patch_ad1888_specific,
20228c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
20238c2ecf20Sopenharmony_ci	.resume = ad1888_resume,
20248c2ecf20Sopenharmony_ci#endif
20258c2ecf20Sopenharmony_ci	.update_jacks = ad1888_update_jacks,
20268c2ecf20Sopenharmony_ci};
20278c2ecf20Sopenharmony_ci
20288c2ecf20Sopenharmony_cistatic int patch_ad1888(struct snd_ac97 * ac97)
20298c2ecf20Sopenharmony_ci{
20308c2ecf20Sopenharmony_ci	unsigned short misc;
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_ci	patch_ad1881(ac97);
20338c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_ad1888_build_ops;
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_ci	/*
20368c2ecf20Sopenharmony_ci	 * LO can be used as a real line-out on some devices,
20378c2ecf20Sopenharmony_ci	 * and we need to revert the front/surround mixer switches
20388c2ecf20Sopenharmony_ci	 */
20398c2ecf20Sopenharmony_ci	if (ac97->subsystem_vendor == 0x1043 &&
20408c2ecf20Sopenharmony_ci	    ac97->subsystem_device == 0x1193) /* ASUS A9T laptop */
20418c2ecf20Sopenharmony_ci		ac97->spec.ad18xx.lo_as_master = 1;
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci	misc = snd_ac97_read(ac97, AC97_AD_MISC);
20448c2ecf20Sopenharmony_ci	/* AD-compatible mode */
20458c2ecf20Sopenharmony_ci	/* Stereo mutes enabled */
20468c2ecf20Sopenharmony_ci	misc |= AC97_AD198X_MSPLT | AC97_AD198X_AC97NC;
20478c2ecf20Sopenharmony_ci	if (!ac97->spec.ad18xx.lo_as_master)
20488c2ecf20Sopenharmony_ci		/* Switch FRONT/SURROUND LINE-OUT/HP-OUT default connection */
20498c2ecf20Sopenharmony_ci		/* it seems that most vendors connect line-out connector to
20508c2ecf20Sopenharmony_ci		 * headphone out of AC'97
20518c2ecf20Sopenharmony_ci		 */
20528c2ecf20Sopenharmony_ci		misc |= AC97_AD198X_LOSEL | AC97_AD198X_HPSEL;
20538c2ecf20Sopenharmony_ci
20548c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_AD_MISC, misc);
20558c2ecf20Sopenharmony_ci	ac97->flags |= AC97_STEREO_MUTES;
20568c2ecf20Sopenharmony_ci	return 0;
20578c2ecf20Sopenharmony_ci}
20588c2ecf20Sopenharmony_ci
20598c2ecf20Sopenharmony_cistatic int patch_ad1980_specific(struct snd_ac97 *ac97)
20608c2ecf20Sopenharmony_ci{
20618c2ecf20Sopenharmony_ci	int err;
20628c2ecf20Sopenharmony_ci
20638c2ecf20Sopenharmony_ci	if ((err = patch_ad1888_specific(ac97)) < 0)
20648c2ecf20Sopenharmony_ci		return err;
20658c2ecf20Sopenharmony_ci	return patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1);
20668c2ecf20Sopenharmony_ci}
20678c2ecf20Sopenharmony_ci
20688c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_ad1980_build_ops = {
20698c2ecf20Sopenharmony_ci	.build_post_spdif = patch_ad198x_post_spdif,
20708c2ecf20Sopenharmony_ci	.build_specific = patch_ad1980_specific,
20718c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
20728c2ecf20Sopenharmony_ci	.resume = ad18xx_resume,
20738c2ecf20Sopenharmony_ci#endif
20748c2ecf20Sopenharmony_ci	.update_jacks = ad1888_update_jacks,
20758c2ecf20Sopenharmony_ci};
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_cistatic int patch_ad1980(struct snd_ac97 * ac97)
20788c2ecf20Sopenharmony_ci{
20798c2ecf20Sopenharmony_ci	patch_ad1888(ac97);
20808c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_ad1980_build_ops;
20818c2ecf20Sopenharmony_ci	return 0;
20828c2ecf20Sopenharmony_ci}
20838c2ecf20Sopenharmony_ci
20848c2ecf20Sopenharmony_cistatic int snd_ac97_ad1985_vrefout_info(struct snd_kcontrol *kcontrol,
20858c2ecf20Sopenharmony_ci					struct snd_ctl_elem_info *uinfo)
20868c2ecf20Sopenharmony_ci{
20878c2ecf20Sopenharmony_ci	static const char * const texts[4] = {
20888c2ecf20Sopenharmony_ci		"High-Z", "3.7 V", "2.25 V", "0 V"
20898c2ecf20Sopenharmony_ci	};
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 4, texts);
20928c2ecf20Sopenharmony_ci}
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_cistatic int snd_ac97_ad1985_vrefout_get(struct snd_kcontrol *kcontrol,
20958c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
20968c2ecf20Sopenharmony_ci{
20978c2ecf20Sopenharmony_ci	static const int reg2ctrl[4] = {2, 0, 1, 3};
20988c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
20998c2ecf20Sopenharmony_ci	unsigned short val;
21008c2ecf20Sopenharmony_ci	val = (ac97->regs[AC97_AD_MISC] & AC97_AD198X_VREF_MASK)
21018c2ecf20Sopenharmony_ci	      >> AC97_AD198X_VREF_SHIFT;
21028c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = reg2ctrl[val];
21038c2ecf20Sopenharmony_ci	return 0;
21048c2ecf20Sopenharmony_ci}
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_cistatic int snd_ac97_ad1985_vrefout_put(struct snd_kcontrol *kcontrol,
21078c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
21088c2ecf20Sopenharmony_ci{
21098c2ecf20Sopenharmony_ci	static const int ctrl2reg[4] = {1, 2, 0, 3};
21108c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
21118c2ecf20Sopenharmony_ci	unsigned short val;
21128c2ecf20Sopenharmony_ci
21138c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] > 3)
21148c2ecf20Sopenharmony_ci		return -EINVAL;
21158c2ecf20Sopenharmony_ci	val = ctrl2reg[ucontrol->value.enumerated.item[0]]
21168c2ecf20Sopenharmony_ci	      << AC97_AD198X_VREF_SHIFT;
21178c2ecf20Sopenharmony_ci	return snd_ac97_update_bits(ac97, AC97_AD_MISC,
21188c2ecf20Sopenharmony_ci				    AC97_AD198X_VREF_MASK, val);
21198c2ecf20Sopenharmony_ci}
21208c2ecf20Sopenharmony_ci
21218c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_ad1985_controls[] = {
21228c2ecf20Sopenharmony_ci	AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0),
21238c2ecf20Sopenharmony_ci	{
21248c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
21258c2ecf20Sopenharmony_ci		.name = "Exchange Front/Surround",
21268c2ecf20Sopenharmony_ci		.info = snd_ac97_ad1888_lohpsel_info,
21278c2ecf20Sopenharmony_ci		.get = snd_ac97_ad1888_lohpsel_get,
21288c2ecf20Sopenharmony_ci		.put = snd_ac97_ad1888_lohpsel_put
21298c2ecf20Sopenharmony_ci	},
21308c2ecf20Sopenharmony_ci	AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2, 12, 1, 1),
21318c2ecf20Sopenharmony_ci	AC97_SINGLE("Spread Front to Surround and Center/LFE",
21328c2ecf20Sopenharmony_ci		    AC97_AD_MISC, 7, 1, 0),
21338c2ecf20Sopenharmony_ci	{
21348c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
21358c2ecf20Sopenharmony_ci		.name = "Downmix",
21368c2ecf20Sopenharmony_ci		.info = snd_ac97_ad1888_downmix_info,
21378c2ecf20Sopenharmony_ci		.get = snd_ac97_ad1888_downmix_get,
21388c2ecf20Sopenharmony_ci		.put = snd_ac97_ad1888_downmix_put
21398c2ecf20Sopenharmony_ci	},
21408c2ecf20Sopenharmony_ci	{
21418c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
21428c2ecf20Sopenharmony_ci		.name = "V_REFOUT",
21438c2ecf20Sopenharmony_ci		.info = snd_ac97_ad1985_vrefout_info,
21448c2ecf20Sopenharmony_ci		.get = snd_ac97_ad1985_vrefout_get,
21458c2ecf20Sopenharmony_ci		.put = snd_ac97_ad1985_vrefout_put
21468c2ecf20Sopenharmony_ci	},
21478c2ecf20Sopenharmony_ci	AC97_SURROUND_JACK_MODE_CTL,
21488c2ecf20Sopenharmony_ci	AC97_CHANNEL_MODE_CTL,
21498c2ecf20Sopenharmony_ci
21508c2ecf20Sopenharmony_ci	AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 10, 1, 0),
21518c2ecf20Sopenharmony_ci	AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0),
21528c2ecf20Sopenharmony_ci};
21538c2ecf20Sopenharmony_ci
21548c2ecf20Sopenharmony_cistatic void ad1985_update_jacks(struct snd_ac97 *ac97)
21558c2ecf20Sopenharmony_ci{
21568c2ecf20Sopenharmony_ci	ad1888_update_jacks(ac97);
21578c2ecf20Sopenharmony_ci	/* clear OMS if shared jack is to be used for C/LFE out */
21588c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 1 << 9,
21598c2ecf20Sopenharmony_ci			     is_shared_micin(ac97) ? 1 << 9 : 0);
21608c2ecf20Sopenharmony_ci}
21618c2ecf20Sopenharmony_ci
21628c2ecf20Sopenharmony_cistatic int patch_ad1985_specific(struct snd_ac97 *ac97)
21638c2ecf20Sopenharmony_ci{
21648c2ecf20Sopenharmony_ci	int err;
21658c2ecf20Sopenharmony_ci
21668c2ecf20Sopenharmony_ci	/* rename 0x04 as "Master" and 0x02 as "Master Surround" */
21678c2ecf20Sopenharmony_ci	snd_ac97_rename_vol_ctl(ac97, "Master Playback",
21688c2ecf20Sopenharmony_ci				"Master Surround Playback");
21698c2ecf20Sopenharmony_ci	snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback");
21708c2ecf20Sopenharmony_ci
21718c2ecf20Sopenharmony_ci	if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
21728c2ecf20Sopenharmony_ci		return err;
21738c2ecf20Sopenharmony_ci
21748c2ecf20Sopenharmony_ci	return patch_build_controls(ac97, snd_ac97_ad1985_controls,
21758c2ecf20Sopenharmony_ci				    ARRAY_SIZE(snd_ac97_ad1985_controls));
21768c2ecf20Sopenharmony_ci}
21778c2ecf20Sopenharmony_ci
21788c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_ad1985_build_ops = {
21798c2ecf20Sopenharmony_ci	.build_post_spdif = patch_ad198x_post_spdif,
21808c2ecf20Sopenharmony_ci	.build_specific = patch_ad1985_specific,
21818c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
21828c2ecf20Sopenharmony_ci	.resume = ad18xx_resume,
21838c2ecf20Sopenharmony_ci#endif
21848c2ecf20Sopenharmony_ci	.update_jacks = ad1985_update_jacks,
21858c2ecf20Sopenharmony_ci};
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_cistatic int patch_ad1985(struct snd_ac97 * ac97)
21888c2ecf20Sopenharmony_ci{
21898c2ecf20Sopenharmony_ci	unsigned short misc;
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_ci	patch_ad1881(ac97);
21928c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_ad1985_build_ops;
21938c2ecf20Sopenharmony_ci	misc = snd_ac97_read(ac97, AC97_AD_MISC);
21948c2ecf20Sopenharmony_ci	/* switch front/surround line-out/hp-out */
21958c2ecf20Sopenharmony_ci	/* AD-compatible mode */
21968c2ecf20Sopenharmony_ci	/* Stereo mutes enabled */
21978c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_AD_MISC, misc |
21988c2ecf20Sopenharmony_ci			     AC97_AD198X_LOSEL |
21998c2ecf20Sopenharmony_ci			     AC97_AD198X_HPSEL |
22008c2ecf20Sopenharmony_ci			     AC97_AD198X_MSPLT |
22018c2ecf20Sopenharmony_ci			     AC97_AD198X_AC97NC);
22028c2ecf20Sopenharmony_ci	ac97->flags |= AC97_STEREO_MUTES;
22038c2ecf20Sopenharmony_ci
22048c2ecf20Sopenharmony_ci	/* update current jack configuration */
22058c2ecf20Sopenharmony_ci	ad1985_update_jacks(ac97);
22068c2ecf20Sopenharmony_ci
22078c2ecf20Sopenharmony_ci	/* on AD1985 rev. 3, AC'97 revision bits are zero */
22088c2ecf20Sopenharmony_ci	ac97->ext_id = (ac97->ext_id & ~AC97_EI_REV_MASK) | AC97_EI_REV_23;
22098c2ecf20Sopenharmony_ci	return 0;
22108c2ecf20Sopenharmony_ci}
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci#define snd_ac97_ad1986_bool_info	snd_ctl_boolean_mono_info
22138c2ecf20Sopenharmony_ci
22148c2ecf20Sopenharmony_cistatic int snd_ac97_ad1986_lososel_get(struct snd_kcontrol *kcontrol,
22158c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
22168c2ecf20Sopenharmony_ci{
22178c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
22188c2ecf20Sopenharmony_ci	unsigned short val;
22198c2ecf20Sopenharmony_ci
22208c2ecf20Sopenharmony_ci	val = ac97->regs[AC97_AD_MISC3];
22218c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = (val & AC97_AD1986_LOSEL) != 0;
22228c2ecf20Sopenharmony_ci	return 0;
22238c2ecf20Sopenharmony_ci}
22248c2ecf20Sopenharmony_ci
22258c2ecf20Sopenharmony_cistatic int snd_ac97_ad1986_lososel_put(struct snd_kcontrol *kcontrol,
22268c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
22278c2ecf20Sopenharmony_ci{
22288c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
22298c2ecf20Sopenharmony_ci	int ret0;
22308c2ecf20Sopenharmony_ci	int ret1;
22318c2ecf20Sopenharmony_ci	int sprd = (ac97->regs[AC97_AD_MISC] & AC97_AD1986_SPRD) != 0;
22328c2ecf20Sopenharmony_ci
22338c2ecf20Sopenharmony_ci	ret0 = snd_ac97_update_bits(ac97, AC97_AD_MISC3, AC97_AD1986_LOSEL,
22348c2ecf20Sopenharmony_ci					ucontrol->value.integer.value[0] != 0
22358c2ecf20Sopenharmony_ci				    ? AC97_AD1986_LOSEL : 0);
22368c2ecf20Sopenharmony_ci	if (ret0 < 0)
22378c2ecf20Sopenharmony_ci		return ret0;
22388c2ecf20Sopenharmony_ci
22398c2ecf20Sopenharmony_ci	/* SOSEL is set to values of "Spread" or "Exchange F/S" controls */
22408c2ecf20Sopenharmony_ci	ret1 = snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD1986_SOSEL,
22418c2ecf20Sopenharmony_ci				    (ucontrol->value.integer.value[0] != 0
22428c2ecf20Sopenharmony_ci				     || sprd)
22438c2ecf20Sopenharmony_ci				    ? AC97_AD1986_SOSEL : 0);
22448c2ecf20Sopenharmony_ci	if (ret1 < 0)
22458c2ecf20Sopenharmony_ci		return ret1;
22468c2ecf20Sopenharmony_ci
22478c2ecf20Sopenharmony_ci	return (ret0 > 0 || ret1 > 0) ? 1 : 0;
22488c2ecf20Sopenharmony_ci}
22498c2ecf20Sopenharmony_ci
22508c2ecf20Sopenharmony_cistatic int snd_ac97_ad1986_spread_get(struct snd_kcontrol *kcontrol,
22518c2ecf20Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
22528c2ecf20Sopenharmony_ci{
22538c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
22548c2ecf20Sopenharmony_ci	unsigned short val;
22558c2ecf20Sopenharmony_ci
22568c2ecf20Sopenharmony_ci	val = ac97->regs[AC97_AD_MISC];
22578c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = (val & AC97_AD1986_SPRD) != 0;
22588c2ecf20Sopenharmony_ci	return 0;
22598c2ecf20Sopenharmony_ci}
22608c2ecf20Sopenharmony_ci
22618c2ecf20Sopenharmony_cistatic int snd_ac97_ad1986_spread_put(struct snd_kcontrol *kcontrol,
22628c2ecf20Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
22638c2ecf20Sopenharmony_ci{
22648c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
22658c2ecf20Sopenharmony_ci	int ret0;
22668c2ecf20Sopenharmony_ci	int ret1;
22678c2ecf20Sopenharmony_ci	int sprd = (ac97->regs[AC97_AD_MISC3] & AC97_AD1986_LOSEL) != 0;
22688c2ecf20Sopenharmony_ci
22698c2ecf20Sopenharmony_ci	ret0 = snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD1986_SPRD,
22708c2ecf20Sopenharmony_ci					ucontrol->value.integer.value[0] != 0
22718c2ecf20Sopenharmony_ci				    ? AC97_AD1986_SPRD : 0);
22728c2ecf20Sopenharmony_ci	if (ret0 < 0)
22738c2ecf20Sopenharmony_ci		return ret0;
22748c2ecf20Sopenharmony_ci
22758c2ecf20Sopenharmony_ci	/* SOSEL is set to values of "Spread" or "Exchange F/S" controls */
22768c2ecf20Sopenharmony_ci	ret1 = snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD1986_SOSEL,
22778c2ecf20Sopenharmony_ci				    (ucontrol->value.integer.value[0] != 0
22788c2ecf20Sopenharmony_ci				     || sprd)
22798c2ecf20Sopenharmony_ci				    ? AC97_AD1986_SOSEL : 0);
22808c2ecf20Sopenharmony_ci	if (ret1 < 0)
22818c2ecf20Sopenharmony_ci		return ret1;
22828c2ecf20Sopenharmony_ci
22838c2ecf20Sopenharmony_ci	return (ret0 > 0 || ret1 > 0) ? 1 : 0;
22848c2ecf20Sopenharmony_ci}
22858c2ecf20Sopenharmony_ci
22868c2ecf20Sopenharmony_cistatic int snd_ac97_ad1986_miclisel_get(struct snd_kcontrol *kcontrol,
22878c2ecf20Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
22888c2ecf20Sopenharmony_ci{
22898c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
22908c2ecf20Sopenharmony_ci
22918c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = ac97->spec.ad18xx.swap_mic_linein;
22928c2ecf20Sopenharmony_ci	return 0;
22938c2ecf20Sopenharmony_ci}
22948c2ecf20Sopenharmony_ci
22958c2ecf20Sopenharmony_cistatic int snd_ac97_ad1986_miclisel_put(struct snd_kcontrol *kcontrol,
22968c2ecf20Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
22978c2ecf20Sopenharmony_ci{
22988c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
22998c2ecf20Sopenharmony_ci	unsigned char swap = ucontrol->value.integer.value[0] != 0;
23008c2ecf20Sopenharmony_ci
23018c2ecf20Sopenharmony_ci	if (swap != ac97->spec.ad18xx.swap_mic_linein) {
23028c2ecf20Sopenharmony_ci		ac97->spec.ad18xx.swap_mic_linein = swap;
23038c2ecf20Sopenharmony_ci		if (ac97->build_ops->update_jacks)
23048c2ecf20Sopenharmony_ci			ac97->build_ops->update_jacks(ac97);
23058c2ecf20Sopenharmony_ci		return 1;
23068c2ecf20Sopenharmony_ci	}
23078c2ecf20Sopenharmony_ci	return 0;
23088c2ecf20Sopenharmony_ci}
23098c2ecf20Sopenharmony_ci
23108c2ecf20Sopenharmony_cistatic int snd_ac97_ad1986_vrefout_get(struct snd_kcontrol *kcontrol,
23118c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
23128c2ecf20Sopenharmony_ci{
23138c2ecf20Sopenharmony_ci	/* Use MIC_1/2 V_REFOUT as the "get" value */
23148c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
23158c2ecf20Sopenharmony_ci	unsigned short val;
23168c2ecf20Sopenharmony_ci	unsigned short reg = ac97->regs[AC97_AD_MISC2];
23178c2ecf20Sopenharmony_ci	if ((reg & AC97_AD1986_MVREF0) != 0)
23188c2ecf20Sopenharmony_ci		val = 2;
23198c2ecf20Sopenharmony_ci	else if ((reg & AC97_AD1986_MVREF1) != 0)
23208c2ecf20Sopenharmony_ci		val = 3;
23218c2ecf20Sopenharmony_ci	else if ((reg & AC97_AD1986_MVREF2) != 0)
23228c2ecf20Sopenharmony_ci		val = 1;
23238c2ecf20Sopenharmony_ci	else
23248c2ecf20Sopenharmony_ci		val = 0;
23258c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = val;
23268c2ecf20Sopenharmony_ci	return 0;
23278c2ecf20Sopenharmony_ci}
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_cistatic int snd_ac97_ad1986_vrefout_put(struct snd_kcontrol *kcontrol,
23308c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
23318c2ecf20Sopenharmony_ci{
23328c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
23338c2ecf20Sopenharmony_ci	unsigned short cval;
23348c2ecf20Sopenharmony_ci	unsigned short lval;
23358c2ecf20Sopenharmony_ci	unsigned short mval;
23368c2ecf20Sopenharmony_ci	int cret;
23378c2ecf20Sopenharmony_ci	int lret;
23388c2ecf20Sopenharmony_ci	int mret;
23398c2ecf20Sopenharmony_ci
23408c2ecf20Sopenharmony_ci	switch (ucontrol->value.enumerated.item[0])
23418c2ecf20Sopenharmony_ci	{
23428c2ecf20Sopenharmony_ci	case 0: /* High-Z */
23438c2ecf20Sopenharmony_ci		cval = 0;
23448c2ecf20Sopenharmony_ci		lval = 0;
23458c2ecf20Sopenharmony_ci		mval = 0;
23468c2ecf20Sopenharmony_ci		break;
23478c2ecf20Sopenharmony_ci	case 1: /* 3.7 V */
23488c2ecf20Sopenharmony_ci		cval = AC97_AD1986_CVREF2;
23498c2ecf20Sopenharmony_ci		lval = AC97_AD1986_LVREF2;
23508c2ecf20Sopenharmony_ci		mval = AC97_AD1986_MVREF2;
23518c2ecf20Sopenharmony_ci		break;
23528c2ecf20Sopenharmony_ci	case 2: /* 2.25 V */
23538c2ecf20Sopenharmony_ci		cval = AC97_AD1986_CVREF0;
23548c2ecf20Sopenharmony_ci		lval = AC97_AD1986_LVREF0;
23558c2ecf20Sopenharmony_ci		mval = AC97_AD1986_MVREF0;
23568c2ecf20Sopenharmony_ci		break;
23578c2ecf20Sopenharmony_ci	case 3: /* 0 V */
23588c2ecf20Sopenharmony_ci		cval = AC97_AD1986_CVREF1;
23598c2ecf20Sopenharmony_ci		lval = AC97_AD1986_LVREF1;
23608c2ecf20Sopenharmony_ci		mval = AC97_AD1986_MVREF1;
23618c2ecf20Sopenharmony_ci		break;
23628c2ecf20Sopenharmony_ci	default:
23638c2ecf20Sopenharmony_ci		return -EINVAL;
23648c2ecf20Sopenharmony_ci	}
23658c2ecf20Sopenharmony_ci
23668c2ecf20Sopenharmony_ci	cret = snd_ac97_update_bits(ac97, AC97_AD_MISC2,
23678c2ecf20Sopenharmony_ci				    AC97_AD1986_CVREF_MASK, cval);
23688c2ecf20Sopenharmony_ci	if (cret < 0)
23698c2ecf20Sopenharmony_ci		return cret;
23708c2ecf20Sopenharmony_ci	lret = snd_ac97_update_bits(ac97, AC97_AD_MISC3,
23718c2ecf20Sopenharmony_ci				    AC97_AD1986_LVREF_MASK, lval);
23728c2ecf20Sopenharmony_ci	if (lret < 0)
23738c2ecf20Sopenharmony_ci		return lret;
23748c2ecf20Sopenharmony_ci	mret = snd_ac97_update_bits(ac97, AC97_AD_MISC2,
23758c2ecf20Sopenharmony_ci				    AC97_AD1986_MVREF_MASK, mval);
23768c2ecf20Sopenharmony_ci	if (mret < 0)
23778c2ecf20Sopenharmony_ci		return mret;
23788c2ecf20Sopenharmony_ci
23798c2ecf20Sopenharmony_ci	return (cret > 0 || lret > 0 || mret > 0) ? 1 : 0;
23808c2ecf20Sopenharmony_ci}
23818c2ecf20Sopenharmony_ci
23828c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_ad1986_controls[] = {
23838c2ecf20Sopenharmony_ci	AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0),
23848c2ecf20Sopenharmony_ci	{
23858c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
23868c2ecf20Sopenharmony_ci		.name = "Exchange Front/Surround",
23878c2ecf20Sopenharmony_ci		.info = snd_ac97_ad1986_bool_info,
23888c2ecf20Sopenharmony_ci		.get = snd_ac97_ad1986_lososel_get,
23898c2ecf20Sopenharmony_ci		.put = snd_ac97_ad1986_lososel_put
23908c2ecf20Sopenharmony_ci	},
23918c2ecf20Sopenharmony_ci	{
23928c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
23938c2ecf20Sopenharmony_ci		.name = "Exchange Mic/Line In",
23948c2ecf20Sopenharmony_ci		.info = snd_ac97_ad1986_bool_info,
23958c2ecf20Sopenharmony_ci		.get = snd_ac97_ad1986_miclisel_get,
23968c2ecf20Sopenharmony_ci		.put = snd_ac97_ad1986_miclisel_put
23978c2ecf20Sopenharmony_ci	},
23988c2ecf20Sopenharmony_ci	{
23998c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
24008c2ecf20Sopenharmony_ci		.name = "Spread Front to Surround and Center/LFE",
24018c2ecf20Sopenharmony_ci		.info = snd_ac97_ad1986_bool_info,
24028c2ecf20Sopenharmony_ci		.get = snd_ac97_ad1986_spread_get,
24038c2ecf20Sopenharmony_ci		.put = snd_ac97_ad1986_spread_put
24048c2ecf20Sopenharmony_ci	},
24058c2ecf20Sopenharmony_ci	{
24068c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
24078c2ecf20Sopenharmony_ci		.name = "Downmix",
24088c2ecf20Sopenharmony_ci		.info = snd_ac97_ad1888_downmix_info,
24098c2ecf20Sopenharmony_ci		.get = snd_ac97_ad1888_downmix_get,
24108c2ecf20Sopenharmony_ci		.put = snd_ac97_ad1888_downmix_put
24118c2ecf20Sopenharmony_ci	},
24128c2ecf20Sopenharmony_ci	{
24138c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
24148c2ecf20Sopenharmony_ci		.name = "V_REFOUT",
24158c2ecf20Sopenharmony_ci		.info = snd_ac97_ad1985_vrefout_info,
24168c2ecf20Sopenharmony_ci		.get = snd_ac97_ad1986_vrefout_get,
24178c2ecf20Sopenharmony_ci		.put = snd_ac97_ad1986_vrefout_put
24188c2ecf20Sopenharmony_ci	},
24198c2ecf20Sopenharmony_ci	AC97_SURROUND_JACK_MODE_CTL,
24208c2ecf20Sopenharmony_ci	AC97_CHANNEL_MODE_CTL,
24218c2ecf20Sopenharmony_ci
24228c2ecf20Sopenharmony_ci	AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 10, 1, 0),
24238c2ecf20Sopenharmony_ci	AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0)
24248c2ecf20Sopenharmony_ci};
24258c2ecf20Sopenharmony_ci
24268c2ecf20Sopenharmony_cistatic void ad1986_update_jacks(struct snd_ac97 *ac97)
24278c2ecf20Sopenharmony_ci{
24288c2ecf20Sopenharmony_ci	unsigned short misc_val = 0;
24298c2ecf20Sopenharmony_ci	unsigned short ser_val;
24308c2ecf20Sopenharmony_ci
24318c2ecf20Sopenharmony_ci	/* disable SURROUND and CENTER/LFE if not surround mode */
24328c2ecf20Sopenharmony_ci	if (!is_surround_on(ac97))
24338c2ecf20Sopenharmony_ci		misc_val |= AC97_AD1986_SODIS;
24348c2ecf20Sopenharmony_ci	if (!is_clfe_on(ac97))
24358c2ecf20Sopenharmony_ci		misc_val |= AC97_AD1986_CLDIS;
24368c2ecf20Sopenharmony_ci
24378c2ecf20Sopenharmony_ci	/* select line input (default=LINE_IN, SURROUND or MIC_1/2) */
24388c2ecf20Sopenharmony_ci	if (is_shared_linein(ac97))
24398c2ecf20Sopenharmony_ci		misc_val |= AC97_AD1986_LISEL_SURR;
24408c2ecf20Sopenharmony_ci	else if (ac97->spec.ad18xx.swap_mic_linein != 0)
24418c2ecf20Sopenharmony_ci		misc_val |= AC97_AD1986_LISEL_MIC;
24428c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_AD_MISC,
24438c2ecf20Sopenharmony_ci			     AC97_AD1986_SODIS | AC97_AD1986_CLDIS |
24448c2ecf20Sopenharmony_ci			     AC97_AD1986_LISEL_MASK,
24458c2ecf20Sopenharmony_ci			     misc_val);
24468c2ecf20Sopenharmony_ci
24478c2ecf20Sopenharmony_ci	/* select microphone input (MIC_1/2, Center/LFE or LINE_IN) */
24488c2ecf20Sopenharmony_ci	if (is_shared_micin(ac97))
24498c2ecf20Sopenharmony_ci		ser_val = AC97_AD1986_OMS_C;
24508c2ecf20Sopenharmony_ci	else if (ac97->spec.ad18xx.swap_mic_linein != 0)
24518c2ecf20Sopenharmony_ci		ser_val = AC97_AD1986_OMS_L;
24528c2ecf20Sopenharmony_ci	else
24538c2ecf20Sopenharmony_ci		ser_val = AC97_AD1986_OMS_M;
24548c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG,
24558c2ecf20Sopenharmony_ci			     AC97_AD1986_OMS_MASK,
24568c2ecf20Sopenharmony_ci			     ser_val);
24578c2ecf20Sopenharmony_ci}
24588c2ecf20Sopenharmony_ci
24598c2ecf20Sopenharmony_cistatic int patch_ad1986_specific(struct snd_ac97 *ac97)
24608c2ecf20Sopenharmony_ci{
24618c2ecf20Sopenharmony_ci	int err;
24628c2ecf20Sopenharmony_ci
24638c2ecf20Sopenharmony_ci	if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
24648c2ecf20Sopenharmony_ci		return err;
24658c2ecf20Sopenharmony_ci
24668c2ecf20Sopenharmony_ci	return patch_build_controls(ac97, snd_ac97_ad1986_controls,
24678c2ecf20Sopenharmony_ci				    ARRAY_SIZE(snd_ac97_ad1985_controls));
24688c2ecf20Sopenharmony_ci}
24698c2ecf20Sopenharmony_ci
24708c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_ad1986_build_ops = {
24718c2ecf20Sopenharmony_ci	.build_post_spdif = patch_ad198x_post_spdif,
24728c2ecf20Sopenharmony_ci	.build_specific = patch_ad1986_specific,
24738c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
24748c2ecf20Sopenharmony_ci	.resume = ad18xx_resume,
24758c2ecf20Sopenharmony_ci#endif
24768c2ecf20Sopenharmony_ci	.update_jacks = ad1986_update_jacks,
24778c2ecf20Sopenharmony_ci};
24788c2ecf20Sopenharmony_ci
24798c2ecf20Sopenharmony_cistatic int patch_ad1986(struct snd_ac97 * ac97)
24808c2ecf20Sopenharmony_ci{
24818c2ecf20Sopenharmony_ci	patch_ad1881(ac97);
24828c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_ad1986_build_ops;
24838c2ecf20Sopenharmony_ci	ac97->flags |= AC97_STEREO_MUTES;
24848c2ecf20Sopenharmony_ci
24858c2ecf20Sopenharmony_ci	/* update current jack configuration */
24868c2ecf20Sopenharmony_ci	ad1986_update_jacks(ac97);
24878c2ecf20Sopenharmony_ci
24888c2ecf20Sopenharmony_ci	return 0;
24898c2ecf20Sopenharmony_ci}
24908c2ecf20Sopenharmony_ci
24918c2ecf20Sopenharmony_ci/*
24928c2ecf20Sopenharmony_ci * realtek ALC203: use mono-out for pin 37
24938c2ecf20Sopenharmony_ci */
24948c2ecf20Sopenharmony_cistatic int patch_alc203(struct snd_ac97 *ac97)
24958c2ecf20Sopenharmony_ci{
24968c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, 0x7a, 0x400, 0x400);
24978c2ecf20Sopenharmony_ci	return 0;
24988c2ecf20Sopenharmony_ci}
24998c2ecf20Sopenharmony_ci
25008c2ecf20Sopenharmony_ci/*
25018c2ecf20Sopenharmony_ci * realtek ALC65x/850 codecs
25028c2ecf20Sopenharmony_ci */
25038c2ecf20Sopenharmony_cistatic void alc650_update_jacks(struct snd_ac97 *ac97)
25048c2ecf20Sopenharmony_ci{
25058c2ecf20Sopenharmony_ci	int shared;
25068c2ecf20Sopenharmony_ci
25078c2ecf20Sopenharmony_ci	/* shared Line-In / Surround Out */
25088c2ecf20Sopenharmony_ci	shared = is_shared_surrout(ac97);
25098c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 9,
25108c2ecf20Sopenharmony_ci			     shared ? (1 << 9) : 0);
25118c2ecf20Sopenharmony_ci	/* update shared Mic In / Center/LFE Out */
25128c2ecf20Sopenharmony_ci	shared = is_shared_clfeout(ac97);
25138c2ecf20Sopenharmony_ci	/* disable/enable vref */
25148c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12,
25158c2ecf20Sopenharmony_ci			     shared ? (1 << 12) : 0);
25168c2ecf20Sopenharmony_ci	/* turn on/off center-on-mic */
25178c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10,
25188c2ecf20Sopenharmony_ci			     shared ? (1 << 10) : 0);
25198c2ecf20Sopenharmony_ci	/* GPIO0 high for mic */
25208c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_ALC650_GPIO_STATUS, 0x100,
25218c2ecf20Sopenharmony_ci			     shared ? 0 : 0x100);
25228c2ecf20Sopenharmony_ci}
25238c2ecf20Sopenharmony_ci
25248c2ecf20Sopenharmony_cistatic int alc650_swap_surround_put(struct snd_kcontrol *kcontrol,
25258c2ecf20Sopenharmony_ci				    struct snd_ctl_elem_value *ucontrol)
25268c2ecf20Sopenharmony_ci{
25278c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
25288c2ecf20Sopenharmony_ci	struct snd_pcm_chmap *map = ac97->chmaps[SNDRV_PCM_STREAM_PLAYBACK];
25298c2ecf20Sopenharmony_ci
25308c2ecf20Sopenharmony_ci	if (map) {
25318c2ecf20Sopenharmony_ci		if (ucontrol->value.integer.value[0])
25328c2ecf20Sopenharmony_ci			map->chmap = snd_pcm_std_chmaps;
25338c2ecf20Sopenharmony_ci		else
25348c2ecf20Sopenharmony_ci			map->chmap = snd_pcm_alt_chmaps;
25358c2ecf20Sopenharmony_ci	}
25368c2ecf20Sopenharmony_ci	return snd_ac97_put_volsw(kcontrol, ucontrol);
25378c2ecf20Sopenharmony_ci}
25388c2ecf20Sopenharmony_ci
25398c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_alc650[] = {
25408c2ecf20Sopenharmony_ci	AC97_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0),
25418c2ecf20Sopenharmony_ci	AC97_SINGLE("Surround Down Mix", AC97_ALC650_MULTICH, 1, 1, 0),
25428c2ecf20Sopenharmony_ci	AC97_SINGLE("Center/LFE Down Mix", AC97_ALC650_MULTICH, 2, 1, 0),
25438c2ecf20Sopenharmony_ci	AC97_SINGLE("Exchange Center/LFE", AC97_ALC650_MULTICH, 3, 1, 0),
25448c2ecf20Sopenharmony_ci	/* 4: Analog Input To Surround */
25458c2ecf20Sopenharmony_ci	/* 5: Analog Input To Center/LFE */
25468c2ecf20Sopenharmony_ci	/* 6: Independent Master Volume Right */
25478c2ecf20Sopenharmony_ci	/* 7: Independent Master Volume Left */
25488c2ecf20Sopenharmony_ci	/* 8: reserved */
25498c2ecf20Sopenharmony_ci	/* 9: Line-In/Surround share */
25508c2ecf20Sopenharmony_ci	/* 10: Mic/CLFE share */
25518c2ecf20Sopenharmony_ci	/* 11-13: in IEC958 controls */
25528c2ecf20Sopenharmony_ci	{
25538c2ecf20Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
25548c2ecf20Sopenharmony_ci		.name = "Swap Surround Slot",
25558c2ecf20Sopenharmony_ci		.info = snd_ac97_info_volsw,
25568c2ecf20Sopenharmony_ci		.get = snd_ac97_get_volsw,
25578c2ecf20Sopenharmony_ci		.put = alc650_swap_surround_put,
25588c2ecf20Sopenharmony_ci		.private_value =  AC97_SINGLE_VALUE(AC97_ALC650_MULTICH, 14, 1, 0),
25598c2ecf20Sopenharmony_ci	},
25608c2ecf20Sopenharmony_ci#if 0 /* always set in patch_alc650 */
25618c2ecf20Sopenharmony_ci	AC97_SINGLE("IEC958 Input Clock Enable", AC97_ALC650_CLOCK, 0, 1, 0),
25628c2ecf20Sopenharmony_ci	AC97_SINGLE("IEC958 Input Pin Enable", AC97_ALC650_CLOCK, 1, 1, 0),
25638c2ecf20Sopenharmony_ci	AC97_SINGLE("Surround DAC Switch", AC97_ALC650_SURR_DAC_VOL, 15, 1, 1),
25648c2ecf20Sopenharmony_ci	AC97_DOUBLE("Surround DAC Volume", AC97_ALC650_SURR_DAC_VOL, 8, 0, 31, 1),
25658c2ecf20Sopenharmony_ci	AC97_SINGLE("Center/LFE DAC Switch", AC97_ALC650_LFE_DAC_VOL, 15, 1, 1),
25668c2ecf20Sopenharmony_ci	AC97_DOUBLE("Center/LFE DAC Volume", AC97_ALC650_LFE_DAC_VOL, 8, 0, 31, 1),
25678c2ecf20Sopenharmony_ci#endif
25688c2ecf20Sopenharmony_ci	AC97_SURROUND_JACK_MODE_CTL,
25698c2ecf20Sopenharmony_ci	AC97_CHANNEL_MODE_CTL,
25708c2ecf20Sopenharmony_ci};
25718c2ecf20Sopenharmony_ci
25728c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_spdif_controls_alc650[] = {
25738c2ecf20Sopenharmony_ci        AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), AC97_ALC650_MULTICH, 11, 1, 0),
25748c2ecf20Sopenharmony_ci        AC97_SINGLE("Analog to IEC958 Output", AC97_ALC650_MULTICH, 12, 1, 0),
25758c2ecf20Sopenharmony_ci	/* disable this controls since it doesn't work as expected */
25768c2ecf20Sopenharmony_ci	/* AC97_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 13, 1, 0), */
25778c2ecf20Sopenharmony_ci};
25788c2ecf20Sopenharmony_ci
25798c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_max, -4350, 150, 0);
25808c2ecf20Sopenharmony_ci
25818c2ecf20Sopenharmony_cistatic int patch_alc650_specific(struct snd_ac97 * ac97)
25828c2ecf20Sopenharmony_ci{
25838c2ecf20Sopenharmony_ci	int err;
25848c2ecf20Sopenharmony_ci
25858c2ecf20Sopenharmony_ci	if ((err = patch_build_controls(ac97, snd_ac97_controls_alc650, ARRAY_SIZE(snd_ac97_controls_alc650))) < 0)
25868c2ecf20Sopenharmony_ci		return err;
25878c2ecf20Sopenharmony_ci	if (ac97->ext_id & AC97_EI_SPDIF) {
25888c2ecf20Sopenharmony_ci		if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc650, ARRAY_SIZE(snd_ac97_spdif_controls_alc650))) < 0)
25898c2ecf20Sopenharmony_ci			return err;
25908c2ecf20Sopenharmony_ci	}
25918c2ecf20Sopenharmony_ci	if (ac97->id != AC97_ID_ALC650F)
25928c2ecf20Sopenharmony_ci		reset_tlv(ac97, "Master Playback Volume",
25938c2ecf20Sopenharmony_ci			  db_scale_5bit_3db_max);
25948c2ecf20Sopenharmony_ci	return 0;
25958c2ecf20Sopenharmony_ci}
25968c2ecf20Sopenharmony_ci
25978c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_alc650_ops = {
25988c2ecf20Sopenharmony_ci	.build_specific	= patch_alc650_specific,
25998c2ecf20Sopenharmony_ci	.update_jacks = alc650_update_jacks
26008c2ecf20Sopenharmony_ci};
26018c2ecf20Sopenharmony_ci
26028c2ecf20Sopenharmony_cistatic int patch_alc650(struct snd_ac97 * ac97)
26038c2ecf20Sopenharmony_ci{
26048c2ecf20Sopenharmony_ci	unsigned short val;
26058c2ecf20Sopenharmony_ci
26068c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_alc650_ops;
26078c2ecf20Sopenharmony_ci
26088c2ecf20Sopenharmony_ci	/* determine the revision */
26098c2ecf20Sopenharmony_ci	val = snd_ac97_read(ac97, AC97_ALC650_REVISION) & 0x3f;
26108c2ecf20Sopenharmony_ci	if (val < 3)
26118c2ecf20Sopenharmony_ci		ac97->id = 0x414c4720;          /* Old version */
26128c2ecf20Sopenharmony_ci	else if (val < 0x10)
26138c2ecf20Sopenharmony_ci		ac97->id = 0x414c4721;          /* D version */
26148c2ecf20Sopenharmony_ci	else if (val < 0x20)
26158c2ecf20Sopenharmony_ci		ac97->id = 0x414c4722;          /* E version */
26168c2ecf20Sopenharmony_ci	else if (val < 0x30)
26178c2ecf20Sopenharmony_ci		ac97->id = 0x414c4723;          /* F version */
26188c2ecf20Sopenharmony_ci
26198c2ecf20Sopenharmony_ci	/* revision E or F */
26208c2ecf20Sopenharmony_ci	/* FIXME: what about revision D ? */
26218c2ecf20Sopenharmony_ci	ac97->spec.dev_flags = (ac97->id == 0x414c4722 ||
26228c2ecf20Sopenharmony_ci				ac97->id == 0x414c4723);
26238c2ecf20Sopenharmony_ci
26248c2ecf20Sopenharmony_ci	/* enable AC97_ALC650_GPIO_SETUP, AC97_ALC650_CLOCK for R/W */
26258c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_STATUS,
26268c2ecf20Sopenharmony_ci		snd_ac97_read(ac97, AC97_ALC650_GPIO_STATUS) | 0x8000);
26278c2ecf20Sopenharmony_ci
26288c2ecf20Sopenharmony_ci	/* Enable SPDIF-IN only on Rev.E and above */
26298c2ecf20Sopenharmony_ci	val = snd_ac97_read(ac97, AC97_ALC650_CLOCK);
26308c2ecf20Sopenharmony_ci	/* SPDIF IN with pin 47 */
26318c2ecf20Sopenharmony_ci	if (ac97->spec.dev_flags &&
26328c2ecf20Sopenharmony_ci	    /* ASUS A6KM requires EAPD */
26338c2ecf20Sopenharmony_ci	    ! (ac97->subsystem_vendor == 0x1043 &&
26348c2ecf20Sopenharmony_ci	       ac97->subsystem_device == 0x1103))
26358c2ecf20Sopenharmony_ci		val |= 0x03; /* enable */
26368c2ecf20Sopenharmony_ci	else
26378c2ecf20Sopenharmony_ci		val &= ~0x03; /* disable */
26388c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_ALC650_CLOCK, val);
26398c2ecf20Sopenharmony_ci
26408c2ecf20Sopenharmony_ci	/* set default: slot 3,4,7,8,6,9
26418c2ecf20Sopenharmony_ci	   spdif-in monitor off, analog-spdif off, spdif-in off
26428c2ecf20Sopenharmony_ci	   center on mic off, surround on line-in off
26438c2ecf20Sopenharmony_ci	   downmix off, duplicate front off
26448c2ecf20Sopenharmony_ci	*/
26458c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, 0);
26468c2ecf20Sopenharmony_ci
26478c2ecf20Sopenharmony_ci	/* set GPIO0 for mic bias */
26488c2ecf20Sopenharmony_ci	/* GPIO0 pin output, no interrupt, high */
26498c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_SETUP,
26508c2ecf20Sopenharmony_ci			     snd_ac97_read(ac97, AC97_ALC650_GPIO_SETUP) | 0x01);
26518c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_STATUS,
26528c2ecf20Sopenharmony_ci			     (snd_ac97_read(ac97, AC97_ALC650_GPIO_STATUS) | 0x100) & ~0x10);
26538c2ecf20Sopenharmony_ci
26548c2ecf20Sopenharmony_ci	/* full DAC volume */
26558c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808);
26568c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_ALC650_LFE_DAC_VOL, 0x0808);
26578c2ecf20Sopenharmony_ci	return 0;
26588c2ecf20Sopenharmony_ci}
26598c2ecf20Sopenharmony_ci
26608c2ecf20Sopenharmony_cistatic void alc655_update_jacks(struct snd_ac97 *ac97)
26618c2ecf20Sopenharmony_ci{
26628c2ecf20Sopenharmony_ci	int shared;
26638c2ecf20Sopenharmony_ci
26648c2ecf20Sopenharmony_ci	/* shared Line-In / Surround Out */
26658c2ecf20Sopenharmony_ci	shared = is_shared_surrout(ac97);
26668c2ecf20Sopenharmony_ci	ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 1 << 9,
26678c2ecf20Sopenharmony_ci			      shared ? (1 << 9) : 0, 0);
26688c2ecf20Sopenharmony_ci	/* update shared Mic In / Center/LFE Out */
26698c2ecf20Sopenharmony_ci	shared = is_shared_clfeout(ac97);
26708c2ecf20Sopenharmony_ci	/* misc control; vrefout disable */
26718c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12,
26728c2ecf20Sopenharmony_ci			     shared ? (1 << 12) : 0);
26738c2ecf20Sopenharmony_ci	ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 1 << 10,
26748c2ecf20Sopenharmony_ci			      shared ? (1 << 10) : 0, 0);
26758c2ecf20Sopenharmony_ci}
26768c2ecf20Sopenharmony_ci
26778c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_alc655[] = {
26788c2ecf20Sopenharmony_ci	AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0),
26798c2ecf20Sopenharmony_ci	AC97_SURROUND_JACK_MODE_CTL,
26808c2ecf20Sopenharmony_ci	AC97_CHANNEL_MODE_CTL,
26818c2ecf20Sopenharmony_ci};
26828c2ecf20Sopenharmony_ci
26838c2ecf20Sopenharmony_cistatic int alc655_iec958_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
26848c2ecf20Sopenharmony_ci{
26858c2ecf20Sopenharmony_ci	static const char * const texts_655[3] = {
26868c2ecf20Sopenharmony_ci		"PCM", "Analog In", "IEC958 In"
26878c2ecf20Sopenharmony_ci	};
26888c2ecf20Sopenharmony_ci	static const char * const texts_658[4] = {
26898c2ecf20Sopenharmony_ci		"PCM", "Analog1 In", "Analog2 In", "IEC958 In"
26908c2ecf20Sopenharmony_ci	};
26918c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
26928c2ecf20Sopenharmony_ci
26938c2ecf20Sopenharmony_ci	if (ac97->spec.dev_flags)
26948c2ecf20Sopenharmony_ci		return snd_ctl_enum_info(uinfo, 1, 4, texts_658);
26958c2ecf20Sopenharmony_ci	else
26968c2ecf20Sopenharmony_ci		return snd_ctl_enum_info(uinfo, 1, 3, texts_655);
26978c2ecf20Sopenharmony_ci}
26988c2ecf20Sopenharmony_ci
26998c2ecf20Sopenharmony_cistatic int alc655_iec958_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
27008c2ecf20Sopenharmony_ci{
27018c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
27028c2ecf20Sopenharmony_ci	unsigned short val;
27038c2ecf20Sopenharmony_ci
27048c2ecf20Sopenharmony_ci	val = ac97->regs[AC97_ALC650_MULTICH];
27058c2ecf20Sopenharmony_ci	val = (val >> 12) & 3;
27068c2ecf20Sopenharmony_ci	if (ac97->spec.dev_flags && val == 3)
27078c2ecf20Sopenharmony_ci		val = 0;
27088c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = val;
27098c2ecf20Sopenharmony_ci	return 0;
27108c2ecf20Sopenharmony_ci}
27118c2ecf20Sopenharmony_ci
27128c2ecf20Sopenharmony_cistatic int alc655_iec958_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
27138c2ecf20Sopenharmony_ci{
27148c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
27158c2ecf20Sopenharmony_ci
27168c2ecf20Sopenharmony_ci	return ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 3 << 12,
27178c2ecf20Sopenharmony_ci				     (unsigned short)ucontrol->value.enumerated.item[0] << 12,
27188c2ecf20Sopenharmony_ci				     0);
27198c2ecf20Sopenharmony_ci}
27208c2ecf20Sopenharmony_ci
27218c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_spdif_controls_alc655[] = {
27228c2ecf20Sopenharmony_ci        AC97_PAGE_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), AC97_ALC650_MULTICH, 11, 1, 0, 0),
27238c2ecf20Sopenharmony_ci	/* disable this controls since it doesn't work as expected */
27248c2ecf20Sopenharmony_ci        /* AC97_PAGE_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 14, 1, 0, 0), */
27258c2ecf20Sopenharmony_ci	{
27268c2ecf20Sopenharmony_ci		.iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
27278c2ecf20Sopenharmony_ci		.name   = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
27288c2ecf20Sopenharmony_ci		.info   = alc655_iec958_route_info,
27298c2ecf20Sopenharmony_ci		.get    = alc655_iec958_route_get,
27308c2ecf20Sopenharmony_ci		.put    = alc655_iec958_route_put,
27318c2ecf20Sopenharmony_ci	},
27328c2ecf20Sopenharmony_ci};
27338c2ecf20Sopenharmony_ci
27348c2ecf20Sopenharmony_cistatic int patch_alc655_specific(struct snd_ac97 * ac97)
27358c2ecf20Sopenharmony_ci{
27368c2ecf20Sopenharmony_ci	int err;
27378c2ecf20Sopenharmony_ci
27388c2ecf20Sopenharmony_ci	if ((err = patch_build_controls(ac97, snd_ac97_controls_alc655, ARRAY_SIZE(snd_ac97_controls_alc655))) < 0)
27398c2ecf20Sopenharmony_ci		return err;
27408c2ecf20Sopenharmony_ci	if (ac97->ext_id & AC97_EI_SPDIF) {
27418c2ecf20Sopenharmony_ci		if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc655, ARRAY_SIZE(snd_ac97_spdif_controls_alc655))) < 0)
27428c2ecf20Sopenharmony_ci			return err;
27438c2ecf20Sopenharmony_ci	}
27448c2ecf20Sopenharmony_ci	return 0;
27458c2ecf20Sopenharmony_ci}
27468c2ecf20Sopenharmony_ci
27478c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_alc655_ops = {
27488c2ecf20Sopenharmony_ci	.build_specific	= patch_alc655_specific,
27498c2ecf20Sopenharmony_ci	.update_jacks = alc655_update_jacks
27508c2ecf20Sopenharmony_ci};
27518c2ecf20Sopenharmony_ci
27528c2ecf20Sopenharmony_cistatic int patch_alc655(struct snd_ac97 * ac97)
27538c2ecf20Sopenharmony_ci{
27548c2ecf20Sopenharmony_ci	unsigned int val;
27558c2ecf20Sopenharmony_ci
27568c2ecf20Sopenharmony_ci	if (ac97->id == AC97_ID_ALC658) {
27578c2ecf20Sopenharmony_ci		ac97->spec.dev_flags = 1; /* ALC658 */
27588c2ecf20Sopenharmony_ci		if ((snd_ac97_read(ac97, AC97_ALC650_REVISION) & 0x3f) == 2) {
27598c2ecf20Sopenharmony_ci			ac97->id = AC97_ID_ALC658D;
27608c2ecf20Sopenharmony_ci			ac97->spec.dev_flags = 2;
27618c2ecf20Sopenharmony_ci		}
27628c2ecf20Sopenharmony_ci	}
27638c2ecf20Sopenharmony_ci
27648c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_alc655_ops;
27658c2ecf20Sopenharmony_ci
27668c2ecf20Sopenharmony_ci	/* assume only page 0 for writing cache */
27678c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, AC97_PAGE_VENDOR);
27688c2ecf20Sopenharmony_ci
27698c2ecf20Sopenharmony_ci	/* adjust default values */
27708c2ecf20Sopenharmony_ci	val = snd_ac97_read(ac97, 0x7a); /* misc control */
27718c2ecf20Sopenharmony_ci	if (ac97->spec.dev_flags) /* ALC658 */
27728c2ecf20Sopenharmony_ci		val &= ~(1 << 1); /* Pin 47 is spdif input pin */
27738c2ecf20Sopenharmony_ci	else { /* ALC655 */
27748c2ecf20Sopenharmony_ci		if (ac97->subsystem_vendor == 0x1462 &&
27758c2ecf20Sopenharmony_ci		    (ac97->subsystem_device == 0x0131 || /* MSI S270 laptop */
27768c2ecf20Sopenharmony_ci		     ac97->subsystem_device == 0x0161 || /* LG K1 Express */
27778c2ecf20Sopenharmony_ci		     ac97->subsystem_device == 0x0351 || /* MSI L725 laptop */
27788c2ecf20Sopenharmony_ci		     ac97->subsystem_device == 0x0471 || /* MSI L720 laptop */
27798c2ecf20Sopenharmony_ci		     ac97->subsystem_device == 0x0061))  /* MSI S250 laptop */
27808c2ecf20Sopenharmony_ci			val &= ~(1 << 1); /* Pin 47 is EAPD (for internal speaker) */
27818c2ecf20Sopenharmony_ci		else
27828c2ecf20Sopenharmony_ci			val |= (1 << 1); /* Pin 47 is spdif input pin */
27838c2ecf20Sopenharmony_ci		/* this seems missing on some hardwares */
27848c2ecf20Sopenharmony_ci		ac97->ext_id |= AC97_EI_SPDIF;
27858c2ecf20Sopenharmony_ci	}
27868c2ecf20Sopenharmony_ci	val &= ~(1 << 12); /* vref enable */
27878c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, 0x7a, val);
27888c2ecf20Sopenharmony_ci	/* set default: spdif-in enabled,
27898c2ecf20Sopenharmony_ci	   spdif-in monitor off, spdif-in PCM off
27908c2ecf20Sopenharmony_ci	   center on mic off, surround on line-in off
27918c2ecf20Sopenharmony_ci	   duplicate front off
27928c2ecf20Sopenharmony_ci	*/
27938c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, 1<<15);
27948c2ecf20Sopenharmony_ci
27958c2ecf20Sopenharmony_ci	/* full DAC volume */
27968c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808);
27978c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_ALC650_LFE_DAC_VOL, 0x0808);
27988c2ecf20Sopenharmony_ci
27998c2ecf20Sopenharmony_ci	/* update undocumented bit... */
28008c2ecf20Sopenharmony_ci	if (ac97->id == AC97_ID_ALC658D)
28018c2ecf20Sopenharmony_ci		snd_ac97_update_bits(ac97, 0x74, 0x0800, 0x0800);
28028c2ecf20Sopenharmony_ci
28038c2ecf20Sopenharmony_ci	return 0;
28048c2ecf20Sopenharmony_ci}
28058c2ecf20Sopenharmony_ci
28068c2ecf20Sopenharmony_ci
28078c2ecf20Sopenharmony_ci#define AC97_ALC850_JACK_SELECT	0x76
28088c2ecf20Sopenharmony_ci#define AC97_ALC850_MISC1	0x7a
28098c2ecf20Sopenharmony_ci#define AC97_ALC850_MULTICH    0x6a
28108c2ecf20Sopenharmony_ci
28118c2ecf20Sopenharmony_cistatic void alc850_update_jacks(struct snd_ac97 *ac97)
28128c2ecf20Sopenharmony_ci{
28138c2ecf20Sopenharmony_ci	int shared;
28148c2ecf20Sopenharmony_ci	int aux_is_back_surround;
28158c2ecf20Sopenharmony_ci
28168c2ecf20Sopenharmony_ci	/* shared Line-In / Surround Out */
28178c2ecf20Sopenharmony_ci	shared = is_shared_surrout(ac97);
28188c2ecf20Sopenharmony_ci	/* SURR 1kOhm (bit4), Amp (bit5) */
28198c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<4)|(1<<5),
28208c2ecf20Sopenharmony_ci			     shared ? (1<<5) : (1<<4));
28218c2ecf20Sopenharmony_ci	/* LINE-IN = 0, SURROUND = 2 */
28228c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 12,
28238c2ecf20Sopenharmony_ci			     shared ? (2<<12) : (0<<12));
28248c2ecf20Sopenharmony_ci	/* update shared Mic In / Center/LFE Out */
28258c2ecf20Sopenharmony_ci	shared = is_shared_clfeout(ac97);
28268c2ecf20Sopenharmony_ci	/* Vref disable (bit12), 1kOhm (bit13) */
28278c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<12)|(1<<13),
28288c2ecf20Sopenharmony_ci			     shared ? (1<<12) : (1<<13));
28298c2ecf20Sopenharmony_ci	/* MIC-IN = 1, CENTER-LFE = 5 */
28308c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 4,
28318c2ecf20Sopenharmony_ci			     shared ? (5<<4) : (1<<4));
28328c2ecf20Sopenharmony_ci
28338c2ecf20Sopenharmony_ci	aux_is_back_surround = alc850_is_aux_back_surround(ac97);
28348c2ecf20Sopenharmony_ci	/* Aux is Back Surround */
28358c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_ALC850_MULTICH, 1 << 10,
28368c2ecf20Sopenharmony_ci				 aux_is_back_surround ? (1<<10) : (0<<10));
28378c2ecf20Sopenharmony_ci}
28388c2ecf20Sopenharmony_ci
28398c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_alc850[] = {
28408c2ecf20Sopenharmony_ci	AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0),
28418c2ecf20Sopenharmony_ci	AC97_SINGLE("Mic Front Input Switch", AC97_ALC850_JACK_SELECT, 15, 1, 1),
28428c2ecf20Sopenharmony_ci	AC97_SURROUND_JACK_MODE_CTL,
28438c2ecf20Sopenharmony_ci	AC97_CHANNEL_MODE_8CH_CTL,
28448c2ecf20Sopenharmony_ci};
28458c2ecf20Sopenharmony_ci
28468c2ecf20Sopenharmony_cistatic int patch_alc850_specific(struct snd_ac97 *ac97)
28478c2ecf20Sopenharmony_ci{
28488c2ecf20Sopenharmony_ci	int err;
28498c2ecf20Sopenharmony_ci
28508c2ecf20Sopenharmony_ci	if ((err = patch_build_controls(ac97, snd_ac97_controls_alc850, ARRAY_SIZE(snd_ac97_controls_alc850))) < 0)
28518c2ecf20Sopenharmony_ci		return err;
28528c2ecf20Sopenharmony_ci	if (ac97->ext_id & AC97_EI_SPDIF) {
28538c2ecf20Sopenharmony_ci		if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc655, ARRAY_SIZE(snd_ac97_spdif_controls_alc655))) < 0)
28548c2ecf20Sopenharmony_ci			return err;
28558c2ecf20Sopenharmony_ci	}
28568c2ecf20Sopenharmony_ci	return 0;
28578c2ecf20Sopenharmony_ci}
28588c2ecf20Sopenharmony_ci
28598c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_alc850_ops = {
28608c2ecf20Sopenharmony_ci	.build_specific	= patch_alc850_specific,
28618c2ecf20Sopenharmony_ci	.update_jacks = alc850_update_jacks
28628c2ecf20Sopenharmony_ci};
28638c2ecf20Sopenharmony_ci
28648c2ecf20Sopenharmony_cistatic int patch_alc850(struct snd_ac97 *ac97)
28658c2ecf20Sopenharmony_ci{
28668c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_alc850_ops;
28678c2ecf20Sopenharmony_ci
28688c2ecf20Sopenharmony_ci	ac97->spec.dev_flags = 0; /* for IEC958 playback route - ALC655 compatible */
28698c2ecf20Sopenharmony_ci	ac97->flags |= AC97_HAS_8CH;
28708c2ecf20Sopenharmony_ci
28718c2ecf20Sopenharmony_ci	/* assume only page 0 for writing cache */
28728c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, AC97_PAGE_VENDOR);
28738c2ecf20Sopenharmony_ci
28748c2ecf20Sopenharmony_ci	/* adjust default values */
28758c2ecf20Sopenharmony_ci	/* set default: spdif-in enabled,
28768c2ecf20Sopenharmony_ci	   spdif-in monitor off, spdif-in PCM off
28778c2ecf20Sopenharmony_ci	   center on mic off, surround on line-in off
28788c2ecf20Sopenharmony_ci	   duplicate front off
28798c2ecf20Sopenharmony_ci	   NB default bit 10=0 = Aux is Capture, not Back Surround
28808c2ecf20Sopenharmony_ci	*/
28818c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, 1<<15);
28828c2ecf20Sopenharmony_ci	/* SURR_OUT: on, Surr 1kOhm: on, Surr Amp: off, Front 1kOhm: off
28838c2ecf20Sopenharmony_ci	 * Front Amp: on, Vref: enable, Center 1kOhm: on, Mix: on
28848c2ecf20Sopenharmony_ci	 */
28858c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, 0x7a, (1<<1)|(1<<4)|(0<<5)|(1<<6)|
28868c2ecf20Sopenharmony_ci			     (1<<7)|(0<<12)|(1<<13)|(0<<14));
28878c2ecf20Sopenharmony_ci	/* detection UIO2,3: all path floating, UIO3: MIC, Vref2: disable,
28888c2ecf20Sopenharmony_ci	 * UIO1: FRONT, Vref3: disable, UIO3: LINE, Front-Mic: mute
28898c2ecf20Sopenharmony_ci	 */
28908c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, 0x76, (0<<0)|(0<<2)|(1<<4)|(1<<7)|(2<<8)|
28918c2ecf20Sopenharmony_ci			     (1<<11)|(0<<12)|(1<<15));
28928c2ecf20Sopenharmony_ci
28938c2ecf20Sopenharmony_ci	/* full DAC volume */
28948c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808);
28958c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_ALC650_LFE_DAC_VOL, 0x0808);
28968c2ecf20Sopenharmony_ci	return 0;
28978c2ecf20Sopenharmony_ci}
28988c2ecf20Sopenharmony_ci
28998c2ecf20Sopenharmony_cistatic int patch_aztech_azf3328_specific(struct snd_ac97 *ac97)
29008c2ecf20Sopenharmony_ci{
29018c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl_3d_center =
29028c2ecf20Sopenharmony_ci		snd_ac97_find_mixer_ctl(ac97, "3D Control - Center");
29038c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl_3d_depth =
29048c2ecf20Sopenharmony_ci		snd_ac97_find_mixer_ctl(ac97, "3D Control - Depth");
29058c2ecf20Sopenharmony_ci
29068c2ecf20Sopenharmony_ci	/*
29078c2ecf20Sopenharmony_ci	 * 3D register is different from AC97 standard layout
29088c2ecf20Sopenharmony_ci	 * (also do some renaming, to resemble Windows driver naming)
29098c2ecf20Sopenharmony_ci	 */
29108c2ecf20Sopenharmony_ci	if (kctl_3d_center) {
29118c2ecf20Sopenharmony_ci		kctl_3d_center->private_value =
29128c2ecf20Sopenharmony_ci			AC97_SINGLE_VALUE(AC97_3D_CONTROL, 1, 0x07, 0);
29138c2ecf20Sopenharmony_ci		snd_ac97_rename_vol_ctl(ac97,
29148c2ecf20Sopenharmony_ci			"3D Control - Center", "3D Control - Width"
29158c2ecf20Sopenharmony_ci		);
29168c2ecf20Sopenharmony_ci	}
29178c2ecf20Sopenharmony_ci	if (kctl_3d_depth)
29188c2ecf20Sopenharmony_ci		kctl_3d_depth->private_value =
29198c2ecf20Sopenharmony_ci			AC97_SINGLE_VALUE(AC97_3D_CONTROL, 8, 0x03, 0);
29208c2ecf20Sopenharmony_ci
29218c2ecf20Sopenharmony_ci	/* Aztech Windows driver calls the
29228c2ecf20Sopenharmony_ci	   equivalent control "Modem Playback", thus rename it: */
29238c2ecf20Sopenharmony_ci	snd_ac97_rename_vol_ctl(ac97,
29248c2ecf20Sopenharmony_ci		"Master Mono Playback", "Modem Playback"
29258c2ecf20Sopenharmony_ci	);
29268c2ecf20Sopenharmony_ci	snd_ac97_rename_vol_ctl(ac97,
29278c2ecf20Sopenharmony_ci		"Headphone Playback", "FM Synth Playback"
29288c2ecf20Sopenharmony_ci	);
29298c2ecf20Sopenharmony_ci
29308c2ecf20Sopenharmony_ci	return 0;
29318c2ecf20Sopenharmony_ci}
29328c2ecf20Sopenharmony_ci
29338c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_aztech_azf3328_ops = {
29348c2ecf20Sopenharmony_ci	.build_specific	= patch_aztech_azf3328_specific
29358c2ecf20Sopenharmony_ci};
29368c2ecf20Sopenharmony_ci
29378c2ecf20Sopenharmony_cistatic int patch_aztech_azf3328(struct snd_ac97 *ac97)
29388c2ecf20Sopenharmony_ci{
29398c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_aztech_azf3328_ops;
29408c2ecf20Sopenharmony_ci	return 0;
29418c2ecf20Sopenharmony_ci}
29428c2ecf20Sopenharmony_ci
29438c2ecf20Sopenharmony_ci/*
29448c2ecf20Sopenharmony_ci * C-Media CM97xx codecs
29458c2ecf20Sopenharmony_ci */
29468c2ecf20Sopenharmony_cistatic void cm9738_update_jacks(struct snd_ac97 *ac97)
29478c2ecf20Sopenharmony_ci{
29488c2ecf20Sopenharmony_ci	/* shared Line-In / Surround Out */
29498c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_CM9738_VENDOR_CTRL, 1 << 10,
29508c2ecf20Sopenharmony_ci			     is_shared_surrout(ac97) ? (1 << 10) : 0);
29518c2ecf20Sopenharmony_ci}
29528c2ecf20Sopenharmony_ci
29538c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_cm9738_controls[] = {
29548c2ecf20Sopenharmony_ci	AC97_SINGLE("Duplicate Front", AC97_CM9738_VENDOR_CTRL, 13, 1, 0),
29558c2ecf20Sopenharmony_ci	AC97_SURROUND_JACK_MODE_CTL,
29568c2ecf20Sopenharmony_ci	AC97_CHANNEL_MODE_4CH_CTL,
29578c2ecf20Sopenharmony_ci};
29588c2ecf20Sopenharmony_ci
29598c2ecf20Sopenharmony_cistatic int patch_cm9738_specific(struct snd_ac97 * ac97)
29608c2ecf20Sopenharmony_ci{
29618c2ecf20Sopenharmony_ci	return patch_build_controls(ac97, snd_ac97_cm9738_controls, ARRAY_SIZE(snd_ac97_cm9738_controls));
29628c2ecf20Sopenharmony_ci}
29638c2ecf20Sopenharmony_ci
29648c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_cm9738_ops = {
29658c2ecf20Sopenharmony_ci	.build_specific	= patch_cm9738_specific,
29668c2ecf20Sopenharmony_ci	.update_jacks = cm9738_update_jacks
29678c2ecf20Sopenharmony_ci};
29688c2ecf20Sopenharmony_ci
29698c2ecf20Sopenharmony_cistatic int patch_cm9738(struct snd_ac97 * ac97)
29708c2ecf20Sopenharmony_ci{
29718c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_cm9738_ops;
29728c2ecf20Sopenharmony_ci	/* FIXME: can anyone confirm below? */
29738c2ecf20Sopenharmony_ci	/* CM9738 has no PCM volume although the register reacts */
29748c2ecf20Sopenharmony_ci	ac97->flags |= AC97_HAS_NO_PCM_VOL;
29758c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_PCM, 0x8000);
29768c2ecf20Sopenharmony_ci
29778c2ecf20Sopenharmony_ci	return 0;
29788c2ecf20Sopenharmony_ci}
29798c2ecf20Sopenharmony_ci
29808c2ecf20Sopenharmony_cistatic int snd_ac97_cmedia_spdif_playback_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
29818c2ecf20Sopenharmony_ci{
29828c2ecf20Sopenharmony_ci	static const char * const texts[] = { "Analog", "Digital" };
29838c2ecf20Sopenharmony_ci
29848c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 2, texts);
29858c2ecf20Sopenharmony_ci}
29868c2ecf20Sopenharmony_ci
29878c2ecf20Sopenharmony_cistatic int snd_ac97_cmedia_spdif_playback_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
29888c2ecf20Sopenharmony_ci{
29898c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
29908c2ecf20Sopenharmony_ci	unsigned short val;
29918c2ecf20Sopenharmony_ci
29928c2ecf20Sopenharmony_ci	val = ac97->regs[AC97_CM9739_SPDIF_CTRL];
29938c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = (val >> 1) & 0x01;
29948c2ecf20Sopenharmony_ci	return 0;
29958c2ecf20Sopenharmony_ci}
29968c2ecf20Sopenharmony_ci
29978c2ecf20Sopenharmony_cistatic int snd_ac97_cmedia_spdif_playback_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
29988c2ecf20Sopenharmony_ci{
29998c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
30008c2ecf20Sopenharmony_ci
30018c2ecf20Sopenharmony_ci	return snd_ac97_update_bits(ac97, AC97_CM9739_SPDIF_CTRL,
30028c2ecf20Sopenharmony_ci				    0x01 << 1,
30038c2ecf20Sopenharmony_ci				    (ucontrol->value.enumerated.item[0] & 0x01) << 1);
30048c2ecf20Sopenharmony_ci}
30058c2ecf20Sopenharmony_ci
30068c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_cm9739_controls_spdif[] = {
30078c2ecf20Sopenharmony_ci	/* BIT 0: SPDI_EN - always true */
30088c2ecf20Sopenharmony_ci	{ /* BIT 1: SPDIFS */
30098c2ecf20Sopenharmony_ci		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
30108c2ecf20Sopenharmony_ci		.name	= SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
30118c2ecf20Sopenharmony_ci		.info	= snd_ac97_cmedia_spdif_playback_source_info,
30128c2ecf20Sopenharmony_ci		.get	= snd_ac97_cmedia_spdif_playback_source_get,
30138c2ecf20Sopenharmony_ci		.put	= snd_ac97_cmedia_spdif_playback_source_put,
30148c2ecf20Sopenharmony_ci	},
30158c2ecf20Sopenharmony_ci	/* BIT 2: IG_SPIV */
30168c2ecf20Sopenharmony_ci	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Valid Switch", AC97_CM9739_SPDIF_CTRL, 2, 1, 0),
30178c2ecf20Sopenharmony_ci	/* BIT 3: SPI2F */
30188c2ecf20Sopenharmony_ci	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Monitor", AC97_CM9739_SPDIF_CTRL, 3, 1, 0),
30198c2ecf20Sopenharmony_ci	/* BIT 4: SPI2SDI */
30208c2ecf20Sopenharmony_ci	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), AC97_CM9739_SPDIF_CTRL, 4, 1, 0),
30218c2ecf20Sopenharmony_ci	/* BIT 8: SPD32 - 32bit SPDIF - not supported yet */
30228c2ecf20Sopenharmony_ci};
30238c2ecf20Sopenharmony_ci
30248c2ecf20Sopenharmony_cistatic void cm9739_update_jacks(struct snd_ac97 *ac97)
30258c2ecf20Sopenharmony_ci{
30268c2ecf20Sopenharmony_ci	/* shared Line-In / Surround Out */
30278c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 1 << 10,
30288c2ecf20Sopenharmony_ci			     is_shared_surrout(ac97) ? (1 << 10) : 0);
30298c2ecf20Sopenharmony_ci	/* shared Mic In / Center/LFE Out **/
30308c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x3000,
30318c2ecf20Sopenharmony_ci			     is_shared_clfeout(ac97) ? 0x1000 : 0x2000);
30328c2ecf20Sopenharmony_ci}
30338c2ecf20Sopenharmony_ci
30348c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_cm9739_controls[] = {
30358c2ecf20Sopenharmony_ci	AC97_SURROUND_JACK_MODE_CTL,
30368c2ecf20Sopenharmony_ci	AC97_CHANNEL_MODE_CTL,
30378c2ecf20Sopenharmony_ci};
30388c2ecf20Sopenharmony_ci
30398c2ecf20Sopenharmony_cistatic int patch_cm9739_specific(struct snd_ac97 * ac97)
30408c2ecf20Sopenharmony_ci{
30418c2ecf20Sopenharmony_ci	return patch_build_controls(ac97, snd_ac97_cm9739_controls, ARRAY_SIZE(snd_ac97_cm9739_controls));
30428c2ecf20Sopenharmony_ci}
30438c2ecf20Sopenharmony_ci
30448c2ecf20Sopenharmony_cistatic int patch_cm9739_post_spdif(struct snd_ac97 * ac97)
30458c2ecf20Sopenharmony_ci{
30468c2ecf20Sopenharmony_ci	return patch_build_controls(ac97, snd_ac97_cm9739_controls_spdif, ARRAY_SIZE(snd_ac97_cm9739_controls_spdif));
30478c2ecf20Sopenharmony_ci}
30488c2ecf20Sopenharmony_ci
30498c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_cm9739_ops = {
30508c2ecf20Sopenharmony_ci	.build_specific	= patch_cm9739_specific,
30518c2ecf20Sopenharmony_ci	.build_post_spdif = patch_cm9739_post_spdif,
30528c2ecf20Sopenharmony_ci	.update_jacks = cm9739_update_jacks
30538c2ecf20Sopenharmony_ci};
30548c2ecf20Sopenharmony_ci
30558c2ecf20Sopenharmony_cistatic int patch_cm9739(struct snd_ac97 * ac97)
30568c2ecf20Sopenharmony_ci{
30578c2ecf20Sopenharmony_ci	unsigned short val;
30588c2ecf20Sopenharmony_ci
30598c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_cm9739_ops;
30608c2ecf20Sopenharmony_ci
30618c2ecf20Sopenharmony_ci	/* CM9739/A has no Master and PCM volume although the register reacts */
30628c2ecf20Sopenharmony_ci	ac97->flags |= AC97_HAS_NO_MASTER_VOL | AC97_HAS_NO_PCM_VOL;
30638c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_MASTER, 0x8000);
30648c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_PCM, 0x8000);
30658c2ecf20Sopenharmony_ci
30668c2ecf20Sopenharmony_ci	/* check spdif */
30678c2ecf20Sopenharmony_ci	val = snd_ac97_read(ac97, AC97_EXTENDED_STATUS);
30688c2ecf20Sopenharmony_ci	if (val & AC97_EA_SPCV) {
30698c2ecf20Sopenharmony_ci		/* enable spdif in */
30708c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_CM9739_SPDIF_CTRL,
30718c2ecf20Sopenharmony_ci				     snd_ac97_read(ac97, AC97_CM9739_SPDIF_CTRL) | 0x01);
30728c2ecf20Sopenharmony_ci		ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */
30738c2ecf20Sopenharmony_ci	} else {
30748c2ecf20Sopenharmony_ci		ac97->ext_id &= ~AC97_EI_SPDIF; /* disable extended-id */
30758c2ecf20Sopenharmony_ci		ac97->rates[AC97_RATES_SPDIF] = 0;
30768c2ecf20Sopenharmony_ci	}
30778c2ecf20Sopenharmony_ci
30788c2ecf20Sopenharmony_ci	/* set-up multi channel */
30798c2ecf20Sopenharmony_ci	/* bit 14: 0 = SPDIF, 1 = EAPD */
30808c2ecf20Sopenharmony_ci	/* bit 13: enable internal vref output for mic */
30818c2ecf20Sopenharmony_ci	/* bit 12: disable center/lfe (switchable) */
30828c2ecf20Sopenharmony_ci	/* bit 10: disable surround/line (switchable) */
30838c2ecf20Sopenharmony_ci	/* bit 9: mix 2 surround off */
30848c2ecf20Sopenharmony_ci	/* bit 4: undocumented; 0 mutes the CM9739A, which defaults to 1 */
30858c2ecf20Sopenharmony_ci	/* bit 3: undocumented; surround? */
30868c2ecf20Sopenharmony_ci	/* bit 0: dB */
30878c2ecf20Sopenharmony_ci	val = snd_ac97_read(ac97, AC97_CM9739_MULTI_CHAN) & (1 << 4);
30888c2ecf20Sopenharmony_ci	val |= (1 << 3);
30898c2ecf20Sopenharmony_ci	val |= (1 << 13);
30908c2ecf20Sopenharmony_ci	if (! (ac97->ext_id & AC97_EI_SPDIF))
30918c2ecf20Sopenharmony_ci		val |= (1 << 14);
30928c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_CM9739_MULTI_CHAN, val);
30938c2ecf20Sopenharmony_ci
30948c2ecf20Sopenharmony_ci	/* FIXME: set up GPIO */
30958c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, 0x70, 0x0100);
30968c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, 0x72, 0x0020);
30978c2ecf20Sopenharmony_ci	/* Special exception for ASUS W1000/CMI9739. It does not have an SPDIF in. */
30988c2ecf20Sopenharmony_ci	if (ac97->pci &&
30998c2ecf20Sopenharmony_ci	     ac97->subsystem_vendor == 0x1043 &&
31008c2ecf20Sopenharmony_ci	     ac97->subsystem_device == 0x1843) {
31018c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_CM9739_SPDIF_CTRL,
31028c2ecf20Sopenharmony_ci			snd_ac97_read(ac97, AC97_CM9739_SPDIF_CTRL) & ~0x01);
31038c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_CM9739_MULTI_CHAN,
31048c2ecf20Sopenharmony_ci			snd_ac97_read(ac97, AC97_CM9739_MULTI_CHAN) | (1 << 14));
31058c2ecf20Sopenharmony_ci	}
31068c2ecf20Sopenharmony_ci
31078c2ecf20Sopenharmony_ci	return 0;
31088c2ecf20Sopenharmony_ci}
31098c2ecf20Sopenharmony_ci
31108c2ecf20Sopenharmony_ci#define AC97_CM9761_MULTI_CHAN	0x64
31118c2ecf20Sopenharmony_ci#define AC97_CM9761_FUNC	0x66
31128c2ecf20Sopenharmony_ci#define AC97_CM9761_SPDIF_CTRL	0x6c
31138c2ecf20Sopenharmony_ci
31148c2ecf20Sopenharmony_cistatic void cm9761_update_jacks(struct snd_ac97 *ac97)
31158c2ecf20Sopenharmony_ci{
31168c2ecf20Sopenharmony_ci	/* FIXME: check the bits for each model
31178c2ecf20Sopenharmony_ci	 *        model 83 is confirmed to work
31188c2ecf20Sopenharmony_ci	 */
31198c2ecf20Sopenharmony_ci	static const unsigned short surr_on[3][2] = {
31208c2ecf20Sopenharmony_ci		{ 0x0008, 0x0000 }, /* 9761-78 & 82 */
31218c2ecf20Sopenharmony_ci		{ 0x0000, 0x0008 }, /* 9761-82 rev.B */
31228c2ecf20Sopenharmony_ci		{ 0x0000, 0x0008 }, /* 9761-83 */
31238c2ecf20Sopenharmony_ci	};
31248c2ecf20Sopenharmony_ci	static const unsigned short clfe_on[3][2] = {
31258c2ecf20Sopenharmony_ci		{ 0x0000, 0x1000 }, /* 9761-78 & 82 */
31268c2ecf20Sopenharmony_ci		{ 0x1000, 0x0000 }, /* 9761-82 rev.B */
31278c2ecf20Sopenharmony_ci		{ 0x0000, 0x1000 }, /* 9761-83 */
31288c2ecf20Sopenharmony_ci	};
31298c2ecf20Sopenharmony_ci	static const unsigned short surr_shared[3][2] = {
31308c2ecf20Sopenharmony_ci		{ 0x0000, 0x0400 }, /* 9761-78 & 82 */
31318c2ecf20Sopenharmony_ci		{ 0x0000, 0x0400 }, /* 9761-82 rev.B */
31328c2ecf20Sopenharmony_ci		{ 0x0000, 0x0400 }, /* 9761-83 */
31338c2ecf20Sopenharmony_ci	};
31348c2ecf20Sopenharmony_ci	static const unsigned short clfe_shared[3][2] = {
31358c2ecf20Sopenharmony_ci		{ 0x2000, 0x0880 }, /* 9761-78 & 82 */
31368c2ecf20Sopenharmony_ci		{ 0x0000, 0x2880 }, /* 9761-82 rev.B */
31378c2ecf20Sopenharmony_ci		{ 0x2000, 0x0800 }, /* 9761-83 */
31388c2ecf20Sopenharmony_ci	};
31398c2ecf20Sopenharmony_ci	unsigned short val = 0;
31408c2ecf20Sopenharmony_ci
31418c2ecf20Sopenharmony_ci	val |= surr_on[ac97->spec.dev_flags][is_surround_on(ac97)];
31428c2ecf20Sopenharmony_ci	val |= clfe_on[ac97->spec.dev_flags][is_clfe_on(ac97)];
31438c2ecf20Sopenharmony_ci	val |= surr_shared[ac97->spec.dev_flags][is_shared_surrout(ac97)];
31448c2ecf20Sopenharmony_ci	val |= clfe_shared[ac97->spec.dev_flags][is_shared_clfeout(ac97)];
31458c2ecf20Sopenharmony_ci
31468c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_CM9761_MULTI_CHAN, 0x3c88, val);
31478c2ecf20Sopenharmony_ci}
31488c2ecf20Sopenharmony_ci
31498c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_cm9761_controls[] = {
31508c2ecf20Sopenharmony_ci	AC97_SURROUND_JACK_MODE_CTL,
31518c2ecf20Sopenharmony_ci	AC97_CHANNEL_MODE_CTL,
31528c2ecf20Sopenharmony_ci};
31538c2ecf20Sopenharmony_ci
31548c2ecf20Sopenharmony_cistatic int cm9761_spdif_out_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
31558c2ecf20Sopenharmony_ci{
31568c2ecf20Sopenharmony_ci	static const char * const texts[] = { "AC-Link", "ADC", "SPDIF-In" };
31578c2ecf20Sopenharmony_ci
31588c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 3, texts);
31598c2ecf20Sopenharmony_ci}
31608c2ecf20Sopenharmony_ci
31618c2ecf20Sopenharmony_cistatic int cm9761_spdif_out_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
31628c2ecf20Sopenharmony_ci{
31638c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
31648c2ecf20Sopenharmony_ci
31658c2ecf20Sopenharmony_ci	if (ac97->regs[AC97_CM9761_FUNC] & 0x1)
31668c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = 2; /* SPDIF-loopback */
31678c2ecf20Sopenharmony_ci	else if (ac97->regs[AC97_CM9761_SPDIF_CTRL] & 0x2)
31688c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = 1; /* ADC loopback */
31698c2ecf20Sopenharmony_ci	else
31708c2ecf20Sopenharmony_ci		ucontrol->value.enumerated.item[0] = 0; /* AC-link */
31718c2ecf20Sopenharmony_ci	return 0;
31728c2ecf20Sopenharmony_ci}
31738c2ecf20Sopenharmony_ci
31748c2ecf20Sopenharmony_cistatic int cm9761_spdif_out_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
31758c2ecf20Sopenharmony_ci{
31768c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
31778c2ecf20Sopenharmony_ci
31788c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] == 2)
31798c2ecf20Sopenharmony_ci		return snd_ac97_update_bits(ac97, AC97_CM9761_FUNC, 0x1, 0x1);
31808c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, AC97_CM9761_FUNC, 0x1, 0);
31818c2ecf20Sopenharmony_ci	return snd_ac97_update_bits(ac97, AC97_CM9761_SPDIF_CTRL, 0x2,
31828c2ecf20Sopenharmony_ci				    ucontrol->value.enumerated.item[0] == 1 ? 0x2 : 0);
31838c2ecf20Sopenharmony_ci}
31848c2ecf20Sopenharmony_ci
31858c2ecf20Sopenharmony_cistatic const char * const cm9761_dac_clock[] = {
31868c2ecf20Sopenharmony_ci	"AC-Link", "SPDIF-In", "Both"
31878c2ecf20Sopenharmony_ci};
31888c2ecf20Sopenharmony_cistatic const struct ac97_enum cm9761_dac_clock_enum =
31898c2ecf20Sopenharmony_ci	AC97_ENUM_SINGLE(AC97_CM9761_SPDIF_CTRL, 9, 3, cm9761_dac_clock);
31908c2ecf20Sopenharmony_ci
31918c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_cm9761_controls_spdif[] = {
31928c2ecf20Sopenharmony_ci	{ /* BIT 1: SPDIFS */
31938c2ecf20Sopenharmony_ci		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
31948c2ecf20Sopenharmony_ci		.name	= SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
31958c2ecf20Sopenharmony_ci		.info = cm9761_spdif_out_source_info,
31968c2ecf20Sopenharmony_ci		.get = cm9761_spdif_out_source_get,
31978c2ecf20Sopenharmony_ci		.put = cm9761_spdif_out_source_put,
31988c2ecf20Sopenharmony_ci	},
31998c2ecf20Sopenharmony_ci	/* BIT 2: IG_SPIV */
32008c2ecf20Sopenharmony_ci	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Valid Switch", AC97_CM9761_SPDIF_CTRL, 2, 1, 0),
32018c2ecf20Sopenharmony_ci	/* BIT 3: SPI2F */
32028c2ecf20Sopenharmony_ci	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Monitor", AC97_CM9761_SPDIF_CTRL, 3, 1, 0),
32038c2ecf20Sopenharmony_ci	/* BIT 4: SPI2SDI */
32048c2ecf20Sopenharmony_ci	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), AC97_CM9761_SPDIF_CTRL, 4, 1, 0),
32058c2ecf20Sopenharmony_ci	/* BIT 9-10: DAC_CTL */
32068c2ecf20Sopenharmony_ci	AC97_ENUM("DAC Clock Source", cm9761_dac_clock_enum),
32078c2ecf20Sopenharmony_ci};
32088c2ecf20Sopenharmony_ci
32098c2ecf20Sopenharmony_cistatic int patch_cm9761_post_spdif(struct snd_ac97 * ac97)
32108c2ecf20Sopenharmony_ci{
32118c2ecf20Sopenharmony_ci	return patch_build_controls(ac97, snd_ac97_cm9761_controls_spdif, ARRAY_SIZE(snd_ac97_cm9761_controls_spdif));
32128c2ecf20Sopenharmony_ci}
32138c2ecf20Sopenharmony_ci
32148c2ecf20Sopenharmony_cistatic int patch_cm9761_specific(struct snd_ac97 * ac97)
32158c2ecf20Sopenharmony_ci{
32168c2ecf20Sopenharmony_ci	return patch_build_controls(ac97, snd_ac97_cm9761_controls, ARRAY_SIZE(snd_ac97_cm9761_controls));
32178c2ecf20Sopenharmony_ci}
32188c2ecf20Sopenharmony_ci
32198c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_cm9761_ops = {
32208c2ecf20Sopenharmony_ci	.build_specific	= patch_cm9761_specific,
32218c2ecf20Sopenharmony_ci	.build_post_spdif = patch_cm9761_post_spdif,
32228c2ecf20Sopenharmony_ci	.update_jacks = cm9761_update_jacks
32238c2ecf20Sopenharmony_ci};
32248c2ecf20Sopenharmony_ci
32258c2ecf20Sopenharmony_cistatic int patch_cm9761(struct snd_ac97 *ac97)
32268c2ecf20Sopenharmony_ci{
32278c2ecf20Sopenharmony_ci	unsigned short val;
32288c2ecf20Sopenharmony_ci
32298c2ecf20Sopenharmony_ci	/* CM9761 has no PCM volume although the register reacts */
32308c2ecf20Sopenharmony_ci	/* Master volume seems to have _some_ influence on the analog
32318c2ecf20Sopenharmony_ci	 * input sounds
32328c2ecf20Sopenharmony_ci	 */
32338c2ecf20Sopenharmony_ci	ac97->flags |= /*AC97_HAS_NO_MASTER_VOL |*/ AC97_HAS_NO_PCM_VOL;
32348c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_MASTER, 0x8808);
32358c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_PCM, 0x8808);
32368c2ecf20Sopenharmony_ci
32378c2ecf20Sopenharmony_ci	ac97->spec.dev_flags = 0; /* 1 = model 82 revision B, 2 = model 83 */
32388c2ecf20Sopenharmony_ci	if (ac97->id == AC97_ID_CM9761_82) {
32398c2ecf20Sopenharmony_ci		unsigned short tmp;
32408c2ecf20Sopenharmony_ci		/* check page 1, reg 0x60 */
32418c2ecf20Sopenharmony_ci		val = snd_ac97_read(ac97, AC97_INT_PAGING);
32428c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_INT_PAGING, (val & ~0x0f) | 0x01);
32438c2ecf20Sopenharmony_ci		tmp = snd_ac97_read(ac97, 0x60);
32448c2ecf20Sopenharmony_ci		ac97->spec.dev_flags = tmp & 1; /* revision B? */
32458c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_INT_PAGING, val);
32468c2ecf20Sopenharmony_ci	} else if (ac97->id == AC97_ID_CM9761_83)
32478c2ecf20Sopenharmony_ci		ac97->spec.dev_flags = 2;
32488c2ecf20Sopenharmony_ci
32498c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_cm9761_ops;
32508c2ecf20Sopenharmony_ci
32518c2ecf20Sopenharmony_ci	/* enable spdif */
32528c2ecf20Sopenharmony_ci	/* force the SPDIF bit in ext_id - codec doesn't set this bit! */
32538c2ecf20Sopenharmony_ci        ac97->ext_id |= AC97_EI_SPDIF;
32548c2ecf20Sopenharmony_ci	/* to be sure: we overwrite the ext status bits */
32558c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, 0x05c0);
32568c2ecf20Sopenharmony_ci	/* Don't set 0x0200 here.  This results in the silent analog output */
32578c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_CM9761_SPDIF_CTRL, 0x0001); /* enable spdif-in */
32588c2ecf20Sopenharmony_ci	ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */
32598c2ecf20Sopenharmony_ci
32608c2ecf20Sopenharmony_ci	/* set-up multi channel */
32618c2ecf20Sopenharmony_ci	/* bit 15: pc master beep off
32628c2ecf20Sopenharmony_ci	 * bit 14: pin47 = EAPD/SPDIF
32638c2ecf20Sopenharmony_ci	 * bit 13: vref ctl [= cm9739]
32648c2ecf20Sopenharmony_ci	 * bit 12: CLFE control (reverted on rev B)
32658c2ecf20Sopenharmony_ci	 * bit 11: Mic/center share (reverted on rev B)
32668c2ecf20Sopenharmony_ci	 * bit 10: suddound/line share
32678c2ecf20Sopenharmony_ci	 * bit  9: Analog-in mix -> surround
32688c2ecf20Sopenharmony_ci	 * bit  8: Analog-in mix -> CLFE
32698c2ecf20Sopenharmony_ci	 * bit  7: Mic/LFE share (mic/center/lfe)
32708c2ecf20Sopenharmony_ci	 * bit  5: vref select (9761A)
32718c2ecf20Sopenharmony_ci	 * bit  4: front control
32728c2ecf20Sopenharmony_ci	 * bit  3: surround control (revereted with rev B)
32738c2ecf20Sopenharmony_ci	 * bit  2: front mic
32748c2ecf20Sopenharmony_ci	 * bit  1: stereo mic
32758c2ecf20Sopenharmony_ci	 * bit  0: mic boost level (0=20dB, 1=30dB)
32768c2ecf20Sopenharmony_ci	 */
32778c2ecf20Sopenharmony_ci
32788c2ecf20Sopenharmony_ci#if 0
32798c2ecf20Sopenharmony_ci	if (ac97->spec.dev_flags)
32808c2ecf20Sopenharmony_ci		val = 0x0214;
32818c2ecf20Sopenharmony_ci	else
32828c2ecf20Sopenharmony_ci		val = 0x321c;
32838c2ecf20Sopenharmony_ci#endif
32848c2ecf20Sopenharmony_ci	val = snd_ac97_read(ac97, AC97_CM9761_MULTI_CHAN);
32858c2ecf20Sopenharmony_ci	val |= (1 << 4); /* front on */
32868c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, AC97_CM9761_MULTI_CHAN, val);
32878c2ecf20Sopenharmony_ci
32888c2ecf20Sopenharmony_ci	/* FIXME: set up GPIO */
32898c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, 0x70, 0x0100);
32908c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, 0x72, 0x0020);
32918c2ecf20Sopenharmony_ci
32928c2ecf20Sopenharmony_ci	return 0;
32938c2ecf20Sopenharmony_ci}
32948c2ecf20Sopenharmony_ci
32958c2ecf20Sopenharmony_ci#define AC97_CM9780_SIDE	0x60
32968c2ecf20Sopenharmony_ci#define AC97_CM9780_JACK	0x62
32978c2ecf20Sopenharmony_ci#define AC97_CM9780_MIXER	0x64
32988c2ecf20Sopenharmony_ci#define AC97_CM9780_MULTI_CHAN	0x66
32998c2ecf20Sopenharmony_ci#define AC97_CM9780_SPDIF	0x6c
33008c2ecf20Sopenharmony_ci
33018c2ecf20Sopenharmony_cistatic const char * const cm9780_ch_select[] = {
33028c2ecf20Sopenharmony_ci	"Front", "Side", "Center/LFE", "Rear"
33038c2ecf20Sopenharmony_ci};
33048c2ecf20Sopenharmony_cistatic const struct ac97_enum cm9780_ch_select_enum =
33058c2ecf20Sopenharmony_ci	AC97_ENUM_SINGLE(AC97_CM9780_MULTI_CHAN, 6, 4, cm9780_ch_select);
33068c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new cm9780_controls[] = {
33078c2ecf20Sopenharmony_ci	AC97_DOUBLE("Side Playback Switch", AC97_CM9780_SIDE, 15, 7, 1, 1),
33088c2ecf20Sopenharmony_ci	AC97_DOUBLE("Side Playback Volume", AC97_CM9780_SIDE, 8, 0, 31, 0),
33098c2ecf20Sopenharmony_ci	AC97_ENUM("Side Playback Route", cm9780_ch_select_enum),
33108c2ecf20Sopenharmony_ci};
33118c2ecf20Sopenharmony_ci
33128c2ecf20Sopenharmony_cistatic int patch_cm9780_specific(struct snd_ac97 *ac97)
33138c2ecf20Sopenharmony_ci{
33148c2ecf20Sopenharmony_ci	return patch_build_controls(ac97, cm9780_controls, ARRAY_SIZE(cm9780_controls));
33158c2ecf20Sopenharmony_ci}
33168c2ecf20Sopenharmony_ci
33178c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_cm9780_ops = {
33188c2ecf20Sopenharmony_ci	.build_specific	= patch_cm9780_specific,
33198c2ecf20Sopenharmony_ci	.build_post_spdif = patch_cm9761_post_spdif	/* identical with CM9761 */
33208c2ecf20Sopenharmony_ci};
33218c2ecf20Sopenharmony_ci
33228c2ecf20Sopenharmony_cistatic int patch_cm9780(struct snd_ac97 *ac97)
33238c2ecf20Sopenharmony_ci{
33248c2ecf20Sopenharmony_ci	unsigned short val;
33258c2ecf20Sopenharmony_ci
33268c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_cm9780_ops;
33278c2ecf20Sopenharmony_ci
33288c2ecf20Sopenharmony_ci	/* enable spdif */
33298c2ecf20Sopenharmony_ci	if (ac97->ext_id & AC97_EI_SPDIF) {
33308c2ecf20Sopenharmony_ci		ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */
33318c2ecf20Sopenharmony_ci		val = snd_ac97_read(ac97, AC97_CM9780_SPDIF);
33328c2ecf20Sopenharmony_ci		val |= 0x1; /* SPDI_EN */
33338c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, AC97_CM9780_SPDIF, val);
33348c2ecf20Sopenharmony_ci	}
33358c2ecf20Sopenharmony_ci
33368c2ecf20Sopenharmony_ci	return 0;
33378c2ecf20Sopenharmony_ci}
33388c2ecf20Sopenharmony_ci
33398c2ecf20Sopenharmony_ci/*
33408c2ecf20Sopenharmony_ci * VIA VT1613 codec
33418c2ecf20Sopenharmony_ci */
33428c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_vt1613[] = {
33438c2ecf20Sopenharmony_ciAC97_SINGLE("DC Offset removal", 0x5a, 10, 1, 0),
33448c2ecf20Sopenharmony_ci};
33458c2ecf20Sopenharmony_ci
33468c2ecf20Sopenharmony_cistatic int patch_vt1613_specific(struct snd_ac97 *ac97)
33478c2ecf20Sopenharmony_ci{
33488c2ecf20Sopenharmony_ci	return patch_build_controls(ac97, &snd_ac97_controls_vt1613[0],
33498c2ecf20Sopenharmony_ci				    ARRAY_SIZE(snd_ac97_controls_vt1613));
33508c2ecf20Sopenharmony_ci};
33518c2ecf20Sopenharmony_ci
33528c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_vt1613_ops = {
33538c2ecf20Sopenharmony_ci	.build_specific	= patch_vt1613_specific
33548c2ecf20Sopenharmony_ci};
33558c2ecf20Sopenharmony_ci
33568c2ecf20Sopenharmony_cistatic int patch_vt1613(struct snd_ac97 *ac97)
33578c2ecf20Sopenharmony_ci{
33588c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_vt1613_ops;
33598c2ecf20Sopenharmony_ci
33608c2ecf20Sopenharmony_ci	ac97->flags |= AC97_HAS_NO_VIDEO;
33618c2ecf20Sopenharmony_ci	ac97->caps |= AC97_BC_HEADPHONE;
33628c2ecf20Sopenharmony_ci
33638c2ecf20Sopenharmony_ci	return 0;
33648c2ecf20Sopenharmony_ci}
33658c2ecf20Sopenharmony_ci
33668c2ecf20Sopenharmony_ci/*
33678c2ecf20Sopenharmony_ci * VIA VT1616 codec
33688c2ecf20Sopenharmony_ci */
33698c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_vt1616[] = {
33708c2ecf20Sopenharmony_ciAC97_SINGLE("DC Offset removal", 0x5a, 10, 1, 0),
33718c2ecf20Sopenharmony_ciAC97_SINGLE("Alternate Level to Surround Out", 0x5a, 15, 1, 0),
33728c2ecf20Sopenharmony_ciAC97_SINGLE("Downmix LFE and Center to Front", 0x5a, 12, 1, 0),
33738c2ecf20Sopenharmony_ciAC97_SINGLE("Downmix Surround to Front", 0x5a, 11, 1, 0),
33748c2ecf20Sopenharmony_ci};
33758c2ecf20Sopenharmony_ci
33768c2ecf20Sopenharmony_cistatic const char * const follower_vols_vt1616[] = {
33778c2ecf20Sopenharmony_ci	"Front Playback Volume",
33788c2ecf20Sopenharmony_ci	"Surround Playback Volume",
33798c2ecf20Sopenharmony_ci	"Center Playback Volume",
33808c2ecf20Sopenharmony_ci	"LFE Playback Volume",
33818c2ecf20Sopenharmony_ci	NULL
33828c2ecf20Sopenharmony_ci};
33838c2ecf20Sopenharmony_ci
33848c2ecf20Sopenharmony_cistatic const char * const follower_sws_vt1616[] = {
33858c2ecf20Sopenharmony_ci	"Front Playback Switch",
33868c2ecf20Sopenharmony_ci	"Surround Playback Switch",
33878c2ecf20Sopenharmony_ci	"Center Playback Switch",
33888c2ecf20Sopenharmony_ci	"LFE Playback Switch",
33898c2ecf20Sopenharmony_ci	NULL
33908c2ecf20Sopenharmony_ci};
33918c2ecf20Sopenharmony_ci
33928c2ecf20Sopenharmony_ci/* find a mixer control element with the given name */
33938c2ecf20Sopenharmony_cistatic struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97,
33948c2ecf20Sopenharmony_ci						    const char *name)
33958c2ecf20Sopenharmony_ci{
33968c2ecf20Sopenharmony_ci	struct snd_ctl_elem_id id;
33978c2ecf20Sopenharmony_ci	memset(&id, 0, sizeof(id));
33988c2ecf20Sopenharmony_ci	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
33998c2ecf20Sopenharmony_ci	strcpy(id.name, name);
34008c2ecf20Sopenharmony_ci	return snd_ctl_find_id(ac97->bus->card, &id);
34018c2ecf20Sopenharmony_ci}
34028c2ecf20Sopenharmony_ci
34038c2ecf20Sopenharmony_ci/* create a virtual master control and add followers */
34048c2ecf20Sopenharmony_cistatic int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
34058c2ecf20Sopenharmony_ci				const unsigned int *tlv,
34068c2ecf20Sopenharmony_ci				const char * const *followers)
34078c2ecf20Sopenharmony_ci{
34088c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
34098c2ecf20Sopenharmony_ci	const char * const *s;
34108c2ecf20Sopenharmony_ci	int err;
34118c2ecf20Sopenharmony_ci
34128c2ecf20Sopenharmony_ci	kctl = snd_ctl_make_virtual_master(name, tlv);
34138c2ecf20Sopenharmony_ci	if (!kctl)
34148c2ecf20Sopenharmony_ci		return -ENOMEM;
34158c2ecf20Sopenharmony_ci	err = snd_ctl_add(ac97->bus->card, kctl);
34168c2ecf20Sopenharmony_ci	if (err < 0)
34178c2ecf20Sopenharmony_ci		return err;
34188c2ecf20Sopenharmony_ci
34198c2ecf20Sopenharmony_ci	for (s = followers; *s; s++) {
34208c2ecf20Sopenharmony_ci		struct snd_kcontrol *sctl;
34218c2ecf20Sopenharmony_ci
34228c2ecf20Sopenharmony_ci		sctl = snd_ac97_find_mixer_ctl(ac97, *s);
34238c2ecf20Sopenharmony_ci		if (!sctl) {
34248c2ecf20Sopenharmony_ci			dev_dbg(ac97->bus->card->dev,
34258c2ecf20Sopenharmony_ci				"Cannot find follower %s, skipped\n", *s);
34268c2ecf20Sopenharmony_ci			continue;
34278c2ecf20Sopenharmony_ci		}
34288c2ecf20Sopenharmony_ci		err = snd_ctl_add_follower(kctl, sctl);
34298c2ecf20Sopenharmony_ci		if (err < 0)
34308c2ecf20Sopenharmony_ci			return err;
34318c2ecf20Sopenharmony_ci	}
34328c2ecf20Sopenharmony_ci	return 0;
34338c2ecf20Sopenharmony_ci}
34348c2ecf20Sopenharmony_ci
34358c2ecf20Sopenharmony_cistatic int patch_vt1616_specific(struct snd_ac97 * ac97)
34368c2ecf20Sopenharmony_ci{
34378c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
34388c2ecf20Sopenharmony_ci	int err;
34398c2ecf20Sopenharmony_ci
34408c2ecf20Sopenharmony_ci	if (snd_ac97_try_bit(ac97, 0x5a, 9))
34418c2ecf20Sopenharmony_ci		if ((err = patch_build_controls(ac97, &snd_ac97_controls_vt1616[0], 1)) < 0)
34428c2ecf20Sopenharmony_ci			return err;
34438c2ecf20Sopenharmony_ci	if ((err = patch_build_controls(ac97, &snd_ac97_controls_vt1616[1], ARRAY_SIZE(snd_ac97_controls_vt1616) - 1)) < 0)
34448c2ecf20Sopenharmony_ci		return err;
34458c2ecf20Sopenharmony_ci
34468c2ecf20Sopenharmony_ci	/* There is already a misnamed master switch.  Rename it.  */
34478c2ecf20Sopenharmony_ci	kctl = snd_ac97_find_mixer_ctl(ac97, "Master Playback Volume");
34488c2ecf20Sopenharmony_ci	if (!kctl)
34498c2ecf20Sopenharmony_ci		return -EINVAL;
34508c2ecf20Sopenharmony_ci
34518c2ecf20Sopenharmony_ci	snd_ac97_rename_vol_ctl(ac97, "Master Playback", "Front Playback");
34528c2ecf20Sopenharmony_ci
34538c2ecf20Sopenharmony_ci	err = snd_ac97_add_vmaster(ac97, "Master Playback Volume",
34548c2ecf20Sopenharmony_ci				   kctl->tlv.p, follower_vols_vt1616);
34558c2ecf20Sopenharmony_ci	if (err < 0)
34568c2ecf20Sopenharmony_ci		return err;
34578c2ecf20Sopenharmony_ci
34588c2ecf20Sopenharmony_ci	err = snd_ac97_add_vmaster(ac97, "Master Playback Switch",
34598c2ecf20Sopenharmony_ci				   NULL, follower_sws_vt1616);
34608c2ecf20Sopenharmony_ci	if (err < 0)
34618c2ecf20Sopenharmony_ci		return err;
34628c2ecf20Sopenharmony_ci
34638c2ecf20Sopenharmony_ci	return 0;
34648c2ecf20Sopenharmony_ci}
34658c2ecf20Sopenharmony_ci
34668c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_vt1616_ops = {
34678c2ecf20Sopenharmony_ci	.build_specific	= patch_vt1616_specific
34688c2ecf20Sopenharmony_ci};
34698c2ecf20Sopenharmony_ci
34708c2ecf20Sopenharmony_cistatic int patch_vt1616(struct snd_ac97 * ac97)
34718c2ecf20Sopenharmony_ci{
34728c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_vt1616_ops;
34738c2ecf20Sopenharmony_ci	return 0;
34748c2ecf20Sopenharmony_ci}
34758c2ecf20Sopenharmony_ci
34768c2ecf20Sopenharmony_ci/*
34778c2ecf20Sopenharmony_ci * VT1617A codec
34788c2ecf20Sopenharmony_ci */
34798c2ecf20Sopenharmony_ci
34808c2ecf20Sopenharmony_ci/*
34818c2ecf20Sopenharmony_ci * unfortunately, the vt1617a stashes the twiddlers required for
34828c2ecf20Sopenharmony_ci * noodling the i/o jacks on 2 different regs. that means that we can't
34838c2ecf20Sopenharmony_ci * use the easy way provided by AC97_ENUM_DOUBLE() we have to write
34848c2ecf20Sopenharmony_ci * are own funcs.
34858c2ecf20Sopenharmony_ci *
34868c2ecf20Sopenharmony_ci * NB: this is absolutely and utterly different from the vt1618. dunno
34878c2ecf20Sopenharmony_ci * about the 1616.
34888c2ecf20Sopenharmony_ci */
34898c2ecf20Sopenharmony_ci
34908c2ecf20Sopenharmony_ci/* copied from ac97_surround_jack_mode_info() */
34918c2ecf20Sopenharmony_cistatic int snd_ac97_vt1617a_smart51_info(struct snd_kcontrol *kcontrol,
34928c2ecf20Sopenharmony_ci					 struct snd_ctl_elem_info *uinfo)
34938c2ecf20Sopenharmony_ci{
34948c2ecf20Sopenharmony_ci	/* ordering in this list reflects vt1617a docs for Reg 20 and
34958c2ecf20Sopenharmony_ci	 * 7a and Table 6 that lays out the matrix NB WRT Table6: SM51
34968c2ecf20Sopenharmony_ci	 * is SM51EN *AND* it's Bit14, not Bit15 so the table is very
34978c2ecf20Sopenharmony_ci	 * counter-intuitive */
34988c2ecf20Sopenharmony_ci
34998c2ecf20Sopenharmony_ci	static const char * const texts[] = {"LineIn Mic1", "LineIn Mic1 Mic3",
35008c2ecf20Sopenharmony_ci				       "Surr LFE/C Mic3", "LineIn LFE/C Mic3",
35018c2ecf20Sopenharmony_ci				       "LineIn Mic2", "LineIn Mic2 Mic1",
35028c2ecf20Sopenharmony_ci				       "Surr LFE Mic1", "Surr LFE Mic1 Mic2"};
35038c2ecf20Sopenharmony_ci
35048c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 8, texts);
35058c2ecf20Sopenharmony_ci}
35068c2ecf20Sopenharmony_ci
35078c2ecf20Sopenharmony_cistatic int snd_ac97_vt1617a_smart51_get(struct snd_kcontrol *kcontrol,
35088c2ecf20Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
35098c2ecf20Sopenharmony_ci{
35108c2ecf20Sopenharmony_ci	ushort usSM51, usMS;
35118c2ecf20Sopenharmony_ci
35128c2ecf20Sopenharmony_ci	struct snd_ac97 *pac97;
35138c2ecf20Sopenharmony_ci
35148c2ecf20Sopenharmony_ci	pac97 = snd_kcontrol_chip(kcontrol); /* grab codec handle */
35158c2ecf20Sopenharmony_ci
35168c2ecf20Sopenharmony_ci	/* grab our desired bits, then mash them together in a manner
35178c2ecf20Sopenharmony_ci	 * consistent with Table 6 on page 17 in the 1617a docs */
35188c2ecf20Sopenharmony_ci
35198c2ecf20Sopenharmony_ci	usSM51 = snd_ac97_read(pac97, 0x7a) >> 14;
35208c2ecf20Sopenharmony_ci	usMS   = snd_ac97_read(pac97, 0x20) >> 8;
35218c2ecf20Sopenharmony_ci
35228c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = (usSM51 << 1) + usMS;
35238c2ecf20Sopenharmony_ci
35248c2ecf20Sopenharmony_ci	return 0;
35258c2ecf20Sopenharmony_ci}
35268c2ecf20Sopenharmony_ci
35278c2ecf20Sopenharmony_cistatic int snd_ac97_vt1617a_smart51_put(struct snd_kcontrol *kcontrol,
35288c2ecf20Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
35298c2ecf20Sopenharmony_ci{
35308c2ecf20Sopenharmony_ci	ushort usSM51, usMS, usReg;
35318c2ecf20Sopenharmony_ci
35328c2ecf20Sopenharmony_ci	struct snd_ac97 *pac97;
35338c2ecf20Sopenharmony_ci
35348c2ecf20Sopenharmony_ci	pac97 = snd_kcontrol_chip(kcontrol); /* grab codec handle */
35358c2ecf20Sopenharmony_ci
35368c2ecf20Sopenharmony_ci	usSM51 = ucontrol->value.enumerated.item[0] >> 1;
35378c2ecf20Sopenharmony_ci	usMS   = ucontrol->value.enumerated.item[0] &  1;
35388c2ecf20Sopenharmony_ci
35398c2ecf20Sopenharmony_ci	/* push our values into the register - consider that things will be left
35408c2ecf20Sopenharmony_ci	 * in a funky state if the write fails */
35418c2ecf20Sopenharmony_ci
35428c2ecf20Sopenharmony_ci	usReg = snd_ac97_read(pac97, 0x7a);
35438c2ecf20Sopenharmony_ci	snd_ac97_write_cache(pac97, 0x7a, (usReg & 0x3FFF) + (usSM51 << 14));
35448c2ecf20Sopenharmony_ci	usReg = snd_ac97_read(pac97, 0x20);
35458c2ecf20Sopenharmony_ci	snd_ac97_write_cache(pac97, 0x20, (usReg & 0xFEFF) + (usMS   <<  8));
35468c2ecf20Sopenharmony_ci
35478c2ecf20Sopenharmony_ci	return 0;
35488c2ecf20Sopenharmony_ci}
35498c2ecf20Sopenharmony_ci
35508c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_vt1617a[] = {
35518c2ecf20Sopenharmony_ci
35528c2ecf20Sopenharmony_ci	AC97_SINGLE("Center/LFE Exchange", 0x5a, 8, 1, 0),
35538c2ecf20Sopenharmony_ci	/*
35548c2ecf20Sopenharmony_ci	 * These are used to enable/disable surround sound on motherboards
35558c2ecf20Sopenharmony_ci	 * that have 3 bidirectional analog jacks
35568c2ecf20Sopenharmony_ci	 */
35578c2ecf20Sopenharmony_ci	{
35588c2ecf20Sopenharmony_ci		.iface         = SNDRV_CTL_ELEM_IFACE_MIXER,
35598c2ecf20Sopenharmony_ci		.name          = "Smart 5.1 Select",
35608c2ecf20Sopenharmony_ci		.info          = snd_ac97_vt1617a_smart51_info,
35618c2ecf20Sopenharmony_ci		.get           = snd_ac97_vt1617a_smart51_get,
35628c2ecf20Sopenharmony_ci		.put           = snd_ac97_vt1617a_smart51_put,
35638c2ecf20Sopenharmony_ci	},
35648c2ecf20Sopenharmony_ci};
35658c2ecf20Sopenharmony_ci
35668c2ecf20Sopenharmony_cistatic int patch_vt1617a(struct snd_ac97 * ac97)
35678c2ecf20Sopenharmony_ci{
35688c2ecf20Sopenharmony_ci	int err = 0;
35698c2ecf20Sopenharmony_ci	int val;
35708c2ecf20Sopenharmony_ci
35718c2ecf20Sopenharmony_ci	/* we choose to not fail out at this point, but we tell the
35728c2ecf20Sopenharmony_ci	   caller when we return */
35738c2ecf20Sopenharmony_ci
35748c2ecf20Sopenharmony_ci	err = patch_build_controls(ac97, &snd_ac97_controls_vt1617a[0],
35758c2ecf20Sopenharmony_ci				   ARRAY_SIZE(snd_ac97_controls_vt1617a));
35768c2ecf20Sopenharmony_ci
35778c2ecf20Sopenharmony_ci	/* bring analog power consumption to normal by turning off the
35788c2ecf20Sopenharmony_ci	 * headphone amplifier, like WinXP driver for EPIA SP
35798c2ecf20Sopenharmony_ci	 */
35808c2ecf20Sopenharmony_ci	/* We need to check the bit before writing it.
35818c2ecf20Sopenharmony_ci	 * On some (many?) hardwares, setting bit actually clears it!
35828c2ecf20Sopenharmony_ci	 */
35838c2ecf20Sopenharmony_ci	val = snd_ac97_read(ac97, 0x5c);
35848c2ecf20Sopenharmony_ci	if (!(val & 0x20))
35858c2ecf20Sopenharmony_ci		snd_ac97_write_cache(ac97, 0x5c, 0x20);
35868c2ecf20Sopenharmony_ci
35878c2ecf20Sopenharmony_ci	ac97->ext_id |= AC97_EI_SPDIF;	/* force the detection of spdif */
35888c2ecf20Sopenharmony_ci	ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000;
35898c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_vt1616_ops;
35908c2ecf20Sopenharmony_ci
35918c2ecf20Sopenharmony_ci	return err;
35928c2ecf20Sopenharmony_ci}
35938c2ecf20Sopenharmony_ci
35948c2ecf20Sopenharmony_ci/* VIA VT1618 8 CHANNEL AC97 CODEC
35958c2ecf20Sopenharmony_ci *
35968c2ecf20Sopenharmony_ci * VIA implements 'Smart 5.1' completely differently on the 1618 than
35978c2ecf20Sopenharmony_ci * it does on the 1617a. awesome! They seem to have sourced this
35988c2ecf20Sopenharmony_ci * particular revision of the technology from somebody else, it's
35998c2ecf20Sopenharmony_ci * called Universal Audio Jack and it shows up on some other folk's chips
36008c2ecf20Sopenharmony_ci * as well.
36018c2ecf20Sopenharmony_ci *
36028c2ecf20Sopenharmony_ci * ordering in this list reflects vt1618 docs for Reg 60h and
36038c2ecf20Sopenharmony_ci * the block diagram, DACs are as follows:
36048c2ecf20Sopenharmony_ci *
36058c2ecf20Sopenharmony_ci *        OUT_O -> Front,
36068c2ecf20Sopenharmony_ci *	  OUT_1 -> Surround,
36078c2ecf20Sopenharmony_ci *	  OUT_2 -> C/LFE
36088c2ecf20Sopenharmony_ci *
36098c2ecf20Sopenharmony_ci * Unlike the 1617a, each OUT has a consistent set of mappings
36108c2ecf20Sopenharmony_ci * for all bitpatterns other than 00:
36118c2ecf20Sopenharmony_ci *
36128c2ecf20Sopenharmony_ci *        01       Unmixed Output
36138c2ecf20Sopenharmony_ci *        10       Line In
36148c2ecf20Sopenharmony_ci *        11       Mic  In
36158c2ecf20Sopenharmony_ci *
36168c2ecf20Sopenharmony_ci * Special Case of 00:
36178c2ecf20Sopenharmony_ci *
36188c2ecf20Sopenharmony_ci *        OUT_0    Mixed Output
36198c2ecf20Sopenharmony_ci *        OUT_1    Reserved
36208c2ecf20Sopenharmony_ci *        OUT_2    Reserved
36218c2ecf20Sopenharmony_ci *
36228c2ecf20Sopenharmony_ci * I have no idea what the hell Reserved does, but on an MSI
36238c2ecf20Sopenharmony_ci * CN700T, i have to set it to get 5.1 output - YMMV, bad
36248c2ecf20Sopenharmony_ci * shit may happen.
36258c2ecf20Sopenharmony_ci *
36268c2ecf20Sopenharmony_ci * If other chips use Universal Audio Jack, then this code might be applicable
36278c2ecf20Sopenharmony_ci * to them.
36288c2ecf20Sopenharmony_ci */
36298c2ecf20Sopenharmony_ci
36308c2ecf20Sopenharmony_cistruct vt1618_uaj_item {
36318c2ecf20Sopenharmony_ci	unsigned short mask;
36328c2ecf20Sopenharmony_ci	unsigned short shift;
36338c2ecf20Sopenharmony_ci	const char * const items[4];
36348c2ecf20Sopenharmony_ci};
36358c2ecf20Sopenharmony_ci
36368c2ecf20Sopenharmony_ci/* This list reflects the vt1618 docs for Vendor Defined Register 0x60. */
36378c2ecf20Sopenharmony_ci
36388c2ecf20Sopenharmony_cistatic const struct vt1618_uaj_item vt1618_uaj[3] = {
36398c2ecf20Sopenharmony_ci	{
36408c2ecf20Sopenharmony_ci		/* speaker jack */
36418c2ecf20Sopenharmony_ci		.mask  = 0x03,
36428c2ecf20Sopenharmony_ci		.shift = 0,
36438c2ecf20Sopenharmony_ci		.items = {
36448c2ecf20Sopenharmony_ci			"Speaker Out", "DAC Unmixed Out", "Line In", "Mic In"
36458c2ecf20Sopenharmony_ci		}
36468c2ecf20Sopenharmony_ci	},
36478c2ecf20Sopenharmony_ci	{
36488c2ecf20Sopenharmony_ci		/* line jack */
36498c2ecf20Sopenharmony_ci		.mask  = 0x0c,
36508c2ecf20Sopenharmony_ci		.shift = 2,
36518c2ecf20Sopenharmony_ci		.items = {
36528c2ecf20Sopenharmony_ci			"Surround Out", "DAC Unmixed Out", "Line In", "Mic In"
36538c2ecf20Sopenharmony_ci		}
36548c2ecf20Sopenharmony_ci	},
36558c2ecf20Sopenharmony_ci	{
36568c2ecf20Sopenharmony_ci		/* mic jack */
36578c2ecf20Sopenharmony_ci		.mask  = 0x30,
36588c2ecf20Sopenharmony_ci		.shift = 4,
36598c2ecf20Sopenharmony_ci		.items = {
36608c2ecf20Sopenharmony_ci			"Center LFE Out", "DAC Unmixed Out", "Line In", "Mic In"
36618c2ecf20Sopenharmony_ci		},
36628c2ecf20Sopenharmony_ci	},
36638c2ecf20Sopenharmony_ci};
36648c2ecf20Sopenharmony_ci
36658c2ecf20Sopenharmony_cistatic int snd_ac97_vt1618_UAJ_info(struct snd_kcontrol *kcontrol,
36668c2ecf20Sopenharmony_ci				    struct snd_ctl_elem_info *uinfo)
36678c2ecf20Sopenharmony_ci{
36688c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 4,
36698c2ecf20Sopenharmony_ci				 vt1618_uaj[kcontrol->private_value].items);
36708c2ecf20Sopenharmony_ci}
36718c2ecf20Sopenharmony_ci
36728c2ecf20Sopenharmony_ci/* All of the vt1618 Universal Audio Jack twiddlers are on
36738c2ecf20Sopenharmony_ci * Vendor Defined Register 0x60, page 0. The bits, and thus
36748c2ecf20Sopenharmony_ci * the mask, are the only thing that changes
36758c2ecf20Sopenharmony_ci */
36768c2ecf20Sopenharmony_cistatic int snd_ac97_vt1618_UAJ_get(struct snd_kcontrol *kcontrol,
36778c2ecf20Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
36788c2ecf20Sopenharmony_ci{
36798c2ecf20Sopenharmony_ci	unsigned short datpag, uaj;
36808c2ecf20Sopenharmony_ci	struct snd_ac97 *pac97 = snd_kcontrol_chip(kcontrol);
36818c2ecf20Sopenharmony_ci
36828c2ecf20Sopenharmony_ci	mutex_lock(&pac97->page_mutex);
36838c2ecf20Sopenharmony_ci
36848c2ecf20Sopenharmony_ci	datpag = snd_ac97_read(pac97, AC97_INT_PAGING) & AC97_PAGE_MASK;
36858c2ecf20Sopenharmony_ci	snd_ac97_update_bits(pac97, AC97_INT_PAGING, AC97_PAGE_MASK, 0);
36868c2ecf20Sopenharmony_ci
36878c2ecf20Sopenharmony_ci	uaj = snd_ac97_read(pac97, 0x60) &
36888c2ecf20Sopenharmony_ci		vt1618_uaj[kcontrol->private_value].mask;
36898c2ecf20Sopenharmony_ci
36908c2ecf20Sopenharmony_ci	snd_ac97_update_bits(pac97, AC97_INT_PAGING, AC97_PAGE_MASK, datpag);
36918c2ecf20Sopenharmony_ci	mutex_unlock(&pac97->page_mutex);
36928c2ecf20Sopenharmony_ci
36938c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = uaj >>
36948c2ecf20Sopenharmony_ci		vt1618_uaj[kcontrol->private_value].shift;
36958c2ecf20Sopenharmony_ci
36968c2ecf20Sopenharmony_ci	return 0;
36978c2ecf20Sopenharmony_ci}
36988c2ecf20Sopenharmony_ci
36998c2ecf20Sopenharmony_cistatic int snd_ac97_vt1618_UAJ_put(struct snd_kcontrol *kcontrol,
37008c2ecf20Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
37018c2ecf20Sopenharmony_ci{
37028c2ecf20Sopenharmony_ci	return ac97_update_bits_page(snd_kcontrol_chip(kcontrol), 0x60,
37038c2ecf20Sopenharmony_ci				     vt1618_uaj[kcontrol->private_value].mask,
37048c2ecf20Sopenharmony_ci				     ucontrol->value.enumerated.item[0]<<
37058c2ecf20Sopenharmony_ci				     vt1618_uaj[kcontrol->private_value].shift,
37068c2ecf20Sopenharmony_ci				     0);
37078c2ecf20Sopenharmony_ci}
37088c2ecf20Sopenharmony_ci
37098c2ecf20Sopenharmony_ci/* config aux in jack - not found on 3 jack motherboards or soundcards */
37108c2ecf20Sopenharmony_ci
37118c2ecf20Sopenharmony_cistatic int snd_ac97_vt1618_aux_info(struct snd_kcontrol *kcontrol,
37128c2ecf20Sopenharmony_ci				     struct snd_ctl_elem_info *uinfo)
37138c2ecf20Sopenharmony_ci{
37148c2ecf20Sopenharmony_ci	static const char * const txt_aux[] = {"Aux In", "Back Surr Out"};
37158c2ecf20Sopenharmony_ci
37168c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 2, txt_aux);
37178c2ecf20Sopenharmony_ci}
37188c2ecf20Sopenharmony_ci
37198c2ecf20Sopenharmony_cistatic int snd_ac97_vt1618_aux_get(struct snd_kcontrol *kcontrol,
37208c2ecf20Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
37218c2ecf20Sopenharmony_ci{
37228c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] =
37238c2ecf20Sopenharmony_ci		(snd_ac97_read(snd_kcontrol_chip(kcontrol), 0x5c) & 0x0008)>>3;
37248c2ecf20Sopenharmony_ci	return 0;
37258c2ecf20Sopenharmony_ci}
37268c2ecf20Sopenharmony_ci
37278c2ecf20Sopenharmony_cistatic int snd_ac97_vt1618_aux_put(struct snd_kcontrol *kcontrol,
37288c2ecf20Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
37298c2ecf20Sopenharmony_ci{
37308c2ecf20Sopenharmony_ci	/* toggle surround rear dac power */
37318c2ecf20Sopenharmony_ci
37328c2ecf20Sopenharmony_ci	snd_ac97_update_bits(snd_kcontrol_chip(kcontrol), 0x5c, 0x0008,
37338c2ecf20Sopenharmony_ci			     ucontrol->value.enumerated.item[0] << 3);
37348c2ecf20Sopenharmony_ci
37358c2ecf20Sopenharmony_ci	/* toggle aux in surround rear out jack */
37368c2ecf20Sopenharmony_ci
37378c2ecf20Sopenharmony_ci	return snd_ac97_update_bits(snd_kcontrol_chip(kcontrol), 0x76, 0x0008,
37388c2ecf20Sopenharmony_ci				    ucontrol->value.enumerated.item[0] << 3);
37398c2ecf20Sopenharmony_ci}
37408c2ecf20Sopenharmony_ci
37418c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_vt1618[] = {
37428c2ecf20Sopenharmony_ci	AC97_SINGLE("Exchange Center/LFE", 0x5a,  8, 1,     0),
37438c2ecf20Sopenharmony_ci	AC97_SINGLE("DC Offset",           0x5a, 10, 1,     0),
37448c2ecf20Sopenharmony_ci	AC97_SINGLE("Soft Mute",           0x5c,  0, 1,     1),
37458c2ecf20Sopenharmony_ci	AC97_SINGLE("Headphone Amp",       0x5c,  5, 1,     1),
37468c2ecf20Sopenharmony_ci	AC97_DOUBLE("Back Surr Volume",    0x5e,  8, 0, 31, 1),
37478c2ecf20Sopenharmony_ci	AC97_SINGLE("Back Surr Switch",    0x5e, 15, 1,     1),
37488c2ecf20Sopenharmony_ci	{
37498c2ecf20Sopenharmony_ci		.iface         = SNDRV_CTL_ELEM_IFACE_MIXER,
37508c2ecf20Sopenharmony_ci		.name          = "Speaker Jack Mode",
37518c2ecf20Sopenharmony_ci		.info          = snd_ac97_vt1618_UAJ_info,
37528c2ecf20Sopenharmony_ci		.get           = snd_ac97_vt1618_UAJ_get,
37538c2ecf20Sopenharmony_ci		.put           = snd_ac97_vt1618_UAJ_put,
37548c2ecf20Sopenharmony_ci		.private_value = 0
37558c2ecf20Sopenharmony_ci	},
37568c2ecf20Sopenharmony_ci	{
37578c2ecf20Sopenharmony_ci		.iface         = SNDRV_CTL_ELEM_IFACE_MIXER,
37588c2ecf20Sopenharmony_ci		.name          = "Line Jack Mode",
37598c2ecf20Sopenharmony_ci		.info          = snd_ac97_vt1618_UAJ_info,
37608c2ecf20Sopenharmony_ci		.get           = snd_ac97_vt1618_UAJ_get,
37618c2ecf20Sopenharmony_ci		.put           = snd_ac97_vt1618_UAJ_put,
37628c2ecf20Sopenharmony_ci		.private_value = 1
37638c2ecf20Sopenharmony_ci	},
37648c2ecf20Sopenharmony_ci	{
37658c2ecf20Sopenharmony_ci		.iface         = SNDRV_CTL_ELEM_IFACE_MIXER,
37668c2ecf20Sopenharmony_ci		.name          = "Mic Jack Mode",
37678c2ecf20Sopenharmony_ci		.info          = snd_ac97_vt1618_UAJ_info,
37688c2ecf20Sopenharmony_ci		.get           = snd_ac97_vt1618_UAJ_get,
37698c2ecf20Sopenharmony_ci		.put           = snd_ac97_vt1618_UAJ_put,
37708c2ecf20Sopenharmony_ci		.private_value = 2
37718c2ecf20Sopenharmony_ci	},
37728c2ecf20Sopenharmony_ci	{
37738c2ecf20Sopenharmony_ci		.iface         = SNDRV_CTL_ELEM_IFACE_MIXER,
37748c2ecf20Sopenharmony_ci		.name          = "Aux Jack Mode",
37758c2ecf20Sopenharmony_ci		.info          = snd_ac97_vt1618_aux_info,
37768c2ecf20Sopenharmony_ci		.get           = snd_ac97_vt1618_aux_get,
37778c2ecf20Sopenharmony_ci		.put           = snd_ac97_vt1618_aux_put,
37788c2ecf20Sopenharmony_ci	}
37798c2ecf20Sopenharmony_ci};
37808c2ecf20Sopenharmony_ci
37818c2ecf20Sopenharmony_cistatic int patch_vt1618(struct snd_ac97 *ac97)
37828c2ecf20Sopenharmony_ci{
37838c2ecf20Sopenharmony_ci	return patch_build_controls(ac97, snd_ac97_controls_vt1618,
37848c2ecf20Sopenharmony_ci				    ARRAY_SIZE(snd_ac97_controls_vt1618));
37858c2ecf20Sopenharmony_ci}
37868c2ecf20Sopenharmony_ci
37878c2ecf20Sopenharmony_ci/*
37888c2ecf20Sopenharmony_ci */
37898c2ecf20Sopenharmony_cistatic void it2646_update_jacks(struct snd_ac97 *ac97)
37908c2ecf20Sopenharmony_ci{
37918c2ecf20Sopenharmony_ci	/* shared Line-In / Surround Out */
37928c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, 0x76, 1 << 9,
37938c2ecf20Sopenharmony_ci			     is_shared_surrout(ac97) ? (1<<9) : 0);
37948c2ecf20Sopenharmony_ci	/* shared Mic / Center/LFE Out */
37958c2ecf20Sopenharmony_ci	snd_ac97_update_bits(ac97, 0x76, 1 << 10,
37968c2ecf20Sopenharmony_ci			     is_shared_clfeout(ac97) ? (1<<10) : 0);
37978c2ecf20Sopenharmony_ci}
37988c2ecf20Sopenharmony_ci
37998c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_it2646[] = {
38008c2ecf20Sopenharmony_ci	AC97_SURROUND_JACK_MODE_CTL,
38018c2ecf20Sopenharmony_ci	AC97_CHANNEL_MODE_CTL,
38028c2ecf20Sopenharmony_ci};
38038c2ecf20Sopenharmony_ci
38048c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_spdif_controls_it2646[] = {
38058c2ecf20Sopenharmony_ci	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0x76, 11, 1, 0),
38068c2ecf20Sopenharmony_ci	AC97_SINGLE("Analog to IEC958 Output", 0x76, 12, 1, 0),
38078c2ecf20Sopenharmony_ci	AC97_SINGLE("IEC958 Input Monitor", 0x76, 13, 1, 0),
38088c2ecf20Sopenharmony_ci};
38098c2ecf20Sopenharmony_ci
38108c2ecf20Sopenharmony_cistatic int patch_it2646_specific(struct snd_ac97 * ac97)
38118c2ecf20Sopenharmony_ci{
38128c2ecf20Sopenharmony_ci	int err;
38138c2ecf20Sopenharmony_ci	if ((err = patch_build_controls(ac97, snd_ac97_controls_it2646, ARRAY_SIZE(snd_ac97_controls_it2646))) < 0)
38148c2ecf20Sopenharmony_ci		return err;
38158c2ecf20Sopenharmony_ci	if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_it2646, ARRAY_SIZE(snd_ac97_spdif_controls_it2646))) < 0)
38168c2ecf20Sopenharmony_ci		return err;
38178c2ecf20Sopenharmony_ci	return 0;
38188c2ecf20Sopenharmony_ci}
38198c2ecf20Sopenharmony_ci
38208c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_it2646_ops = {
38218c2ecf20Sopenharmony_ci	.build_specific	= patch_it2646_specific,
38228c2ecf20Sopenharmony_ci	.update_jacks = it2646_update_jacks
38238c2ecf20Sopenharmony_ci};
38248c2ecf20Sopenharmony_ci
38258c2ecf20Sopenharmony_cistatic int patch_it2646(struct snd_ac97 * ac97)
38268c2ecf20Sopenharmony_ci{
38278c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_it2646_ops;
38288c2ecf20Sopenharmony_ci	/* full DAC volume */
38298c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, 0x5E, 0x0808);
38308c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, 0x7A, 0x0808);
38318c2ecf20Sopenharmony_ci	return 0;
38328c2ecf20Sopenharmony_ci}
38338c2ecf20Sopenharmony_ci
38348c2ecf20Sopenharmony_ci/*
38358c2ecf20Sopenharmony_ci * Si3036 codec
38368c2ecf20Sopenharmony_ci */
38378c2ecf20Sopenharmony_ci
38388c2ecf20Sopenharmony_ci#define AC97_SI3036_CHIP_ID     0x5a
38398c2ecf20Sopenharmony_ci#define AC97_SI3036_LINE_CFG    0x5c
38408c2ecf20Sopenharmony_ci
38418c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_si3036[] = {
38428c2ecf20Sopenharmony_ciAC97_DOUBLE("Modem Speaker Volume", 0x5c, 14, 12, 3, 1)
38438c2ecf20Sopenharmony_ci};
38448c2ecf20Sopenharmony_ci
38458c2ecf20Sopenharmony_cistatic int patch_si3036_specific(struct snd_ac97 * ac97)
38468c2ecf20Sopenharmony_ci{
38478c2ecf20Sopenharmony_ci	int idx, err;
38488c2ecf20Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_si3036); idx++)
38498c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(ac97->bus->card, snd_ctl_new1(&snd_ac97_controls_si3036[idx], ac97))) < 0)
38508c2ecf20Sopenharmony_ci			return err;
38518c2ecf20Sopenharmony_ci	return 0;
38528c2ecf20Sopenharmony_ci}
38538c2ecf20Sopenharmony_ci
38548c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_si3036_ops = {
38558c2ecf20Sopenharmony_ci	.build_specific	= patch_si3036_specific,
38568c2ecf20Sopenharmony_ci};
38578c2ecf20Sopenharmony_ci
38588c2ecf20Sopenharmony_cistatic int mpatch_si3036(struct snd_ac97 * ac97)
38598c2ecf20Sopenharmony_ci{
38608c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_si3036_ops;
38618c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, 0x5c, 0xf210 );
38628c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, 0x68, 0);
38638c2ecf20Sopenharmony_ci	return 0;
38648c2ecf20Sopenharmony_ci}
38658c2ecf20Sopenharmony_ci
38668c2ecf20Sopenharmony_ci/*
38678c2ecf20Sopenharmony_ci * LM 4550 Codec
38688c2ecf20Sopenharmony_ci *
38698c2ecf20Sopenharmony_ci * We use a static resolution table since LM4550 codec cannot be
38708c2ecf20Sopenharmony_ci * properly autoprobed to determine the resolution via
38718c2ecf20Sopenharmony_ci * check_volume_resolution().
38728c2ecf20Sopenharmony_ci */
38738c2ecf20Sopenharmony_ci
38748c2ecf20Sopenharmony_cistatic const struct snd_ac97_res_table lm4550_restbl[] = {
38758c2ecf20Sopenharmony_ci	{ AC97_MASTER, 0x1f1f },
38768c2ecf20Sopenharmony_ci	{ AC97_HEADPHONE, 0x1f1f },
38778c2ecf20Sopenharmony_ci	{ AC97_MASTER_MONO, 0x001f },
38788c2ecf20Sopenharmony_ci	{ AC97_PC_BEEP, 0x001f },	/* LSB is ignored */
38798c2ecf20Sopenharmony_ci	{ AC97_PHONE, 0x001f },
38808c2ecf20Sopenharmony_ci	{ AC97_MIC, 0x001f },
38818c2ecf20Sopenharmony_ci	{ AC97_LINE, 0x1f1f },
38828c2ecf20Sopenharmony_ci	{ AC97_CD, 0x1f1f },
38838c2ecf20Sopenharmony_ci	{ AC97_VIDEO, 0x1f1f },
38848c2ecf20Sopenharmony_ci	{ AC97_AUX, 0x1f1f },
38858c2ecf20Sopenharmony_ci	{ AC97_PCM, 0x1f1f },
38868c2ecf20Sopenharmony_ci	{ AC97_REC_GAIN, 0x0f0f },
38878c2ecf20Sopenharmony_ci	{ } /* terminator */
38888c2ecf20Sopenharmony_ci};
38898c2ecf20Sopenharmony_ci
38908c2ecf20Sopenharmony_cistatic int patch_lm4550(struct snd_ac97 *ac97)
38918c2ecf20Sopenharmony_ci{
38928c2ecf20Sopenharmony_ci	ac97->res_table = lm4550_restbl;
38938c2ecf20Sopenharmony_ci	return 0;
38948c2ecf20Sopenharmony_ci}
38958c2ecf20Sopenharmony_ci
38968c2ecf20Sopenharmony_ci/*
38978c2ecf20Sopenharmony_ci *  UCB1400 codec (http://www.semiconductors.philips.com/acrobat_download/datasheets/UCB1400-02.pdf)
38988c2ecf20Sopenharmony_ci */
38998c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_ucb1400[] = {
39008c2ecf20Sopenharmony_ci/* enable/disable headphone driver which allows direct connection to
39018c2ecf20Sopenharmony_ci   stereo headphone without the use of external DC blocking
39028c2ecf20Sopenharmony_ci   capacitors */
39038c2ecf20Sopenharmony_ciAC97_SINGLE("Headphone Driver", 0x6a, 6, 1, 0),
39048c2ecf20Sopenharmony_ci/* Filter used to compensate the DC offset is added in the ADC to remove idle
39058c2ecf20Sopenharmony_ci   tones from the audio band. */
39068c2ecf20Sopenharmony_ciAC97_SINGLE("DC Filter", 0x6a, 4, 1, 0),
39078c2ecf20Sopenharmony_ci/* Control smart-low-power mode feature. Allows automatic power down
39088c2ecf20Sopenharmony_ci   of unused blocks in the ADC analog front end and the PLL. */
39098c2ecf20Sopenharmony_ciAC97_SINGLE("Smart Low Power Mode", 0x6c, 4, 3, 0),
39108c2ecf20Sopenharmony_ci};
39118c2ecf20Sopenharmony_ci
39128c2ecf20Sopenharmony_cistatic int patch_ucb1400_specific(struct snd_ac97 * ac97)
39138c2ecf20Sopenharmony_ci{
39148c2ecf20Sopenharmony_ci	int idx, err;
39158c2ecf20Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_ucb1400); idx++)
39168c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(ac97->bus->card, snd_ctl_new1(&snd_ac97_controls_ucb1400[idx], ac97))) < 0)
39178c2ecf20Sopenharmony_ci			return err;
39188c2ecf20Sopenharmony_ci	return 0;
39198c2ecf20Sopenharmony_ci}
39208c2ecf20Sopenharmony_ci
39218c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops patch_ucb1400_ops = {
39228c2ecf20Sopenharmony_ci	.build_specific	= patch_ucb1400_specific,
39238c2ecf20Sopenharmony_ci};
39248c2ecf20Sopenharmony_ci
39258c2ecf20Sopenharmony_cistatic int patch_ucb1400(struct snd_ac97 * ac97)
39268c2ecf20Sopenharmony_ci{
39278c2ecf20Sopenharmony_ci	ac97->build_ops = &patch_ucb1400_ops;
39288c2ecf20Sopenharmony_ci	/* enable headphone driver and smart low power mode by default */
39298c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, 0x6a, 0x0050);
39308c2ecf20Sopenharmony_ci	snd_ac97_write_cache(ac97, 0x6c, 0x0030);
39318c2ecf20Sopenharmony_ci	return 0;
39328c2ecf20Sopenharmony_ci}
3933