11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk> 41da177e4SLinus Torvalds * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit 5b18cd538STrent Piepho * Version: 0.0.18 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * FEATURES currently supported: 81da177e4SLinus Torvalds * See ca0106_main.c for features. 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * Changelog: 111da177e4SLinus Torvalds * Support interrupts per period. 121da177e4SLinus Torvalds * Removed noise from Center/LFE channel when in Analog mode. 131da177e4SLinus Torvalds * Rename and remove mixer controls. 141da177e4SLinus Torvalds * 0.0.6 151da177e4SLinus Torvalds * Use separate card based DMA buffer for periods table list. 161da177e4SLinus Torvalds * 0.0.7 171da177e4SLinus Torvalds * Change remove and rename ctrls into lists. 181da177e4SLinus Torvalds * 0.0.8 191da177e4SLinus Torvalds * Try to fix capture sources. 201da177e4SLinus Torvalds * 0.0.9 211da177e4SLinus Torvalds * Fix AC3 output. 221da177e4SLinus Torvalds * Enable S32_LE format support. 231da177e4SLinus Torvalds * 0.0.10 241da177e4SLinus Torvalds * Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".) 251da177e4SLinus Torvalds * 0.0.11 261da177e4SLinus Torvalds * Add Model name recognition. 271da177e4SLinus Torvalds * 0.0.12 281da177e4SLinus Torvalds * Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period. 291da177e4SLinus Torvalds * Remove redundent "voice" handling. 301da177e4SLinus Torvalds * 0.0.13 311da177e4SLinus Torvalds * Single trigger call for multi channels. 321da177e4SLinus Torvalds * 0.0.14 331da177e4SLinus Torvalds * Set limits based on what the sound card hardware can do. 341da177e4SLinus Torvalds * playback periods_min=2, periods_max=8 351da177e4SLinus Torvalds * capture hw constraints require period_size = n * 64 bytes. 361da177e4SLinus Torvalds * playback hw constraints require period_size = n * 64 bytes. 371da177e4SLinus Torvalds * 0.0.15 381da177e4SLinus Torvalds * Separated ca0106.c into separate functional .c files. 391da177e4SLinus Torvalds * 0.0.16 401da177e4SLinus Torvalds * Modified Copyright message. 41ed144f3cSJames Courtier-Dutton * 0.0.17 42ed144f3cSJames Courtier-Dutton * Implement Mic and Line in Capture. 43b18cd538STrent Piepho * 0.0.18 44b18cd538STrent Piepho * Add support for mute control on SB Live 24bit (cards w/ SPI DAC) 451da177e4SLinus Torvalds * 4625985edcSLucas De Marchi * This code was initially based on code from ALSA's emu10k1x.c which is: 471da177e4SLinus Torvalds * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com> 481da177e4SLinus Torvalds */ 491da177e4SLinus Torvalds #include <linux/delay.h> 501da177e4SLinus Torvalds #include <linux/init.h> 511da177e4SLinus Torvalds #include <linux/interrupt.h> 521da177e4SLinus Torvalds #include <linux/moduleparam.h> 531da177e4SLinus Torvalds #include <sound/core.h> 541da177e4SLinus Torvalds #include <sound/initval.h> 551da177e4SLinus Torvalds #include <sound/pcm.h> 561da177e4SLinus Torvalds #include <sound/ac97_codec.h> 571da177e4SLinus Torvalds #include <sound/info.h> 5842750b04SJaroslav Kysela #include <sound/tlv.h> 596cbbfe1cSTakashi Iwai #include <linux/io.h> 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds #include "ca0106.h" 621da177e4SLinus Torvalds 635da95273STakashi Iwai static void ca0106_spdif_enable(struct snd_ca0106 *emu) 645da95273STakashi Iwai { 655da95273STakashi Iwai unsigned int val; 665da95273STakashi Iwai 675da95273STakashi Iwai if (emu->spdif_enable) { 685da95273STakashi Iwai /* Digital */ 695da95273STakashi Iwai snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); 705da95273STakashi Iwai snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000); 715da95273STakashi Iwai val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000; 725da95273STakashi Iwai snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val); 735da95273STakashi Iwai val = inl(emu->port + GPIO) & ~0x101; 745da95273STakashi Iwai outl(val, emu->port + GPIO); 755da95273STakashi Iwai 765da95273STakashi Iwai } else { 775da95273STakashi Iwai /* Analog */ 785da95273STakashi Iwai snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); 795da95273STakashi Iwai snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000); 805da95273STakashi Iwai val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000; 815da95273STakashi Iwai snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val); 825da95273STakashi Iwai val = inl(emu->port + GPIO) | 0x101; 835da95273STakashi Iwai outl(val, emu->port + GPIO); 845da95273STakashi Iwai } 855da95273STakashi Iwai } 865da95273STakashi Iwai 875da95273STakashi Iwai static void ca0106_set_capture_source(struct snd_ca0106 *emu) 885da95273STakashi Iwai { 895da95273STakashi Iwai unsigned int val = emu->capture_source; 905da95273STakashi Iwai unsigned int source, mask; 915da95273STakashi Iwai source = (val << 28) | (val << 24) | (val << 20) | (val << 16); 925da95273STakashi Iwai mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff; 935da95273STakashi Iwai snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask); 945da95273STakashi Iwai } 955da95273STakashi Iwai 965da95273STakashi Iwai static void ca0106_set_i2c_capture_source(struct snd_ca0106 *emu, 975da95273STakashi Iwai unsigned int val, int force) 985da95273STakashi Iwai { 995da95273STakashi Iwai unsigned int ngain, ogain; 1005da95273STakashi Iwai u32 source; 1015da95273STakashi Iwai 1025da95273STakashi Iwai snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ 1035da95273STakashi Iwai ngain = emu->i2c_capture_volume[val][0]; /* Left */ 1045da95273STakashi Iwai ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */ 1055da95273STakashi Iwai if (force || ngain != ogain) 1065da95273STakashi Iwai snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ngain & 0xff); 1075da95273STakashi Iwai ngain = emu->i2c_capture_volume[val][1]; /* Right */ 1085da95273STakashi Iwai ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */ 1095da95273STakashi Iwai if (force || ngain != ogain) 1105da95273STakashi Iwai snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ngain & 0xff); 1115da95273STakashi Iwai source = 1 << val; 1125da95273STakashi Iwai snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */ 1135da95273STakashi Iwai emu->i2c_capture_source = val; 1145da95273STakashi Iwai } 1155da95273STakashi Iwai 1165da95273STakashi Iwai static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu) 1175da95273STakashi Iwai { 1185da95273STakashi Iwai u32 tmp; 1195da95273STakashi Iwai 1205da95273STakashi Iwai if (emu->capture_mic_line_in) { 1215da95273STakashi Iwai /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */ 1225da95273STakashi Iwai tmp = inl(emu->port+GPIO) & ~0x400; 1235da95273STakashi Iwai tmp = tmp | 0x400; 1245da95273STakashi Iwai outl(tmp, emu->port+GPIO); 1255da95273STakashi Iwai /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */ 1265da95273STakashi Iwai } else { 1275da95273STakashi Iwai /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */ 1285da95273STakashi Iwai tmp = inl(emu->port+GPIO) & ~0x400; 1295da95273STakashi Iwai outl(tmp, emu->port+GPIO); 1305da95273STakashi Iwai /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */ 1315da95273STakashi Iwai } 1325da95273STakashi Iwai } 1335da95273STakashi Iwai 1345da95273STakashi Iwai static void ca0106_set_spdif_bits(struct snd_ca0106 *emu, int idx) 1355da95273STakashi Iwai { 1363d475829STakashi Iwai snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, emu->spdif_str_bits[idx]); 1375da95273STakashi Iwai } 1385da95273STakashi Iwai 1395da95273STakashi Iwai /* 1405da95273STakashi Iwai */ 1410cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1); 1420cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1); 14342750b04SJaroslav Kysela 144a5ce8890STakashi Iwai #define snd_ca0106_shared_spdif_info snd_ctl_boolean_mono_info 1451da177e4SLinus Torvalds 146e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol, 147e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 1481da177e4SLinus Torvalds { 149e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 1501da177e4SLinus Torvalds 1515fe619f9STakashi Iwai ucontrol->value.integer.value[0] = emu->spdif_enable; 1521da177e4SLinus Torvalds return 0; 1531da177e4SLinus Torvalds } 1541da177e4SLinus Torvalds 155e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol, 156e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 1571da177e4SLinus Torvalds { 158e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 1591da177e4SLinus Torvalds unsigned int val; 1601da177e4SLinus Torvalds int change = 0; 1611da177e4SLinus Torvalds 1625fe619f9STakashi Iwai val = !!ucontrol->value.integer.value[0]; 1631da177e4SLinus Torvalds change = (emu->spdif_enable != val); 1641da177e4SLinus Torvalds if (change) { 1651da177e4SLinus Torvalds emu->spdif_enable = val; 1665da95273STakashi Iwai ca0106_spdif_enable(emu); 1671da177e4SLinus Torvalds } 1681da177e4SLinus Torvalds return change; 1691da177e4SLinus Torvalds } 1701da177e4SLinus Torvalds 171e4a3d145STakashi Iwai static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol, 172e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 1731da177e4SLinus Torvalds { 174de95eae2STakashi Iwai static const char * const texts[6] = { 17539596dc8SJames Courtier-Dutton "IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out" 17695a98265STakashi Iwai }; 1771da177e4SLinus Torvalds 178de95eae2STakashi Iwai return snd_ctl_enum_info(uinfo, 1, 6, texts); 1791da177e4SLinus Torvalds } 1801da177e4SLinus Torvalds 181e4a3d145STakashi Iwai static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol, 182e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 1831da177e4SLinus Torvalds { 184e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds ucontrol->value.enumerated.item[0] = emu->capture_source; 1871da177e4SLinus Torvalds return 0; 1881da177e4SLinus Torvalds } 1891da177e4SLinus Torvalds 190e4a3d145STakashi Iwai static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol, 191e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 1921da177e4SLinus Torvalds { 193e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 1941da177e4SLinus Torvalds unsigned int val; 1951da177e4SLinus Torvalds int change = 0; 1961da177e4SLinus Torvalds 1971da177e4SLinus Torvalds val = ucontrol->value.enumerated.item[0] ; 1985fe619f9STakashi Iwai if (val >= 6) 1995fe619f9STakashi Iwai return -EINVAL; 2001da177e4SLinus Torvalds change = (emu->capture_source != val); 2011da177e4SLinus Torvalds if (change) { 2021da177e4SLinus Torvalds emu->capture_source = val; 2035da95273STakashi Iwai ca0106_set_capture_source(emu); 2041da177e4SLinus Torvalds } 2051da177e4SLinus Torvalds return change; 2061da177e4SLinus Torvalds } 2071da177e4SLinus Torvalds 2086129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol, 2096129daaaSJames Courtier-Dutton struct snd_ctl_elem_info *uinfo) 2106129daaaSJames Courtier-Dutton { 211de95eae2STakashi Iwai static const char * const texts[4] = { 2126129daaaSJames Courtier-Dutton "Phone", "Mic", "Line in", "Aux" 2136129daaaSJames Courtier-Dutton }; 2146129daaaSJames Courtier-Dutton 215de95eae2STakashi Iwai return snd_ctl_enum_info(uinfo, 1, 4, texts); 2166129daaaSJames Courtier-Dutton } 2176129daaaSJames Courtier-Dutton 2186129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol, 2196129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 2206129daaaSJames Courtier-Dutton { 2216129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 2226129daaaSJames Courtier-Dutton 2236129daaaSJames Courtier-Dutton ucontrol->value.enumerated.item[0] = emu->i2c_capture_source; 2246129daaaSJames Courtier-Dutton return 0; 2256129daaaSJames Courtier-Dutton } 2266129daaaSJames Courtier-Dutton 2276129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol, 2286129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 2296129daaaSJames Courtier-Dutton { 2306129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 2316129daaaSJames Courtier-Dutton unsigned int source_id; 2326129daaaSJames Courtier-Dutton int change = 0; 2336129daaaSJames Courtier-Dutton /* If the capture source has changed, 2346129daaaSJames Courtier-Dutton * update the capture volume from the cached value 2356129daaaSJames Courtier-Dutton * for the particular source. 2366129daaaSJames Courtier-Dutton */ 2376129daaaSJames Courtier-Dutton source_id = ucontrol->value.enumerated.item[0] ; 2385fe619f9STakashi Iwai if (source_id >= 4) 2395fe619f9STakashi Iwai return -EINVAL; 2406129daaaSJames Courtier-Dutton change = (emu->i2c_capture_source != source_id); 2416129daaaSJames Courtier-Dutton if (change) { 2425da95273STakashi Iwai ca0106_set_i2c_capture_source(emu, source_id, 0); 2436129daaaSJames Courtier-Dutton } 2446129daaaSJames Courtier-Dutton return change; 2456129daaaSJames Courtier-Dutton } 2466129daaaSJames Courtier-Dutton 247be0b7b01SJames Courtier-Dutton static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol, 248be0b7b01SJames Courtier-Dutton struct snd_ctl_elem_info *uinfo) 249be0b7b01SJames Courtier-Dutton { 250de95eae2STakashi Iwai static const char * const texts[2] = { "Side out", "Line in" }; 251be0b7b01SJames Courtier-Dutton 252de95eae2STakashi Iwai return snd_ctl_enum_info(uinfo, 1, 2, texts); 253be0b7b01SJames Courtier-Dutton } 254be0b7b01SJames Courtier-Dutton 255e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol, 256e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 257ed144f3cSJames Courtier-Dutton { 258de95eae2STakashi Iwai static const char * const texts[2] = { "Line in", "Mic in" }; 259ed144f3cSJames Courtier-Dutton 260de95eae2STakashi Iwai return snd_ctl_enum_info(uinfo, 1, 2, texts); 261ed144f3cSJames Courtier-Dutton } 262ed144f3cSJames Courtier-Dutton 263e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol, 264e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 265ed144f3cSJames Courtier-Dutton { 266e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 267ed144f3cSJames Courtier-Dutton 268ed144f3cSJames Courtier-Dutton ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in; 269ed144f3cSJames Courtier-Dutton return 0; 270ed144f3cSJames Courtier-Dutton } 271ed144f3cSJames Courtier-Dutton 272e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol, 273e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 274ed144f3cSJames Courtier-Dutton { 275e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 276ed144f3cSJames Courtier-Dutton unsigned int val; 277ed144f3cSJames Courtier-Dutton int change = 0; 278ed144f3cSJames Courtier-Dutton 279ed144f3cSJames Courtier-Dutton val = ucontrol->value.enumerated.item[0] ; 2805fe619f9STakashi Iwai if (val > 1) 2815fe619f9STakashi Iwai return -EINVAL; 282ed144f3cSJames Courtier-Dutton change = (emu->capture_mic_line_in != val); 283ed144f3cSJames Courtier-Dutton if (change) { 284ed144f3cSJames Courtier-Dutton emu->capture_mic_line_in = val; 2855da95273STakashi Iwai ca0106_set_capture_mic_line_in(emu); 286ed144f3cSJames Courtier-Dutton } 287ed144f3cSJames Courtier-Dutton return change; 288ed144f3cSJames Courtier-Dutton } 289ed144f3cSJames Courtier-Dutton 290f3b827e0SBhumika Goyal static const struct snd_kcontrol_new snd_ca0106_capture_mic_line_in = 291ed144f3cSJames Courtier-Dutton { 292ed144f3cSJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2936129daaaSJames Courtier-Dutton .name = "Shared Mic/Line in Capture Switch", 294ed144f3cSJames Courtier-Dutton .info = snd_ca0106_capture_mic_line_in_info, 295ed144f3cSJames Courtier-Dutton .get = snd_ca0106_capture_mic_line_in_get, 296ed144f3cSJames Courtier-Dutton .put = snd_ca0106_capture_mic_line_in_put 297ed144f3cSJames Courtier-Dutton }; 298ed144f3cSJames Courtier-Dutton 299f3b827e0SBhumika Goyal static const struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out = 300be0b7b01SJames Courtier-Dutton { 301be0b7b01SJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 302be0b7b01SJames Courtier-Dutton .name = "Shared Line in/Side out Capture Switch", 303be0b7b01SJames Courtier-Dutton .info = snd_ca0106_capture_line_in_side_out_info, 304be0b7b01SJames Courtier-Dutton .get = snd_ca0106_capture_mic_line_in_get, 305be0b7b01SJames Courtier-Dutton .put = snd_ca0106_capture_mic_line_in_put 306be0b7b01SJames Courtier-Dutton }; 307be0b7b01SJames Courtier-Dutton 308be0b7b01SJames Courtier-Dutton 309e4a3d145STakashi Iwai static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol, 310e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 3111da177e4SLinus Torvalds { 3121da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 3131da177e4SLinus Torvalds uinfo->count = 1; 3141da177e4SLinus Torvalds return 0; 3151da177e4SLinus Torvalds } 3161da177e4SLinus Torvalds 3173d475829STakashi Iwai static void decode_spdif_bits(unsigned char *status, unsigned int bits) 3183d475829STakashi Iwai { 3193d475829STakashi Iwai status[0] = (bits >> 0) & 0xff; 3203d475829STakashi Iwai status[1] = (bits >> 8) & 0xff; 3213d475829STakashi Iwai status[2] = (bits >> 16) & 0xff; 3223d475829STakashi Iwai status[3] = (bits >> 24) & 0xff; 3233d475829STakashi Iwai } 3243d475829STakashi Iwai 3253d475829STakashi Iwai static int snd_ca0106_spdif_get_default(struct snd_kcontrol *kcontrol, 326e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3271da177e4SLinus Torvalds { 328e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 3291da177e4SLinus Torvalds unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 3301da177e4SLinus Torvalds 3313d475829STakashi Iwai decode_spdif_bits(ucontrol->value.iec958.status, 3323d475829STakashi Iwai emu->spdif_bits[idx]); 3333d475829STakashi Iwai return 0; 3343d475829STakashi Iwai } 3353d475829STakashi Iwai 3363d475829STakashi Iwai static int snd_ca0106_spdif_get_stream(struct snd_kcontrol *kcontrol, 3373d475829STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3383d475829STakashi Iwai { 3393d475829STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 3403d475829STakashi Iwai unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 3413d475829STakashi Iwai 3423d475829STakashi Iwai decode_spdif_bits(ucontrol->value.iec958.status, 3433d475829STakashi Iwai emu->spdif_str_bits[idx]); 3441da177e4SLinus Torvalds return 0; 3451da177e4SLinus Torvalds } 3461da177e4SLinus Torvalds 347e4a3d145STakashi Iwai static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol, 348e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3491da177e4SLinus Torvalds { 3501da177e4SLinus Torvalds ucontrol->value.iec958.status[0] = 0xff; 3511da177e4SLinus Torvalds ucontrol->value.iec958.status[1] = 0xff; 3521da177e4SLinus Torvalds ucontrol->value.iec958.status[2] = 0xff; 3531da177e4SLinus Torvalds ucontrol->value.iec958.status[3] = 0xff; 3541da177e4SLinus Torvalds return 0; 3551da177e4SLinus Torvalds } 3561da177e4SLinus Torvalds 3573d475829STakashi Iwai static unsigned int encode_spdif_bits(unsigned char *status) 3583d475829STakashi Iwai { 3593d475829STakashi Iwai return ((unsigned int)status[0] << 0) | 3603d475829STakashi Iwai ((unsigned int)status[1] << 8) | 3613d475829STakashi Iwai ((unsigned int)status[2] << 16) | 3623d475829STakashi Iwai ((unsigned int)status[3] << 24); 3633d475829STakashi Iwai } 3643d475829STakashi Iwai 3653d475829STakashi Iwai static int snd_ca0106_spdif_put_default(struct snd_kcontrol *kcontrol, 366e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3671da177e4SLinus Torvalds { 368e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 3691da177e4SLinus Torvalds unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 3701da177e4SLinus Torvalds unsigned int val; 3711da177e4SLinus Torvalds 3723d475829STakashi Iwai val = encode_spdif_bits(ucontrol->value.iec958.status); 3733d475829STakashi Iwai if (val != emu->spdif_bits[idx]) { 3741da177e4SLinus Torvalds emu->spdif_bits[idx] = val; 3753d475829STakashi Iwai /* FIXME: this isn't safe, but needed to keep the compatibility 3763d475829STakashi Iwai * with older alsa-lib config 3773d475829STakashi Iwai */ 3783d475829STakashi Iwai emu->spdif_str_bits[idx] = val; 3795da95273STakashi Iwai ca0106_set_spdif_bits(emu, idx); 3803d475829STakashi Iwai return 1; 3811da177e4SLinus Torvalds } 3823d475829STakashi Iwai return 0; 3833d475829STakashi Iwai } 3843d475829STakashi Iwai 3853d475829STakashi Iwai static int snd_ca0106_spdif_put_stream(struct snd_kcontrol *kcontrol, 3863d475829STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3873d475829STakashi Iwai { 3883d475829STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 3893d475829STakashi Iwai unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 3903d475829STakashi Iwai unsigned int val; 3913d475829STakashi Iwai 3923d475829STakashi Iwai val = encode_spdif_bits(ucontrol->value.iec958.status); 3933d475829STakashi Iwai if (val != emu->spdif_str_bits[idx]) { 3943d475829STakashi Iwai emu->spdif_str_bits[idx] = val; 3953d475829STakashi Iwai ca0106_set_spdif_bits(emu, idx); 3963d475829STakashi Iwai return 1; 3973d475829STakashi Iwai } 3983d475829STakashi Iwai return 0; 3991da177e4SLinus Torvalds } 4001da177e4SLinus Torvalds 401e4a3d145STakashi Iwai static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol, 402e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 4031da177e4SLinus Torvalds { 4041da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 4051da177e4SLinus Torvalds uinfo->count = 2; 4061da177e4SLinus Torvalds uinfo->value.integer.min = 0; 4071da177e4SLinus Torvalds uinfo->value.integer.max = 255; 4081da177e4SLinus Torvalds return 0; 4091da177e4SLinus Torvalds } 4101da177e4SLinus Torvalds 411e4a3d145STakashi Iwai static int snd_ca0106_volume_get(struct snd_kcontrol *kcontrol, 412e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 4131da177e4SLinus Torvalds { 414e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 4151da177e4SLinus Torvalds unsigned int value; 41695a98265STakashi Iwai int channel_id, reg; 41795a98265STakashi Iwai 41895a98265STakashi Iwai channel_id = (kcontrol->private_value >> 8) & 0xff; 41995a98265STakashi Iwai reg = kcontrol->private_value & 0xff; 4201da177e4SLinus Torvalds 4211da177e4SLinus Torvalds value = snd_ca0106_ptr_read(emu, reg, channel_id); 4221da177e4SLinus Torvalds ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */ 4231da177e4SLinus Torvalds ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */ 4241da177e4SLinus Torvalds return 0; 4251da177e4SLinus Torvalds } 4261da177e4SLinus Torvalds 427e4a3d145STakashi Iwai static int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol, 428e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 4291da177e4SLinus Torvalds { 430e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 43195a98265STakashi Iwai unsigned int oval, nval; 43295a98265STakashi Iwai int channel_id, reg; 43395a98265STakashi Iwai 43495a98265STakashi Iwai channel_id = (kcontrol->private_value >> 8) & 0xff; 43595a98265STakashi Iwai reg = kcontrol->private_value & 0xff; 43695a98265STakashi Iwai 43795a98265STakashi Iwai oval = snd_ca0106_ptr_read(emu, reg, channel_id); 43895a98265STakashi Iwai nval = ((0xff - ucontrol->value.integer.value[0]) << 24) | 43995a98265STakashi Iwai ((0xff - ucontrol->value.integer.value[1]) << 16); 44095a98265STakashi Iwai nval |= ((0xff - ucontrol->value.integer.value[0]) << 8) | 44195a98265STakashi Iwai ((0xff - ucontrol->value.integer.value[1]) ); 44295a98265STakashi Iwai if (oval == nval) 44395a98265STakashi Iwai return 0; 44495a98265STakashi Iwai snd_ca0106_ptr_write(emu, reg, channel_id, nval); 4451da177e4SLinus Torvalds return 1; 4461da177e4SLinus Torvalds } 44795a98265STakashi Iwai 4486129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_info(struct snd_kcontrol *kcontrol, 4496129daaaSJames Courtier-Dutton struct snd_ctl_elem_info *uinfo) 4506129daaaSJames Courtier-Dutton { 4516129daaaSJames Courtier-Dutton uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 4526129daaaSJames Courtier-Dutton uinfo->count = 2; 4536129daaaSJames Courtier-Dutton uinfo->value.integer.min = 0; 4546129daaaSJames Courtier-Dutton uinfo->value.integer.max = 255; 4556129daaaSJames Courtier-Dutton return 0; 4566129daaaSJames Courtier-Dutton } 4576129daaaSJames Courtier-Dutton 4586129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol, 4596129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 4606129daaaSJames Courtier-Dutton { 4616129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 4626129daaaSJames Courtier-Dutton int source_id; 4636129daaaSJames Courtier-Dutton 4646129daaaSJames Courtier-Dutton source_id = kcontrol->private_value; 4656129daaaSJames Courtier-Dutton 4666129daaaSJames Courtier-Dutton ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0]; 4676129daaaSJames Courtier-Dutton ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1]; 4686129daaaSJames Courtier-Dutton return 0; 4696129daaaSJames Courtier-Dutton } 4706129daaaSJames Courtier-Dutton 4716129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol, 4726129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 4736129daaaSJames Courtier-Dutton { 4746129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 4756129daaaSJames Courtier-Dutton unsigned int ogain; 4766129daaaSJames Courtier-Dutton unsigned int ngain; 4776129daaaSJames Courtier-Dutton int source_id; 4786129daaaSJames Courtier-Dutton int change = 0; 4796129daaaSJames Courtier-Dutton 4806129daaaSJames Courtier-Dutton source_id = kcontrol->private_value; 4816129daaaSJames Courtier-Dutton ogain = emu->i2c_capture_volume[source_id][0]; /* Left */ 4826129daaaSJames Courtier-Dutton ngain = ucontrol->value.integer.value[0]; 4836129daaaSJames Courtier-Dutton if (ngain > 0xff) 4845fe619f9STakashi Iwai return -EINVAL; 4856129daaaSJames Courtier-Dutton if (ogain != ngain) { 4866129daaaSJames Courtier-Dutton if (emu->i2c_capture_source == source_id) 4876129daaaSJames Courtier-Dutton snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) ); 4886129daaaSJames Courtier-Dutton emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0]; 4896129daaaSJames Courtier-Dutton change = 1; 4906129daaaSJames Courtier-Dutton } 4916129daaaSJames Courtier-Dutton ogain = emu->i2c_capture_volume[source_id][1]; /* Right */ 4926129daaaSJames Courtier-Dutton ngain = ucontrol->value.integer.value[1]; 4936129daaaSJames Courtier-Dutton if (ngain > 0xff) 4945fe619f9STakashi Iwai return -EINVAL; 4956129daaaSJames Courtier-Dutton if (ogain != ngain) { 4966129daaaSJames Courtier-Dutton if (emu->i2c_capture_source == source_id) 4976129daaaSJames Courtier-Dutton snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff)); 4986129daaaSJames Courtier-Dutton emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1]; 4996129daaaSJames Courtier-Dutton change = 1; 5006129daaaSJames Courtier-Dutton } 5016129daaaSJames Courtier-Dutton 5026129daaaSJames Courtier-Dutton return change; 5036129daaaSJames Courtier-Dutton } 5046129daaaSJames Courtier-Dutton 505b18cd538STrent Piepho #define spi_mute_info snd_ctl_boolean_mono_info 506b18cd538STrent Piepho 507b18cd538STrent Piepho static int spi_mute_get(struct snd_kcontrol *kcontrol, 508b18cd538STrent Piepho struct snd_ctl_elem_value *ucontrol) 509b18cd538STrent Piepho { 510b18cd538STrent Piepho struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 511b18cd538STrent Piepho unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT; 512b18cd538STrent Piepho unsigned int bit = kcontrol->private_value & SPI_REG_MASK; 513b18cd538STrent Piepho 514b18cd538STrent Piepho ucontrol->value.integer.value[0] = !(emu->spi_dac_reg[reg] & bit); 515b18cd538STrent Piepho return 0; 516b18cd538STrent Piepho } 517b18cd538STrent Piepho 518b18cd538STrent Piepho static int spi_mute_put(struct snd_kcontrol *kcontrol, 519b18cd538STrent Piepho struct snd_ctl_elem_value *ucontrol) 520b18cd538STrent Piepho { 521b18cd538STrent Piepho struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 522b18cd538STrent Piepho unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT; 523b18cd538STrent Piepho unsigned int bit = kcontrol->private_value & SPI_REG_MASK; 524b18cd538STrent Piepho int ret; 525b18cd538STrent Piepho 526b18cd538STrent Piepho ret = emu->spi_dac_reg[reg] & bit; 527b18cd538STrent Piepho if (ucontrol->value.integer.value[0]) { 528b18cd538STrent Piepho if (!ret) /* bit already cleared, do nothing */ 529b18cd538STrent Piepho return 0; 530b18cd538STrent Piepho emu->spi_dac_reg[reg] &= ~bit; 531b18cd538STrent Piepho } else { 532b18cd538STrent Piepho if (ret) /* bit already set, do nothing */ 533b18cd538STrent Piepho return 0; 534b18cd538STrent Piepho emu->spi_dac_reg[reg] |= bit; 535b18cd538STrent Piepho } 536b18cd538STrent Piepho 537b18cd538STrent Piepho ret = snd_ca0106_spi_write(emu, emu->spi_dac_reg[reg]); 5385fe619f9STakashi Iwai return ret ? -EINVAL : 1; 539b18cd538STrent Piepho } 540b18cd538STrent Piepho 54195a98265STakashi Iwai #define CA_VOLUME(xname,chid,reg) \ 54295a98265STakashi Iwai { \ 54395a98265STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 544302e9c5aSJaroslav Kysela .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ 545302e9c5aSJaroslav Kysela SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 54695a98265STakashi Iwai .info = snd_ca0106_volume_info, \ 54795a98265STakashi Iwai .get = snd_ca0106_volume_get, \ 54895a98265STakashi Iwai .put = snd_ca0106_volume_put, \ 5497cf0a953STakashi Iwai .tlv = { .p = snd_ca0106_db_scale1 }, \ 55095a98265STakashi Iwai .private_value = ((chid) << 8) | (reg) \ 5511da177e4SLinus Torvalds } 5521da177e4SLinus Torvalds 553b4e5e707STakashi Iwai static const struct snd_kcontrol_new snd_ca0106_volume_ctls[] = { 55495a98265STakashi Iwai CA_VOLUME("Analog Front Playback Volume", 55595a98265STakashi Iwai CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2), 55695a98265STakashi Iwai CA_VOLUME("Analog Rear Playback Volume", 55795a98265STakashi Iwai CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2), 55895a98265STakashi Iwai CA_VOLUME("Analog Center/LFE Playback Volume", 55995a98265STakashi Iwai CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2), 56095a98265STakashi Iwai CA_VOLUME("Analog Side Playback Volume", 56195a98265STakashi Iwai CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2), 56295a98265STakashi Iwai 56339596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Front Playback Volume", 56495a98265STakashi Iwai CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1), 56539596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Rear Playback Volume", 56695a98265STakashi Iwai CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1), 56739596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Center/LFE Playback Volume", 56895a98265STakashi Iwai CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1), 56939596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Unknown Playback Volume", 57095a98265STakashi Iwai CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1), 57195a98265STakashi Iwai 57295a98265STakashi Iwai CA_VOLUME("CAPTURE feedback Playback Volume", 57395a98265STakashi Iwai 1, CAPTURE_CONTROL), 57495a98265STakashi Iwai 57595a98265STakashi Iwai { 57695a98265STakashi Iwai .access = SNDRV_CTL_ELEM_ACCESS_READ, 57795a98265STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_PCM, 57895a98265STakashi Iwai .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), 57995a98265STakashi Iwai .count = 4, 58095a98265STakashi Iwai .info = snd_ca0106_spdif_info, 58195a98265STakashi Iwai .get = snd_ca0106_spdif_get_mask 58295a98265STakashi Iwai }, 5831da177e4SLinus Torvalds { 5841da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 58539596dc8SJames Courtier-Dutton .name = "IEC958 Playback Switch", 58695a98265STakashi Iwai .info = snd_ca0106_shared_spdif_info, 58795a98265STakashi Iwai .get = snd_ca0106_shared_spdif_get, 58895a98265STakashi Iwai .put = snd_ca0106_shared_spdif_put 58995a98265STakashi Iwai }, 5901da177e4SLinus Torvalds { 5911da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 592e6327cf9SJames Courtier-Dutton .name = "Digital Source Capture Enum", 59395a98265STakashi Iwai .info = snd_ca0106_capture_source_info, 59495a98265STakashi Iwai .get = snd_ca0106_capture_source_get, 59595a98265STakashi Iwai .put = snd_ca0106_capture_source_put 59695a98265STakashi Iwai }, 5971da177e4SLinus Torvalds { 5986129daaaSJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 599e6327cf9SJames Courtier-Dutton .name = "Analog Source Capture Enum", 6006129daaaSJames Courtier-Dutton .info = snd_ca0106_i2c_capture_source_info, 6016129daaaSJames Courtier-Dutton .get = snd_ca0106_i2c_capture_source_get, 6026129daaaSJames Courtier-Dutton .put = snd_ca0106_i2c_capture_source_put 6036129daaaSJames Courtier-Dutton }, 6046129daaaSJames Courtier-Dutton { 60595a98265STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_PCM, 60695a98265STakashi Iwai .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), 60795a98265STakashi Iwai .count = 4, 60895a98265STakashi Iwai .info = snd_ca0106_spdif_info, 6093d475829STakashi Iwai .get = snd_ca0106_spdif_get_default, 6103d475829STakashi Iwai .put = snd_ca0106_spdif_put_default 6113d475829STakashi Iwai }, 6123d475829STakashi Iwai { 6133d475829STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_PCM, 6143d475829STakashi Iwai .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), 6153d475829STakashi Iwai .count = 4, 6163d475829STakashi Iwai .info = snd_ca0106_spdif_info, 6173d475829STakashi Iwai .get = snd_ca0106_spdif_get_stream, 6183d475829STakashi Iwai .put = snd_ca0106_spdif_put_stream 61995a98265STakashi Iwai }, 6201da177e4SLinus Torvalds }; 6211da177e4SLinus Torvalds 6227c157069SJames Courtier-Dutton #define I2C_VOLUME(xname,chid) \ 6237c157069SJames Courtier-Dutton { \ 6247c157069SJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 6257c157069SJames Courtier-Dutton .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ 6267c157069SJames Courtier-Dutton SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 6277c157069SJames Courtier-Dutton .info = snd_ca0106_i2c_volume_info, \ 6287c157069SJames Courtier-Dutton .get = snd_ca0106_i2c_volume_get, \ 6297c157069SJames Courtier-Dutton .put = snd_ca0106_i2c_volume_put, \ 6307c157069SJames Courtier-Dutton .tlv = { .p = snd_ca0106_db_scale2 }, \ 6317c157069SJames Courtier-Dutton .private_value = chid \ 6327c157069SJames Courtier-Dutton } 6337c157069SJames Courtier-Dutton 634b4e5e707STakashi Iwai static const struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] = { 6357c157069SJames Courtier-Dutton I2C_VOLUME("Phone Capture Volume", 0), 6367c157069SJames Courtier-Dutton I2C_VOLUME("Mic Capture Volume", 1), 6377c157069SJames Courtier-Dutton I2C_VOLUME("Line in Capture Volume", 2), 6387c157069SJames Courtier-Dutton I2C_VOLUME("Aux Capture Volume", 3), 6397c157069SJames Courtier-Dutton }; 6407c157069SJames Courtier-Dutton 64164e5310aSAndy Owen static const int spi_dmute_reg[] = { 64264e5310aSAndy Owen SPI_DMUTE0_REG, 64364e5310aSAndy Owen SPI_DMUTE1_REG, 64464e5310aSAndy Owen SPI_DMUTE2_REG, 64564e5310aSAndy Owen 0, 64664e5310aSAndy Owen SPI_DMUTE4_REG, 647b18cd538STrent Piepho }; 64864e5310aSAndy Owen static const int spi_dmute_bit[] = { 64964e5310aSAndy Owen SPI_DMUTE0_BIT, 65064e5310aSAndy Owen SPI_DMUTE1_BIT, 65164e5310aSAndy Owen SPI_DMUTE2_BIT, 65264e5310aSAndy Owen 0, 65364e5310aSAndy Owen SPI_DMUTE4_BIT, 65464e5310aSAndy Owen }; 65564e5310aSAndy Owen 656e23e7a14SBill Pemberton static struct snd_kcontrol_new 657dc6ffaf8STakashi Iwai snd_ca0106_volume_spi_dac_ctl(const struct snd_ca0106_details *details, 65864e5310aSAndy Owen int channel_id) 65964e5310aSAndy Owen { 66064e5310aSAndy Owen struct snd_kcontrol_new spi_switch = {0}; 66164e5310aSAndy Owen int reg, bit; 66264e5310aSAndy Owen int dac_id; 66364e5310aSAndy Owen 66464e5310aSAndy Owen spi_switch.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 66564e5310aSAndy Owen spi_switch.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 66664e5310aSAndy Owen spi_switch.info = spi_mute_info; 66764e5310aSAndy Owen spi_switch.get = spi_mute_get; 66864e5310aSAndy Owen spi_switch.put = spi_mute_put; 66964e5310aSAndy Owen 67064e5310aSAndy Owen switch (channel_id) { 67164e5310aSAndy Owen case PCM_FRONT_CHANNEL: 67264e5310aSAndy Owen spi_switch.name = "Analog Front Playback Switch"; 67364e5310aSAndy Owen dac_id = (details->spi_dac & 0xf000) >> (4 * 3); 67464e5310aSAndy Owen break; 67564e5310aSAndy Owen case PCM_REAR_CHANNEL: 67664e5310aSAndy Owen spi_switch.name = "Analog Rear Playback Switch"; 67764e5310aSAndy Owen dac_id = (details->spi_dac & 0x0f00) >> (4 * 2); 67864e5310aSAndy Owen break; 67964e5310aSAndy Owen case PCM_CENTER_LFE_CHANNEL: 68064e5310aSAndy Owen spi_switch.name = "Analog Center/LFE Playback Switch"; 68164e5310aSAndy Owen dac_id = (details->spi_dac & 0x00f0) >> (4 * 1); 68264e5310aSAndy Owen break; 68364e5310aSAndy Owen case PCM_UNKNOWN_CHANNEL: 68464e5310aSAndy Owen spi_switch.name = "Analog Side Playback Switch"; 68564e5310aSAndy Owen dac_id = (details->spi_dac & 0x000f) >> (4 * 0); 68664e5310aSAndy Owen break; 68764e5310aSAndy Owen default: 68864e5310aSAndy Owen /* Unused channel */ 68964e5310aSAndy Owen spi_switch.name = NULL; 69064e5310aSAndy Owen dac_id = 0; 69164e5310aSAndy Owen } 69264e5310aSAndy Owen reg = spi_dmute_reg[dac_id]; 69364e5310aSAndy Owen bit = spi_dmute_bit[dac_id]; 69464e5310aSAndy Owen 69564e5310aSAndy Owen spi_switch.private_value = (reg << SPI_REG_SHIFT) | bit; 69664e5310aSAndy Owen 69764e5310aSAndy Owen return spi_switch; 69864e5310aSAndy Owen } 699b18cd538STrent Piepho 700e23e7a14SBill Pemberton static int remove_ctl(struct snd_card *card, const char *name) 7011da177e4SLinus Torvalds { 702e4a3d145STakashi Iwai struct snd_ctl_elem_id id; 7031da177e4SLinus Torvalds memset(&id, 0, sizeof(id)); 7041da177e4SLinus Torvalds strcpy(id.name, name); 7051da177e4SLinus Torvalds id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 7061da177e4SLinus Torvalds return snd_ctl_remove_id(card, &id); 7071da177e4SLinus Torvalds } 7081da177e4SLinus Torvalds 709e23e7a14SBill Pemberton static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name) 7101da177e4SLinus Torvalds { 711e4a3d145STakashi Iwai struct snd_ctl_elem_id sid; 7121da177e4SLinus Torvalds memset(&sid, 0, sizeof(sid)); 7131da177e4SLinus Torvalds /* FIXME: strcpy is bad. */ 7141da177e4SLinus Torvalds strcpy(sid.name, name); 7151da177e4SLinus Torvalds sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 7161da177e4SLinus Torvalds return snd_ctl_find_id(card, &sid); 7171da177e4SLinus Torvalds } 7181da177e4SLinus Torvalds 719e23e7a14SBill Pemberton static int rename_ctl(struct snd_card *card, const char *src, const char *dst) 7201da177e4SLinus Torvalds { 721e4a3d145STakashi Iwai struct snd_kcontrol *kctl = ctl_find(card, src); 7221da177e4SLinus Torvalds if (kctl) { 7231da177e4SLinus Torvalds strcpy(kctl->id.name, dst); 7241da177e4SLinus Torvalds return 0; 7251da177e4SLinus Torvalds } 7261da177e4SLinus Torvalds return -ENOENT; 7271da177e4SLinus Torvalds } 7281da177e4SLinus Torvalds 729fca7f388STrent Piepho #define ADD_CTLS(emu, ctls) \ 730fca7f388STrent Piepho do { \ 731bed515b0SHarvey Harrison int i, _err; \ 732fca7f388STrent Piepho for (i = 0; i < ARRAY_SIZE(ctls); i++) { \ 733bed515b0SHarvey Harrison _err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \ 734bed515b0SHarvey Harrison if (_err < 0) \ 735bed515b0SHarvey Harrison return _err; \ 736fca7f388STrent Piepho } \ 737fca7f388STrent Piepho } while (0) 738fca7f388STrent Piepho 739e23e7a14SBill Pemberton static 740c4865679STakashi Iwai DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 25, 1); 74149c88b85STakashi Iwai 742*9ab0cb30STakashi Iwai static const char * const follower_vols[] = { 74349c88b85STakashi Iwai "Analog Front Playback Volume", 74449c88b85STakashi Iwai "Analog Rear Playback Volume", 74549c88b85STakashi Iwai "Analog Center/LFE Playback Volume", 74649c88b85STakashi Iwai "Analog Side Playback Volume", 74749c88b85STakashi Iwai "IEC958 Front Playback Volume", 74849c88b85STakashi Iwai "IEC958 Rear Playback Volume", 74949c88b85STakashi Iwai "IEC958 Center/LFE Playback Volume", 75049c88b85STakashi Iwai "IEC958 Unknown Playback Volume", 75149c88b85STakashi Iwai "CAPTURE feedback Playback Volume", 75249c88b85STakashi Iwai NULL 75349c88b85STakashi Iwai }; 75449c88b85STakashi Iwai 755*9ab0cb30STakashi Iwai static const char * const follower_sws[] = { 75649c88b85STakashi Iwai "Analog Front Playback Switch", 75749c88b85STakashi Iwai "Analog Rear Playback Switch", 75849c88b85STakashi Iwai "Analog Center/LFE Playback Switch", 75949c88b85STakashi Iwai "Analog Side Playback Switch", 76049c88b85STakashi Iwai "IEC958 Playback Switch", 76149c88b85STakashi Iwai NULL 76249c88b85STakashi Iwai }; 76349c88b85STakashi Iwai 764*9ab0cb30STakashi Iwai static void add_followers(struct snd_card *card, 76597974309STakashi Iwai struct snd_kcontrol *master, const char * const *list) 76649c88b85STakashi Iwai { 76749c88b85STakashi Iwai for (; *list; list++) { 768*9ab0cb30STakashi Iwai struct snd_kcontrol *follower = ctl_find(card, *list); 769*9ab0cb30STakashi Iwai if (follower) 770*9ab0cb30STakashi Iwai snd_ctl_add_follower(master, follower); 77149c88b85STakashi Iwai } 77249c88b85STakashi Iwai } 77349c88b85STakashi Iwai 774e23e7a14SBill Pemberton int snd_ca0106_mixer(struct snd_ca0106 *emu) 7751da177e4SLinus Torvalds { 776fca7f388STrent Piepho int err; 777e4a3d145STakashi Iwai struct snd_card *card = emu->card; 77897974309STakashi Iwai const char * const *c; 77949c88b85STakashi Iwai struct snd_kcontrol *vmaster; 78097974309STakashi Iwai static const char * const ca0106_remove_ctls[] = { 7811da177e4SLinus Torvalds "Master Mono Playback Switch", 7821da177e4SLinus Torvalds "Master Mono Playback Volume", 7831da177e4SLinus Torvalds "3D Control - Switch", 7841da177e4SLinus Torvalds "3D Control Sigmatel - Depth", 7851da177e4SLinus Torvalds "PCM Playback Switch", 7861da177e4SLinus Torvalds "PCM Playback Volume", 7871da177e4SLinus Torvalds "CD Playback Switch", 7881da177e4SLinus Torvalds "CD Playback Volume", 7891da177e4SLinus Torvalds "Phone Playback Switch", 7901da177e4SLinus Torvalds "Phone Playback Volume", 7911da177e4SLinus Torvalds "Video Playback Switch", 7921da177e4SLinus Torvalds "Video Playback Volume", 793d355c82aSJaroslav Kysela "Beep Playback Switch", 794d355c82aSJaroslav Kysela "Beep Playback Volume", 7951da177e4SLinus Torvalds "Mono Output Select", 7961da177e4SLinus Torvalds "Capture Source", 7971da177e4SLinus Torvalds "Capture Switch", 7981da177e4SLinus Torvalds "Capture Volume", 7991da177e4SLinus Torvalds "External Amplifier", 8001da177e4SLinus Torvalds "Sigmatel 4-Speaker Stereo Playback Switch", 801afe6d7e3SAndreas Mohr "Surround Phase Inversion Playback Switch", 8021da177e4SLinus Torvalds NULL 8031da177e4SLinus Torvalds }; 80497974309STakashi Iwai static const char * const ca0106_rename_ctls[] = { 8051da177e4SLinus Torvalds "Master Playback Switch", "Capture Switch", 8061da177e4SLinus Torvalds "Master Playback Volume", "Capture Volume", 8071da177e4SLinus Torvalds "Line Playback Switch", "AC97 Line Capture Switch", 8081da177e4SLinus Torvalds "Line Playback Volume", "AC97 Line Capture Volume", 8091da177e4SLinus Torvalds "Aux Playback Switch", "AC97 Aux Capture Switch", 8101da177e4SLinus Torvalds "Aux Playback Volume", "AC97 Aux Capture Volume", 8111da177e4SLinus Torvalds "Mic Playback Switch", "AC97 Mic Capture Switch", 8121da177e4SLinus Torvalds "Mic Playback Volume", "AC97 Mic Capture Volume", 8131da177e4SLinus Torvalds "Mic Select", "AC97 Mic Select", 8141da177e4SLinus Torvalds "Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)", 8151da177e4SLinus Torvalds NULL 8161da177e4SLinus Torvalds }; 8171da177e4SLinus Torvalds #if 1 8181da177e4SLinus Torvalds for (c = ca0106_remove_ctls; *c; c++) 8191da177e4SLinus Torvalds remove_ctl(card, *c); 8201da177e4SLinus Torvalds for (c = ca0106_rename_ctls; *c; c += 2) 8211da177e4SLinus Torvalds rename_ctl(card, c[0], c[1]); 8221da177e4SLinus Torvalds #endif 8231da177e4SLinus Torvalds 824fca7f388STrent Piepho ADD_CTLS(emu, snd_ca0106_volume_ctls); 82595a98265STakashi Iwai if (emu->details->i2c_adc == 1) { 826fca7f388STrent Piepho ADD_CTLS(emu, snd_ca0106_volume_i2c_adc_ctls); 827be0b7b01SJames Courtier-Dutton if (emu->details->gpio_type == 1) 82895a98265STakashi Iwai err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu)); 829be0b7b01SJames Courtier-Dutton else /* gpio_type == 2 */ 830be0b7b01SJames Courtier-Dutton err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu)); 83195a98265STakashi Iwai if (err < 0) 8321da177e4SLinus Torvalds return err; 83395a98265STakashi Iwai } 83464e5310aSAndy Owen if (emu->details->spi_dac) { 83564e5310aSAndy Owen int i; 83664e5310aSAndy Owen for (i = 0;; i++) { 83764e5310aSAndy Owen struct snd_kcontrol_new ctl; 83864e5310aSAndy Owen ctl = snd_ca0106_volume_spi_dac_ctl(emu->details, i); 83964e5310aSAndy Owen if (!ctl.name) 84064e5310aSAndy Owen break; 84164e5310aSAndy Owen err = snd_ctl_add(card, snd_ctl_new1(&ctl, emu)); 84264e5310aSAndy Owen if (err < 0) 84364e5310aSAndy Owen return err; 84464e5310aSAndy Owen } 84564e5310aSAndy Owen } 84649c88b85STakashi Iwai 84749c88b85STakashi Iwai /* Create virtual master controls */ 84849c88b85STakashi Iwai vmaster = snd_ctl_make_virtual_master("Master Playback Volume", 84949c88b85STakashi Iwai snd_ca0106_master_db_scale); 85049c88b85STakashi Iwai if (!vmaster) 85149c88b85STakashi Iwai return -ENOMEM; 852601e1cc5STakashi Iwai err = snd_ctl_add(card, vmaster); 853601e1cc5STakashi Iwai if (err < 0) 854601e1cc5STakashi Iwai return err; 855*9ab0cb30STakashi Iwai add_followers(card, vmaster, follower_vols); 85649c88b85STakashi Iwai 8576fef153aSAndy Owen if (emu->details->spi_dac) { 85849c88b85STakashi Iwai vmaster = snd_ctl_make_virtual_master("Master Playback Switch", 85949c88b85STakashi Iwai NULL); 86049c88b85STakashi Iwai if (!vmaster) 86149c88b85STakashi Iwai return -ENOMEM; 862601e1cc5STakashi Iwai err = snd_ctl_add(card, vmaster); 863601e1cc5STakashi Iwai if (err < 0) 864601e1cc5STakashi Iwai return err; 865*9ab0cb30STakashi Iwai add_followers(card, vmaster, follower_sws); 86649c88b85STakashi Iwai } 867eeaf100dSTakashi Iwai 868eeaf100dSTakashi Iwai strcpy(card->mixername, "CA0106"); 8691da177e4SLinus Torvalds return 0; 8701da177e4SLinus Torvalds } 8711da177e4SLinus Torvalds 872c7561cd8STakashi Iwai #ifdef CONFIG_PM_SLEEP 8735da95273STakashi Iwai struct ca0106_vol_tbl { 8745da95273STakashi Iwai unsigned int channel_id; 8758df0f707STakashi Iwai unsigned int reg; 8765da95273STakashi Iwai }; 8775da95273STakashi Iwai 87897974309STakashi Iwai static const struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = { 8795da95273STakashi Iwai { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2 }, 8805da95273STakashi Iwai { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2 }, 8815da95273STakashi Iwai { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2 }, 8825da95273STakashi Iwai { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2 }, 8835da95273STakashi Iwai { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1 }, 8845da95273STakashi Iwai { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1 }, 8855da95273STakashi Iwai { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1 }, 8865da95273STakashi Iwai { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1 }, 8875da95273STakashi Iwai { 1, CAPTURE_CONTROL }, 8885da95273STakashi Iwai }; 8895da95273STakashi Iwai 8905da95273STakashi Iwai void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip) 8915da95273STakashi Iwai { 8925da95273STakashi Iwai int i; 8935da95273STakashi Iwai 8945da95273STakashi Iwai /* save volumes */ 8955da95273STakashi Iwai for (i = 0; i < NUM_SAVED_VOLUMES; i++) 8965da95273STakashi Iwai chip->saved_vol[i] = 8975da95273STakashi Iwai snd_ca0106_ptr_read(chip, saved_volumes[i].reg, 8985da95273STakashi Iwai saved_volumes[i].channel_id); 8995da95273STakashi Iwai } 9005da95273STakashi Iwai 9015da95273STakashi Iwai void snd_ca0106_mixer_resume(struct snd_ca0106 *chip) 9025da95273STakashi Iwai { 9035da95273STakashi Iwai int i; 9045da95273STakashi Iwai 9055da95273STakashi Iwai for (i = 0; i < NUM_SAVED_VOLUMES; i++) 9065da95273STakashi Iwai snd_ca0106_ptr_write(chip, saved_volumes[i].reg, 9075da95273STakashi Iwai saved_volumes[i].channel_id, 9085da95273STakashi Iwai chip->saved_vol[i]); 9095da95273STakashi Iwai 9105da95273STakashi Iwai ca0106_spdif_enable(chip); 9115da95273STakashi Iwai ca0106_set_capture_source(chip); 9125da95273STakashi Iwai ca0106_set_i2c_capture_source(chip, chip->i2c_capture_source, 1); 9135da95273STakashi Iwai for (i = 0; i < 4; i++) 9145da95273STakashi Iwai ca0106_set_spdif_bits(chip, i); 9155da95273STakashi Iwai if (chip->details->i2c_adc) 9165da95273STakashi Iwai ca0106_set_capture_mic_line_in(chip); 9175da95273STakashi Iwai } 918c7561cd8STakashi Iwai #endif /* CONFIG_PM_SLEEP */ 919