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