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). 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/pci.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/mutex.h> 168c2ecf20Sopenharmony_ci#include <sound/core.h> 178c2ecf20Sopenharmony_ci#include <sound/pcm.h> 188c2ecf20Sopenharmony_ci#include <sound/tlv.h> 198c2ecf20Sopenharmony_ci#include <sound/ac97_codec.h> 208c2ecf20Sopenharmony_ci#include <sound/asoundef.h> 218c2ecf20Sopenharmony_ci#include <sound/initval.h> 228c2ecf20Sopenharmony_ci#include "ac97_id.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "ac97_patch.c" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Universal interface for Audio Codec '97"); 288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic bool enable_loopback; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cimodule_param(enable_loopback, bool, 0444); 338c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control"); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE 368c2ecf20Sopenharmony_cistatic int power_save = CONFIG_SND_AC97_POWER_SAVE_DEFAULT; 378c2ecf20Sopenharmony_cimodule_param(power_save, int, 0644); 388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(power_save, "Automatic power-saving timeout " 398c2ecf20Sopenharmony_ci "(in second, 0 = disable)."); 408c2ecf20Sopenharmony_ci#endif 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistruct ac97_codec_id { 468c2ecf20Sopenharmony_ci unsigned int id; 478c2ecf20Sopenharmony_ci unsigned int mask; 488c2ecf20Sopenharmony_ci const char *name; 498c2ecf20Sopenharmony_ci int (*patch)(struct snd_ac97 *ac97); 508c2ecf20Sopenharmony_ci int (*mpatch)(struct snd_ac97 *ac97); 518c2ecf20Sopenharmony_ci unsigned int flags; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic const struct ac97_codec_id snd_ac97_codec_id_vendors[] = { 558c2ecf20Sopenharmony_ci{ 0x41445300, 0xffffff00, "Analog Devices", NULL, NULL }, 568c2ecf20Sopenharmony_ci{ 0x414b4d00, 0xffffff00, "Asahi Kasei", NULL, NULL }, 578c2ecf20Sopenharmony_ci{ 0x414c4300, 0xffffff00, "Realtek", NULL, NULL }, 588c2ecf20Sopenharmony_ci{ 0x414c4700, 0xffffff00, "Realtek", NULL, NULL }, 598c2ecf20Sopenharmony_ci/* 608c2ecf20Sopenharmony_ci * This is an _inofficial_ Aztech Labs entry 618c2ecf20Sopenharmony_ci * (value might differ from unknown official Aztech ID), 628c2ecf20Sopenharmony_ci * currently used by the AC97 emulation of the almost-AC97 PCI168 card. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_ci{ 0x415a5400, 0xffffff00, "Aztech Labs (emulated)", NULL, NULL }, 658c2ecf20Sopenharmony_ci{ 0x434d4900, 0xffffff00, "C-Media Electronics", NULL, NULL }, 668c2ecf20Sopenharmony_ci{ 0x43525900, 0xffffff00, "Cirrus Logic", NULL, NULL }, 678c2ecf20Sopenharmony_ci{ 0x43585400, 0xffffff00, "Conexant", NULL, NULL }, 688c2ecf20Sopenharmony_ci{ 0x44543000, 0xffffff00, "Diamond Technology", NULL, NULL }, 698c2ecf20Sopenharmony_ci{ 0x454d4300, 0xffffff00, "eMicro", NULL, NULL }, 708c2ecf20Sopenharmony_ci{ 0x45838300, 0xffffff00, "ESS Technology", NULL, NULL }, 718c2ecf20Sopenharmony_ci{ 0x48525300, 0xffffff00, "Intersil", NULL, NULL }, 728c2ecf20Sopenharmony_ci{ 0x49434500, 0xffffff00, "ICEnsemble", NULL, NULL }, 738c2ecf20Sopenharmony_ci{ 0x49544500, 0xffffff00, "ITE Tech.Inc", NULL, NULL }, 748c2ecf20Sopenharmony_ci{ 0x4e534300, 0xffffff00, "National Semiconductor", NULL, NULL }, 758c2ecf20Sopenharmony_ci{ 0x50534300, 0xffffff00, "Philips", NULL, NULL }, 768c2ecf20Sopenharmony_ci{ 0x53494c00, 0xffffff00, "Silicon Laboratory", NULL, NULL }, 778c2ecf20Sopenharmony_ci{ 0x53544d00, 0xffffff00, "STMicroelectronics", NULL, NULL }, 788c2ecf20Sopenharmony_ci{ 0x54524100, 0xffffff00, "TriTech", NULL, NULL }, 798c2ecf20Sopenharmony_ci{ 0x54584e00, 0xffffff00, "Texas Instruments", NULL, NULL }, 808c2ecf20Sopenharmony_ci{ 0x56494100, 0xffffff00, "VIA Technologies", NULL, NULL }, 818c2ecf20Sopenharmony_ci{ 0x57454300, 0xffffff00, "Winbond", NULL, NULL }, 828c2ecf20Sopenharmony_ci{ 0x574d4c00, 0xffffff00, "Wolfson", NULL, NULL }, 838c2ecf20Sopenharmony_ci{ 0x594d4800, 0xffffff00, "Yamaha", NULL, NULL }, 848c2ecf20Sopenharmony_ci{ 0x83847600, 0xffffff00, "SigmaTel", NULL, NULL }, 858c2ecf20Sopenharmony_ci{ 0, 0, NULL, NULL, NULL } 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic const struct ac97_codec_id snd_ac97_codec_ids[] = { 898c2ecf20Sopenharmony_ci{ 0x41445303, 0xffffffff, "AD1819", patch_ad1819, NULL }, 908c2ecf20Sopenharmony_ci{ 0x41445340, 0xffffffff, "AD1881", patch_ad1881, NULL }, 918c2ecf20Sopenharmony_ci{ 0x41445348, 0xffffffff, "AD1881A", patch_ad1881, NULL }, 928c2ecf20Sopenharmony_ci{ 0x41445360, 0xffffffff, "AD1885", patch_ad1885, NULL }, 938c2ecf20Sopenharmony_ci{ 0x41445361, 0xffffffff, "AD1886", patch_ad1886, NULL }, 948c2ecf20Sopenharmony_ci{ 0x41445362, 0xffffffff, "AD1887", patch_ad1881, NULL }, 958c2ecf20Sopenharmony_ci{ 0x41445363, 0xffffffff, "AD1886A", patch_ad1881, NULL }, 968c2ecf20Sopenharmony_ci{ 0x41445368, 0xffffffff, "AD1888", patch_ad1888, NULL }, 978c2ecf20Sopenharmony_ci{ 0x41445370, 0xffffffff, "AD1980", patch_ad1980, NULL }, 988c2ecf20Sopenharmony_ci{ 0x41445372, 0xffffffff, "AD1981A", patch_ad1981a, NULL }, 998c2ecf20Sopenharmony_ci{ 0x41445374, 0xffffffff, "AD1981B", patch_ad1981b, NULL }, 1008c2ecf20Sopenharmony_ci{ 0x41445375, 0xffffffff, "AD1985", patch_ad1985, NULL }, 1018c2ecf20Sopenharmony_ci{ 0x41445378, 0xffffffff, "AD1986", patch_ad1986, NULL }, 1028c2ecf20Sopenharmony_ci{ 0x414b4d00, 0xffffffff, "AK4540", NULL, NULL }, 1038c2ecf20Sopenharmony_ci{ 0x414b4d01, 0xffffffff, "AK4542", NULL, NULL }, 1048c2ecf20Sopenharmony_ci{ 0x414b4d02, 0xffffffff, "AK4543", NULL, NULL }, 1058c2ecf20Sopenharmony_ci{ 0x414b4d06, 0xffffffff, "AK4544A", NULL, NULL }, 1068c2ecf20Sopenharmony_ci{ 0x414b4d07, 0xffffffff, "AK4545", NULL, NULL }, 1078c2ecf20Sopenharmony_ci{ 0x414c4300, 0xffffff00, "ALC100,100P", NULL, NULL }, 1088c2ecf20Sopenharmony_ci{ 0x414c4710, 0xfffffff0, "ALC200,200P", NULL, NULL }, 1098c2ecf20Sopenharmony_ci{ 0x414c4721, 0xffffffff, "ALC650D", NULL, NULL }, /* already patched */ 1108c2ecf20Sopenharmony_ci{ 0x414c4722, 0xffffffff, "ALC650E", NULL, NULL }, /* already patched */ 1118c2ecf20Sopenharmony_ci{ 0x414c4723, 0xffffffff, "ALC650F", NULL, NULL }, /* already patched */ 1128c2ecf20Sopenharmony_ci{ 0x414c4720, 0xfffffff0, "ALC650", patch_alc650, NULL }, 1138c2ecf20Sopenharmony_ci{ 0x414c4730, 0xffffffff, "ALC101", NULL, NULL }, 1148c2ecf20Sopenharmony_ci{ 0x414c4740, 0xfffffff0, "ALC202", NULL, NULL }, 1158c2ecf20Sopenharmony_ci{ 0x414c4750, 0xfffffff0, "ALC250", NULL, NULL }, 1168c2ecf20Sopenharmony_ci{ 0x414c4760, 0xfffffff0, "ALC655", patch_alc655, NULL }, 1178c2ecf20Sopenharmony_ci{ 0x414c4770, 0xfffffff0, "ALC203", patch_alc203, NULL }, 1188c2ecf20Sopenharmony_ci{ 0x414c4781, 0xffffffff, "ALC658D", NULL, NULL }, /* already patched */ 1198c2ecf20Sopenharmony_ci{ 0x414c4780, 0xfffffff0, "ALC658", patch_alc655, NULL }, 1208c2ecf20Sopenharmony_ci{ 0x414c4790, 0xfffffff0, "ALC850", patch_alc850, NULL }, 1218c2ecf20Sopenharmony_ci{ 0x415a5401, 0xffffffff, "AZF3328", patch_aztech_azf3328, NULL }, 1228c2ecf20Sopenharmony_ci{ 0x434d4941, 0xffffffff, "CMI9738", patch_cm9738, NULL }, 1238c2ecf20Sopenharmony_ci{ 0x434d4961, 0xffffffff, "CMI9739", patch_cm9739, NULL }, 1248c2ecf20Sopenharmony_ci{ 0x434d4969, 0xffffffff, "CMI9780", patch_cm9780, NULL }, 1258c2ecf20Sopenharmony_ci{ 0x434d4978, 0xffffffff, "CMI9761A", patch_cm9761, NULL }, 1268c2ecf20Sopenharmony_ci{ 0x434d4982, 0xffffffff, "CMI9761B", patch_cm9761, NULL }, 1278c2ecf20Sopenharmony_ci{ 0x434d4983, 0xffffffff, "CMI9761A+", patch_cm9761, NULL }, 1288c2ecf20Sopenharmony_ci{ 0x43525900, 0xfffffff8, "CS4297", NULL, NULL }, 1298c2ecf20Sopenharmony_ci{ 0x43525910, 0xfffffff8, "CS4297A", patch_cirrus_spdif, NULL }, 1308c2ecf20Sopenharmony_ci{ 0x43525920, 0xfffffff8, "CS4298", patch_cirrus_spdif, NULL }, 1318c2ecf20Sopenharmony_ci{ 0x43525928, 0xfffffff8, "CS4294", NULL, NULL }, 1328c2ecf20Sopenharmony_ci{ 0x43525930, 0xfffffff8, "CS4299", patch_cirrus_cs4299, NULL }, 1338c2ecf20Sopenharmony_ci{ 0x43525948, 0xfffffff8, "CS4201", NULL, NULL }, 1348c2ecf20Sopenharmony_ci{ 0x43525958, 0xfffffff8, "CS4205", patch_cirrus_spdif, NULL }, 1358c2ecf20Sopenharmony_ci{ 0x43525960, 0xfffffff8, "CS4291", NULL, NULL }, 1368c2ecf20Sopenharmony_ci{ 0x43525970, 0xfffffff8, "CS4202", NULL, NULL }, 1378c2ecf20Sopenharmony_ci{ 0x43585421, 0xffffffff, "HSD11246", NULL, NULL }, // SmartMC II 1388c2ecf20Sopenharmony_ci{ 0x43585428, 0xfffffff8, "Cx20468", patch_conexant, NULL }, // SmartAMC fixme: the mask might be different 1398c2ecf20Sopenharmony_ci{ 0x43585430, 0xffffffff, "Cx20468-31", patch_conexant, NULL }, 1408c2ecf20Sopenharmony_ci{ 0x43585431, 0xffffffff, "Cx20551", patch_cx20551, NULL }, 1418c2ecf20Sopenharmony_ci{ 0x44543031, 0xfffffff0, "DT0398", NULL, NULL }, 1428c2ecf20Sopenharmony_ci{ 0x454d4328, 0xffffffff, "EM28028", NULL, NULL }, // same as TR28028? 1438c2ecf20Sopenharmony_ci{ 0x45838308, 0xffffffff, "ESS1988", NULL, NULL }, 1448c2ecf20Sopenharmony_ci{ 0x48525300, 0xffffff00, "HMP9701", NULL, NULL }, 1458c2ecf20Sopenharmony_ci{ 0x49434501, 0xffffffff, "ICE1230", NULL, NULL }, 1468c2ecf20Sopenharmony_ci{ 0x49434511, 0xffffffff, "ICE1232", NULL, NULL }, // alias VIA VT1611A? 1478c2ecf20Sopenharmony_ci{ 0x49434514, 0xffffffff, "ICE1232A", NULL, NULL }, 1488c2ecf20Sopenharmony_ci{ 0x49434551, 0xffffffff, "VT1616", patch_vt1616, NULL }, 1498c2ecf20Sopenharmony_ci{ 0x49434552, 0xffffffff, "VT1616i", patch_vt1616, NULL }, // VT1616 compatible (chipset integrated) 1508c2ecf20Sopenharmony_ci{ 0x49544520, 0xffffffff, "IT2226E", NULL, NULL }, 1518c2ecf20Sopenharmony_ci{ 0x49544561, 0xffffffff, "IT2646E", patch_it2646, NULL }, 1528c2ecf20Sopenharmony_ci{ 0x4e534300, 0xffffffff, "LM4540,43,45,46,48", NULL, NULL }, // only guess --jk 1538c2ecf20Sopenharmony_ci{ 0x4e534331, 0xffffffff, "LM4549", NULL, NULL }, 1548c2ecf20Sopenharmony_ci{ 0x4e534350, 0xffffffff, "LM4550", patch_lm4550, NULL }, // volume wrap fix 1558c2ecf20Sopenharmony_ci{ 0x50534304, 0xffffffff, "UCB1400", patch_ucb1400, NULL }, 1568c2ecf20Sopenharmony_ci{ 0x53494c20, 0xffffffe0, "Si3036,8", mpatch_si3036, mpatch_si3036, AC97_MODEM_PATCH }, 1578c2ecf20Sopenharmony_ci{ 0x53544d02, 0xffffffff, "ST7597", NULL, NULL }, 1588c2ecf20Sopenharmony_ci{ 0x54524102, 0xffffffff, "TR28022", NULL, NULL }, 1598c2ecf20Sopenharmony_ci{ 0x54524103, 0xffffffff, "TR28023", NULL, NULL }, 1608c2ecf20Sopenharmony_ci{ 0x54524106, 0xffffffff, "TR28026", NULL, NULL }, 1618c2ecf20Sopenharmony_ci{ 0x54524108, 0xffffffff, "TR28028", patch_tritech_tr28028, NULL }, // added by xin jin [07/09/99] 1628c2ecf20Sopenharmony_ci{ 0x54524123, 0xffffffff, "TR28602", NULL, NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)] 1638c2ecf20Sopenharmony_ci{ 0x54584e03, 0xffffffff, "TLV320AIC27", NULL, NULL }, 1648c2ecf20Sopenharmony_ci{ 0x54584e20, 0xffffffff, "TLC320AD9xC", NULL, NULL }, 1658c2ecf20Sopenharmony_ci{ 0x56494120, 0xfffffff0, "VIA1613", patch_vt1613, NULL }, 1668c2ecf20Sopenharmony_ci{ 0x56494161, 0xffffffff, "VIA1612A", NULL, NULL }, // modified ICE1232 with S/PDIF 1678c2ecf20Sopenharmony_ci{ 0x56494170, 0xffffffff, "VIA1617A", patch_vt1617a, NULL }, // modified VT1616 with S/PDIF 1688c2ecf20Sopenharmony_ci{ 0x56494182, 0xffffffff, "VIA1618", patch_vt1618, NULL }, 1698c2ecf20Sopenharmony_ci{ 0x57454301, 0xffffffff, "W83971D", NULL, NULL }, 1708c2ecf20Sopenharmony_ci{ 0x574d4c00, 0xffffffff, "WM9701,WM9701A", NULL, NULL }, 1718c2ecf20Sopenharmony_ci{ 0x574d4C03, 0xffffffff, "WM9703,WM9707,WM9708,WM9717", patch_wolfson03, NULL}, 1728c2ecf20Sopenharmony_ci{ 0x574d4C04, 0xffffffff, "WM9704M,WM9704Q", patch_wolfson04, NULL}, 1738c2ecf20Sopenharmony_ci{ 0x574d4C05, 0xffffffff, "WM9705,WM9710", patch_wolfson05, NULL}, 1748c2ecf20Sopenharmony_ci{ 0x574d4C09, 0xffffffff, "WM9709", NULL, NULL}, 1758c2ecf20Sopenharmony_ci{ 0x574d4C12, 0xffffffff, "WM9711,WM9712,WM9715", patch_wolfson11, NULL}, 1768c2ecf20Sopenharmony_ci{ 0x574d4c13, 0xffffffff, "WM9713,WM9714", patch_wolfson13, NULL, AC97_DEFAULT_POWER_OFF}, 1778c2ecf20Sopenharmony_ci{ 0x594d4800, 0xffffffff, "YMF743", patch_yamaha_ymf743, NULL }, 1788c2ecf20Sopenharmony_ci{ 0x594d4802, 0xffffffff, "YMF752", NULL, NULL }, 1798c2ecf20Sopenharmony_ci{ 0x594d4803, 0xffffffff, "YMF753", patch_yamaha_ymf753, NULL }, 1808c2ecf20Sopenharmony_ci{ 0x83847600, 0xffffffff, "STAC9700,83,84", patch_sigmatel_stac9700, NULL }, 1818c2ecf20Sopenharmony_ci{ 0x83847604, 0xffffffff, "STAC9701,3,4,5", NULL, NULL }, 1828c2ecf20Sopenharmony_ci{ 0x83847605, 0xffffffff, "STAC9704", NULL, NULL }, 1838c2ecf20Sopenharmony_ci{ 0x83847608, 0xffffffff, "STAC9708,11", patch_sigmatel_stac9708, NULL }, 1848c2ecf20Sopenharmony_ci{ 0x83847609, 0xffffffff, "STAC9721,23", patch_sigmatel_stac9721, NULL }, 1858c2ecf20Sopenharmony_ci{ 0x83847644, 0xffffffff, "STAC9744", patch_sigmatel_stac9744, NULL }, 1868c2ecf20Sopenharmony_ci{ 0x83847650, 0xffffffff, "STAC9750,51", NULL, NULL }, // patch? 1878c2ecf20Sopenharmony_ci{ 0x83847652, 0xffffffff, "STAC9752,53", NULL, NULL }, // patch? 1888c2ecf20Sopenharmony_ci{ 0x83847656, 0xffffffff, "STAC9756,57", patch_sigmatel_stac9756, NULL }, 1898c2ecf20Sopenharmony_ci{ 0x83847658, 0xffffffff, "STAC9758,59", patch_sigmatel_stac9758, NULL }, 1908c2ecf20Sopenharmony_ci{ 0x83847666, 0xffffffff, "STAC9766,67", NULL, NULL }, // patch? 1918c2ecf20Sopenharmony_ci{ 0, 0, NULL, NULL, NULL } 1928c2ecf20Sopenharmony_ci}; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic void update_power_regs(struct snd_ac97 *ac97); 1968c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE 1978c2ecf20Sopenharmony_ci#define ac97_is_power_save_mode(ac97) \ 1988c2ecf20Sopenharmony_ci ((ac97->scaps & AC97_SCAP_POWER_SAVE) && power_save) 1998c2ecf20Sopenharmony_ci#else 2008c2ecf20Sopenharmony_ci#define ac97_is_power_save_mode(ac97) 0 2018c2ecf20Sopenharmony_ci#endif 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci#define ac97_err(ac97, fmt, args...) \ 2048c2ecf20Sopenharmony_ci dev_err((ac97)->bus->card->dev, fmt, ##args) 2058c2ecf20Sopenharmony_ci#define ac97_warn(ac97, fmt, args...) \ 2068c2ecf20Sopenharmony_ci dev_warn((ac97)->bus->card->dev, fmt, ##args) 2078c2ecf20Sopenharmony_ci#define ac97_dbg(ac97, fmt, args...) \ 2088c2ecf20Sopenharmony_ci dev_dbg((ac97)->bus->card->dev, fmt, ##args) 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci/* 2118c2ecf20Sopenharmony_ci * I/O routines 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int snd_ac97_valid_reg(struct snd_ac97 *ac97, unsigned short reg) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci /* filter some registers for buggy codecs */ 2178c2ecf20Sopenharmony_ci switch (ac97->id) { 2188c2ecf20Sopenharmony_ci case AC97_ID_ST_AC97_ID4: 2198c2ecf20Sopenharmony_ci if (reg == 0x08) 2208c2ecf20Sopenharmony_ci return 0; 2218c2ecf20Sopenharmony_ci fallthrough; 2228c2ecf20Sopenharmony_ci case AC97_ID_ST7597: 2238c2ecf20Sopenharmony_ci if (reg == 0x22 || reg == 0x7a) 2248c2ecf20Sopenharmony_ci return 1; 2258c2ecf20Sopenharmony_ci fallthrough; 2268c2ecf20Sopenharmony_ci case AC97_ID_AK4540: 2278c2ecf20Sopenharmony_ci case AC97_ID_AK4542: 2288c2ecf20Sopenharmony_ci if (reg <= 0x1c || reg == 0x20 || reg == 0x26 || reg >= 0x7c) 2298c2ecf20Sopenharmony_ci return 1; 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci case AC97_ID_AD1819: /* AD1819 */ 2328c2ecf20Sopenharmony_ci case AC97_ID_AD1881: /* AD1881 */ 2338c2ecf20Sopenharmony_ci case AC97_ID_AD1881A: /* AD1881A */ 2348c2ecf20Sopenharmony_ci if (reg >= 0x3a && reg <= 0x6e) /* 0x59 */ 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci return 1; 2378c2ecf20Sopenharmony_ci case AC97_ID_AD1885: /* AD1885 */ 2388c2ecf20Sopenharmony_ci case AC97_ID_AD1886: /* AD1886 */ 2398c2ecf20Sopenharmony_ci case AC97_ID_AD1886A: /* AD1886A - !!verify!! --jk */ 2408c2ecf20Sopenharmony_ci case AC97_ID_AD1887: /* AD1887 - !!verify!! --jk */ 2418c2ecf20Sopenharmony_ci if (reg == 0x5a) 2428c2ecf20Sopenharmony_ci return 1; 2438c2ecf20Sopenharmony_ci if (reg >= 0x3c && reg <= 0x6e) /* 0x59 */ 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci return 1; 2468c2ecf20Sopenharmony_ci case AC97_ID_STAC9700: 2478c2ecf20Sopenharmony_ci case AC97_ID_STAC9704: 2488c2ecf20Sopenharmony_ci case AC97_ID_STAC9705: 2498c2ecf20Sopenharmony_ci case AC97_ID_STAC9708: 2508c2ecf20Sopenharmony_ci case AC97_ID_STAC9721: 2518c2ecf20Sopenharmony_ci case AC97_ID_STAC9744: 2528c2ecf20Sopenharmony_ci case AC97_ID_STAC9756: 2538c2ecf20Sopenharmony_ci if (reg <= 0x3a || reg >= 0x5a) 2548c2ecf20Sopenharmony_ci return 1; 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci return 1; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci/** 2618c2ecf20Sopenharmony_ci * snd_ac97_write - write a value on the given register 2628c2ecf20Sopenharmony_ci * @ac97: the ac97 instance 2638c2ecf20Sopenharmony_ci * @reg: the register to change 2648c2ecf20Sopenharmony_ci * @value: the value to set 2658c2ecf20Sopenharmony_ci * 2668c2ecf20Sopenharmony_ci * Writes a value on the given register. This will invoke the write 2678c2ecf20Sopenharmony_ci * callback directly after the register check. 2688c2ecf20Sopenharmony_ci * This function doesn't change the register cache unlike 2698c2ecf20Sopenharmony_ci * #snd_ca97_write_cache(), so use this only when you don't want to 2708c2ecf20Sopenharmony_ci * reflect the change to the suspend/resume state. 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_civoid snd_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short value) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci if (!snd_ac97_valid_reg(ac97, reg)) 2758c2ecf20Sopenharmony_ci return; 2768c2ecf20Sopenharmony_ci if ((ac97->id & 0xffffff00) == AC97_ID_ALC100) { 2778c2ecf20Sopenharmony_ci /* Fix H/W bug of ALC100/100P */ 2788c2ecf20Sopenharmony_ci if (reg == AC97_MASTER || reg == AC97_HEADPHONE) 2798c2ecf20Sopenharmony_ci ac97->bus->ops->write(ac97, AC97_RESET, 0); /* reset audio codec */ 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci ac97->bus->ops->write(ac97, reg, value); 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_write); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci/** 2878c2ecf20Sopenharmony_ci * snd_ac97_read - read a value from the given register 2888c2ecf20Sopenharmony_ci * 2898c2ecf20Sopenharmony_ci * @ac97: the ac97 instance 2908c2ecf20Sopenharmony_ci * @reg: the register to read 2918c2ecf20Sopenharmony_ci * 2928c2ecf20Sopenharmony_ci * Reads a value from the given register. This will invoke the read 2938c2ecf20Sopenharmony_ci * callback directly after the register check. 2948c2ecf20Sopenharmony_ci * 2958c2ecf20Sopenharmony_ci * Return: The read value. 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_ciunsigned short snd_ac97_read(struct snd_ac97 *ac97, unsigned short reg) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci if (!snd_ac97_valid_reg(ac97, reg)) 3008c2ecf20Sopenharmony_ci return 0; 3018c2ecf20Sopenharmony_ci return ac97->bus->ops->read(ac97, reg); 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/* read a register - return the cached value if already read */ 3058c2ecf20Sopenharmony_cistatic inline unsigned short snd_ac97_read_cache(struct snd_ac97 *ac97, unsigned short reg) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci if (! test_bit(reg, ac97->reg_accessed)) { 3088c2ecf20Sopenharmony_ci ac97->regs[reg] = ac97->bus->ops->read(ac97, reg); 3098c2ecf20Sopenharmony_ci // set_bit(reg, ac97->reg_accessed); 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci return ac97->regs[reg]; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_read); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci/** 3178c2ecf20Sopenharmony_ci * snd_ac97_write_cache - write a value on the given register and update the cache 3188c2ecf20Sopenharmony_ci * @ac97: the ac97 instance 3198c2ecf20Sopenharmony_ci * @reg: the register to change 3208c2ecf20Sopenharmony_ci * @value: the value to set 3218c2ecf20Sopenharmony_ci * 3228c2ecf20Sopenharmony_ci * Writes a value on the given register and updates the register 3238c2ecf20Sopenharmony_ci * cache. The cached values are used for the cached-read and the 3248c2ecf20Sopenharmony_ci * suspend/resume. 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_civoid snd_ac97_write_cache(struct snd_ac97 *ac97, unsigned short reg, unsigned short value) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci if (!snd_ac97_valid_reg(ac97, reg)) 3298c2ecf20Sopenharmony_ci return; 3308c2ecf20Sopenharmony_ci mutex_lock(&ac97->reg_mutex); 3318c2ecf20Sopenharmony_ci ac97->regs[reg] = value; 3328c2ecf20Sopenharmony_ci ac97->bus->ops->write(ac97, reg, value); 3338c2ecf20Sopenharmony_ci set_bit(reg, ac97->reg_accessed); 3348c2ecf20Sopenharmony_ci mutex_unlock(&ac97->reg_mutex); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_write_cache); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci/** 3408c2ecf20Sopenharmony_ci * snd_ac97_update - update the value on the given register 3418c2ecf20Sopenharmony_ci * @ac97: the ac97 instance 3428c2ecf20Sopenharmony_ci * @reg: the register to change 3438c2ecf20Sopenharmony_ci * @value: the value to set 3448c2ecf20Sopenharmony_ci * 3458c2ecf20Sopenharmony_ci * Compares the value with the register cache and updates the value 3468c2ecf20Sopenharmony_ci * only when the value is changed. 3478c2ecf20Sopenharmony_ci * 3488c2ecf20Sopenharmony_ci * Return: 1 if the value is changed, 0 if no change, or a negative 3498c2ecf20Sopenharmony_ci * code on failure. 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_ciint snd_ac97_update(struct snd_ac97 *ac97, unsigned short reg, unsigned short value) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci int change; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (!snd_ac97_valid_reg(ac97, reg)) 3568c2ecf20Sopenharmony_ci return -EINVAL; 3578c2ecf20Sopenharmony_ci mutex_lock(&ac97->reg_mutex); 3588c2ecf20Sopenharmony_ci change = ac97->regs[reg] != value; 3598c2ecf20Sopenharmony_ci if (change) { 3608c2ecf20Sopenharmony_ci ac97->regs[reg] = value; 3618c2ecf20Sopenharmony_ci ac97->bus->ops->write(ac97, reg, value); 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci set_bit(reg, ac97->reg_accessed); 3648c2ecf20Sopenharmony_ci mutex_unlock(&ac97->reg_mutex); 3658c2ecf20Sopenharmony_ci return change; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_update); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci/** 3718c2ecf20Sopenharmony_ci * snd_ac97_update_bits - update the bits on the given register 3728c2ecf20Sopenharmony_ci * @ac97: the ac97 instance 3738c2ecf20Sopenharmony_ci * @reg: the register to change 3748c2ecf20Sopenharmony_ci * @mask: the bit-mask to change 3758c2ecf20Sopenharmony_ci * @value: the value to set 3768c2ecf20Sopenharmony_ci * 3778c2ecf20Sopenharmony_ci * Updates the masked-bits on the given register only when the value 3788c2ecf20Sopenharmony_ci * is changed. 3798c2ecf20Sopenharmony_ci * 3808c2ecf20Sopenharmony_ci * Return: 1 if the bits are changed, 0 if no change, or a negative 3818c2ecf20Sopenharmony_ci * code on failure. 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_ciint snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci int change; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (!snd_ac97_valid_reg(ac97, reg)) 3888c2ecf20Sopenharmony_ci return -EINVAL; 3898c2ecf20Sopenharmony_ci mutex_lock(&ac97->reg_mutex); 3908c2ecf20Sopenharmony_ci change = snd_ac97_update_bits_nolock(ac97, reg, mask, value); 3918c2ecf20Sopenharmony_ci mutex_unlock(&ac97->reg_mutex); 3928c2ecf20Sopenharmony_ci return change; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_update_bits); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci/* no lock version - see snd_ac97_update_bits() */ 3988c2ecf20Sopenharmony_ciint snd_ac97_update_bits_nolock(struct snd_ac97 *ac97, unsigned short reg, 3998c2ecf20Sopenharmony_ci unsigned short mask, unsigned short value) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci int change; 4028c2ecf20Sopenharmony_ci unsigned short old, new; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci old = snd_ac97_read_cache(ac97, reg); 4058c2ecf20Sopenharmony_ci new = (old & ~mask) | (value & mask); 4068c2ecf20Sopenharmony_ci change = old != new; 4078c2ecf20Sopenharmony_ci if (change) { 4088c2ecf20Sopenharmony_ci ac97->regs[reg] = new; 4098c2ecf20Sopenharmony_ci ac97->bus->ops->write(ac97, reg, new); 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci set_bit(reg, ac97->reg_accessed); 4128c2ecf20Sopenharmony_ci return change; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic int snd_ac97_ad18xx_update_pcm_bits(struct snd_ac97 *ac97, int codec, unsigned short mask, unsigned short value) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci int change; 4188c2ecf20Sopenharmony_ci unsigned short old, new, cfg; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci mutex_lock(&ac97->page_mutex); 4218c2ecf20Sopenharmony_ci old = ac97->spec.ad18xx.pcmreg[codec]; 4228c2ecf20Sopenharmony_ci new = (old & ~mask) | (value & mask); 4238c2ecf20Sopenharmony_ci change = old != new; 4248c2ecf20Sopenharmony_ci if (change) { 4258c2ecf20Sopenharmony_ci mutex_lock(&ac97->reg_mutex); 4268c2ecf20Sopenharmony_ci cfg = snd_ac97_read_cache(ac97, AC97_AD_SERIAL_CFG); 4278c2ecf20Sopenharmony_ci ac97->spec.ad18xx.pcmreg[codec] = new; 4288c2ecf20Sopenharmony_ci /* select single codec */ 4298c2ecf20Sopenharmony_ci ac97->bus->ops->write(ac97, AC97_AD_SERIAL_CFG, 4308c2ecf20Sopenharmony_ci (cfg & ~0x7000) | 4318c2ecf20Sopenharmony_ci ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]); 4328c2ecf20Sopenharmony_ci /* update PCM bits */ 4338c2ecf20Sopenharmony_ci ac97->bus->ops->write(ac97, AC97_PCM, new); 4348c2ecf20Sopenharmony_ci /* select all codecs */ 4358c2ecf20Sopenharmony_ci ac97->bus->ops->write(ac97, AC97_AD_SERIAL_CFG, 4368c2ecf20Sopenharmony_ci cfg | 0x7000); 4378c2ecf20Sopenharmony_ci mutex_unlock(&ac97->reg_mutex); 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci mutex_unlock(&ac97->page_mutex); 4408c2ecf20Sopenharmony_ci return change; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci/* 4448c2ecf20Sopenharmony_ci * Controls 4458c2ecf20Sopenharmony_ci */ 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic int snd_ac97_info_enum_double(struct snd_kcontrol *kcontrol, 4488c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, e->shift_l == e->shift_r ? 1 : 2, 4538c2ecf20Sopenharmony_ci e->mask, e->texts); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic int snd_ac97_get_enum_double(struct snd_kcontrol *kcontrol, 4578c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); 4608c2ecf20Sopenharmony_ci struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value; 4618c2ecf20Sopenharmony_ci unsigned short val, bitmask; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) 4648c2ecf20Sopenharmony_ci ; 4658c2ecf20Sopenharmony_ci val = snd_ac97_read_cache(ac97, e->reg); 4668c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1); 4678c2ecf20Sopenharmony_ci if (e->shift_l != e->shift_r) 4688c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[1] = (val >> e->shift_r) & (bitmask - 1); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic int snd_ac97_put_enum_double(struct snd_kcontrol *kcontrol, 4748c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); 4778c2ecf20Sopenharmony_ci struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value; 4788c2ecf20Sopenharmony_ci unsigned short val; 4798c2ecf20Sopenharmony_ci unsigned short mask, bitmask; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) 4828c2ecf20Sopenharmony_ci ; 4838c2ecf20Sopenharmony_ci if (ucontrol->value.enumerated.item[0] > e->mask - 1) 4848c2ecf20Sopenharmony_ci return -EINVAL; 4858c2ecf20Sopenharmony_ci val = ucontrol->value.enumerated.item[0] << e->shift_l; 4868c2ecf20Sopenharmony_ci mask = (bitmask - 1) << e->shift_l; 4878c2ecf20Sopenharmony_ci if (e->shift_l != e->shift_r) { 4888c2ecf20Sopenharmony_ci if (ucontrol->value.enumerated.item[1] > e->mask - 1) 4898c2ecf20Sopenharmony_ci return -EINVAL; 4908c2ecf20Sopenharmony_ci val |= ucontrol->value.enumerated.item[1] << e->shift_r; 4918c2ecf20Sopenharmony_ci mask |= (bitmask - 1) << e->shift_r; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci return snd_ac97_update_bits(ac97, e->reg, mask, val); 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci/* save/restore ac97 v2.3 paging */ 4978c2ecf20Sopenharmony_cistatic int snd_ac97_page_save(struct snd_ac97 *ac97, int reg, struct snd_kcontrol *kcontrol) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci int page_save = -1; 5008c2ecf20Sopenharmony_ci if ((kcontrol->private_value & (1<<25)) && 5018c2ecf20Sopenharmony_ci (ac97->ext_id & AC97_EI_REV_MASK) >= AC97_EI_REV_23 && 5028c2ecf20Sopenharmony_ci (reg >= 0x60 && reg < 0x70)) { 5038c2ecf20Sopenharmony_ci unsigned short page = (kcontrol->private_value >> 26) & 0x0f; 5048c2ecf20Sopenharmony_ci mutex_lock(&ac97->page_mutex); /* lock paging */ 5058c2ecf20Sopenharmony_ci page_save = snd_ac97_read(ac97, AC97_INT_PAGING) & AC97_PAGE_MASK; 5068c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page); 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci return page_save; 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic void snd_ac97_page_restore(struct snd_ac97 *ac97, int page_save) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci if (page_save >= 0) { 5148c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page_save); 5158c2ecf20Sopenharmony_ci mutex_unlock(&ac97->page_mutex); /* unlock paging */ 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci/* volume and switch controls */ 5208c2ecf20Sopenharmony_cistatic int snd_ac97_info_volsw(struct snd_kcontrol *kcontrol, 5218c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 5248c2ecf20Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0x0f; 5258c2ecf20Sopenharmony_ci int rshift = (kcontrol->private_value >> 12) & 0x0f; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 5288c2ecf20Sopenharmony_ci uinfo->count = shift == rshift ? 1 : 2; 5298c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 5308c2ecf20Sopenharmony_ci uinfo->value.integer.max = mask; 5318c2ecf20Sopenharmony_ci return 0; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic int snd_ac97_get_volsw(struct snd_kcontrol *kcontrol, 5358c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); 5388c2ecf20Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 5398c2ecf20Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0x0f; 5408c2ecf20Sopenharmony_ci int rshift = (kcontrol->private_value >> 12) & 0x0f; 5418c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 5428c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0x01; 5438c2ecf20Sopenharmony_ci int page_save; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci page_save = snd_ac97_page_save(ac97, reg, kcontrol); 5468c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = (snd_ac97_read_cache(ac97, reg) >> shift) & mask; 5478c2ecf20Sopenharmony_ci if (shift != rshift) 5488c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = (snd_ac97_read_cache(ac97, reg) >> rshift) & mask; 5498c2ecf20Sopenharmony_ci if (invert) { 5508c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 5518c2ecf20Sopenharmony_ci if (shift != rshift) 5528c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci snd_ac97_page_restore(ac97, page_save); 5558c2ecf20Sopenharmony_ci return 0; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol, 5598c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); 5628c2ecf20Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 5638c2ecf20Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0x0f; 5648c2ecf20Sopenharmony_ci int rshift = (kcontrol->private_value >> 12) & 0x0f; 5658c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 5668c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0x01; 5678c2ecf20Sopenharmony_ci int err, page_save; 5688c2ecf20Sopenharmony_ci unsigned short val, val2, val_mask; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci page_save = snd_ac97_page_save(ac97, reg, kcontrol); 5718c2ecf20Sopenharmony_ci val = (ucontrol->value.integer.value[0] & mask); 5728c2ecf20Sopenharmony_ci if (invert) 5738c2ecf20Sopenharmony_ci val = mask - val; 5748c2ecf20Sopenharmony_ci val_mask = mask << shift; 5758c2ecf20Sopenharmony_ci val = val << shift; 5768c2ecf20Sopenharmony_ci if (shift != rshift) { 5778c2ecf20Sopenharmony_ci val2 = (ucontrol->value.integer.value[1] & mask); 5788c2ecf20Sopenharmony_ci if (invert) 5798c2ecf20Sopenharmony_ci val2 = mask - val2; 5808c2ecf20Sopenharmony_ci val_mask |= mask << rshift; 5818c2ecf20Sopenharmony_ci val |= val2 << rshift; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci err = snd_ac97_update_bits(ac97, reg, val_mask, val); 5848c2ecf20Sopenharmony_ci snd_ac97_page_restore(ac97, page_save); 5858c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE 5868c2ecf20Sopenharmony_ci /* check analog mixer power-down */ 5878c2ecf20Sopenharmony_ci if ((val_mask & AC97_PD_EAPD) && 5888c2ecf20Sopenharmony_ci (kcontrol->private_value & (1<<30))) { 5898c2ecf20Sopenharmony_ci if (val & AC97_PD_EAPD) 5908c2ecf20Sopenharmony_ci ac97->power_up &= ~(1 << (reg>>1)); 5918c2ecf20Sopenharmony_ci else 5928c2ecf20Sopenharmony_ci ac97->power_up |= 1 << (reg>>1); 5938c2ecf20Sopenharmony_ci update_power_regs(ac97); 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci#endif 5968c2ecf20Sopenharmony_ci return err; 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_tone[2] = { 6008c2ecf20Sopenharmony_ciAC97_SINGLE("Tone Control - Bass", AC97_MASTER_TONE, 8, 15, 1), 6018c2ecf20Sopenharmony_ciAC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1) 6028c2ecf20Sopenharmony_ci}; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_pc_beep[2] = { 6058c2ecf20Sopenharmony_ciAC97_SINGLE("Beep Playback Switch", AC97_PC_BEEP, 15, 1, 1), 6068c2ecf20Sopenharmony_ciAC97_SINGLE("Beep Playback Volume", AC97_PC_BEEP, 1, 15, 1) 6078c2ecf20Sopenharmony_ci}; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_mic_boost = 6108c2ecf20Sopenharmony_ci AC97_SINGLE("Mic Boost (+20dB)", AC97_MIC, 6, 1, 0); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic const char* std_rec_sel[] = {"Mic", "CD", "Video", "Aux", "Line", "Mix", "Mix Mono", "Phone"}; 6148c2ecf20Sopenharmony_cistatic const char* std_3d_path[] = {"pre 3D", "post 3D"}; 6158c2ecf20Sopenharmony_cistatic const char* std_mix[] = {"Mix", "Mic"}; 6168c2ecf20Sopenharmony_cistatic const char* std_mic[] = {"Mic1", "Mic2"}; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cistatic const struct ac97_enum std_enum[] = { 6198c2ecf20Sopenharmony_ciAC97_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 8, std_rec_sel), 6208c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, std_3d_path), 6218c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 9, 2, std_mix), 6228c2ecf20Sopenharmony_ciAC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 8, 2, std_mic), 6238c2ecf20Sopenharmony_ci}; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_control_capture_src = 6268c2ecf20Sopenharmony_ciAC97_ENUM("Capture Source", std_enum[0]); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_control_capture_vol = 6298c2ecf20Sopenharmony_ciAC97_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 15, 0); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_mic_capture[2] = { 6328c2ecf20Sopenharmony_ciAC97_SINGLE("Mic Capture Switch", AC97_REC_GAIN_MIC, 15, 1, 1), 6338c2ecf20Sopenharmony_ciAC97_SINGLE("Mic Capture Volume", AC97_REC_GAIN_MIC, 0, 15, 0) 6348c2ecf20Sopenharmony_ci}; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cienum { 6378c2ecf20Sopenharmony_ci AC97_GENERAL_PCM_OUT = 0, 6388c2ecf20Sopenharmony_ci AC97_GENERAL_STEREO_ENHANCEMENT, 6398c2ecf20Sopenharmony_ci AC97_GENERAL_3D, 6408c2ecf20Sopenharmony_ci AC97_GENERAL_LOUDNESS, 6418c2ecf20Sopenharmony_ci AC97_GENERAL_MONO, 6428c2ecf20Sopenharmony_ci AC97_GENERAL_MIC, 6438c2ecf20Sopenharmony_ci AC97_GENERAL_LOOPBACK 6448c2ecf20Sopenharmony_ci}; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_general[7] = { 6478c2ecf20Sopenharmony_ciAC97_ENUM("PCM Out Path & Mute", std_enum[1]), 6488c2ecf20Sopenharmony_ciAC97_SINGLE("Simulated Stereo Enhancement", AC97_GENERAL_PURPOSE, 14, 1, 0), 6498c2ecf20Sopenharmony_ciAC97_SINGLE("3D Control - Switch", AC97_GENERAL_PURPOSE, 13, 1, 0), 6508c2ecf20Sopenharmony_ciAC97_SINGLE("Loudness (bass boost)", AC97_GENERAL_PURPOSE, 12, 1, 0), 6518c2ecf20Sopenharmony_ciAC97_ENUM("Mono Output Select", std_enum[2]), 6528c2ecf20Sopenharmony_ciAC97_ENUM("Mic Select", std_enum[3]), 6538c2ecf20Sopenharmony_ciAC97_SINGLE("ADC/DAC Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0) 6548c2ecf20Sopenharmony_ci}; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_3d[2] = { 6578c2ecf20Sopenharmony_ciAC97_SINGLE("3D Control - Center", AC97_3D_CONTROL, 8, 15, 0), 6588c2ecf20Sopenharmony_ciAC97_SINGLE("3D Control - Depth", AC97_3D_CONTROL, 0, 15, 0) 6598c2ecf20Sopenharmony_ci}; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_center[2] = { 6628c2ecf20Sopenharmony_ciAC97_SINGLE("Center Playback Switch", AC97_CENTER_LFE_MASTER, 7, 1, 1), 6638c2ecf20Sopenharmony_ciAC97_SINGLE("Center Playback Volume", AC97_CENTER_LFE_MASTER, 0, 31, 1) 6648c2ecf20Sopenharmony_ci}; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_lfe[2] = { 6678c2ecf20Sopenharmony_ciAC97_SINGLE("LFE Playback Switch", AC97_CENTER_LFE_MASTER, 15, 1, 1), 6688c2ecf20Sopenharmony_ciAC97_SINGLE("LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 31, 1) 6698c2ecf20Sopenharmony_ci}; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_control_eapd = 6728c2ecf20Sopenharmony_ciAC97_SINGLE("External Amplifier", AC97_POWERDOWN, 15, 1, 1); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_modem_switches[2] = { 6758c2ecf20Sopenharmony_ciAC97_SINGLE("Off-hook Switch", AC97_GPIO_STATUS, 0, 1, 0), 6768c2ecf20Sopenharmony_ciAC97_SINGLE("Caller ID Switch", AC97_GPIO_STATUS, 2, 1, 0) 6778c2ecf20Sopenharmony_ci}; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci/* change the existing EAPD control as inverted */ 6808c2ecf20Sopenharmony_cistatic void set_inv_eapd(struct snd_ac97 *ac97, struct snd_kcontrol *kctl) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci kctl->private_value = AC97_SINGLE_VALUE(AC97_POWERDOWN, 15, 1, 0); 6838c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_POWERDOWN, (1<<15), (1<<15)); /* EAPD up */ 6848c2ecf20Sopenharmony_ci ac97->scaps |= AC97_SCAP_INV_EAPD; 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_cistatic int snd_ac97_spdif_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 6908c2ecf20Sopenharmony_ci uinfo->count = 1; 6918c2ecf20Sopenharmony_ci return 0; 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic int snd_ac97_spdif_cmask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | 6978c2ecf20Sopenharmony_ci IEC958_AES0_NONAUDIO | 6988c2ecf20Sopenharmony_ci IEC958_AES0_CON_EMPHASIS_5015 | 6998c2ecf20Sopenharmony_ci IEC958_AES0_CON_NOT_COPYRIGHT; 7008c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY | 7018c2ecf20Sopenharmony_ci IEC958_AES1_CON_ORIGINAL; 7028c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS; 7038c2ecf20Sopenharmony_ci return 0; 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic int snd_ac97_spdif_pmask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci /* FIXME: AC'97 spec doesn't say which bits are used for what */ 7098c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | 7108c2ecf20Sopenharmony_ci IEC958_AES0_NONAUDIO | 7118c2ecf20Sopenharmony_ci IEC958_AES0_PRO_FS | 7128c2ecf20Sopenharmony_ci IEC958_AES0_PRO_EMPHASIS_5015; 7138c2ecf20Sopenharmony_ci return 0; 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic int snd_ac97_spdif_default_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci mutex_lock(&ac97->reg_mutex); 7218c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[0] = ac97->spdif_status & 0xff; 7228c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[1] = (ac97->spdif_status >> 8) & 0xff; 7238c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[2] = (ac97->spdif_status >> 16) & 0xff; 7248c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[3] = (ac97->spdif_status >> 24) & 0xff; 7258c2ecf20Sopenharmony_ci mutex_unlock(&ac97->reg_mutex); 7268c2ecf20Sopenharmony_ci return 0; 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_cistatic int snd_ac97_spdif_default_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 7308c2ecf20Sopenharmony_ci{ 7318c2ecf20Sopenharmony_ci struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); 7328c2ecf20Sopenharmony_ci unsigned int new = 0; 7338c2ecf20Sopenharmony_ci unsigned short val = 0; 7348c2ecf20Sopenharmony_ci int change; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci new = val = ucontrol->value.iec958.status[0] & (IEC958_AES0_PROFESSIONAL|IEC958_AES0_NONAUDIO); 7378c2ecf20Sopenharmony_ci if (ucontrol->value.iec958.status[0] & IEC958_AES0_PROFESSIONAL) { 7388c2ecf20Sopenharmony_ci new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_PRO_FS|IEC958_AES0_PRO_EMPHASIS_5015); 7398c2ecf20Sopenharmony_ci switch (new & IEC958_AES0_PRO_FS) { 7408c2ecf20Sopenharmony_ci case IEC958_AES0_PRO_FS_44100: val |= 0<<12; break; 7418c2ecf20Sopenharmony_ci case IEC958_AES0_PRO_FS_48000: val |= 2<<12; break; 7428c2ecf20Sopenharmony_ci case IEC958_AES0_PRO_FS_32000: val |= 3<<12; break; 7438c2ecf20Sopenharmony_ci default: val |= 1<<12; break; 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci if ((new & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015) 7468c2ecf20Sopenharmony_ci val |= 1<<3; 7478c2ecf20Sopenharmony_ci } else { 7488c2ecf20Sopenharmony_ci new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT); 7498c2ecf20Sopenharmony_ci new |= ((ucontrol->value.iec958.status[1] & (IEC958_AES1_CON_CATEGORY|IEC958_AES1_CON_ORIGINAL)) << 8); 7508c2ecf20Sopenharmony_ci new |= ((ucontrol->value.iec958.status[3] & IEC958_AES3_CON_FS) << 24); 7518c2ecf20Sopenharmony_ci if ((new & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015) 7528c2ecf20Sopenharmony_ci val |= 1<<3; 7538c2ecf20Sopenharmony_ci if (!(new & IEC958_AES0_CON_NOT_COPYRIGHT)) 7548c2ecf20Sopenharmony_ci val |= 1<<2; 7558c2ecf20Sopenharmony_ci val |= ((new >> 8) & 0xff) << 4; // category + original 7568c2ecf20Sopenharmony_ci switch ((new >> 24) & 0xff) { 7578c2ecf20Sopenharmony_ci case IEC958_AES3_CON_FS_44100: val |= 0<<12; break; 7588c2ecf20Sopenharmony_ci case IEC958_AES3_CON_FS_48000: val |= 2<<12; break; 7598c2ecf20Sopenharmony_ci case IEC958_AES3_CON_FS_32000: val |= 3<<12; break; 7608c2ecf20Sopenharmony_ci default: val |= 1<<12; break; 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci mutex_lock(&ac97->reg_mutex); 7658c2ecf20Sopenharmony_ci change = ac97->spdif_status != new; 7668c2ecf20Sopenharmony_ci ac97->spdif_status = new; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if (ac97->flags & AC97_CS_SPDIF) { 7698c2ecf20Sopenharmony_ci int x = (val >> 12) & 0x03; 7708c2ecf20Sopenharmony_ci switch (x) { 7718c2ecf20Sopenharmony_ci case 0: x = 1; break; // 44.1 7728c2ecf20Sopenharmony_ci case 2: x = 0; break; // 48.0 7738c2ecf20Sopenharmony_ci default: x = 0; break; // illegal. 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci change |= snd_ac97_update_bits_nolock(ac97, AC97_CSR_SPDIF, 0x3fff, ((val & 0xcfff) | (x << 12))); 7768c2ecf20Sopenharmony_ci } else if (ac97->flags & AC97_CX_SPDIF) { 7778c2ecf20Sopenharmony_ci int v; 7788c2ecf20Sopenharmony_ci v = new & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT) ? 0 : AC97_CXR_COPYRGT; 7798c2ecf20Sopenharmony_ci v |= new & IEC958_AES0_NONAUDIO ? AC97_CXR_SPDIF_AC3 : AC97_CXR_SPDIF_PCM; 7808c2ecf20Sopenharmony_ci change |= snd_ac97_update_bits_nolock(ac97, AC97_CXR_AUDIO_MISC, 7818c2ecf20Sopenharmony_ci AC97_CXR_SPDIF_MASK | AC97_CXR_COPYRGT, 7828c2ecf20Sopenharmony_ci v); 7838c2ecf20Sopenharmony_ci } else if (ac97->id == AC97_ID_YMF743) { 7848c2ecf20Sopenharmony_ci change |= snd_ac97_update_bits_nolock(ac97, 7858c2ecf20Sopenharmony_ci AC97_YMF7X3_DIT_CTRL, 7868c2ecf20Sopenharmony_ci 0xff38, 7878c2ecf20Sopenharmony_ci ((val << 4) & 0xff00) | 7888c2ecf20Sopenharmony_ci ((val << 2) & 0x0038)); 7898c2ecf20Sopenharmony_ci } else { 7908c2ecf20Sopenharmony_ci unsigned short extst = snd_ac97_read_cache(ac97, AC97_EXTENDED_STATUS); 7918c2ecf20Sopenharmony_ci snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); /* turn off */ 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci change |= snd_ac97_update_bits_nolock(ac97, AC97_SPDIF, 0x3fff, val); 7948c2ecf20Sopenharmony_ci if (extst & AC97_EA_SPDIF) { 7958c2ecf20Sopenharmony_ci snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); /* turn on again */ 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci mutex_unlock(&ac97->reg_mutex); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci return change; 8018c2ecf20Sopenharmony_ci} 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_cistatic int snd_ac97_put_spsa(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 8048c2ecf20Sopenharmony_ci{ 8058c2ecf20Sopenharmony_ci struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); 8068c2ecf20Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 8078c2ecf20Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0x0f; 8088c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 8098c2ecf20Sopenharmony_ci // int invert = (kcontrol->private_value >> 24) & 0xff; 8108c2ecf20Sopenharmony_ci unsigned short value, old, new; 8118c2ecf20Sopenharmony_ci int change; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci value = (ucontrol->value.integer.value[0] & mask); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci mutex_lock(&ac97->reg_mutex); 8168c2ecf20Sopenharmony_ci mask <<= shift; 8178c2ecf20Sopenharmony_ci value <<= shift; 8188c2ecf20Sopenharmony_ci old = snd_ac97_read_cache(ac97, reg); 8198c2ecf20Sopenharmony_ci new = (old & ~mask) | value; 8208c2ecf20Sopenharmony_ci change = old != new; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if (change) { 8238c2ecf20Sopenharmony_ci unsigned short extst = snd_ac97_read_cache(ac97, AC97_EXTENDED_STATUS); 8248c2ecf20Sopenharmony_ci snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); /* turn off */ 8258c2ecf20Sopenharmony_ci change = snd_ac97_update_bits_nolock(ac97, reg, mask, value); 8268c2ecf20Sopenharmony_ci if (extst & AC97_EA_SPDIF) 8278c2ecf20Sopenharmony_ci snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); /* turn on again */ 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci mutex_unlock(&ac97->reg_mutex); 8308c2ecf20Sopenharmony_ci return change; 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_spdif[5] = { 8348c2ecf20Sopenharmony_ci { 8358c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 8368c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8378c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), 8388c2ecf20Sopenharmony_ci .info = snd_ac97_spdif_mask_info, 8398c2ecf20Sopenharmony_ci .get = snd_ac97_spdif_cmask_get, 8408c2ecf20Sopenharmony_ci }, 8418c2ecf20Sopenharmony_ci { 8428c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 8438c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8448c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), 8458c2ecf20Sopenharmony_ci .info = snd_ac97_spdif_mask_info, 8468c2ecf20Sopenharmony_ci .get = snd_ac97_spdif_pmask_get, 8478c2ecf20Sopenharmony_ci }, 8488c2ecf20Sopenharmony_ci { 8498c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8508c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), 8518c2ecf20Sopenharmony_ci .info = snd_ac97_spdif_mask_info, 8528c2ecf20Sopenharmony_ci .get = snd_ac97_spdif_default_get, 8538c2ecf20Sopenharmony_ci .put = snd_ac97_spdif_default_put, 8548c2ecf20Sopenharmony_ci }, 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),AC97_EXTENDED_STATUS, 2, 1, 0), 8578c2ecf20Sopenharmony_ci { 8588c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8598c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA", 8608c2ecf20Sopenharmony_ci .info = snd_ac97_info_volsw, 8618c2ecf20Sopenharmony_ci .get = snd_ac97_get_volsw, 8628c2ecf20Sopenharmony_ci .put = snd_ac97_put_spsa, 8638c2ecf20Sopenharmony_ci .private_value = AC97_SINGLE_VALUE(AC97_EXTENDED_STATUS, 4, 3, 0) 8648c2ecf20Sopenharmony_ci }, 8658c2ecf20Sopenharmony_ci}; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci#define AD18XX_PCM_BITS(xname, codec, lshift, rshift, mask) \ 8688c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_ad18xx_pcm_info_bits, \ 8698c2ecf20Sopenharmony_ci .get = snd_ac97_ad18xx_pcm_get_bits, .put = snd_ac97_ad18xx_pcm_put_bits, \ 8708c2ecf20Sopenharmony_ci .private_value = (codec) | ((lshift) << 8) | ((rshift) << 12) | ((mask) << 16) } 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_cistatic int snd_ac97_ad18xx_pcm_info_bits(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); 8758c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0x0f; 8768c2ecf20Sopenharmony_ci int lshift = (kcontrol->private_value >> 8) & 0x0f; 8778c2ecf20Sopenharmony_ci int rshift = (kcontrol->private_value >> 12) & 0x0f; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 8808c2ecf20Sopenharmony_ci if (lshift != rshift && (ac97->flags & AC97_STEREO_MUTES)) 8818c2ecf20Sopenharmony_ci uinfo->count = 2; 8828c2ecf20Sopenharmony_ci else 8838c2ecf20Sopenharmony_ci uinfo->count = 1; 8848c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 8858c2ecf20Sopenharmony_ci uinfo->value.integer.max = mask; 8868c2ecf20Sopenharmony_ci return 0; 8878c2ecf20Sopenharmony_ci} 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_cistatic int snd_ac97_ad18xx_pcm_get_bits(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 8908c2ecf20Sopenharmony_ci{ 8918c2ecf20Sopenharmony_ci struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); 8928c2ecf20Sopenharmony_ci int codec = kcontrol->private_value & 3; 8938c2ecf20Sopenharmony_ci int lshift = (kcontrol->private_value >> 8) & 0x0f; 8948c2ecf20Sopenharmony_ci int rshift = (kcontrol->private_value >> 12) & 0x0f; 8958c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ((ac97->spec.ad18xx.pcmreg[codec] >> lshift) & mask); 8988c2ecf20Sopenharmony_ci if (lshift != rshift && (ac97->flags & AC97_STEREO_MUTES)) 8998c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = mask - ((ac97->spec.ad18xx.pcmreg[codec] >> rshift) & mask); 9008c2ecf20Sopenharmony_ci return 0; 9018c2ecf20Sopenharmony_ci} 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_cistatic int snd_ac97_ad18xx_pcm_put_bits(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); 9068c2ecf20Sopenharmony_ci int codec = kcontrol->private_value & 3; 9078c2ecf20Sopenharmony_ci int lshift = (kcontrol->private_value >> 8) & 0x0f; 9088c2ecf20Sopenharmony_ci int rshift = (kcontrol->private_value >> 12) & 0x0f; 9098c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 9108c2ecf20Sopenharmony_ci unsigned short val, valmask; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci val = (mask - (ucontrol->value.integer.value[0] & mask)) << lshift; 9138c2ecf20Sopenharmony_ci valmask = mask << lshift; 9148c2ecf20Sopenharmony_ci if (lshift != rshift && (ac97->flags & AC97_STEREO_MUTES)) { 9158c2ecf20Sopenharmony_ci val |= (mask - (ucontrol->value.integer.value[1] & mask)) << rshift; 9168c2ecf20Sopenharmony_ci valmask |= mask << rshift; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci return snd_ac97_ad18xx_update_pcm_bits(ac97, codec, valmask, val); 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci#define AD18XX_PCM_VOLUME(xname, codec) \ 9228c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_ad18xx_pcm_info_volume, \ 9238c2ecf20Sopenharmony_ci .get = snd_ac97_ad18xx_pcm_get_volume, .put = snd_ac97_ad18xx_pcm_put_volume, \ 9248c2ecf20Sopenharmony_ci .private_value = codec } 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_cistatic int snd_ac97_ad18xx_pcm_info_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 9278c2ecf20Sopenharmony_ci{ 9288c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 9298c2ecf20Sopenharmony_ci uinfo->count = 2; 9308c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 9318c2ecf20Sopenharmony_ci uinfo->value.integer.max = 31; 9328c2ecf20Sopenharmony_ci return 0; 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_cistatic int snd_ac97_ad18xx_pcm_get_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); 9388c2ecf20Sopenharmony_ci int codec = kcontrol->private_value & 3; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci mutex_lock(&ac97->page_mutex); 9418c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 8) & 31); 9428c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 0) & 31); 9438c2ecf20Sopenharmony_ci mutex_unlock(&ac97->page_mutex); 9448c2ecf20Sopenharmony_ci return 0; 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_cistatic int snd_ac97_ad18xx_pcm_put_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 9488c2ecf20Sopenharmony_ci{ 9498c2ecf20Sopenharmony_ci struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); 9508c2ecf20Sopenharmony_ci int codec = kcontrol->private_value & 3; 9518c2ecf20Sopenharmony_ci unsigned short val1, val2; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci val1 = 31 - (ucontrol->value.integer.value[0] & 31); 9548c2ecf20Sopenharmony_ci val2 = 31 - (ucontrol->value.integer.value[1] & 31); 9558c2ecf20Sopenharmony_ci return snd_ac97_ad18xx_update_pcm_bits(ac97, codec, 0x1f1f, (val1 << 8) | val2); 9568c2ecf20Sopenharmony_ci} 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_ad18xx_pcm[2] = { 9598c2ecf20Sopenharmony_ciAD18XX_PCM_BITS("PCM Playback Switch", 0, 15, 7, 1), 9608c2ecf20Sopenharmony_ciAD18XX_PCM_VOLUME("PCM Playback Volume", 0) 9618c2ecf20Sopenharmony_ci}; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_ad18xx_surround[2] = { 9648c2ecf20Sopenharmony_ciAD18XX_PCM_BITS("Surround Playback Switch", 1, 15, 7, 1), 9658c2ecf20Sopenharmony_ciAD18XX_PCM_VOLUME("Surround Playback Volume", 1) 9668c2ecf20Sopenharmony_ci}; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_ad18xx_center[2] = { 9698c2ecf20Sopenharmony_ciAD18XX_PCM_BITS("Center Playback Switch", 2, 15, 15, 1), 9708c2ecf20Sopenharmony_ciAD18XX_PCM_BITS("Center Playback Volume", 2, 8, 8, 31) 9718c2ecf20Sopenharmony_ci}; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_controls_ad18xx_lfe[2] = { 9748c2ecf20Sopenharmony_ciAD18XX_PCM_BITS("LFE Playback Switch", 2, 7, 7, 1), 9758c2ecf20Sopenharmony_ciAD18XX_PCM_BITS("LFE Playback Volume", 2, 0, 0, 31) 9768c2ecf20Sopenharmony_ci}; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci/* 9798c2ecf20Sopenharmony_ci * 9808c2ecf20Sopenharmony_ci */ 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_cistatic void snd_ac97_powerdown(struct snd_ac97 *ac97); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_cistatic int snd_ac97_bus_free(struct snd_ac97_bus *bus) 9858c2ecf20Sopenharmony_ci{ 9868c2ecf20Sopenharmony_ci if (bus) { 9878c2ecf20Sopenharmony_ci snd_ac97_bus_proc_done(bus); 9888c2ecf20Sopenharmony_ci kfree(bus->pcms); 9898c2ecf20Sopenharmony_ci if (bus->private_free) 9908c2ecf20Sopenharmony_ci bus->private_free(bus); 9918c2ecf20Sopenharmony_ci kfree(bus); 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci return 0; 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic int snd_ac97_bus_dev_free(struct snd_device *device) 9978c2ecf20Sopenharmony_ci{ 9988c2ecf20Sopenharmony_ci struct snd_ac97_bus *bus = device->device_data; 9998c2ecf20Sopenharmony_ci return snd_ac97_bus_free(bus); 10008c2ecf20Sopenharmony_ci} 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cistatic int snd_ac97_free(struct snd_ac97 *ac97) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci if (ac97) { 10058c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE 10068c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&ac97->power_work); 10078c2ecf20Sopenharmony_ci#endif 10088c2ecf20Sopenharmony_ci snd_ac97_proc_done(ac97); 10098c2ecf20Sopenharmony_ci if (ac97->bus) 10108c2ecf20Sopenharmony_ci ac97->bus->codec[ac97->num] = NULL; 10118c2ecf20Sopenharmony_ci if (ac97->private_free) 10128c2ecf20Sopenharmony_ci ac97->private_free(ac97); 10138c2ecf20Sopenharmony_ci kfree(ac97); 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci return 0; 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_cistatic int snd_ac97_dev_free(struct snd_device *device) 10198c2ecf20Sopenharmony_ci{ 10208c2ecf20Sopenharmony_ci struct snd_ac97 *ac97 = device->device_data; 10218c2ecf20Sopenharmony_ci snd_ac97_powerdown(ac97); /* for avoiding click noises during shut down */ 10228c2ecf20Sopenharmony_ci return snd_ac97_free(ac97); 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic int snd_ac97_try_volume_mix(struct snd_ac97 * ac97, int reg) 10268c2ecf20Sopenharmony_ci{ 10278c2ecf20Sopenharmony_ci unsigned short val, mask = AC97_MUTE_MASK_MONO; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci if (! snd_ac97_valid_reg(ac97, reg)) 10308c2ecf20Sopenharmony_ci return 0; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci switch (reg) { 10338c2ecf20Sopenharmony_ci case AC97_MASTER_TONE: 10348c2ecf20Sopenharmony_ci return ac97->caps & AC97_BC_BASS_TREBLE ? 1 : 0; 10358c2ecf20Sopenharmony_ci case AC97_HEADPHONE: 10368c2ecf20Sopenharmony_ci return ac97->caps & AC97_BC_HEADPHONE ? 1 : 0; 10378c2ecf20Sopenharmony_ci case AC97_REC_GAIN_MIC: 10388c2ecf20Sopenharmony_ci return ac97->caps & AC97_BC_DEDICATED_MIC ? 1 : 0; 10398c2ecf20Sopenharmony_ci case AC97_3D_CONTROL: 10408c2ecf20Sopenharmony_ci if (ac97->caps & AC97_BC_3D_TECH_ID_MASK) { 10418c2ecf20Sopenharmony_ci val = snd_ac97_read(ac97, reg); 10428c2ecf20Sopenharmony_ci /* if nonzero - fixed and we can't set it */ 10438c2ecf20Sopenharmony_ci return val == 0; 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci return 0; 10468c2ecf20Sopenharmony_ci case AC97_CENTER_LFE_MASTER: /* center */ 10478c2ecf20Sopenharmony_ci if ((ac97->ext_id & AC97_EI_CDAC) == 0) 10488c2ecf20Sopenharmony_ci return 0; 10498c2ecf20Sopenharmony_ci break; 10508c2ecf20Sopenharmony_ci case AC97_CENTER_LFE_MASTER+1: /* lfe */ 10518c2ecf20Sopenharmony_ci if ((ac97->ext_id & AC97_EI_LDAC) == 0) 10528c2ecf20Sopenharmony_ci return 0; 10538c2ecf20Sopenharmony_ci reg = AC97_CENTER_LFE_MASTER; 10548c2ecf20Sopenharmony_ci mask = 0x0080; 10558c2ecf20Sopenharmony_ci break; 10568c2ecf20Sopenharmony_ci case AC97_SURROUND_MASTER: 10578c2ecf20Sopenharmony_ci if ((ac97->ext_id & AC97_EI_SDAC) == 0) 10588c2ecf20Sopenharmony_ci return 0; 10598c2ecf20Sopenharmony_ci break; 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci val = snd_ac97_read(ac97, reg); 10638c2ecf20Sopenharmony_ci if (!(val & mask)) { 10648c2ecf20Sopenharmony_ci /* nothing seems to be here - mute flag is not set */ 10658c2ecf20Sopenharmony_ci /* try another test */ 10668c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, reg, val | mask); 10678c2ecf20Sopenharmony_ci val = snd_ac97_read(ac97, reg); 10688c2ecf20Sopenharmony_ci val = snd_ac97_read(ac97, reg); 10698c2ecf20Sopenharmony_ci if (!(val & mask)) 10708c2ecf20Sopenharmony_ci return 0; /* nothing here */ 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci return 1; /* success, useable */ 10738c2ecf20Sopenharmony_ci} 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_cistatic void check_volume_resolution(struct snd_ac97 *ac97, int reg, unsigned char *lo_max, unsigned char *hi_max) 10768c2ecf20Sopenharmony_ci{ 10778c2ecf20Sopenharmony_ci unsigned short cbit[3] = { 0x20, 0x10, 0x01 }; 10788c2ecf20Sopenharmony_ci unsigned char max[3] = { 63, 31, 15 }; 10798c2ecf20Sopenharmony_ci int i; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci /* first look up the static resolution table */ 10828c2ecf20Sopenharmony_ci if (ac97->res_table) { 10838c2ecf20Sopenharmony_ci const struct snd_ac97_res_table *tbl; 10848c2ecf20Sopenharmony_ci for (tbl = ac97->res_table; tbl->reg; tbl++) { 10858c2ecf20Sopenharmony_ci if (tbl->reg == reg) { 10868c2ecf20Sopenharmony_ci *lo_max = tbl->bits & 0xff; 10878c2ecf20Sopenharmony_ci *hi_max = (tbl->bits >> 8) & 0xff; 10888c2ecf20Sopenharmony_ci return; 10898c2ecf20Sopenharmony_ci } 10908c2ecf20Sopenharmony_ci } 10918c2ecf20Sopenharmony_ci } 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci *lo_max = *hi_max = 0; 10948c2ecf20Sopenharmony_ci for (i = 0 ; i < ARRAY_SIZE(cbit); i++) { 10958c2ecf20Sopenharmony_ci unsigned short val; 10968c2ecf20Sopenharmony_ci snd_ac97_write( 10978c2ecf20Sopenharmony_ci ac97, reg, 10988c2ecf20Sopenharmony_ci AC97_MUTE_MASK_STEREO | cbit[i] | (cbit[i] << 8) 10998c2ecf20Sopenharmony_ci ); 11008c2ecf20Sopenharmony_ci /* Do the read twice due to buffers on some ac97 codecs. 11018c2ecf20Sopenharmony_ci * e.g. The STAC9704 returns exactly what you wrote to the register 11028c2ecf20Sopenharmony_ci * if you read it immediately. This causes the detect routine to fail. 11038c2ecf20Sopenharmony_ci */ 11048c2ecf20Sopenharmony_ci val = snd_ac97_read(ac97, reg); 11058c2ecf20Sopenharmony_ci val = snd_ac97_read(ac97, reg); 11068c2ecf20Sopenharmony_ci if (! *lo_max && (val & 0x7f) == cbit[i]) 11078c2ecf20Sopenharmony_ci *lo_max = max[i]; 11088c2ecf20Sopenharmony_ci if (! *hi_max && ((val >> 8) & 0x7f) == cbit[i]) 11098c2ecf20Sopenharmony_ci *hi_max = max[i]; 11108c2ecf20Sopenharmony_ci if (*lo_max && *hi_max) 11118c2ecf20Sopenharmony_ci break; 11128c2ecf20Sopenharmony_ci } 11138c2ecf20Sopenharmony_ci} 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_cistatic int snd_ac97_try_bit(struct snd_ac97 * ac97, int reg, int bit) 11168c2ecf20Sopenharmony_ci{ 11178c2ecf20Sopenharmony_ci unsigned short mask, val, orig, res; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci mask = 1 << bit; 11208c2ecf20Sopenharmony_ci orig = snd_ac97_read(ac97, reg); 11218c2ecf20Sopenharmony_ci val = orig ^ mask; 11228c2ecf20Sopenharmony_ci snd_ac97_write(ac97, reg, val); 11238c2ecf20Sopenharmony_ci res = snd_ac97_read(ac97, reg); 11248c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, reg, orig); 11258c2ecf20Sopenharmony_ci return res == val; 11268c2ecf20Sopenharmony_ci} 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci/* check the volume resolution of center/lfe */ 11298c2ecf20Sopenharmony_cistatic void snd_ac97_change_volume_params2(struct snd_ac97 * ac97, int reg, int shift, unsigned char *max) 11308c2ecf20Sopenharmony_ci{ 11318c2ecf20Sopenharmony_ci unsigned short val, val1; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci *max = 63; 11348c2ecf20Sopenharmony_ci val = AC97_MUTE_MASK_STEREO | (0x20 << shift); 11358c2ecf20Sopenharmony_ci snd_ac97_write(ac97, reg, val); 11368c2ecf20Sopenharmony_ci val1 = snd_ac97_read(ac97, reg); 11378c2ecf20Sopenharmony_ci if (val != val1) { 11388c2ecf20Sopenharmony_ci *max = 31; 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci /* reset volume to zero */ 11418c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, reg, AC97_MUTE_MASK_STEREO); 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_cistatic inline int printable(unsigned int x) 11458c2ecf20Sopenharmony_ci{ 11468c2ecf20Sopenharmony_ci x &= 0xff; 11478c2ecf20Sopenharmony_ci if (x < ' ' || x >= 0x71) { 11488c2ecf20Sopenharmony_ci if (x <= 0x89) 11498c2ecf20Sopenharmony_ci return x - 0x71 + 'A'; 11508c2ecf20Sopenharmony_ci return '?'; 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci return x; 11538c2ecf20Sopenharmony_ci} 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_cistatic struct snd_kcontrol *snd_ac97_cnew(const struct snd_kcontrol_new *_template, 11568c2ecf20Sopenharmony_ci struct snd_ac97 * ac97) 11578c2ecf20Sopenharmony_ci{ 11588c2ecf20Sopenharmony_ci struct snd_kcontrol_new template; 11598c2ecf20Sopenharmony_ci memcpy(&template, _template, sizeof(template)); 11608c2ecf20Sopenharmony_ci template.index = ac97->num; 11618c2ecf20Sopenharmony_ci return snd_ctl_new1(&template, ac97); 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci/* 11658c2ecf20Sopenharmony_ci * create mute switch(es) for normal stereo controls 11668c2ecf20Sopenharmony_ci */ 11678c2ecf20Sopenharmony_cistatic int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg, 11688c2ecf20Sopenharmony_ci int check_stereo, int check_amix, 11698c2ecf20Sopenharmony_ci struct snd_ac97 *ac97) 11708c2ecf20Sopenharmony_ci{ 11718c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 11728c2ecf20Sopenharmony_ci int err; 11738c2ecf20Sopenharmony_ci unsigned short val, val1, mute_mask; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci if (! snd_ac97_valid_reg(ac97, reg)) 11768c2ecf20Sopenharmony_ci return 0; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci mute_mask = AC97_MUTE_MASK_MONO; 11798c2ecf20Sopenharmony_ci val = snd_ac97_read(ac97, reg); 11808c2ecf20Sopenharmony_ci if (check_stereo || (ac97->flags & AC97_STEREO_MUTES)) { 11818c2ecf20Sopenharmony_ci /* check whether both mute bits work */ 11828c2ecf20Sopenharmony_ci val1 = val | AC97_MUTE_MASK_STEREO; 11838c2ecf20Sopenharmony_ci snd_ac97_write(ac97, reg, val1); 11848c2ecf20Sopenharmony_ci if (val1 == snd_ac97_read(ac97, reg)) 11858c2ecf20Sopenharmony_ci mute_mask = AC97_MUTE_MASK_STEREO; 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci if (mute_mask == AC97_MUTE_MASK_STEREO) { 11888c2ecf20Sopenharmony_ci struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 15, 7, 1, 1); 11898c2ecf20Sopenharmony_ci if (check_amix) 11908c2ecf20Sopenharmony_ci tmp.private_value |= (1 << 30); 11918c2ecf20Sopenharmony_ci tmp.index = ac97->num; 11928c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(&tmp, ac97); 11938c2ecf20Sopenharmony_ci } else { 11948c2ecf20Sopenharmony_ci struct snd_kcontrol_new tmp = AC97_SINGLE(name, reg, 15, 1, 1); 11958c2ecf20Sopenharmony_ci if (check_amix) 11968c2ecf20Sopenharmony_ci tmp.private_value |= (1 << 30); 11978c2ecf20Sopenharmony_ci tmp.index = ac97->num; 11988c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(&tmp, ac97); 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci err = snd_ctl_add(card, kctl); 12018c2ecf20Sopenharmony_ci if (err < 0) 12028c2ecf20Sopenharmony_ci return err; 12038c2ecf20Sopenharmony_ci /* mute as default */ 12048c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, reg, val | mute_mask); 12058c2ecf20Sopenharmony_ci return 0; 12068c2ecf20Sopenharmony_ci} 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci/* 12098c2ecf20Sopenharmony_ci * set dB information 12108c2ecf20Sopenharmony_ci */ 12118c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0); 12128c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0); 12138c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0); 12148c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0); 12158c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0); 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_cistatic const unsigned int *find_db_scale(unsigned int maxval) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci switch (maxval) { 12208c2ecf20Sopenharmony_ci case 0x0f: return db_scale_4bit; 12218c2ecf20Sopenharmony_ci case 0x1f: return db_scale_5bit; 12228c2ecf20Sopenharmony_ci case 0x3f: return db_scale_6bit; 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci return NULL; 12258c2ecf20Sopenharmony_ci} 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_cistatic void set_tlv_db_scale(struct snd_kcontrol *kctl, const unsigned int *tlv) 12288c2ecf20Sopenharmony_ci{ 12298c2ecf20Sopenharmony_ci kctl->tlv.p = tlv; 12308c2ecf20Sopenharmony_ci if (tlv) 12318c2ecf20Sopenharmony_ci kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; 12328c2ecf20Sopenharmony_ci} 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci/* 12358c2ecf20Sopenharmony_ci * create a volume for normal stereo/mono controls 12368c2ecf20Sopenharmony_ci */ 12378c2ecf20Sopenharmony_cistatic int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigned int lo_max, 12388c2ecf20Sopenharmony_ci unsigned int hi_max, struct snd_ac97 *ac97) 12398c2ecf20Sopenharmony_ci{ 12408c2ecf20Sopenharmony_ci int err; 12418c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci if (! snd_ac97_valid_reg(ac97, reg)) 12448c2ecf20Sopenharmony_ci return 0; 12458c2ecf20Sopenharmony_ci if (hi_max) { 12468c2ecf20Sopenharmony_ci /* invert */ 12478c2ecf20Sopenharmony_ci struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 8, 0, lo_max, 1); 12488c2ecf20Sopenharmony_ci tmp.index = ac97->num; 12498c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(&tmp, ac97); 12508c2ecf20Sopenharmony_ci } else { 12518c2ecf20Sopenharmony_ci /* invert */ 12528c2ecf20Sopenharmony_ci struct snd_kcontrol_new tmp = AC97_SINGLE(name, reg, 0, lo_max, 1); 12538c2ecf20Sopenharmony_ci tmp.index = ac97->num; 12548c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(&tmp, ac97); 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci if (!kctl) 12578c2ecf20Sopenharmony_ci return -ENOMEM; 12588c2ecf20Sopenharmony_ci if (reg >= AC97_PHONE && reg <= AC97_PCM) 12598c2ecf20Sopenharmony_ci set_tlv_db_scale(kctl, db_scale_5bit_12db_max); 12608c2ecf20Sopenharmony_ci else 12618c2ecf20Sopenharmony_ci set_tlv_db_scale(kctl, find_db_scale(lo_max)); 12628c2ecf20Sopenharmony_ci err = snd_ctl_add(card, kctl); 12638c2ecf20Sopenharmony_ci if (err < 0) 12648c2ecf20Sopenharmony_ci return err; 12658c2ecf20Sopenharmony_ci snd_ac97_write_cache( 12668c2ecf20Sopenharmony_ci ac97, reg, 12678c2ecf20Sopenharmony_ci (snd_ac97_read(ac97, reg) & AC97_MUTE_MASK_STEREO) 12688c2ecf20Sopenharmony_ci | lo_max | (hi_max << 8) 12698c2ecf20Sopenharmony_ci ); 12708c2ecf20Sopenharmony_ci return 0; 12718c2ecf20Sopenharmony_ci} 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci/* 12748c2ecf20Sopenharmony_ci * create a mute-switch and a volume for normal stereo/mono controls 12758c2ecf20Sopenharmony_ci */ 12768c2ecf20Sopenharmony_cistatic int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx, 12778c2ecf20Sopenharmony_ci int reg, int check_stereo, int check_amix, 12788c2ecf20Sopenharmony_ci struct snd_ac97 *ac97) 12798c2ecf20Sopenharmony_ci{ 12808c2ecf20Sopenharmony_ci int err; 12818c2ecf20Sopenharmony_ci char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 12828c2ecf20Sopenharmony_ci unsigned char lo_max, hi_max; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci if (! snd_ac97_valid_reg(ac97, reg)) 12858c2ecf20Sopenharmony_ci return 0; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci if (snd_ac97_try_bit(ac97, reg, 15)) { 12888c2ecf20Sopenharmony_ci sprintf(name, "%s Switch", pfx); 12898c2ecf20Sopenharmony_ci if ((err = snd_ac97_cmute_new_stereo(card, name, reg, 12908c2ecf20Sopenharmony_ci check_stereo, check_amix, 12918c2ecf20Sopenharmony_ci ac97)) < 0) 12928c2ecf20Sopenharmony_ci return err; 12938c2ecf20Sopenharmony_ci } 12948c2ecf20Sopenharmony_ci check_volume_resolution(ac97, reg, &lo_max, &hi_max); 12958c2ecf20Sopenharmony_ci if (lo_max) { 12968c2ecf20Sopenharmony_ci sprintf(name, "%s Volume", pfx); 12978c2ecf20Sopenharmony_ci if ((err = snd_ac97_cvol_new(card, name, reg, lo_max, hi_max, ac97)) < 0) 12988c2ecf20Sopenharmony_ci return err; 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci return 0; 13018c2ecf20Sopenharmony_ci} 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci#define snd_ac97_cmix_new(card, pfx, reg, acheck, ac97) \ 13048c2ecf20Sopenharmony_ci snd_ac97_cmix_new_stereo(card, pfx, reg, 0, acheck, ac97) 13058c2ecf20Sopenharmony_ci#define snd_ac97_cmute_new(card, name, reg, acheck, ac97) \ 13068c2ecf20Sopenharmony_ci snd_ac97_cmute_new_stereo(card, name, reg, 0, acheck, ac97) 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_cistatic unsigned int snd_ac97_determine_spdif_rates(struct snd_ac97 *ac97); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_cistatic int snd_ac97_mixer_build(struct snd_ac97 * ac97) 13118c2ecf20Sopenharmony_ci{ 13128c2ecf20Sopenharmony_ci struct snd_card *card = ac97->bus->card; 13138c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 13148c2ecf20Sopenharmony_ci int err; 13158c2ecf20Sopenharmony_ci unsigned int idx; 13168c2ecf20Sopenharmony_ci unsigned char max; 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci /* build master controls */ 13198c2ecf20Sopenharmony_ci /* AD claims to remove this control from AD1887, although spec v2.2 does not allow this */ 13208c2ecf20Sopenharmony_ci if (snd_ac97_try_volume_mix(ac97, AC97_MASTER)) { 13218c2ecf20Sopenharmony_ci if (ac97->flags & AC97_HAS_NO_MASTER_VOL) 13228c2ecf20Sopenharmony_ci err = snd_ac97_cmute_new(card, "Master Playback Switch", 13238c2ecf20Sopenharmony_ci AC97_MASTER, 0, ac97); 13248c2ecf20Sopenharmony_ci else 13258c2ecf20Sopenharmony_ci err = snd_ac97_cmix_new(card, "Master Playback", 13268c2ecf20Sopenharmony_ci AC97_MASTER, 0, ac97); 13278c2ecf20Sopenharmony_ci if (err < 0) 13288c2ecf20Sopenharmony_ci return err; 13298c2ecf20Sopenharmony_ci } 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci ac97->regs[AC97_CENTER_LFE_MASTER] = AC97_MUTE_MASK_STEREO; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci /* build center controls */ 13348c2ecf20Sopenharmony_ci if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER)) 13358c2ecf20Sopenharmony_ci && !(ac97->flags & AC97_AD_MULTI)) { 13368c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_center[0], ac97))) < 0) 13378c2ecf20Sopenharmony_ci return err; 13388c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_center[1], ac97))) < 0) 13398c2ecf20Sopenharmony_ci return err; 13408c2ecf20Sopenharmony_ci snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 0, &max); 13418c2ecf20Sopenharmony_ci kctl->private_value &= ~(0xff << 16); 13428c2ecf20Sopenharmony_ci kctl->private_value |= (int)max << 16; 13438c2ecf20Sopenharmony_ci set_tlv_db_scale(kctl, find_db_scale(max)); 13448c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max); 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci /* build LFE controls */ 13488c2ecf20Sopenharmony_ci if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER+1)) 13498c2ecf20Sopenharmony_ci && !(ac97->flags & AC97_AD_MULTI)) { 13508c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_lfe[0], ac97))) < 0) 13518c2ecf20Sopenharmony_ci return err; 13528c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_lfe[1], ac97))) < 0) 13538c2ecf20Sopenharmony_ci return err; 13548c2ecf20Sopenharmony_ci snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 8, &max); 13558c2ecf20Sopenharmony_ci kctl->private_value &= ~(0xff << 16); 13568c2ecf20Sopenharmony_ci kctl->private_value |= (int)max << 16; 13578c2ecf20Sopenharmony_ci set_tlv_db_scale(kctl, find_db_scale(max)); 13588c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max << 8); 13598c2ecf20Sopenharmony_ci } 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci /* build surround controls */ 13628c2ecf20Sopenharmony_ci if ((snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER)) 13638c2ecf20Sopenharmony_ci && !(ac97->flags & AC97_AD_MULTI)) { 13648c2ecf20Sopenharmony_ci /* Surround Master (0x38) is with stereo mutes */ 13658c2ecf20Sopenharmony_ci if ((err = snd_ac97_cmix_new_stereo(card, "Surround Playback", 13668c2ecf20Sopenharmony_ci AC97_SURROUND_MASTER, 1, 0, 13678c2ecf20Sopenharmony_ci ac97)) < 0) 13688c2ecf20Sopenharmony_ci return err; 13698c2ecf20Sopenharmony_ci } 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci /* build headphone controls */ 13728c2ecf20Sopenharmony_ci if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE)) { 13738c2ecf20Sopenharmony_ci if ((err = snd_ac97_cmix_new(card, "Headphone Playback", 13748c2ecf20Sopenharmony_ci AC97_HEADPHONE, 0, ac97)) < 0) 13758c2ecf20Sopenharmony_ci return err; 13768c2ecf20Sopenharmony_ci } 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci /* build master mono controls */ 13798c2ecf20Sopenharmony_ci if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_MONO)) { 13808c2ecf20Sopenharmony_ci if ((err = snd_ac97_cmix_new(card, "Master Mono Playback", 13818c2ecf20Sopenharmony_ci AC97_MASTER_MONO, 0, ac97)) < 0) 13828c2ecf20Sopenharmony_ci return err; 13838c2ecf20Sopenharmony_ci } 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci /* build master tone controls */ 13868c2ecf20Sopenharmony_ci if (!(ac97->flags & AC97_HAS_NO_TONE)) { 13878c2ecf20Sopenharmony_ci if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_TONE)) { 13888c2ecf20Sopenharmony_ci for (idx = 0; idx < 2; idx++) { 13898c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_tone[idx], ac97))) < 0) 13908c2ecf20Sopenharmony_ci return err; 13918c2ecf20Sopenharmony_ci if (ac97->id == AC97_ID_YMF743 || 13928c2ecf20Sopenharmony_ci ac97->id == AC97_ID_YMF753) { 13938c2ecf20Sopenharmony_ci kctl->private_value &= ~(0xff << 16); 13948c2ecf20Sopenharmony_ci kctl->private_value |= 7 << 16; 13958c2ecf20Sopenharmony_ci } 13968c2ecf20Sopenharmony_ci } 13978c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_MASTER_TONE, 0x0f0f); 13988c2ecf20Sopenharmony_ci } 13998c2ecf20Sopenharmony_ci } 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci /* build Beep controls */ 14028c2ecf20Sopenharmony_ci if (!(ac97->flags & AC97_HAS_NO_PC_BEEP) && 14038c2ecf20Sopenharmony_ci ((ac97->flags & AC97_HAS_PC_BEEP) || 14048c2ecf20Sopenharmony_ci snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) { 14058c2ecf20Sopenharmony_ci for (idx = 0; idx < 2; idx++) 14068c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0) 14078c2ecf20Sopenharmony_ci return err; 14088c2ecf20Sopenharmony_ci set_tlv_db_scale(kctl, db_scale_4bit); 14098c2ecf20Sopenharmony_ci snd_ac97_write_cache( 14108c2ecf20Sopenharmony_ci ac97, 14118c2ecf20Sopenharmony_ci AC97_PC_BEEP, 14128c2ecf20Sopenharmony_ci (snd_ac97_read(ac97, AC97_PC_BEEP) 14138c2ecf20Sopenharmony_ci | AC97_MUTE_MASK_MONO | 0x001e) 14148c2ecf20Sopenharmony_ci ); 14158c2ecf20Sopenharmony_ci } 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci /* build Phone controls */ 14188c2ecf20Sopenharmony_ci if (!(ac97->flags & AC97_HAS_NO_PHONE)) { 14198c2ecf20Sopenharmony_ci if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) { 14208c2ecf20Sopenharmony_ci if ((err = snd_ac97_cmix_new(card, "Phone Playback", 14218c2ecf20Sopenharmony_ci AC97_PHONE, 1, ac97)) < 0) 14228c2ecf20Sopenharmony_ci return err; 14238c2ecf20Sopenharmony_ci } 14248c2ecf20Sopenharmony_ci } 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci /* build MIC controls */ 14278c2ecf20Sopenharmony_ci if (!(ac97->flags & AC97_HAS_NO_MIC)) { 14288c2ecf20Sopenharmony_ci if (snd_ac97_try_volume_mix(ac97, AC97_MIC)) { 14298c2ecf20Sopenharmony_ci if ((err = snd_ac97_cmix_new(card, "Mic Playback", 14308c2ecf20Sopenharmony_ci AC97_MIC, 1, ac97)) < 0) 14318c2ecf20Sopenharmony_ci return err; 14328c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_boost, ac97))) < 0) 14338c2ecf20Sopenharmony_ci return err; 14348c2ecf20Sopenharmony_ci } 14358c2ecf20Sopenharmony_ci } 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci /* build Line controls */ 14388c2ecf20Sopenharmony_ci if (snd_ac97_try_volume_mix(ac97, AC97_LINE)) { 14398c2ecf20Sopenharmony_ci if ((err = snd_ac97_cmix_new(card, "Line Playback", 14408c2ecf20Sopenharmony_ci AC97_LINE, 1, ac97)) < 0) 14418c2ecf20Sopenharmony_ci return err; 14428c2ecf20Sopenharmony_ci } 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci /* build CD controls */ 14458c2ecf20Sopenharmony_ci if (!(ac97->flags & AC97_HAS_NO_CD)) { 14468c2ecf20Sopenharmony_ci if (snd_ac97_try_volume_mix(ac97, AC97_CD)) { 14478c2ecf20Sopenharmony_ci if ((err = snd_ac97_cmix_new(card, "CD Playback", 14488c2ecf20Sopenharmony_ci AC97_CD, 1, ac97)) < 0) 14498c2ecf20Sopenharmony_ci return err; 14508c2ecf20Sopenharmony_ci } 14518c2ecf20Sopenharmony_ci } 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci /* build Video controls */ 14548c2ecf20Sopenharmony_ci if (!(ac97->flags & AC97_HAS_NO_VIDEO)) { 14558c2ecf20Sopenharmony_ci if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) { 14568c2ecf20Sopenharmony_ci if ((err = snd_ac97_cmix_new(card, "Video Playback", 14578c2ecf20Sopenharmony_ci AC97_VIDEO, 1, ac97)) < 0) 14588c2ecf20Sopenharmony_ci return err; 14598c2ecf20Sopenharmony_ci } 14608c2ecf20Sopenharmony_ci } 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci /* build Aux controls */ 14638c2ecf20Sopenharmony_ci if (!(ac97->flags & AC97_HAS_NO_AUX)) { 14648c2ecf20Sopenharmony_ci if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) { 14658c2ecf20Sopenharmony_ci if ((err = snd_ac97_cmix_new(card, "Aux Playback", 14668c2ecf20Sopenharmony_ci AC97_AUX, 1, ac97)) < 0) 14678c2ecf20Sopenharmony_ci return err; 14688c2ecf20Sopenharmony_ci } 14698c2ecf20Sopenharmony_ci } 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci /* build PCM controls */ 14728c2ecf20Sopenharmony_ci if (ac97->flags & AC97_AD_MULTI) { 14738c2ecf20Sopenharmony_ci unsigned short init_val; 14748c2ecf20Sopenharmony_ci if (ac97->flags & AC97_STEREO_MUTES) 14758c2ecf20Sopenharmony_ci init_val = 0x9f9f; 14768c2ecf20Sopenharmony_ci else 14778c2ecf20Sopenharmony_ci init_val = 0x9f1f; 14788c2ecf20Sopenharmony_ci for (idx = 0; idx < 2; idx++) 14798c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97))) < 0) 14808c2ecf20Sopenharmony_ci return err; 14818c2ecf20Sopenharmony_ci set_tlv_db_scale(kctl, db_scale_5bit); 14828c2ecf20Sopenharmony_ci ac97->spec.ad18xx.pcmreg[0] = init_val; 14838c2ecf20Sopenharmony_ci if (ac97->scaps & AC97_SCAP_SURROUND_DAC) { 14848c2ecf20Sopenharmony_ci for (idx = 0; idx < 2; idx++) 14858c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97))) < 0) 14868c2ecf20Sopenharmony_ci return err; 14878c2ecf20Sopenharmony_ci set_tlv_db_scale(kctl, db_scale_5bit); 14888c2ecf20Sopenharmony_ci ac97->spec.ad18xx.pcmreg[1] = init_val; 14898c2ecf20Sopenharmony_ci } 14908c2ecf20Sopenharmony_ci if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) { 14918c2ecf20Sopenharmony_ci for (idx = 0; idx < 2; idx++) 14928c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97))) < 0) 14938c2ecf20Sopenharmony_ci return err; 14948c2ecf20Sopenharmony_ci set_tlv_db_scale(kctl, db_scale_5bit); 14958c2ecf20Sopenharmony_ci for (idx = 0; idx < 2; idx++) 14968c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[idx], ac97))) < 0) 14978c2ecf20Sopenharmony_ci return err; 14988c2ecf20Sopenharmony_ci set_tlv_db_scale(kctl, db_scale_5bit); 14998c2ecf20Sopenharmony_ci ac97->spec.ad18xx.pcmreg[2] = init_val; 15008c2ecf20Sopenharmony_ci } 15018c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_PCM, init_val); 15028c2ecf20Sopenharmony_ci } else { 15038c2ecf20Sopenharmony_ci if (!(ac97->flags & AC97_HAS_NO_STD_PCM)) { 15048c2ecf20Sopenharmony_ci if (ac97->flags & AC97_HAS_NO_PCM_VOL) 15058c2ecf20Sopenharmony_ci err = snd_ac97_cmute_new(card, 15068c2ecf20Sopenharmony_ci "PCM Playback Switch", 15078c2ecf20Sopenharmony_ci AC97_PCM, 0, ac97); 15088c2ecf20Sopenharmony_ci else 15098c2ecf20Sopenharmony_ci err = snd_ac97_cmix_new(card, "PCM Playback", 15108c2ecf20Sopenharmony_ci AC97_PCM, 0, ac97); 15118c2ecf20Sopenharmony_ci if (err < 0) 15128c2ecf20Sopenharmony_ci return err; 15138c2ecf20Sopenharmony_ci } 15148c2ecf20Sopenharmony_ci } 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci /* build Capture controls */ 15178c2ecf20Sopenharmony_ci if (!(ac97->flags & AC97_HAS_NO_REC_GAIN)) { 15188c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97))) < 0) 15198c2ecf20Sopenharmony_ci return err; 15208c2ecf20Sopenharmony_ci if (snd_ac97_try_bit(ac97, AC97_REC_GAIN, 15)) { 15218c2ecf20Sopenharmony_ci err = snd_ac97_cmute_new(card, "Capture Switch", 15228c2ecf20Sopenharmony_ci AC97_REC_GAIN, 0, ac97); 15238c2ecf20Sopenharmony_ci if (err < 0) 15248c2ecf20Sopenharmony_ci return err; 15258c2ecf20Sopenharmony_ci } 15268c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97))) < 0) 15278c2ecf20Sopenharmony_ci return err; 15288c2ecf20Sopenharmony_ci set_tlv_db_scale(kctl, db_scale_rec_gain); 15298c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000); 15308c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x0000); 15318c2ecf20Sopenharmony_ci } 15328c2ecf20Sopenharmony_ci /* build MIC Capture controls */ 15338c2ecf20Sopenharmony_ci if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) { 15348c2ecf20Sopenharmony_ci for (idx = 0; idx < 2; idx++) 15358c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97))) < 0) 15368c2ecf20Sopenharmony_ci return err; 15378c2ecf20Sopenharmony_ci set_tlv_db_scale(kctl, db_scale_rec_gain); 15388c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_REC_GAIN_MIC, 0x0000); 15398c2ecf20Sopenharmony_ci } 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci /* build PCM out path & mute control */ 15428c2ecf20Sopenharmony_ci if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 15)) { 15438c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_PCM_OUT], ac97))) < 0) 15448c2ecf20Sopenharmony_ci return err; 15458c2ecf20Sopenharmony_ci } 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci /* build Simulated Stereo Enhancement control */ 15488c2ecf20Sopenharmony_ci if (ac97->caps & AC97_BC_SIM_STEREO) { 15498c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_STEREO_ENHANCEMENT], ac97))) < 0) 15508c2ecf20Sopenharmony_ci return err; 15518c2ecf20Sopenharmony_ci } 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci /* build 3D Stereo Enhancement control */ 15548c2ecf20Sopenharmony_ci if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 13)) { 15558c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_3D], ac97))) < 0) 15568c2ecf20Sopenharmony_ci return err; 15578c2ecf20Sopenharmony_ci } 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci /* build Loudness control */ 15608c2ecf20Sopenharmony_ci if (ac97->caps & AC97_BC_LOUDNESS) { 15618c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOUDNESS], ac97))) < 0) 15628c2ecf20Sopenharmony_ci return err; 15638c2ecf20Sopenharmony_ci } 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci /* build Mono output select control */ 15668c2ecf20Sopenharmony_ci if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 9)) { 15678c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MONO], ac97))) < 0) 15688c2ecf20Sopenharmony_ci return err; 15698c2ecf20Sopenharmony_ci } 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci /* build Mic select control */ 15728c2ecf20Sopenharmony_ci if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 8)) { 15738c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MIC], ac97))) < 0) 15748c2ecf20Sopenharmony_ci return err; 15758c2ecf20Sopenharmony_ci } 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci /* build ADC/DAC loopback control */ 15788c2ecf20Sopenharmony_ci if (enable_loopback && snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 7)) { 15798c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOOPBACK], ac97))) < 0) 15808c2ecf20Sopenharmony_ci return err; 15818c2ecf20Sopenharmony_ci } 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_GENERAL_PURPOSE, ~AC97_GP_DRSS_MASK, 0x0000); 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci /* build 3D controls */ 15868c2ecf20Sopenharmony_ci if (ac97->build_ops->build_3d) { 15878c2ecf20Sopenharmony_ci ac97->build_ops->build_3d(ac97); 15888c2ecf20Sopenharmony_ci } else { 15898c2ecf20Sopenharmony_ci if (snd_ac97_try_volume_mix(ac97, AC97_3D_CONTROL)) { 15908c2ecf20Sopenharmony_ci unsigned short val; 15918c2ecf20Sopenharmony_ci val = 0x0707; 15928c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_3D_CONTROL, val); 15938c2ecf20Sopenharmony_ci val = snd_ac97_read(ac97, AC97_3D_CONTROL); 15948c2ecf20Sopenharmony_ci val = val == 0x0606; 15958c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) 15968c2ecf20Sopenharmony_ci return err; 15978c2ecf20Sopenharmony_ci if (val) 15988c2ecf20Sopenharmony_ci kctl->private_value = AC97_3D_CONTROL | (9 << 8) | (7 << 16); 15998c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[1], ac97))) < 0) 16008c2ecf20Sopenharmony_ci return err; 16018c2ecf20Sopenharmony_ci if (val) 16028c2ecf20Sopenharmony_ci kctl->private_value = AC97_3D_CONTROL | (1 << 8) | (7 << 16); 16038c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); 16048c2ecf20Sopenharmony_ci } 16058c2ecf20Sopenharmony_ci } 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci /* build S/PDIF controls */ 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci /* Hack for ASUS P5P800-VM, which does not indicate S/PDIF capability */ 16108c2ecf20Sopenharmony_ci if (ac97->subsystem_vendor == 0x1043 && 16118c2ecf20Sopenharmony_ci ac97->subsystem_device == 0x810f) 16128c2ecf20Sopenharmony_ci ac97->ext_id |= AC97_EI_SPDIF; 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci if ((ac97->ext_id & AC97_EI_SPDIF) && !(ac97->scaps & AC97_SCAP_NO_SPDIF)) { 16158c2ecf20Sopenharmony_ci if (ac97->build_ops->build_spdif) { 16168c2ecf20Sopenharmony_ci if ((err = ac97->build_ops->build_spdif(ac97)) < 0) 16178c2ecf20Sopenharmony_ci return err; 16188c2ecf20Sopenharmony_ci } else { 16198c2ecf20Sopenharmony_ci for (idx = 0; idx < 5; idx++) 16208c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97))) < 0) 16218c2ecf20Sopenharmony_ci return err; 16228c2ecf20Sopenharmony_ci if (ac97->build_ops->build_post_spdif) { 16238c2ecf20Sopenharmony_ci if ((err = ac97->build_ops->build_post_spdif(ac97)) < 0) 16248c2ecf20Sopenharmony_ci return err; 16258c2ecf20Sopenharmony_ci } 16268c2ecf20Sopenharmony_ci /* set default PCM S/PDIF params */ 16278c2ecf20Sopenharmony_ci /* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */ 16288c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_SPDIF, 0x2a20); 16298c2ecf20Sopenharmony_ci ac97->rates[AC97_RATES_SPDIF] = snd_ac97_determine_spdif_rates(ac97); 16308c2ecf20Sopenharmony_ci } 16318c2ecf20Sopenharmony_ci ac97->spdif_status = SNDRV_PCM_DEFAULT_CON_SPDIF; 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci /* build chip specific controls */ 16358c2ecf20Sopenharmony_ci if (ac97->build_ops->build_specific) 16368c2ecf20Sopenharmony_ci if ((err = ac97->build_ops->build_specific(ac97)) < 0) 16378c2ecf20Sopenharmony_ci return err; 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci if (snd_ac97_try_bit(ac97, AC97_POWERDOWN, 15)) { 16408c2ecf20Sopenharmony_ci kctl = snd_ac97_cnew(&snd_ac97_control_eapd, ac97); 16418c2ecf20Sopenharmony_ci if (! kctl) 16428c2ecf20Sopenharmony_ci return -ENOMEM; 16438c2ecf20Sopenharmony_ci if (ac97->scaps & AC97_SCAP_INV_EAPD) 16448c2ecf20Sopenharmony_ci set_inv_eapd(ac97, kctl); 16458c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl)) < 0) 16468c2ecf20Sopenharmony_ci return err; 16478c2ecf20Sopenharmony_ci } 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci return 0; 16508c2ecf20Sopenharmony_ci} 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_cistatic int snd_ac97_modem_build(struct snd_card *card, struct snd_ac97 * ac97) 16538c2ecf20Sopenharmony_ci{ 16548c2ecf20Sopenharmony_ci int err, idx; 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci /* 16578c2ecf20Sopenharmony_ci ac97_dbg(ac97, "AC97_GPIO_CFG = %x\n", 16588c2ecf20Sopenharmony_ci snd_ac97_read(ac97,AC97_GPIO_CFG)); 16598c2ecf20Sopenharmony_ci */ 16608c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_GPIO_CFG, 0xffff & ~(AC97_GPIO_LINE1_OH)); 16618c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_GPIO_POLARITY, 0xffff & ~(AC97_GPIO_LINE1_OH)); 16628c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_GPIO_STICKY, 0xffff); 16638c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_GPIO_WAKEUP, 0x0); 16648c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_MISC_AFE, 0x0); 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci /* build modem switches */ 16678c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_modem_switches); idx++) 16688c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ac97_controls_modem_switches[idx], ac97))) < 0) 16698c2ecf20Sopenharmony_ci return err; 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci /* build chip specific controls */ 16728c2ecf20Sopenharmony_ci if (ac97->build_ops->build_specific) 16738c2ecf20Sopenharmony_ci if ((err = ac97->build_ops->build_specific(ac97)) < 0) 16748c2ecf20Sopenharmony_ci return err; 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci return 0; 16778c2ecf20Sopenharmony_ci} 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_cistatic int snd_ac97_test_rate(struct snd_ac97 *ac97, int reg, int shadow_reg, int rate) 16808c2ecf20Sopenharmony_ci{ 16818c2ecf20Sopenharmony_ci unsigned short val; 16828c2ecf20Sopenharmony_ci unsigned int tmp; 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci tmp = ((unsigned int)rate * ac97->bus->clock) / 48000; 16858c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, reg, tmp & 0xffff); 16868c2ecf20Sopenharmony_ci if (shadow_reg) 16878c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, shadow_reg, tmp & 0xffff); 16888c2ecf20Sopenharmony_ci val = snd_ac97_read(ac97, reg); 16898c2ecf20Sopenharmony_ci return val == (tmp & 0xffff); 16908c2ecf20Sopenharmony_ci} 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_cistatic void snd_ac97_determine_rates(struct snd_ac97 *ac97, int reg, int shadow_reg, unsigned int *r_result) 16938c2ecf20Sopenharmony_ci{ 16948c2ecf20Sopenharmony_ci unsigned int result = 0; 16958c2ecf20Sopenharmony_ci unsigned short saved; 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci if (ac97->bus->no_vra) { 16988c2ecf20Sopenharmony_ci *r_result = SNDRV_PCM_RATE_48000; 16998c2ecf20Sopenharmony_ci if ((ac97->flags & AC97_DOUBLE_RATE) && 17008c2ecf20Sopenharmony_ci reg == AC97_PCM_FRONT_DAC_RATE) 17018c2ecf20Sopenharmony_ci *r_result |= SNDRV_PCM_RATE_96000; 17028c2ecf20Sopenharmony_ci return; 17038c2ecf20Sopenharmony_ci } 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci saved = snd_ac97_read(ac97, reg); 17068c2ecf20Sopenharmony_ci if ((ac97->ext_id & AC97_EI_DRA) && reg == AC97_PCM_FRONT_DAC_RATE) 17078c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 17088c2ecf20Sopenharmony_ci AC97_EA_DRA, 0); 17098c2ecf20Sopenharmony_ci /* test a non-standard rate */ 17108c2ecf20Sopenharmony_ci if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11000)) 17118c2ecf20Sopenharmony_ci result |= SNDRV_PCM_RATE_CONTINUOUS; 17128c2ecf20Sopenharmony_ci /* let's try to obtain standard rates */ 17138c2ecf20Sopenharmony_ci if (snd_ac97_test_rate(ac97, reg, shadow_reg, 8000)) 17148c2ecf20Sopenharmony_ci result |= SNDRV_PCM_RATE_8000; 17158c2ecf20Sopenharmony_ci if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11025)) 17168c2ecf20Sopenharmony_ci result |= SNDRV_PCM_RATE_11025; 17178c2ecf20Sopenharmony_ci if (snd_ac97_test_rate(ac97, reg, shadow_reg, 16000)) 17188c2ecf20Sopenharmony_ci result |= SNDRV_PCM_RATE_16000; 17198c2ecf20Sopenharmony_ci if (snd_ac97_test_rate(ac97, reg, shadow_reg, 22050)) 17208c2ecf20Sopenharmony_ci result |= SNDRV_PCM_RATE_22050; 17218c2ecf20Sopenharmony_ci if (snd_ac97_test_rate(ac97, reg, shadow_reg, 32000)) 17228c2ecf20Sopenharmony_ci result |= SNDRV_PCM_RATE_32000; 17238c2ecf20Sopenharmony_ci if (snd_ac97_test_rate(ac97, reg, shadow_reg, 44100)) 17248c2ecf20Sopenharmony_ci result |= SNDRV_PCM_RATE_44100; 17258c2ecf20Sopenharmony_ci if (snd_ac97_test_rate(ac97, reg, shadow_reg, 48000)) 17268c2ecf20Sopenharmony_ci result |= SNDRV_PCM_RATE_48000; 17278c2ecf20Sopenharmony_ci if ((ac97->flags & AC97_DOUBLE_RATE) && 17288c2ecf20Sopenharmony_ci reg == AC97_PCM_FRONT_DAC_RATE) { 17298c2ecf20Sopenharmony_ci /* test standard double rates */ 17308c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 17318c2ecf20Sopenharmony_ci AC97_EA_DRA, AC97_EA_DRA); 17328c2ecf20Sopenharmony_ci if (snd_ac97_test_rate(ac97, reg, shadow_reg, 64000 / 2)) 17338c2ecf20Sopenharmony_ci result |= SNDRV_PCM_RATE_64000; 17348c2ecf20Sopenharmony_ci if (snd_ac97_test_rate(ac97, reg, shadow_reg, 88200 / 2)) 17358c2ecf20Sopenharmony_ci result |= SNDRV_PCM_RATE_88200; 17368c2ecf20Sopenharmony_ci if (snd_ac97_test_rate(ac97, reg, shadow_reg, 96000 / 2)) 17378c2ecf20Sopenharmony_ci result |= SNDRV_PCM_RATE_96000; 17388c2ecf20Sopenharmony_ci /* some codecs don't support variable double rates */ 17398c2ecf20Sopenharmony_ci if (!snd_ac97_test_rate(ac97, reg, shadow_reg, 76100 / 2)) 17408c2ecf20Sopenharmony_ci result &= ~SNDRV_PCM_RATE_CONTINUOUS; 17418c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 17428c2ecf20Sopenharmony_ci AC97_EA_DRA, 0); 17438c2ecf20Sopenharmony_ci } 17448c2ecf20Sopenharmony_ci /* restore the default value */ 17458c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, reg, saved); 17468c2ecf20Sopenharmony_ci if (shadow_reg) 17478c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, shadow_reg, saved); 17488c2ecf20Sopenharmony_ci *r_result = result; 17498c2ecf20Sopenharmony_ci} 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci/* check AC97_SPDIF register to accept which sample rates */ 17528c2ecf20Sopenharmony_cistatic unsigned int snd_ac97_determine_spdif_rates(struct snd_ac97 *ac97) 17538c2ecf20Sopenharmony_ci{ 17548c2ecf20Sopenharmony_ci unsigned int result = 0; 17558c2ecf20Sopenharmony_ci int i; 17568c2ecf20Sopenharmony_ci static const unsigned short ctl_bits[] = { 17578c2ecf20Sopenharmony_ci AC97_SC_SPSR_44K, AC97_SC_SPSR_32K, AC97_SC_SPSR_48K 17588c2ecf20Sopenharmony_ci }; 17598c2ecf20Sopenharmony_ci static const unsigned int rate_bits[] = { 17608c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_32000, SNDRV_PCM_RATE_48000 17618c2ecf20Sopenharmony_ci }; 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci for (i = 0; i < (int)ARRAY_SIZE(ctl_bits); i++) { 17648c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_SPDIF, AC97_SC_SPSR_MASK, ctl_bits[i]); 17658c2ecf20Sopenharmony_ci if ((snd_ac97_read(ac97, AC97_SPDIF) & AC97_SC_SPSR_MASK) == ctl_bits[i]) 17668c2ecf20Sopenharmony_ci result |= rate_bits[i]; 17678c2ecf20Sopenharmony_ci } 17688c2ecf20Sopenharmony_ci return result; 17698c2ecf20Sopenharmony_ci} 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci/* look for the codec id table matching with the given id */ 17728c2ecf20Sopenharmony_cistatic const struct ac97_codec_id *look_for_codec_id(const struct ac97_codec_id *table, 17738c2ecf20Sopenharmony_ci unsigned int id) 17748c2ecf20Sopenharmony_ci{ 17758c2ecf20Sopenharmony_ci const struct ac97_codec_id *pid; 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci for (pid = table; pid->id; pid++) 17788c2ecf20Sopenharmony_ci if (pid->id == (id & pid->mask)) 17798c2ecf20Sopenharmony_ci return pid; 17808c2ecf20Sopenharmony_ci return NULL; 17818c2ecf20Sopenharmony_ci} 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_civoid snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int modem) 17848c2ecf20Sopenharmony_ci{ 17858c2ecf20Sopenharmony_ci const struct ac97_codec_id *pid; 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci sprintf(name, "0x%x %c%c%c", id, 17888c2ecf20Sopenharmony_ci printable(id >> 24), 17898c2ecf20Sopenharmony_ci printable(id >> 16), 17908c2ecf20Sopenharmony_ci printable(id >> 8)); 17918c2ecf20Sopenharmony_ci pid = look_for_codec_id(snd_ac97_codec_id_vendors, id); 17928c2ecf20Sopenharmony_ci if (! pid) 17938c2ecf20Sopenharmony_ci return; 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci strcpy(name, pid->name); 17968c2ecf20Sopenharmony_ci if (ac97 && pid->patch) { 17978c2ecf20Sopenharmony_ci if ((modem && (pid->flags & AC97_MODEM_PATCH)) || 17988c2ecf20Sopenharmony_ci (! modem && ! (pid->flags & AC97_MODEM_PATCH))) 17998c2ecf20Sopenharmony_ci pid->patch(ac97); 18008c2ecf20Sopenharmony_ci } 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci pid = look_for_codec_id(snd_ac97_codec_ids, id); 18038c2ecf20Sopenharmony_ci if (pid) { 18048c2ecf20Sopenharmony_ci strcat(name, " "); 18058c2ecf20Sopenharmony_ci strcat(name, pid->name); 18068c2ecf20Sopenharmony_ci if (pid->mask != 0xffffffff) 18078c2ecf20Sopenharmony_ci sprintf(name + strlen(name), " rev %d", id & ~pid->mask); 18088c2ecf20Sopenharmony_ci if (ac97 && pid->patch) { 18098c2ecf20Sopenharmony_ci if ((modem && (pid->flags & AC97_MODEM_PATCH)) || 18108c2ecf20Sopenharmony_ci (! modem && ! (pid->flags & AC97_MODEM_PATCH))) 18118c2ecf20Sopenharmony_ci pid->patch(ac97); 18128c2ecf20Sopenharmony_ci } 18138c2ecf20Sopenharmony_ci } else 18148c2ecf20Sopenharmony_ci sprintf(name + strlen(name), " id %x", id & 0xff); 18158c2ecf20Sopenharmony_ci} 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci/** 18188c2ecf20Sopenharmony_ci * snd_ac97_get_short_name - retrieve codec name 18198c2ecf20Sopenharmony_ci * @ac97: the codec instance 18208c2ecf20Sopenharmony_ci * 18218c2ecf20Sopenharmony_ci * Return: The short identifying name of the codec. 18228c2ecf20Sopenharmony_ci */ 18238c2ecf20Sopenharmony_ciconst char *snd_ac97_get_short_name(struct snd_ac97 *ac97) 18248c2ecf20Sopenharmony_ci{ 18258c2ecf20Sopenharmony_ci const struct ac97_codec_id *pid; 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci for (pid = snd_ac97_codec_ids; pid->id; pid++) 18288c2ecf20Sopenharmony_ci if (pid->id == (ac97->id & pid->mask)) 18298c2ecf20Sopenharmony_ci return pid->name; 18308c2ecf20Sopenharmony_ci return "unknown codec"; 18318c2ecf20Sopenharmony_ci} 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_get_short_name); 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci/* wait for a while until registers are accessible after RESET 18368c2ecf20Sopenharmony_ci * return 0 if ok, negative not ready 18378c2ecf20Sopenharmony_ci */ 18388c2ecf20Sopenharmony_cistatic int ac97_reset_wait(struct snd_ac97 *ac97, int timeout, int with_modem) 18398c2ecf20Sopenharmony_ci{ 18408c2ecf20Sopenharmony_ci unsigned long end_time; 18418c2ecf20Sopenharmony_ci unsigned short val; 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci end_time = jiffies + timeout; 18448c2ecf20Sopenharmony_ci do { 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci /* use preliminary reads to settle the communication */ 18478c2ecf20Sopenharmony_ci snd_ac97_read(ac97, AC97_RESET); 18488c2ecf20Sopenharmony_ci snd_ac97_read(ac97, AC97_VENDOR_ID1); 18498c2ecf20Sopenharmony_ci snd_ac97_read(ac97, AC97_VENDOR_ID2); 18508c2ecf20Sopenharmony_ci /* modem? */ 18518c2ecf20Sopenharmony_ci if (with_modem) { 18528c2ecf20Sopenharmony_ci val = snd_ac97_read(ac97, AC97_EXTENDED_MID); 18538c2ecf20Sopenharmony_ci if (val != 0xffff && (val & 1) != 0) 18548c2ecf20Sopenharmony_ci return 0; 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci if (ac97->scaps & AC97_SCAP_DETECT_BY_VENDOR) { 18578c2ecf20Sopenharmony_ci /* probably only Xbox issue - all registers are read as zero */ 18588c2ecf20Sopenharmony_ci val = snd_ac97_read(ac97, AC97_VENDOR_ID1); 18598c2ecf20Sopenharmony_ci if (val != 0 && val != 0xffff) 18608c2ecf20Sopenharmony_ci return 0; 18618c2ecf20Sopenharmony_ci } else { 18628c2ecf20Sopenharmony_ci /* because the PCM or MASTER volume registers can be modified, 18638c2ecf20Sopenharmony_ci * the REC_GAIN register is used for tests 18648c2ecf20Sopenharmony_ci */ 18658c2ecf20Sopenharmony_ci /* test if we can write to the record gain volume register */ 18668c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05); 18678c2ecf20Sopenharmony_ci if ((snd_ac97_read(ac97, AC97_REC_GAIN) & 0x7fff) == 0x0a05) 18688c2ecf20Sopenharmony_ci return 0; 18698c2ecf20Sopenharmony_ci } 18708c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(1); 18718c2ecf20Sopenharmony_ci } while (time_after_eq(end_time, jiffies)); 18728c2ecf20Sopenharmony_ci return -ENODEV; 18738c2ecf20Sopenharmony_ci} 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci/** 18768c2ecf20Sopenharmony_ci * snd_ac97_bus - create an AC97 bus component 18778c2ecf20Sopenharmony_ci * @card: the card instance 18788c2ecf20Sopenharmony_ci * @num: the bus number 18798c2ecf20Sopenharmony_ci * @ops: the bus callbacks table 18808c2ecf20Sopenharmony_ci * @private_data: private data pointer for the new instance 18818c2ecf20Sopenharmony_ci * @rbus: the pointer to store the new AC97 bus instance. 18828c2ecf20Sopenharmony_ci * 18838c2ecf20Sopenharmony_ci * Creates an AC97 bus component. An struct snd_ac97_bus instance is newly 18848c2ecf20Sopenharmony_ci * allocated and initialized. 18858c2ecf20Sopenharmony_ci * 18868c2ecf20Sopenharmony_ci * The ops table must include valid callbacks (at least read and 18878c2ecf20Sopenharmony_ci * write). The other callbacks, wait and reset, are not mandatory. 18888c2ecf20Sopenharmony_ci * 18898c2ecf20Sopenharmony_ci * The clock is set to 48000. If another clock is needed, set 18908c2ecf20Sopenharmony_ci * ``(*rbus)->clock`` manually. 18918c2ecf20Sopenharmony_ci * 18928c2ecf20Sopenharmony_ci * The AC97 bus instance is registered as a low-level device, so you don't 18938c2ecf20Sopenharmony_ci * have to release it manually. 18948c2ecf20Sopenharmony_ci * 18958c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 18968c2ecf20Sopenharmony_ci */ 18978c2ecf20Sopenharmony_ciint snd_ac97_bus(struct snd_card *card, int num, 18988c2ecf20Sopenharmony_ci const struct snd_ac97_bus_ops *ops, 18998c2ecf20Sopenharmony_ci void *private_data, struct snd_ac97_bus **rbus) 19008c2ecf20Sopenharmony_ci{ 19018c2ecf20Sopenharmony_ci int err; 19028c2ecf20Sopenharmony_ci struct snd_ac97_bus *bus; 19038c2ecf20Sopenharmony_ci static const struct snd_device_ops dev_ops = { 19048c2ecf20Sopenharmony_ci .dev_free = snd_ac97_bus_dev_free, 19058c2ecf20Sopenharmony_ci }; 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci if (snd_BUG_ON(!card)) 19088c2ecf20Sopenharmony_ci return -EINVAL; 19098c2ecf20Sopenharmony_ci bus = kzalloc(sizeof(*bus), GFP_KERNEL); 19108c2ecf20Sopenharmony_ci if (bus == NULL) 19118c2ecf20Sopenharmony_ci return -ENOMEM; 19128c2ecf20Sopenharmony_ci bus->card = card; 19138c2ecf20Sopenharmony_ci bus->num = num; 19148c2ecf20Sopenharmony_ci bus->ops = ops; 19158c2ecf20Sopenharmony_ci bus->private_data = private_data; 19168c2ecf20Sopenharmony_ci bus->clock = 48000; 19178c2ecf20Sopenharmony_ci spin_lock_init(&bus->bus_lock); 19188c2ecf20Sopenharmony_ci snd_ac97_bus_proc_init(bus); 19198c2ecf20Sopenharmony_ci if ((err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops)) < 0) { 19208c2ecf20Sopenharmony_ci snd_ac97_bus_free(bus); 19218c2ecf20Sopenharmony_ci return err; 19228c2ecf20Sopenharmony_ci } 19238c2ecf20Sopenharmony_ci if (rbus) 19248c2ecf20Sopenharmony_ci *rbus = bus; 19258c2ecf20Sopenharmony_ci return 0; 19268c2ecf20Sopenharmony_ci} 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_bus); 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci/* stop no dev release warning */ 19318c2ecf20Sopenharmony_cistatic void ac97_device_release(struct device * dev) 19328c2ecf20Sopenharmony_ci{ 19338c2ecf20Sopenharmony_ci} 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci/* register ac97 codec to bus */ 19368c2ecf20Sopenharmony_cistatic int snd_ac97_dev_register(struct snd_device *device) 19378c2ecf20Sopenharmony_ci{ 19388c2ecf20Sopenharmony_ci struct snd_ac97 *ac97 = device->device_data; 19398c2ecf20Sopenharmony_ci int err; 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci ac97->dev.bus = &ac97_bus_type; 19428c2ecf20Sopenharmony_ci ac97->dev.parent = ac97->bus->card->dev; 19438c2ecf20Sopenharmony_ci ac97->dev.release = ac97_device_release; 19448c2ecf20Sopenharmony_ci dev_set_name(&ac97->dev, "%d-%d:%s", 19458c2ecf20Sopenharmony_ci ac97->bus->card->number, ac97->num, 19468c2ecf20Sopenharmony_ci snd_ac97_get_short_name(ac97)); 19478c2ecf20Sopenharmony_ci if ((err = device_register(&ac97->dev)) < 0) { 19488c2ecf20Sopenharmony_ci ac97_err(ac97, "Can't register ac97 bus\n"); 19498c2ecf20Sopenharmony_ci put_device(&ac97->dev); 19508c2ecf20Sopenharmony_ci ac97->dev.bus = NULL; 19518c2ecf20Sopenharmony_ci return err; 19528c2ecf20Sopenharmony_ci } 19538c2ecf20Sopenharmony_ci return 0; 19548c2ecf20Sopenharmony_ci} 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci/* disconnect ac97 codec */ 19578c2ecf20Sopenharmony_cistatic int snd_ac97_dev_disconnect(struct snd_device *device) 19588c2ecf20Sopenharmony_ci{ 19598c2ecf20Sopenharmony_ci struct snd_ac97 *ac97 = device->device_data; 19608c2ecf20Sopenharmony_ci if (ac97->dev.bus) 19618c2ecf20Sopenharmony_ci device_unregister(&ac97->dev); 19628c2ecf20Sopenharmony_ci return 0; 19638c2ecf20Sopenharmony_ci} 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci/* build_ops to do nothing */ 19668c2ecf20Sopenharmony_cistatic const struct snd_ac97_build_ops null_build_ops; 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE 19698c2ecf20Sopenharmony_cistatic void do_update_power(struct work_struct *work) 19708c2ecf20Sopenharmony_ci{ 19718c2ecf20Sopenharmony_ci update_power_regs( 19728c2ecf20Sopenharmony_ci container_of(work, struct snd_ac97, power_work.work)); 19738c2ecf20Sopenharmony_ci} 19748c2ecf20Sopenharmony_ci#endif 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci/** 19778c2ecf20Sopenharmony_ci * snd_ac97_mixer - create an Codec97 component 19788c2ecf20Sopenharmony_ci * @bus: the AC97 bus which codec is attached to 19798c2ecf20Sopenharmony_ci * @template: the template of ac97, including index, callbacks and 19808c2ecf20Sopenharmony_ci * the private data. 19818c2ecf20Sopenharmony_ci * @rac97: the pointer to store the new ac97 instance. 19828c2ecf20Sopenharmony_ci * 19838c2ecf20Sopenharmony_ci * Creates an Codec97 component. An struct snd_ac97 instance is newly 19848c2ecf20Sopenharmony_ci * allocated and initialized from the template. The codec 19858c2ecf20Sopenharmony_ci * is then initialized by the standard procedure. 19868c2ecf20Sopenharmony_ci * 19878c2ecf20Sopenharmony_ci * The template must include the codec number (num) and address (addr), 19888c2ecf20Sopenharmony_ci * and the private data (private_data). 19898c2ecf20Sopenharmony_ci * 19908c2ecf20Sopenharmony_ci * The ac97 instance is registered as a low-level device, so you don't 19918c2ecf20Sopenharmony_ci * have to release it manually. 19928c2ecf20Sopenharmony_ci * 19938c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 19948c2ecf20Sopenharmony_ci */ 19958c2ecf20Sopenharmony_ciint snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, struct snd_ac97 **rac97) 19968c2ecf20Sopenharmony_ci{ 19978c2ecf20Sopenharmony_ci int err; 19988c2ecf20Sopenharmony_ci struct snd_ac97 *ac97; 19998c2ecf20Sopenharmony_ci struct snd_card *card; 20008c2ecf20Sopenharmony_ci char name[64]; 20018c2ecf20Sopenharmony_ci unsigned long end_time; 20028c2ecf20Sopenharmony_ci unsigned int reg; 20038c2ecf20Sopenharmony_ci const struct ac97_codec_id *pid; 20048c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 20058c2ecf20Sopenharmony_ci .dev_free = snd_ac97_dev_free, 20068c2ecf20Sopenharmony_ci .dev_register = snd_ac97_dev_register, 20078c2ecf20Sopenharmony_ci .dev_disconnect = snd_ac97_dev_disconnect, 20088c2ecf20Sopenharmony_ci }; 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_ci if (snd_BUG_ON(!bus || !template || !rac97)) 20118c2ecf20Sopenharmony_ci return -EINVAL; 20128c2ecf20Sopenharmony_ci *rac97 = NULL; 20138c2ecf20Sopenharmony_ci if (snd_BUG_ON(template->num >= 4)) 20148c2ecf20Sopenharmony_ci return -EINVAL; 20158c2ecf20Sopenharmony_ci if (bus->codec[template->num]) 20168c2ecf20Sopenharmony_ci return -EBUSY; 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci card = bus->card; 20198c2ecf20Sopenharmony_ci ac97 = kzalloc(sizeof(*ac97), GFP_KERNEL); 20208c2ecf20Sopenharmony_ci if (ac97 == NULL) 20218c2ecf20Sopenharmony_ci return -ENOMEM; 20228c2ecf20Sopenharmony_ci ac97->private_data = template->private_data; 20238c2ecf20Sopenharmony_ci ac97->private_free = template->private_free; 20248c2ecf20Sopenharmony_ci ac97->bus = bus; 20258c2ecf20Sopenharmony_ci ac97->pci = template->pci; 20268c2ecf20Sopenharmony_ci ac97->num = template->num; 20278c2ecf20Sopenharmony_ci ac97->addr = template->addr; 20288c2ecf20Sopenharmony_ci ac97->scaps = template->scaps; 20298c2ecf20Sopenharmony_ci ac97->res_table = template->res_table; 20308c2ecf20Sopenharmony_ci bus->codec[ac97->num] = ac97; 20318c2ecf20Sopenharmony_ci mutex_init(&ac97->reg_mutex); 20328c2ecf20Sopenharmony_ci mutex_init(&ac97->page_mutex); 20338c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE 20348c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&ac97->power_work, do_update_power); 20358c2ecf20Sopenharmony_ci#endif 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 20388c2ecf20Sopenharmony_ci if (ac97->pci) { 20398c2ecf20Sopenharmony_ci pci_read_config_word(ac97->pci, PCI_SUBSYSTEM_VENDOR_ID, &ac97->subsystem_vendor); 20408c2ecf20Sopenharmony_ci pci_read_config_word(ac97->pci, PCI_SUBSYSTEM_ID, &ac97->subsystem_device); 20418c2ecf20Sopenharmony_ci } 20428c2ecf20Sopenharmony_ci#endif 20438c2ecf20Sopenharmony_ci if (bus->ops->reset) { 20448c2ecf20Sopenharmony_ci bus->ops->reset(ac97); 20458c2ecf20Sopenharmony_ci goto __access_ok; 20468c2ecf20Sopenharmony_ci } 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16; 20498c2ecf20Sopenharmony_ci ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2); 20508c2ecf20Sopenharmony_ci if (ac97->id && ac97->id != (unsigned int)-1) { 20518c2ecf20Sopenharmony_ci pid = look_for_codec_id(snd_ac97_codec_ids, ac97->id); 20528c2ecf20Sopenharmony_ci if (pid && (pid->flags & AC97_DEFAULT_POWER_OFF)) 20538c2ecf20Sopenharmony_ci goto __access_ok; 20548c2ecf20Sopenharmony_ci } 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ci /* reset to defaults */ 20578c2ecf20Sopenharmony_ci if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO)) 20588c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_RESET, 0); 20598c2ecf20Sopenharmony_ci if (!(ac97->scaps & AC97_SCAP_SKIP_MODEM)) 20608c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_EXTENDED_MID, 0); 20618c2ecf20Sopenharmony_ci if (bus->ops->wait) 20628c2ecf20Sopenharmony_ci bus->ops->wait(ac97); 20638c2ecf20Sopenharmony_ci else { 20648c2ecf20Sopenharmony_ci udelay(50); 20658c2ecf20Sopenharmony_ci if (ac97->scaps & AC97_SCAP_SKIP_AUDIO) 20668c2ecf20Sopenharmony_ci err = ac97_reset_wait(ac97, msecs_to_jiffies(500), 1); 20678c2ecf20Sopenharmony_ci else { 20688c2ecf20Sopenharmony_ci err = ac97_reset_wait(ac97, msecs_to_jiffies(500), 0); 20698c2ecf20Sopenharmony_ci if (err < 0) 20708c2ecf20Sopenharmony_ci err = ac97_reset_wait(ac97, 20718c2ecf20Sopenharmony_ci msecs_to_jiffies(500), 1); 20728c2ecf20Sopenharmony_ci } 20738c2ecf20Sopenharmony_ci if (err < 0) { 20748c2ecf20Sopenharmony_ci ac97_warn(ac97, "AC'97 %d does not respond - RESET\n", 20758c2ecf20Sopenharmony_ci ac97->num); 20768c2ecf20Sopenharmony_ci /* proceed anyway - it's often non-critical */ 20778c2ecf20Sopenharmony_ci } 20788c2ecf20Sopenharmony_ci } 20798c2ecf20Sopenharmony_ci __access_ok: 20808c2ecf20Sopenharmony_ci ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16; 20818c2ecf20Sopenharmony_ci ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2); 20828c2ecf20Sopenharmony_ci if (! (ac97->scaps & AC97_SCAP_DETECT_BY_VENDOR) && 20838c2ecf20Sopenharmony_ci (ac97->id == 0x00000000 || ac97->id == 0xffffffff)) { 20848c2ecf20Sopenharmony_ci ac97_err(ac97, 20858c2ecf20Sopenharmony_ci "AC'97 %d access is not valid [0x%x], removing mixer.\n", 20868c2ecf20Sopenharmony_ci ac97->num, ac97->id); 20878c2ecf20Sopenharmony_ci snd_ac97_free(ac97); 20888c2ecf20Sopenharmony_ci return -EIO; 20898c2ecf20Sopenharmony_ci } 20908c2ecf20Sopenharmony_ci pid = look_for_codec_id(snd_ac97_codec_ids, ac97->id); 20918c2ecf20Sopenharmony_ci if (pid) 20928c2ecf20Sopenharmony_ci ac97->flags |= pid->flags; 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci /* test for AC'97 */ 20958c2ecf20Sopenharmony_ci if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO) && !(ac97->scaps & AC97_SCAP_AUDIO)) { 20968c2ecf20Sopenharmony_ci /* test if we can write to the record gain volume register */ 20978c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a06); 20988c2ecf20Sopenharmony_ci if (((err = snd_ac97_read(ac97, AC97_REC_GAIN)) & 0x7fff) == 0x0a06) 20998c2ecf20Sopenharmony_ci ac97->scaps |= AC97_SCAP_AUDIO; 21008c2ecf20Sopenharmony_ci } 21018c2ecf20Sopenharmony_ci if (ac97->scaps & AC97_SCAP_AUDIO) { 21028c2ecf20Sopenharmony_ci ac97->caps = snd_ac97_read(ac97, AC97_RESET); 21038c2ecf20Sopenharmony_ci ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID); 21048c2ecf20Sopenharmony_ci if (ac97->ext_id == 0xffff) /* invalid combination */ 21058c2ecf20Sopenharmony_ci ac97->ext_id = 0; 21068c2ecf20Sopenharmony_ci } 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci /* test for MC'97 */ 21098c2ecf20Sopenharmony_ci if (!(ac97->scaps & AC97_SCAP_SKIP_MODEM) && !(ac97->scaps & AC97_SCAP_MODEM)) { 21108c2ecf20Sopenharmony_ci ac97->ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID); 21118c2ecf20Sopenharmony_ci if (ac97->ext_mid == 0xffff) /* invalid combination */ 21128c2ecf20Sopenharmony_ci ac97->ext_mid = 0; 21138c2ecf20Sopenharmony_ci if (ac97->ext_mid & 1) 21148c2ecf20Sopenharmony_ci ac97->scaps |= AC97_SCAP_MODEM; 21158c2ecf20Sopenharmony_ci } 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci if (!ac97_is_audio(ac97) && !ac97_is_modem(ac97)) { 21188c2ecf20Sopenharmony_ci if (!(ac97->scaps & (AC97_SCAP_SKIP_AUDIO|AC97_SCAP_SKIP_MODEM))) 21198c2ecf20Sopenharmony_ci ac97_err(ac97, 21208c2ecf20Sopenharmony_ci "AC'97 %d access error (not audio or modem codec)\n", 21218c2ecf20Sopenharmony_ci ac97->num); 21228c2ecf20Sopenharmony_ci snd_ac97_free(ac97); 21238c2ecf20Sopenharmony_ci return -EACCES; 21248c2ecf20Sopenharmony_ci } 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci if (bus->ops->reset) // FIXME: always skipping? 21278c2ecf20Sopenharmony_ci goto __ready_ok; 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci /* FIXME: add powerdown control */ 21308c2ecf20Sopenharmony_ci if (ac97_is_audio(ac97)) { 21318c2ecf20Sopenharmony_ci /* nothing should be in powerdown mode */ 21328c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0); 21338c2ecf20Sopenharmony_ci if (! (ac97->flags & AC97_DEFAULT_POWER_OFF)) { 21348c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_RESET, 0); /* reset to defaults */ 21358c2ecf20Sopenharmony_ci udelay(100); 21368c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0); 21378c2ecf20Sopenharmony_ci } 21388c2ecf20Sopenharmony_ci /* nothing should be in powerdown mode */ 21398c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0); 21408c2ecf20Sopenharmony_ci end_time = jiffies + msecs_to_jiffies(5000); 21418c2ecf20Sopenharmony_ci do { 21428c2ecf20Sopenharmony_ci if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f) 21438c2ecf20Sopenharmony_ci goto __ready_ok; 21448c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(1); 21458c2ecf20Sopenharmony_ci } while (time_after_eq(end_time, jiffies)); 21468c2ecf20Sopenharmony_ci ac97_warn(ac97, 21478c2ecf20Sopenharmony_ci "AC'97 %d analog subsections not ready\n", ac97->num); 21488c2ecf20Sopenharmony_ci } 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_ci /* FIXME: add powerdown control */ 21518c2ecf20Sopenharmony_ci if (ac97_is_modem(ac97)) { 21528c2ecf20Sopenharmony_ci unsigned char tmp; 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci /* nothing should be in powerdown mode */ 21558c2ecf20Sopenharmony_ci /* note: it's important to set the rate at first */ 21568c2ecf20Sopenharmony_ci tmp = AC97_MEA_GPIO; 21578c2ecf20Sopenharmony_ci if (ac97->ext_mid & AC97_MEI_LINE1) { 21588c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_LINE1_RATE, 8000); 21598c2ecf20Sopenharmony_ci tmp |= AC97_MEA_ADC1 | AC97_MEA_DAC1; 21608c2ecf20Sopenharmony_ci } 21618c2ecf20Sopenharmony_ci if (ac97->ext_mid & AC97_MEI_LINE2) { 21628c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_LINE2_RATE, 8000); 21638c2ecf20Sopenharmony_ci tmp |= AC97_MEA_ADC2 | AC97_MEA_DAC2; 21648c2ecf20Sopenharmony_ci } 21658c2ecf20Sopenharmony_ci if (ac97->ext_mid & AC97_MEI_HANDSET) { 21668c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_HANDSET_RATE, 8000); 21678c2ecf20Sopenharmony_ci tmp |= AC97_MEA_HADC | AC97_MEA_HDAC; 21688c2ecf20Sopenharmony_ci } 21698c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0); 21708c2ecf20Sopenharmony_ci udelay(100); 21718c2ecf20Sopenharmony_ci /* nothing should be in powerdown mode */ 21728c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0); 21738c2ecf20Sopenharmony_ci end_time = jiffies + msecs_to_jiffies(100); 21748c2ecf20Sopenharmony_ci do { 21758c2ecf20Sopenharmony_ci if ((snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS) & tmp) == tmp) 21768c2ecf20Sopenharmony_ci goto __ready_ok; 21778c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(1); 21788c2ecf20Sopenharmony_ci } while (time_after_eq(end_time, jiffies)); 21798c2ecf20Sopenharmony_ci ac97_warn(ac97, 21808c2ecf20Sopenharmony_ci "MC'97 %d converters and GPIO not ready (0x%x)\n", 21818c2ecf20Sopenharmony_ci ac97->num, 21828c2ecf20Sopenharmony_ci snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS)); 21838c2ecf20Sopenharmony_ci } 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci __ready_ok: 21868c2ecf20Sopenharmony_ci if (ac97_is_audio(ac97)) 21878c2ecf20Sopenharmony_ci ac97->addr = (ac97->ext_id & AC97_EI_ADDR_MASK) >> AC97_EI_ADDR_SHIFT; 21888c2ecf20Sopenharmony_ci else 21898c2ecf20Sopenharmony_ci ac97->addr = (ac97->ext_mid & AC97_MEI_ADDR_MASK) >> AC97_MEI_ADDR_SHIFT; 21908c2ecf20Sopenharmony_ci if (ac97->ext_id & 0x01c9) { /* L/R, MIC, SDAC, LDAC VRA support */ 21918c2ecf20Sopenharmony_ci reg = snd_ac97_read(ac97, AC97_EXTENDED_STATUS); 21928c2ecf20Sopenharmony_ci reg |= ac97->ext_id & 0x01c0; /* LDAC/SDAC/CDAC */ 21938c2ecf20Sopenharmony_ci if (! bus->no_vra) 21948c2ecf20Sopenharmony_ci reg |= ac97->ext_id & 0x0009; /* VRA/VRM */ 21958c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, reg); 21968c2ecf20Sopenharmony_ci } 21978c2ecf20Sopenharmony_ci if ((ac97->ext_id & AC97_EI_DRA) && bus->dra) { 21988c2ecf20Sopenharmony_ci /* Intel controllers require double rate data to be put in 21998c2ecf20Sopenharmony_ci * slots 7+8, so let's hope the codec supports it. */ 22008c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_GENERAL_PURPOSE, AC97_GP_DRSS_MASK, AC97_GP_DRSS_78); 22018c2ecf20Sopenharmony_ci if ((snd_ac97_read(ac97, AC97_GENERAL_PURPOSE) & AC97_GP_DRSS_MASK) == AC97_GP_DRSS_78) 22028c2ecf20Sopenharmony_ci ac97->flags |= AC97_DOUBLE_RATE; 22038c2ecf20Sopenharmony_ci /* restore to slots 10/11 to avoid the confliction with surrounds */ 22048c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_GENERAL_PURPOSE, AC97_GP_DRSS_MASK, 0); 22058c2ecf20Sopenharmony_ci } 22068c2ecf20Sopenharmony_ci if (ac97->ext_id & AC97_EI_VRA) { /* VRA support */ 22078c2ecf20Sopenharmony_ci snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, 0, &ac97->rates[AC97_RATES_FRONT_DAC]); 22088c2ecf20Sopenharmony_ci snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, 0, &ac97->rates[AC97_RATES_ADC]); 22098c2ecf20Sopenharmony_ci } else { 22108c2ecf20Sopenharmony_ci ac97->rates[AC97_RATES_FRONT_DAC] = SNDRV_PCM_RATE_48000; 22118c2ecf20Sopenharmony_ci if (ac97->flags & AC97_DOUBLE_RATE) 22128c2ecf20Sopenharmony_ci ac97->rates[AC97_RATES_FRONT_DAC] |= SNDRV_PCM_RATE_96000; 22138c2ecf20Sopenharmony_ci ac97->rates[AC97_RATES_ADC] = SNDRV_PCM_RATE_48000; 22148c2ecf20Sopenharmony_ci } 22158c2ecf20Sopenharmony_ci if (ac97->ext_id & AC97_EI_SPDIF) { 22168c2ecf20Sopenharmony_ci /* codec specific code (patch) should override these values */ 22178c2ecf20Sopenharmony_ci ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_32000; 22188c2ecf20Sopenharmony_ci } 22198c2ecf20Sopenharmony_ci if (ac97->ext_id & AC97_EI_VRM) { /* MIC VRA support */ 22208c2ecf20Sopenharmony_ci snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, 0, &ac97->rates[AC97_RATES_MIC_ADC]); 22218c2ecf20Sopenharmony_ci } else { 22228c2ecf20Sopenharmony_ci ac97->rates[AC97_RATES_MIC_ADC] = SNDRV_PCM_RATE_48000; 22238c2ecf20Sopenharmony_ci } 22248c2ecf20Sopenharmony_ci if (ac97->ext_id & AC97_EI_SDAC) { /* SDAC support */ 22258c2ecf20Sopenharmony_ci snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_SURR_DAC]); 22268c2ecf20Sopenharmony_ci ac97->scaps |= AC97_SCAP_SURROUND_DAC; 22278c2ecf20Sopenharmony_ci } 22288c2ecf20Sopenharmony_ci if (ac97->ext_id & AC97_EI_LDAC) { /* LDAC support */ 22298c2ecf20Sopenharmony_ci snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_LFE_DAC]); 22308c2ecf20Sopenharmony_ci ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC; 22318c2ecf20Sopenharmony_ci } 22328c2ecf20Sopenharmony_ci /* additional initializations */ 22338c2ecf20Sopenharmony_ci if (bus->ops->init) 22348c2ecf20Sopenharmony_ci bus->ops->init(ac97); 22358c2ecf20Sopenharmony_ci snd_ac97_get_name(ac97, ac97->id, name, !ac97_is_audio(ac97)); 22368c2ecf20Sopenharmony_ci snd_ac97_get_name(NULL, ac97->id, name, !ac97_is_audio(ac97)); // ac97->id might be changed in the special setup code 22378c2ecf20Sopenharmony_ci if (! ac97->build_ops) 22388c2ecf20Sopenharmony_ci ac97->build_ops = &null_build_ops; 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci if (ac97_is_audio(ac97)) { 22418c2ecf20Sopenharmony_ci char comp[16]; 22428c2ecf20Sopenharmony_ci if (card->mixername[0] == '\0') { 22438c2ecf20Sopenharmony_ci strcpy(card->mixername, name); 22448c2ecf20Sopenharmony_ci } else { 22458c2ecf20Sopenharmony_ci if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) { 22468c2ecf20Sopenharmony_ci strcat(card->mixername, ","); 22478c2ecf20Sopenharmony_ci strcat(card->mixername, name); 22488c2ecf20Sopenharmony_ci } 22498c2ecf20Sopenharmony_ci } 22508c2ecf20Sopenharmony_ci sprintf(comp, "AC97a:%08x", ac97->id); 22518c2ecf20Sopenharmony_ci if ((err = snd_component_add(card, comp)) < 0) { 22528c2ecf20Sopenharmony_ci snd_ac97_free(ac97); 22538c2ecf20Sopenharmony_ci return err; 22548c2ecf20Sopenharmony_ci } 22558c2ecf20Sopenharmony_ci if (snd_ac97_mixer_build(ac97) < 0) { 22568c2ecf20Sopenharmony_ci snd_ac97_free(ac97); 22578c2ecf20Sopenharmony_ci return -ENOMEM; 22588c2ecf20Sopenharmony_ci } 22598c2ecf20Sopenharmony_ci } 22608c2ecf20Sopenharmony_ci if (ac97_is_modem(ac97)) { 22618c2ecf20Sopenharmony_ci char comp[16]; 22628c2ecf20Sopenharmony_ci if (card->mixername[0] == '\0') { 22638c2ecf20Sopenharmony_ci strcpy(card->mixername, name); 22648c2ecf20Sopenharmony_ci } else { 22658c2ecf20Sopenharmony_ci if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) { 22668c2ecf20Sopenharmony_ci strcat(card->mixername, ","); 22678c2ecf20Sopenharmony_ci strcat(card->mixername, name); 22688c2ecf20Sopenharmony_ci } 22698c2ecf20Sopenharmony_ci } 22708c2ecf20Sopenharmony_ci sprintf(comp, "AC97m:%08x", ac97->id); 22718c2ecf20Sopenharmony_ci if ((err = snd_component_add(card, comp)) < 0) { 22728c2ecf20Sopenharmony_ci snd_ac97_free(ac97); 22738c2ecf20Sopenharmony_ci return err; 22748c2ecf20Sopenharmony_ci } 22758c2ecf20Sopenharmony_ci if (snd_ac97_modem_build(card, ac97) < 0) { 22768c2ecf20Sopenharmony_ci snd_ac97_free(ac97); 22778c2ecf20Sopenharmony_ci return -ENOMEM; 22788c2ecf20Sopenharmony_ci } 22798c2ecf20Sopenharmony_ci } 22808c2ecf20Sopenharmony_ci if (ac97_is_audio(ac97)) 22818c2ecf20Sopenharmony_ci update_power_regs(ac97); 22828c2ecf20Sopenharmony_ci snd_ac97_proc_init(ac97); 22838c2ecf20Sopenharmony_ci if ((err = snd_device_new(card, SNDRV_DEV_CODEC, ac97, &ops)) < 0) { 22848c2ecf20Sopenharmony_ci snd_ac97_free(ac97); 22858c2ecf20Sopenharmony_ci return err; 22868c2ecf20Sopenharmony_ci } 22878c2ecf20Sopenharmony_ci *rac97 = ac97; 22888c2ecf20Sopenharmony_ci return 0; 22898c2ecf20Sopenharmony_ci} 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_mixer); 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci/* 22948c2ecf20Sopenharmony_ci * Power down the chip. 22958c2ecf20Sopenharmony_ci * 22968c2ecf20Sopenharmony_ci * MASTER and HEADPHONE registers are muted but the register cache values 22978c2ecf20Sopenharmony_ci * are not changed, so that the values can be restored in snd_ac97_resume(). 22988c2ecf20Sopenharmony_ci */ 22998c2ecf20Sopenharmony_cistatic void snd_ac97_powerdown(struct snd_ac97 *ac97) 23008c2ecf20Sopenharmony_ci{ 23018c2ecf20Sopenharmony_ci unsigned short power; 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ci if (ac97_is_audio(ac97)) { 23048c2ecf20Sopenharmony_ci /* some codecs have stereo mute bits */ 23058c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_MASTER, 0x9f9f); 23068c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_HEADPHONE, 0x9f9f); 23078c2ecf20Sopenharmony_ci } 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci /* surround, CLFE, mic powerdown */ 23108c2ecf20Sopenharmony_ci power = ac97->regs[AC97_EXTENDED_STATUS]; 23118c2ecf20Sopenharmony_ci if (ac97->scaps & AC97_SCAP_SURROUND_DAC) 23128c2ecf20Sopenharmony_ci power |= AC97_EA_PRJ; 23138c2ecf20Sopenharmony_ci if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) 23148c2ecf20Sopenharmony_ci power |= AC97_EA_PRI | AC97_EA_PRK; 23158c2ecf20Sopenharmony_ci power |= AC97_EA_PRL; 23168c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_EXTENDED_STATUS, power); 23178c2ecf20Sopenharmony_ci 23188c2ecf20Sopenharmony_ci /* powerdown external amplifier */ 23198c2ecf20Sopenharmony_ci if (ac97->scaps & AC97_SCAP_INV_EAPD) 23208c2ecf20Sopenharmony_ci power = ac97->regs[AC97_POWERDOWN] & ~AC97_PD_EAPD; 23218c2ecf20Sopenharmony_ci else if (! (ac97->scaps & AC97_SCAP_EAPD_LED)) 23228c2ecf20Sopenharmony_ci power = ac97->regs[AC97_POWERDOWN] | AC97_PD_EAPD; 23238c2ecf20Sopenharmony_ci power |= AC97_PD_PR6; /* Headphone amplifier powerdown */ 23248c2ecf20Sopenharmony_ci power |= AC97_PD_PR0 | AC97_PD_PR1; /* ADC & DAC powerdown */ 23258c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_POWERDOWN, power); 23268c2ecf20Sopenharmony_ci udelay(100); 23278c2ecf20Sopenharmony_ci power |= AC97_PD_PR2; /* Analog Mixer powerdown (Vref on) */ 23288c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_POWERDOWN, power); 23298c2ecf20Sopenharmony_ci if (ac97_is_power_save_mode(ac97)) { 23308c2ecf20Sopenharmony_ci power |= AC97_PD_PR3; /* Analog Mixer powerdown */ 23318c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_POWERDOWN, power); 23328c2ecf20Sopenharmony_ci udelay(100); 23338c2ecf20Sopenharmony_ci /* AC-link powerdown, internal Clk disable */ 23348c2ecf20Sopenharmony_ci /* FIXME: this may cause click noises on some boards */ 23358c2ecf20Sopenharmony_ci power |= AC97_PD_PR4 | AC97_PD_PR5; 23368c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_POWERDOWN, power); 23378c2ecf20Sopenharmony_ci } 23388c2ecf20Sopenharmony_ci} 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_cistruct ac97_power_reg { 23428c2ecf20Sopenharmony_ci unsigned short reg; 23438c2ecf20Sopenharmony_ci unsigned short power_reg; 23448c2ecf20Sopenharmony_ci unsigned short mask; 23458c2ecf20Sopenharmony_ci}; 23468c2ecf20Sopenharmony_ci 23478c2ecf20Sopenharmony_cienum { PWIDX_ADC, PWIDX_FRONT, PWIDX_CLFE, PWIDX_SURR, PWIDX_MIC, PWIDX_SIZE }; 23488c2ecf20Sopenharmony_ci 23498c2ecf20Sopenharmony_cistatic const struct ac97_power_reg power_regs[PWIDX_SIZE] = { 23508c2ecf20Sopenharmony_ci [PWIDX_ADC] = { AC97_PCM_LR_ADC_RATE, AC97_POWERDOWN, AC97_PD_PR0}, 23518c2ecf20Sopenharmony_ci [PWIDX_FRONT] = { AC97_PCM_FRONT_DAC_RATE, AC97_POWERDOWN, AC97_PD_PR1}, 23528c2ecf20Sopenharmony_ci [PWIDX_CLFE] = { AC97_PCM_LFE_DAC_RATE, AC97_EXTENDED_STATUS, 23538c2ecf20Sopenharmony_ci AC97_EA_PRI | AC97_EA_PRK}, 23548c2ecf20Sopenharmony_ci [PWIDX_SURR] = { AC97_PCM_SURR_DAC_RATE, AC97_EXTENDED_STATUS, 23558c2ecf20Sopenharmony_ci AC97_EA_PRJ}, 23568c2ecf20Sopenharmony_ci [PWIDX_MIC] = { AC97_PCM_MIC_ADC_RATE, AC97_EXTENDED_STATUS, 23578c2ecf20Sopenharmony_ci AC97_EA_PRL}, 23588c2ecf20Sopenharmony_ci}; 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE 23618c2ecf20Sopenharmony_ci/** 23628c2ecf20Sopenharmony_ci * snd_ac97_update_power - update the powerdown register 23638c2ecf20Sopenharmony_ci * @ac97: the codec instance 23648c2ecf20Sopenharmony_ci * @reg: the rate register, e.g. AC97_PCM_FRONT_DAC_RATE 23658c2ecf20Sopenharmony_ci * @powerup: non-zero when power up the part 23668c2ecf20Sopenharmony_ci * 23678c2ecf20Sopenharmony_ci * Update the AC97 powerdown register bits of the given part. 23688c2ecf20Sopenharmony_ci * 23698c2ecf20Sopenharmony_ci * Return: Zero. 23708c2ecf20Sopenharmony_ci */ 23718c2ecf20Sopenharmony_ciint snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup) 23728c2ecf20Sopenharmony_ci{ 23738c2ecf20Sopenharmony_ci int i; 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ci if (! ac97) 23768c2ecf20Sopenharmony_ci return 0; 23778c2ecf20Sopenharmony_ci 23788c2ecf20Sopenharmony_ci if (reg) { 23798c2ecf20Sopenharmony_ci /* SPDIF requires DAC power, too */ 23808c2ecf20Sopenharmony_ci if (reg == AC97_SPDIF) 23818c2ecf20Sopenharmony_ci reg = AC97_PCM_FRONT_DAC_RATE; 23828c2ecf20Sopenharmony_ci for (i = 0; i < PWIDX_SIZE; i++) { 23838c2ecf20Sopenharmony_ci if (power_regs[i].reg == reg) { 23848c2ecf20Sopenharmony_ci if (powerup) 23858c2ecf20Sopenharmony_ci ac97->power_up |= (1 << i); 23868c2ecf20Sopenharmony_ci else 23878c2ecf20Sopenharmony_ci ac97->power_up &= ~(1 << i); 23888c2ecf20Sopenharmony_ci break; 23898c2ecf20Sopenharmony_ci } 23908c2ecf20Sopenharmony_ci } 23918c2ecf20Sopenharmony_ci } 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_ci if (ac97_is_power_save_mode(ac97) && !powerup) 23948c2ecf20Sopenharmony_ci /* adjust power-down bits after two seconds delay 23958c2ecf20Sopenharmony_ci * (for avoiding loud click noises for many (OSS) apps 23968c2ecf20Sopenharmony_ci * that open/close frequently) 23978c2ecf20Sopenharmony_ci */ 23988c2ecf20Sopenharmony_ci schedule_delayed_work(&ac97->power_work, 23998c2ecf20Sopenharmony_ci msecs_to_jiffies(power_save * 1000)); 24008c2ecf20Sopenharmony_ci else { 24018c2ecf20Sopenharmony_ci cancel_delayed_work(&ac97->power_work); 24028c2ecf20Sopenharmony_ci update_power_regs(ac97); 24038c2ecf20Sopenharmony_ci } 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci return 0; 24068c2ecf20Sopenharmony_ci} 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_update_power); 24098c2ecf20Sopenharmony_ci#endif /* CONFIG_SND_AC97_POWER_SAVE */ 24108c2ecf20Sopenharmony_ci 24118c2ecf20Sopenharmony_cistatic void update_power_regs(struct snd_ac97 *ac97) 24128c2ecf20Sopenharmony_ci{ 24138c2ecf20Sopenharmony_ci unsigned int power_up, bits; 24148c2ecf20Sopenharmony_ci int i; 24158c2ecf20Sopenharmony_ci 24168c2ecf20Sopenharmony_ci power_up = (1 << PWIDX_FRONT) | (1 << PWIDX_ADC); 24178c2ecf20Sopenharmony_ci power_up |= (1 << PWIDX_MIC); 24188c2ecf20Sopenharmony_ci if (ac97->scaps & AC97_SCAP_SURROUND_DAC) 24198c2ecf20Sopenharmony_ci power_up |= (1 << PWIDX_SURR); 24208c2ecf20Sopenharmony_ci if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) 24218c2ecf20Sopenharmony_ci power_up |= (1 << PWIDX_CLFE); 24228c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE 24238c2ecf20Sopenharmony_ci if (ac97_is_power_save_mode(ac97)) 24248c2ecf20Sopenharmony_ci power_up = ac97->power_up; 24258c2ecf20Sopenharmony_ci#endif 24268c2ecf20Sopenharmony_ci if (power_up) { 24278c2ecf20Sopenharmony_ci if (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2) { 24288c2ecf20Sopenharmony_ci /* needs power-up analog mix and vref */ 24298c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_POWERDOWN, 24308c2ecf20Sopenharmony_ci AC97_PD_PR3, 0); 24318c2ecf20Sopenharmony_ci msleep(1); 24328c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_POWERDOWN, 24338c2ecf20Sopenharmony_ci AC97_PD_PR2, 0); 24348c2ecf20Sopenharmony_ci } 24358c2ecf20Sopenharmony_ci } 24368c2ecf20Sopenharmony_ci for (i = 0; i < PWIDX_SIZE; i++) { 24378c2ecf20Sopenharmony_ci if (power_up & (1 << i)) 24388c2ecf20Sopenharmony_ci bits = 0; 24398c2ecf20Sopenharmony_ci else 24408c2ecf20Sopenharmony_ci bits = power_regs[i].mask; 24418c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, power_regs[i].power_reg, 24428c2ecf20Sopenharmony_ci power_regs[i].mask, bits); 24438c2ecf20Sopenharmony_ci } 24448c2ecf20Sopenharmony_ci if (! power_up) { 24458c2ecf20Sopenharmony_ci if (! (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2)) { 24468c2ecf20Sopenharmony_ci /* power down analog mix and vref */ 24478c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_POWERDOWN, 24488c2ecf20Sopenharmony_ci AC97_PD_PR2, AC97_PD_PR2); 24498c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_POWERDOWN, 24508c2ecf20Sopenharmony_ci AC97_PD_PR3, AC97_PD_PR3); 24518c2ecf20Sopenharmony_ci } 24528c2ecf20Sopenharmony_ci } 24538c2ecf20Sopenharmony_ci} 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 24578c2ecf20Sopenharmony_ci/** 24588c2ecf20Sopenharmony_ci * snd_ac97_suspend - General suspend function for AC97 codec 24598c2ecf20Sopenharmony_ci * @ac97: the ac97 instance 24608c2ecf20Sopenharmony_ci * 24618c2ecf20Sopenharmony_ci * Suspends the codec, power down the chip. 24628c2ecf20Sopenharmony_ci */ 24638c2ecf20Sopenharmony_civoid snd_ac97_suspend(struct snd_ac97 *ac97) 24648c2ecf20Sopenharmony_ci{ 24658c2ecf20Sopenharmony_ci if (! ac97) 24668c2ecf20Sopenharmony_ci return; 24678c2ecf20Sopenharmony_ci if (ac97->build_ops->suspend) 24688c2ecf20Sopenharmony_ci ac97->build_ops->suspend(ac97); 24698c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE 24708c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&ac97->power_work); 24718c2ecf20Sopenharmony_ci#endif 24728c2ecf20Sopenharmony_ci snd_ac97_powerdown(ac97); 24738c2ecf20Sopenharmony_ci} 24748c2ecf20Sopenharmony_ci 24758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_suspend); 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_ci/* 24788c2ecf20Sopenharmony_ci * restore ac97 status 24798c2ecf20Sopenharmony_ci */ 24808c2ecf20Sopenharmony_cistatic void snd_ac97_restore_status(struct snd_ac97 *ac97) 24818c2ecf20Sopenharmony_ci{ 24828c2ecf20Sopenharmony_ci int i; 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_ci for (i = 2; i < 0x7c ; i += 2) { 24858c2ecf20Sopenharmony_ci if (i == AC97_POWERDOWN || i == AC97_EXTENDED_ID) 24868c2ecf20Sopenharmony_ci continue; 24878c2ecf20Sopenharmony_ci /* restore only accessible registers 24888c2ecf20Sopenharmony_ci * some chip (e.g. nm256) may hang up when unsupported registers 24898c2ecf20Sopenharmony_ci * are accessed..! 24908c2ecf20Sopenharmony_ci */ 24918c2ecf20Sopenharmony_ci if (test_bit(i, ac97->reg_accessed)) { 24928c2ecf20Sopenharmony_ci snd_ac97_write(ac97, i, ac97->regs[i]); 24938c2ecf20Sopenharmony_ci snd_ac97_read(ac97, i); 24948c2ecf20Sopenharmony_ci } 24958c2ecf20Sopenharmony_ci } 24968c2ecf20Sopenharmony_ci} 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci/* 24998c2ecf20Sopenharmony_ci * restore IEC958 status 25008c2ecf20Sopenharmony_ci */ 25018c2ecf20Sopenharmony_cistatic void snd_ac97_restore_iec958(struct snd_ac97 *ac97) 25028c2ecf20Sopenharmony_ci{ 25038c2ecf20Sopenharmony_ci if (ac97->ext_id & AC97_EI_SPDIF) { 25048c2ecf20Sopenharmony_ci if (ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_SPDIF) { 25058c2ecf20Sopenharmony_ci /* reset spdif status */ 25068c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); 25078c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_EXTENDED_STATUS, ac97->regs[AC97_EXTENDED_STATUS]); 25088c2ecf20Sopenharmony_ci if (ac97->flags & AC97_CS_SPDIF) 25098c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_CSR_SPDIF, ac97->regs[AC97_CSR_SPDIF]); 25108c2ecf20Sopenharmony_ci else 25118c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_SPDIF, ac97->regs[AC97_SPDIF]); 25128c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); /* turn on again */ 25138c2ecf20Sopenharmony_ci } 25148c2ecf20Sopenharmony_ci } 25158c2ecf20Sopenharmony_ci} 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci/** 25188c2ecf20Sopenharmony_ci * snd_ac97_resume - General resume function for AC97 codec 25198c2ecf20Sopenharmony_ci * @ac97: the ac97 instance 25208c2ecf20Sopenharmony_ci * 25218c2ecf20Sopenharmony_ci * Do the standard resume procedure, power up and restoring the 25228c2ecf20Sopenharmony_ci * old register values. 25238c2ecf20Sopenharmony_ci */ 25248c2ecf20Sopenharmony_civoid snd_ac97_resume(struct snd_ac97 *ac97) 25258c2ecf20Sopenharmony_ci{ 25268c2ecf20Sopenharmony_ci unsigned long end_time; 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_ci if (! ac97) 25298c2ecf20Sopenharmony_ci return; 25308c2ecf20Sopenharmony_ci 25318c2ecf20Sopenharmony_ci if (ac97->bus->ops->reset) { 25328c2ecf20Sopenharmony_ci ac97->bus->ops->reset(ac97); 25338c2ecf20Sopenharmony_ci goto __reset_ready; 25348c2ecf20Sopenharmony_ci } 25358c2ecf20Sopenharmony_ci 25368c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_POWERDOWN, 0); 25378c2ecf20Sopenharmony_ci if (! (ac97->flags & AC97_DEFAULT_POWER_OFF)) { 25388c2ecf20Sopenharmony_ci if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO)) 25398c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_RESET, 0); 25408c2ecf20Sopenharmony_ci else if (!(ac97->scaps & AC97_SCAP_SKIP_MODEM)) 25418c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_EXTENDED_MID, 0); 25428c2ecf20Sopenharmony_ci udelay(100); 25438c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_POWERDOWN, 0); 25448c2ecf20Sopenharmony_ci } 25458c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_GENERAL_PURPOSE, 0); 25468c2ecf20Sopenharmony_ci 25478c2ecf20Sopenharmony_ci snd_ac97_write(ac97, AC97_POWERDOWN, ac97->regs[AC97_POWERDOWN]); 25488c2ecf20Sopenharmony_ci if (ac97_is_audio(ac97)) { 25498c2ecf20Sopenharmony_ci ac97->bus->ops->write(ac97, AC97_MASTER, 0x8101); 25508c2ecf20Sopenharmony_ci end_time = jiffies + msecs_to_jiffies(100); 25518c2ecf20Sopenharmony_ci do { 25528c2ecf20Sopenharmony_ci if (snd_ac97_read(ac97, AC97_MASTER) == 0x8101) 25538c2ecf20Sopenharmony_ci break; 25548c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(1); 25558c2ecf20Sopenharmony_ci } while (time_after_eq(end_time, jiffies)); 25568c2ecf20Sopenharmony_ci /* FIXME: extra delay */ 25578c2ecf20Sopenharmony_ci ac97->bus->ops->write(ac97, AC97_MASTER, AC97_MUTE_MASK_MONO); 25588c2ecf20Sopenharmony_ci if (snd_ac97_read(ac97, AC97_MASTER) != AC97_MUTE_MASK_MONO) 25598c2ecf20Sopenharmony_ci msleep(250); 25608c2ecf20Sopenharmony_ci } else { 25618c2ecf20Sopenharmony_ci end_time = jiffies + msecs_to_jiffies(100); 25628c2ecf20Sopenharmony_ci do { 25638c2ecf20Sopenharmony_ci unsigned short val = snd_ac97_read(ac97, AC97_EXTENDED_MID); 25648c2ecf20Sopenharmony_ci if (val != 0xffff && (val & 1) != 0) 25658c2ecf20Sopenharmony_ci break; 25668c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(1); 25678c2ecf20Sopenharmony_ci } while (time_after_eq(end_time, jiffies)); 25688c2ecf20Sopenharmony_ci } 25698c2ecf20Sopenharmony_ci__reset_ready: 25708c2ecf20Sopenharmony_ci 25718c2ecf20Sopenharmony_ci if (ac97->bus->ops->init) 25728c2ecf20Sopenharmony_ci ac97->bus->ops->init(ac97); 25738c2ecf20Sopenharmony_ci 25748c2ecf20Sopenharmony_ci if (ac97->build_ops->resume) 25758c2ecf20Sopenharmony_ci ac97->build_ops->resume(ac97); 25768c2ecf20Sopenharmony_ci else { 25778c2ecf20Sopenharmony_ci snd_ac97_restore_status(ac97); 25788c2ecf20Sopenharmony_ci snd_ac97_restore_iec958(ac97); 25798c2ecf20Sopenharmony_ci } 25808c2ecf20Sopenharmony_ci} 25818c2ecf20Sopenharmony_ci 25828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_resume); 25838c2ecf20Sopenharmony_ci#endif 25848c2ecf20Sopenharmony_ci 25858c2ecf20Sopenharmony_ci 25868c2ecf20Sopenharmony_ci/* 25878c2ecf20Sopenharmony_ci * Hardware tuning 25888c2ecf20Sopenharmony_ci */ 25898c2ecf20Sopenharmony_cistatic void set_ctl_name(char *dst, const char *src, const char *suffix) 25908c2ecf20Sopenharmony_ci{ 25918c2ecf20Sopenharmony_ci if (suffix) 25928c2ecf20Sopenharmony_ci sprintf(dst, "%s %s", src, suffix); 25938c2ecf20Sopenharmony_ci else 25948c2ecf20Sopenharmony_ci strcpy(dst, src); 25958c2ecf20Sopenharmony_ci} 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_ci/* remove the control with the given name and optional suffix */ 25988c2ecf20Sopenharmony_cistatic int snd_ac97_remove_ctl(struct snd_ac97 *ac97, const char *name, 25998c2ecf20Sopenharmony_ci const char *suffix) 26008c2ecf20Sopenharmony_ci{ 26018c2ecf20Sopenharmony_ci struct snd_ctl_elem_id id; 26028c2ecf20Sopenharmony_ci memset(&id, 0, sizeof(id)); 26038c2ecf20Sopenharmony_ci set_ctl_name(id.name, name, suffix); 26048c2ecf20Sopenharmony_ci id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 26058c2ecf20Sopenharmony_ci return snd_ctl_remove_id(ac97->bus->card, &id); 26068c2ecf20Sopenharmony_ci} 26078c2ecf20Sopenharmony_ci 26088c2ecf20Sopenharmony_cistatic struct snd_kcontrol *ctl_find(struct snd_ac97 *ac97, const char *name, const char *suffix) 26098c2ecf20Sopenharmony_ci{ 26108c2ecf20Sopenharmony_ci struct snd_ctl_elem_id sid; 26118c2ecf20Sopenharmony_ci memset(&sid, 0, sizeof(sid)); 26128c2ecf20Sopenharmony_ci set_ctl_name(sid.name, name, suffix); 26138c2ecf20Sopenharmony_ci sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 26148c2ecf20Sopenharmony_ci return snd_ctl_find_id(ac97->bus->card, &sid); 26158c2ecf20Sopenharmony_ci} 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci/* rename the control with the given name and optional suffix */ 26188c2ecf20Sopenharmony_cistatic int snd_ac97_rename_ctl(struct snd_ac97 *ac97, const char *src, 26198c2ecf20Sopenharmony_ci const char *dst, const char *suffix) 26208c2ecf20Sopenharmony_ci{ 26218c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl = ctl_find(ac97, src, suffix); 26228c2ecf20Sopenharmony_ci if (kctl) { 26238c2ecf20Sopenharmony_ci set_ctl_name(kctl->id.name, dst, suffix); 26248c2ecf20Sopenharmony_ci return 0; 26258c2ecf20Sopenharmony_ci } 26268c2ecf20Sopenharmony_ci return -ENOENT; 26278c2ecf20Sopenharmony_ci} 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ci/* rename both Volume and Switch controls - don't check the return value */ 26308c2ecf20Sopenharmony_cistatic void snd_ac97_rename_vol_ctl(struct snd_ac97 *ac97, const char *src, 26318c2ecf20Sopenharmony_ci const char *dst) 26328c2ecf20Sopenharmony_ci{ 26338c2ecf20Sopenharmony_ci snd_ac97_rename_ctl(ac97, src, dst, "Switch"); 26348c2ecf20Sopenharmony_ci snd_ac97_rename_ctl(ac97, src, dst, "Volume"); 26358c2ecf20Sopenharmony_ci} 26368c2ecf20Sopenharmony_ci 26378c2ecf20Sopenharmony_ci/* swap controls */ 26388c2ecf20Sopenharmony_cistatic int snd_ac97_swap_ctl(struct snd_ac97 *ac97, const char *s1, 26398c2ecf20Sopenharmony_ci const char *s2, const char *suffix) 26408c2ecf20Sopenharmony_ci{ 26418c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl1, *kctl2; 26428c2ecf20Sopenharmony_ci kctl1 = ctl_find(ac97, s1, suffix); 26438c2ecf20Sopenharmony_ci kctl2 = ctl_find(ac97, s2, suffix); 26448c2ecf20Sopenharmony_ci if (kctl1 && kctl2) { 26458c2ecf20Sopenharmony_ci set_ctl_name(kctl1->id.name, s2, suffix); 26468c2ecf20Sopenharmony_ci set_ctl_name(kctl2->id.name, s1, suffix); 26478c2ecf20Sopenharmony_ci return 0; 26488c2ecf20Sopenharmony_ci } 26498c2ecf20Sopenharmony_ci return -ENOENT; 26508c2ecf20Sopenharmony_ci} 26518c2ecf20Sopenharmony_ci 26528c2ecf20Sopenharmony_ci#if 1 26538c2ecf20Sopenharmony_ci/* bind hp and master controls instead of using only hp control */ 26548c2ecf20Sopenharmony_cistatic int bind_hp_volsw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 26558c2ecf20Sopenharmony_ci{ 26568c2ecf20Sopenharmony_ci int err = snd_ac97_put_volsw(kcontrol, ucontrol); 26578c2ecf20Sopenharmony_ci if (err > 0) { 26588c2ecf20Sopenharmony_ci unsigned long priv_saved = kcontrol->private_value; 26598c2ecf20Sopenharmony_ci kcontrol->private_value = (kcontrol->private_value & ~0xff) | AC97_HEADPHONE; 26608c2ecf20Sopenharmony_ci snd_ac97_put_volsw(kcontrol, ucontrol); 26618c2ecf20Sopenharmony_ci kcontrol->private_value = priv_saved; 26628c2ecf20Sopenharmony_ci } 26638c2ecf20Sopenharmony_ci return err; 26648c2ecf20Sopenharmony_ci} 26658c2ecf20Sopenharmony_ci 26668c2ecf20Sopenharmony_ci/* ac97 tune: bind Master and Headphone controls */ 26678c2ecf20Sopenharmony_cistatic int tune_hp_only(struct snd_ac97 *ac97) 26688c2ecf20Sopenharmony_ci{ 26698c2ecf20Sopenharmony_ci struct snd_kcontrol *msw = ctl_find(ac97, "Master Playback Switch", NULL); 26708c2ecf20Sopenharmony_ci struct snd_kcontrol *mvol = ctl_find(ac97, "Master Playback Volume", NULL); 26718c2ecf20Sopenharmony_ci if (! msw || ! mvol) 26728c2ecf20Sopenharmony_ci return -ENOENT; 26738c2ecf20Sopenharmony_ci msw->put = bind_hp_volsw_put; 26748c2ecf20Sopenharmony_ci mvol->put = bind_hp_volsw_put; 26758c2ecf20Sopenharmony_ci snd_ac97_remove_ctl(ac97, "Headphone Playback", "Switch"); 26768c2ecf20Sopenharmony_ci snd_ac97_remove_ctl(ac97, "Headphone Playback", "Volume"); 26778c2ecf20Sopenharmony_ci return 0; 26788c2ecf20Sopenharmony_ci} 26798c2ecf20Sopenharmony_ci 26808c2ecf20Sopenharmony_ci#else 26818c2ecf20Sopenharmony_ci/* ac97 tune: use Headphone control as master */ 26828c2ecf20Sopenharmony_cistatic int tune_hp_only(struct snd_ac97 *ac97) 26838c2ecf20Sopenharmony_ci{ 26848c2ecf20Sopenharmony_ci if (ctl_find(ac97, "Headphone Playback Switch", NULL) == NULL) 26858c2ecf20Sopenharmony_ci return -ENOENT; 26868c2ecf20Sopenharmony_ci snd_ac97_remove_ctl(ac97, "Master Playback", "Switch"); 26878c2ecf20Sopenharmony_ci snd_ac97_remove_ctl(ac97, "Master Playback", "Volume"); 26888c2ecf20Sopenharmony_ci snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback"); 26898c2ecf20Sopenharmony_ci return 0; 26908c2ecf20Sopenharmony_ci} 26918c2ecf20Sopenharmony_ci#endif 26928c2ecf20Sopenharmony_ci 26938c2ecf20Sopenharmony_ci/* ac97 tune: swap Headphone and Master controls */ 26948c2ecf20Sopenharmony_cistatic int tune_swap_hp(struct snd_ac97 *ac97) 26958c2ecf20Sopenharmony_ci{ 26968c2ecf20Sopenharmony_ci if (ctl_find(ac97, "Headphone Playback Switch", NULL) == NULL) 26978c2ecf20Sopenharmony_ci return -ENOENT; 26988c2ecf20Sopenharmony_ci snd_ac97_rename_vol_ctl(ac97, "Master Playback", "Line-Out Playback"); 26998c2ecf20Sopenharmony_ci snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback"); 27008c2ecf20Sopenharmony_ci return 0; 27018c2ecf20Sopenharmony_ci} 27028c2ecf20Sopenharmony_ci 27038c2ecf20Sopenharmony_ci/* ac97 tune: swap Surround and Master controls */ 27048c2ecf20Sopenharmony_cistatic int tune_swap_surround(struct snd_ac97 *ac97) 27058c2ecf20Sopenharmony_ci{ 27068c2ecf20Sopenharmony_ci if (snd_ac97_swap_ctl(ac97, "Master Playback", "Surround Playback", "Switch") || 27078c2ecf20Sopenharmony_ci snd_ac97_swap_ctl(ac97, "Master Playback", "Surround Playback", "Volume")) 27088c2ecf20Sopenharmony_ci return -ENOENT; 27098c2ecf20Sopenharmony_ci return 0; 27108c2ecf20Sopenharmony_ci} 27118c2ecf20Sopenharmony_ci 27128c2ecf20Sopenharmony_ci/* ac97 tune: set up mic sharing for AD codecs */ 27138c2ecf20Sopenharmony_cistatic int tune_ad_sharing(struct snd_ac97 *ac97) 27148c2ecf20Sopenharmony_ci{ 27158c2ecf20Sopenharmony_ci unsigned short scfg; 27168c2ecf20Sopenharmony_ci if ((ac97->id & 0xffffff00) != 0x41445300) { 27178c2ecf20Sopenharmony_ci ac97_err(ac97, "ac97_quirk AD_SHARING is only for AD codecs\n"); 27188c2ecf20Sopenharmony_ci return -EINVAL; 27198c2ecf20Sopenharmony_ci } 27208c2ecf20Sopenharmony_ci /* Turn on OMS bit to route microphone to back panel */ 27218c2ecf20Sopenharmony_ci scfg = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG); 27228c2ecf20Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, scfg | 0x0200); 27238c2ecf20Sopenharmony_ci return 0; 27248c2ecf20Sopenharmony_ci} 27258c2ecf20Sopenharmony_ci 27268c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ac97_alc_jack_detect = 27278c2ecf20Sopenharmony_ciAC97_SINGLE("Jack Detect", AC97_ALC650_CLOCK, 5, 1, 0); 27288c2ecf20Sopenharmony_ci 27298c2ecf20Sopenharmony_ci/* ac97 tune: set up ALC jack-select */ 27308c2ecf20Sopenharmony_cistatic int tune_alc_jack(struct snd_ac97 *ac97) 27318c2ecf20Sopenharmony_ci{ 27328c2ecf20Sopenharmony_ci if ((ac97->id & 0xffffff00) != 0x414c4700) { 27338c2ecf20Sopenharmony_ci ac97_err(ac97, 27348c2ecf20Sopenharmony_ci "ac97_quirk ALC_JACK is only for Realtek codecs\n"); 27358c2ecf20Sopenharmony_ci return -EINVAL; 27368c2ecf20Sopenharmony_ci } 27378c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, 0x7a, 0x20, 0x20); /* select jack detect function */ 27388c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, 0x7a, 0x01, 0x01); /* Line-out auto mute */ 27398c2ecf20Sopenharmony_ci if (ac97->id == AC97_ID_ALC658D) 27408c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, 0x74, 0x0800, 0x0800); 27418c2ecf20Sopenharmony_ci return snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&snd_ac97_alc_jack_detect, ac97)); 27428c2ecf20Sopenharmony_ci} 27438c2ecf20Sopenharmony_ci 27448c2ecf20Sopenharmony_ci/* ac97 tune: inversed EAPD bit */ 27458c2ecf20Sopenharmony_cistatic int tune_inv_eapd(struct snd_ac97 *ac97) 27468c2ecf20Sopenharmony_ci{ 27478c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl = ctl_find(ac97, "External Amplifier", NULL); 27488c2ecf20Sopenharmony_ci if (! kctl) 27498c2ecf20Sopenharmony_ci return -ENOENT; 27508c2ecf20Sopenharmony_ci set_inv_eapd(ac97, kctl); 27518c2ecf20Sopenharmony_ci return 0; 27528c2ecf20Sopenharmony_ci} 27538c2ecf20Sopenharmony_ci 27548c2ecf20Sopenharmony_cistatic int master_mute_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 27558c2ecf20Sopenharmony_ci{ 27568c2ecf20Sopenharmony_ci int err = snd_ac97_put_volsw(kcontrol, ucontrol); 27578c2ecf20Sopenharmony_ci if (err > 0) { 27588c2ecf20Sopenharmony_ci struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); 27598c2ecf20Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0x0f; 27608c2ecf20Sopenharmony_ci int rshift = (kcontrol->private_value >> 12) & 0x0f; 27618c2ecf20Sopenharmony_ci unsigned short mask; 27628c2ecf20Sopenharmony_ci if (shift != rshift) 27638c2ecf20Sopenharmony_ci mask = AC97_MUTE_MASK_STEREO; 27648c2ecf20Sopenharmony_ci else 27658c2ecf20Sopenharmony_ci mask = AC97_MUTE_MASK_MONO; 27668c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_POWERDOWN, AC97_PD_EAPD, 27678c2ecf20Sopenharmony_ci (ac97->regs[AC97_MASTER] & mask) == mask ? 27688c2ecf20Sopenharmony_ci AC97_PD_EAPD : 0); 27698c2ecf20Sopenharmony_ci } 27708c2ecf20Sopenharmony_ci return err; 27718c2ecf20Sopenharmony_ci} 27728c2ecf20Sopenharmony_ci 27738c2ecf20Sopenharmony_ci/* ac97 tune: EAPD controls mute LED bound with the master mute */ 27748c2ecf20Sopenharmony_cistatic int tune_mute_led(struct snd_ac97 *ac97) 27758c2ecf20Sopenharmony_ci{ 27768c2ecf20Sopenharmony_ci struct snd_kcontrol *msw = ctl_find(ac97, "Master Playback Switch", NULL); 27778c2ecf20Sopenharmony_ci if (! msw) 27788c2ecf20Sopenharmony_ci return -ENOENT; 27798c2ecf20Sopenharmony_ci msw->put = master_mute_sw_put; 27808c2ecf20Sopenharmony_ci snd_ac97_remove_ctl(ac97, "External Amplifier", NULL); 27818c2ecf20Sopenharmony_ci snd_ac97_update_bits( 27828c2ecf20Sopenharmony_ci ac97, AC97_POWERDOWN, 27838c2ecf20Sopenharmony_ci AC97_PD_EAPD, AC97_PD_EAPD /* mute LED on */ 27848c2ecf20Sopenharmony_ci ); 27858c2ecf20Sopenharmony_ci ac97->scaps |= AC97_SCAP_EAPD_LED; 27868c2ecf20Sopenharmony_ci return 0; 27878c2ecf20Sopenharmony_ci} 27888c2ecf20Sopenharmony_ci 27898c2ecf20Sopenharmony_cistatic int hp_master_mute_sw_put(struct snd_kcontrol *kcontrol, 27908c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 27918c2ecf20Sopenharmony_ci{ 27928c2ecf20Sopenharmony_ci int err = bind_hp_volsw_put(kcontrol, ucontrol); 27938c2ecf20Sopenharmony_ci if (err > 0) { 27948c2ecf20Sopenharmony_ci struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); 27958c2ecf20Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0x0f; 27968c2ecf20Sopenharmony_ci int rshift = (kcontrol->private_value >> 12) & 0x0f; 27978c2ecf20Sopenharmony_ci unsigned short mask; 27988c2ecf20Sopenharmony_ci if (shift != rshift) 27998c2ecf20Sopenharmony_ci mask = AC97_MUTE_MASK_STEREO; 28008c2ecf20Sopenharmony_ci else 28018c2ecf20Sopenharmony_ci mask = AC97_MUTE_MASK_MONO; 28028c2ecf20Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_POWERDOWN, AC97_PD_EAPD, 28038c2ecf20Sopenharmony_ci (ac97->regs[AC97_MASTER] & mask) == mask ? 28048c2ecf20Sopenharmony_ci AC97_PD_EAPD : 0); 28058c2ecf20Sopenharmony_ci } 28068c2ecf20Sopenharmony_ci return err; 28078c2ecf20Sopenharmony_ci} 28088c2ecf20Sopenharmony_ci 28098c2ecf20Sopenharmony_cistatic int tune_hp_mute_led(struct snd_ac97 *ac97) 28108c2ecf20Sopenharmony_ci{ 28118c2ecf20Sopenharmony_ci struct snd_kcontrol *msw = ctl_find(ac97, "Master Playback Switch", NULL); 28128c2ecf20Sopenharmony_ci struct snd_kcontrol *mvol = ctl_find(ac97, "Master Playback Volume", NULL); 28138c2ecf20Sopenharmony_ci if (! msw || ! mvol) 28148c2ecf20Sopenharmony_ci return -ENOENT; 28158c2ecf20Sopenharmony_ci msw->put = hp_master_mute_sw_put; 28168c2ecf20Sopenharmony_ci mvol->put = bind_hp_volsw_put; 28178c2ecf20Sopenharmony_ci snd_ac97_remove_ctl(ac97, "External Amplifier", NULL); 28188c2ecf20Sopenharmony_ci snd_ac97_remove_ctl(ac97, "Headphone Playback", "Switch"); 28198c2ecf20Sopenharmony_ci snd_ac97_remove_ctl(ac97, "Headphone Playback", "Volume"); 28208c2ecf20Sopenharmony_ci snd_ac97_update_bits( 28218c2ecf20Sopenharmony_ci ac97, AC97_POWERDOWN, 28228c2ecf20Sopenharmony_ci AC97_PD_EAPD, AC97_PD_EAPD /* mute LED on */ 28238c2ecf20Sopenharmony_ci ); 28248c2ecf20Sopenharmony_ci return 0; 28258c2ecf20Sopenharmony_ci} 28268c2ecf20Sopenharmony_ci 28278c2ecf20Sopenharmony_cistruct quirk_table { 28288c2ecf20Sopenharmony_ci const char *name; 28298c2ecf20Sopenharmony_ci int (*func)(struct snd_ac97 *); 28308c2ecf20Sopenharmony_ci}; 28318c2ecf20Sopenharmony_ci 28328c2ecf20Sopenharmony_cistatic const struct quirk_table applicable_quirks[] = { 28338c2ecf20Sopenharmony_ci { "none", NULL }, 28348c2ecf20Sopenharmony_ci { "hp_only", tune_hp_only }, 28358c2ecf20Sopenharmony_ci { "swap_hp", tune_swap_hp }, 28368c2ecf20Sopenharmony_ci { "swap_surround", tune_swap_surround }, 28378c2ecf20Sopenharmony_ci { "ad_sharing", tune_ad_sharing }, 28388c2ecf20Sopenharmony_ci { "alc_jack", tune_alc_jack }, 28398c2ecf20Sopenharmony_ci { "inv_eapd", tune_inv_eapd }, 28408c2ecf20Sopenharmony_ci { "mute_led", tune_mute_led }, 28418c2ecf20Sopenharmony_ci { "hp_mute_led", tune_hp_mute_led }, 28428c2ecf20Sopenharmony_ci}; 28438c2ecf20Sopenharmony_ci 28448c2ecf20Sopenharmony_ci/* apply the quirk with the given type */ 28458c2ecf20Sopenharmony_cistatic int apply_quirk(struct snd_ac97 *ac97, int type) 28468c2ecf20Sopenharmony_ci{ 28478c2ecf20Sopenharmony_ci if (type <= 0) 28488c2ecf20Sopenharmony_ci return 0; 28498c2ecf20Sopenharmony_ci else if (type >= ARRAY_SIZE(applicable_quirks)) 28508c2ecf20Sopenharmony_ci return -EINVAL; 28518c2ecf20Sopenharmony_ci if (applicable_quirks[type].func) 28528c2ecf20Sopenharmony_ci return applicable_quirks[type].func(ac97); 28538c2ecf20Sopenharmony_ci return 0; 28548c2ecf20Sopenharmony_ci} 28558c2ecf20Sopenharmony_ci 28568c2ecf20Sopenharmony_ci/* apply the quirk with the given name */ 28578c2ecf20Sopenharmony_cistatic int apply_quirk_str(struct snd_ac97 *ac97, const char *typestr) 28588c2ecf20Sopenharmony_ci{ 28598c2ecf20Sopenharmony_ci int i; 28608c2ecf20Sopenharmony_ci const struct quirk_table *q; 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(applicable_quirks); i++) { 28638c2ecf20Sopenharmony_ci q = &applicable_quirks[i]; 28648c2ecf20Sopenharmony_ci if (q->name && ! strcmp(typestr, q->name)) 28658c2ecf20Sopenharmony_ci return apply_quirk(ac97, i); 28668c2ecf20Sopenharmony_ci } 28678c2ecf20Sopenharmony_ci /* for compatibility, accept the numbers, too */ 28688c2ecf20Sopenharmony_ci if (*typestr >= '0' && *typestr <= '9') 28698c2ecf20Sopenharmony_ci return apply_quirk(ac97, (int)simple_strtoul(typestr, NULL, 10)); 28708c2ecf20Sopenharmony_ci return -EINVAL; 28718c2ecf20Sopenharmony_ci} 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_ci/** 28748c2ecf20Sopenharmony_ci * snd_ac97_tune_hardware - tune up the hardware 28758c2ecf20Sopenharmony_ci * @ac97: the ac97 instance 28768c2ecf20Sopenharmony_ci * @quirk: quirk list 28778c2ecf20Sopenharmony_ci * @override: explicit quirk value (overrides the list if non-NULL) 28788c2ecf20Sopenharmony_ci * 28798c2ecf20Sopenharmony_ci * Do some workaround for each pci device, such as renaming of the 28808c2ecf20Sopenharmony_ci * headphone (true line-out) control as "Master". 28818c2ecf20Sopenharmony_ci * The quirk-list must be terminated with a zero-filled entry. 28828c2ecf20Sopenharmony_ci * 28838c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 28848c2ecf20Sopenharmony_ci */ 28858c2ecf20Sopenharmony_ci 28868c2ecf20Sopenharmony_ciint snd_ac97_tune_hardware(struct snd_ac97 *ac97, 28878c2ecf20Sopenharmony_ci const struct ac97_quirk *quirk, const char *override) 28888c2ecf20Sopenharmony_ci{ 28898c2ecf20Sopenharmony_ci int result; 28908c2ecf20Sopenharmony_ci 28918c2ecf20Sopenharmony_ci /* quirk overriden? */ 28928c2ecf20Sopenharmony_ci if (override && strcmp(override, "-1") && strcmp(override, "default")) { 28938c2ecf20Sopenharmony_ci result = apply_quirk_str(ac97, override); 28948c2ecf20Sopenharmony_ci if (result < 0) 28958c2ecf20Sopenharmony_ci ac97_err(ac97, "applying quirk type %s failed (%d)\n", 28968c2ecf20Sopenharmony_ci override, result); 28978c2ecf20Sopenharmony_ci return result; 28988c2ecf20Sopenharmony_ci } 28998c2ecf20Sopenharmony_ci 29008c2ecf20Sopenharmony_ci if (! quirk) 29018c2ecf20Sopenharmony_ci return -EINVAL; 29028c2ecf20Sopenharmony_ci 29038c2ecf20Sopenharmony_ci for (; quirk->subvendor; quirk++) { 29048c2ecf20Sopenharmony_ci if (quirk->subvendor != ac97->subsystem_vendor) 29058c2ecf20Sopenharmony_ci continue; 29068c2ecf20Sopenharmony_ci if ((! quirk->mask && quirk->subdevice == ac97->subsystem_device) || 29078c2ecf20Sopenharmony_ci quirk->subdevice == (quirk->mask & ac97->subsystem_device)) { 29088c2ecf20Sopenharmony_ci if (quirk->codec_id && quirk->codec_id != ac97->id) 29098c2ecf20Sopenharmony_ci continue; 29108c2ecf20Sopenharmony_ci ac97_dbg(ac97, "ac97 quirk for %s (%04x:%04x)\n", 29118c2ecf20Sopenharmony_ci quirk->name, ac97->subsystem_vendor, 29128c2ecf20Sopenharmony_ci ac97->subsystem_device); 29138c2ecf20Sopenharmony_ci result = apply_quirk(ac97, quirk->type); 29148c2ecf20Sopenharmony_ci if (result < 0) 29158c2ecf20Sopenharmony_ci ac97_err(ac97, 29168c2ecf20Sopenharmony_ci "applying quirk type %d for %s failed (%d)\n", 29178c2ecf20Sopenharmony_ci quirk->type, quirk->name, result); 29188c2ecf20Sopenharmony_ci return result; 29198c2ecf20Sopenharmony_ci } 29208c2ecf20Sopenharmony_ci } 29218c2ecf20Sopenharmony_ci return 0; 29228c2ecf20Sopenharmony_ci} 29238c2ecf20Sopenharmony_ci 29248c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_tune_hardware); 2925