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