11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk> 31da177e4SLinus Torvalds * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit 4b18cd538STrent Piepho * Version: 0.0.18 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * FEATURES currently supported: 71da177e4SLinus Torvalds * See ca0106_main.c for features. 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * Changelog: 101da177e4SLinus Torvalds * Support interrupts per period. 111da177e4SLinus Torvalds * Removed noise from Center/LFE channel when in Analog mode. 121da177e4SLinus Torvalds * Rename and remove mixer controls. 131da177e4SLinus Torvalds * 0.0.6 141da177e4SLinus Torvalds * Use separate card based DMA buffer for periods table list. 151da177e4SLinus Torvalds * 0.0.7 161da177e4SLinus Torvalds * Change remove and rename ctrls into lists. 171da177e4SLinus Torvalds * 0.0.8 181da177e4SLinus Torvalds * Try to fix capture sources. 191da177e4SLinus Torvalds * 0.0.9 201da177e4SLinus Torvalds * Fix AC3 output. 211da177e4SLinus Torvalds * Enable S32_LE format support. 221da177e4SLinus Torvalds * 0.0.10 231da177e4SLinus Torvalds * Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".) 241da177e4SLinus Torvalds * 0.0.11 251da177e4SLinus Torvalds * Add Model name recognition. 261da177e4SLinus Torvalds * 0.0.12 271da177e4SLinus Torvalds * Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period. 281da177e4SLinus Torvalds * Remove redundent "voice" handling. 291da177e4SLinus Torvalds * 0.0.13 301da177e4SLinus Torvalds * Single trigger call for multi channels. 311da177e4SLinus Torvalds * 0.0.14 321da177e4SLinus Torvalds * Set limits based on what the sound card hardware can do. 331da177e4SLinus Torvalds * playback periods_min=2, periods_max=8 341da177e4SLinus Torvalds * capture hw constraints require period_size = n * 64 bytes. 351da177e4SLinus Torvalds * playback hw constraints require period_size = n * 64 bytes. 361da177e4SLinus Torvalds * 0.0.15 371da177e4SLinus Torvalds * Separated ca0106.c into separate functional .c files. 381da177e4SLinus Torvalds * 0.0.16 391da177e4SLinus Torvalds * Modified Copyright message. 40ed144f3cSJames Courtier-Dutton * 0.0.17 41ed144f3cSJames Courtier-Dutton * Implement Mic and Line in Capture. 42b18cd538STrent Piepho * 0.0.18 43b18cd538STrent Piepho * Add support for mute control on SB Live 24bit (cards w/ SPI DAC) 441da177e4SLinus Torvalds * 451da177e4SLinus Torvalds * This code was initally based on code from ALSA's emu10k1x.c which is: 461da177e4SLinus Torvalds * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com> 471da177e4SLinus Torvalds * 481da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 491da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 501da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 511da177e4SLinus Torvalds * (at your option) any later version. 521da177e4SLinus Torvalds * 531da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 541da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 551da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 561da177e4SLinus Torvalds * GNU General Public License for more details. 571da177e4SLinus Torvalds * 581da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 591da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 601da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 611da177e4SLinus Torvalds * 621da177e4SLinus Torvalds */ 631da177e4SLinus Torvalds #include <linux/delay.h> 641da177e4SLinus Torvalds #include <linux/init.h> 651da177e4SLinus Torvalds #include <linux/interrupt.h> 661da177e4SLinus Torvalds #include <linux/slab.h> 671da177e4SLinus Torvalds #include <linux/moduleparam.h> 681da177e4SLinus Torvalds #include <sound/core.h> 691da177e4SLinus Torvalds #include <sound/initval.h> 701da177e4SLinus Torvalds #include <sound/pcm.h> 711da177e4SLinus Torvalds #include <sound/ac97_codec.h> 721da177e4SLinus Torvalds #include <sound/info.h> 7342750b04SJaroslav Kysela #include <sound/tlv.h> 746473d160SJean Delvare #include <asm/io.h> 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds #include "ca0106.h" 771da177e4SLinus Torvalds 785da95273STakashi Iwai static void ca0106_spdif_enable(struct snd_ca0106 *emu) 795da95273STakashi Iwai { 805da95273STakashi Iwai unsigned int val; 815da95273STakashi Iwai 825da95273STakashi Iwai if (emu->spdif_enable) { 835da95273STakashi Iwai /* Digital */ 845da95273STakashi Iwai snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); 855da95273STakashi Iwai snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000); 865da95273STakashi Iwai val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000; 875da95273STakashi Iwai snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val); 885da95273STakashi Iwai val = inl(emu->port + GPIO) & ~0x101; 895da95273STakashi Iwai outl(val, emu->port + GPIO); 905da95273STakashi Iwai 915da95273STakashi Iwai } else { 925da95273STakashi Iwai /* Analog */ 935da95273STakashi Iwai snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); 945da95273STakashi Iwai snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000); 955da95273STakashi Iwai val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000; 965da95273STakashi Iwai snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val); 975da95273STakashi Iwai val = inl(emu->port + GPIO) | 0x101; 985da95273STakashi Iwai outl(val, emu->port + GPIO); 995da95273STakashi Iwai } 1005da95273STakashi Iwai } 1015da95273STakashi Iwai 1025da95273STakashi Iwai static void ca0106_set_capture_source(struct snd_ca0106 *emu) 1035da95273STakashi Iwai { 1045da95273STakashi Iwai unsigned int val = emu->capture_source; 1055da95273STakashi Iwai unsigned int source, mask; 1065da95273STakashi Iwai source = (val << 28) | (val << 24) | (val << 20) | (val << 16); 1075da95273STakashi Iwai mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff; 1085da95273STakashi Iwai snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask); 1095da95273STakashi Iwai } 1105da95273STakashi Iwai 1115da95273STakashi Iwai static void ca0106_set_i2c_capture_source(struct snd_ca0106 *emu, 1125da95273STakashi Iwai unsigned int val, int force) 1135da95273STakashi Iwai { 1145da95273STakashi Iwai unsigned int ngain, ogain; 1155da95273STakashi Iwai u32 source; 1165da95273STakashi Iwai 1175da95273STakashi Iwai snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ 1185da95273STakashi Iwai ngain = emu->i2c_capture_volume[val][0]; /* Left */ 1195da95273STakashi Iwai ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */ 1205da95273STakashi Iwai if (force || ngain != ogain) 1215da95273STakashi Iwai snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ngain & 0xff); 1225da95273STakashi Iwai ngain = emu->i2c_capture_volume[val][1]; /* Right */ 1235da95273STakashi Iwai ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */ 1245da95273STakashi Iwai if (force || ngain != ogain) 1255da95273STakashi Iwai snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ngain & 0xff); 1265da95273STakashi Iwai source = 1 << val; 1275da95273STakashi Iwai snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */ 1285da95273STakashi Iwai emu->i2c_capture_source = val; 1295da95273STakashi Iwai } 1305da95273STakashi Iwai 1315da95273STakashi Iwai static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu) 1325da95273STakashi Iwai { 1335da95273STakashi Iwai u32 tmp; 1345da95273STakashi Iwai 1355da95273STakashi Iwai if (emu->capture_mic_line_in) { 1365da95273STakashi Iwai /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */ 1375da95273STakashi Iwai tmp = inl(emu->port+GPIO) & ~0x400; 1385da95273STakashi Iwai tmp = tmp | 0x400; 1395da95273STakashi Iwai outl(tmp, emu->port+GPIO); 1405da95273STakashi Iwai /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */ 1415da95273STakashi Iwai } else { 1425da95273STakashi Iwai /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */ 1435da95273STakashi Iwai tmp = inl(emu->port+GPIO) & ~0x400; 1445da95273STakashi Iwai outl(tmp, emu->port+GPIO); 1455da95273STakashi Iwai /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */ 1465da95273STakashi Iwai } 1475da95273STakashi Iwai } 1485da95273STakashi Iwai 1495da95273STakashi Iwai static void ca0106_set_spdif_bits(struct snd_ca0106 *emu, int idx) 1505da95273STakashi Iwai { 151*3d475829STakashi Iwai snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, emu->spdif_str_bits[idx]); 1525da95273STakashi Iwai } 1535da95273STakashi Iwai 1545da95273STakashi Iwai /* 1555da95273STakashi Iwai */ 1560cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1); 1570cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1); 15842750b04SJaroslav Kysela 159a5ce8890STakashi Iwai #define snd_ca0106_shared_spdif_info snd_ctl_boolean_mono_info 1601da177e4SLinus Torvalds 161e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol, 162e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 1631da177e4SLinus Torvalds { 164e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 1651da177e4SLinus Torvalds 1665fe619f9STakashi Iwai ucontrol->value.integer.value[0] = emu->spdif_enable; 1671da177e4SLinus Torvalds return 0; 1681da177e4SLinus Torvalds } 1691da177e4SLinus Torvalds 170e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol, 171e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 1721da177e4SLinus Torvalds { 173e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 1741da177e4SLinus Torvalds unsigned int val; 1751da177e4SLinus Torvalds int change = 0; 1761da177e4SLinus Torvalds 1775fe619f9STakashi Iwai val = !!ucontrol->value.integer.value[0]; 1781da177e4SLinus Torvalds change = (emu->spdif_enable != val); 1791da177e4SLinus Torvalds if (change) { 1801da177e4SLinus Torvalds emu->spdif_enable = val; 1815da95273STakashi Iwai ca0106_spdif_enable(emu); 1821da177e4SLinus Torvalds } 1831da177e4SLinus Torvalds return change; 1841da177e4SLinus Torvalds } 1851da177e4SLinus Torvalds 186e4a3d145STakashi Iwai static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol, 187e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 1881da177e4SLinus Torvalds { 18995a98265STakashi Iwai static char *texts[6] = { 19039596dc8SJames Courtier-Dutton "IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out" 19195a98265STakashi Iwai }; 1921da177e4SLinus Torvalds 1931da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 1941da177e4SLinus Torvalds uinfo->count = 1; 1951da177e4SLinus Torvalds uinfo->value.enumerated.items = 6; 1961da177e4SLinus Torvalds if (uinfo->value.enumerated.item > 5) 1971da177e4SLinus Torvalds uinfo->value.enumerated.item = 5; 1981da177e4SLinus Torvalds strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); 1991da177e4SLinus Torvalds return 0; 2001da177e4SLinus Torvalds } 2011da177e4SLinus Torvalds 202e4a3d145STakashi Iwai static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol, 203e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 2041da177e4SLinus Torvalds { 205e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds ucontrol->value.enumerated.item[0] = emu->capture_source; 2081da177e4SLinus Torvalds return 0; 2091da177e4SLinus Torvalds } 2101da177e4SLinus Torvalds 211e4a3d145STakashi Iwai static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol, 212e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 2131da177e4SLinus Torvalds { 214e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 2151da177e4SLinus Torvalds unsigned int val; 2161da177e4SLinus Torvalds int change = 0; 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds val = ucontrol->value.enumerated.item[0] ; 2195fe619f9STakashi Iwai if (val >= 6) 2205fe619f9STakashi Iwai return -EINVAL; 2211da177e4SLinus Torvalds change = (emu->capture_source != val); 2221da177e4SLinus Torvalds if (change) { 2231da177e4SLinus Torvalds emu->capture_source = val; 2245da95273STakashi Iwai ca0106_set_capture_source(emu); 2251da177e4SLinus Torvalds } 2261da177e4SLinus Torvalds return change; 2271da177e4SLinus Torvalds } 2281da177e4SLinus Torvalds 2296129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol, 2306129daaaSJames Courtier-Dutton struct snd_ctl_elem_info *uinfo) 2316129daaaSJames Courtier-Dutton { 2326129daaaSJames Courtier-Dutton static char *texts[6] = { 2336129daaaSJames Courtier-Dutton "Phone", "Mic", "Line in", "Aux" 2346129daaaSJames Courtier-Dutton }; 2356129daaaSJames Courtier-Dutton 2366129daaaSJames Courtier-Dutton uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 2376129daaaSJames Courtier-Dutton uinfo->count = 1; 2386129daaaSJames Courtier-Dutton uinfo->value.enumerated.items = 4; 2396129daaaSJames Courtier-Dutton if (uinfo->value.enumerated.item > 3) 2406129daaaSJames Courtier-Dutton uinfo->value.enumerated.item = 3; 2416129daaaSJames Courtier-Dutton strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); 2426129daaaSJames Courtier-Dutton return 0; 2436129daaaSJames Courtier-Dutton } 2446129daaaSJames Courtier-Dutton 2456129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol, 2466129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 2476129daaaSJames Courtier-Dutton { 2486129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 2496129daaaSJames Courtier-Dutton 2506129daaaSJames Courtier-Dutton ucontrol->value.enumerated.item[0] = emu->i2c_capture_source; 2516129daaaSJames Courtier-Dutton return 0; 2526129daaaSJames Courtier-Dutton } 2536129daaaSJames Courtier-Dutton 2546129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol, 2556129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 2566129daaaSJames Courtier-Dutton { 2576129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 2586129daaaSJames Courtier-Dutton unsigned int source_id; 2596129daaaSJames Courtier-Dutton int change = 0; 2606129daaaSJames Courtier-Dutton /* If the capture source has changed, 2616129daaaSJames Courtier-Dutton * update the capture volume from the cached value 2626129daaaSJames Courtier-Dutton * for the particular source. 2636129daaaSJames Courtier-Dutton */ 2646129daaaSJames Courtier-Dutton source_id = ucontrol->value.enumerated.item[0] ; 2655fe619f9STakashi Iwai if (source_id >= 4) 2665fe619f9STakashi Iwai return -EINVAL; 2676129daaaSJames Courtier-Dutton change = (emu->i2c_capture_source != source_id); 2686129daaaSJames Courtier-Dutton if (change) { 2695da95273STakashi Iwai ca0106_set_i2c_capture_source(emu, source_id, 0); 2706129daaaSJames Courtier-Dutton } 2716129daaaSJames Courtier-Dutton return change; 2726129daaaSJames Courtier-Dutton } 2736129daaaSJames Courtier-Dutton 274be0b7b01SJames Courtier-Dutton static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol, 275be0b7b01SJames Courtier-Dutton struct snd_ctl_elem_info *uinfo) 276be0b7b01SJames Courtier-Dutton { 277be0b7b01SJames Courtier-Dutton static char *texts[2] = { "Side out", "Line in" }; 278be0b7b01SJames Courtier-Dutton 279be0b7b01SJames Courtier-Dutton uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 280be0b7b01SJames Courtier-Dutton uinfo->count = 1; 281be0b7b01SJames Courtier-Dutton uinfo->value.enumerated.items = 2; 282be0b7b01SJames Courtier-Dutton if (uinfo->value.enumerated.item > 1) 283be0b7b01SJames Courtier-Dutton uinfo->value.enumerated.item = 1; 284be0b7b01SJames Courtier-Dutton strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); 285be0b7b01SJames Courtier-Dutton return 0; 286be0b7b01SJames Courtier-Dutton } 287be0b7b01SJames Courtier-Dutton 288e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol, 289e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 290ed144f3cSJames Courtier-Dutton { 291ed144f3cSJames Courtier-Dutton static char *texts[2] = { "Line in", "Mic in" }; 292ed144f3cSJames Courtier-Dutton 293ed144f3cSJames Courtier-Dutton uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 294ed144f3cSJames Courtier-Dutton uinfo->count = 1; 295ed144f3cSJames Courtier-Dutton uinfo->value.enumerated.items = 2; 296ed144f3cSJames Courtier-Dutton if (uinfo->value.enumerated.item > 1) 297ed144f3cSJames Courtier-Dutton uinfo->value.enumerated.item = 1; 298ed144f3cSJames Courtier-Dutton strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); 299ed144f3cSJames Courtier-Dutton return 0; 300ed144f3cSJames Courtier-Dutton } 301ed144f3cSJames Courtier-Dutton 302e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol, 303e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 304ed144f3cSJames Courtier-Dutton { 305e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 306ed144f3cSJames Courtier-Dutton 307ed144f3cSJames Courtier-Dutton ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in; 308ed144f3cSJames Courtier-Dutton return 0; 309ed144f3cSJames Courtier-Dutton } 310ed144f3cSJames Courtier-Dutton 311e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol, 312e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 313ed144f3cSJames Courtier-Dutton { 314e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 315ed144f3cSJames Courtier-Dutton unsigned int val; 316ed144f3cSJames Courtier-Dutton int change = 0; 317ed144f3cSJames Courtier-Dutton 318ed144f3cSJames Courtier-Dutton val = ucontrol->value.enumerated.item[0] ; 3195fe619f9STakashi Iwai if (val > 1) 3205fe619f9STakashi Iwai return -EINVAL; 321ed144f3cSJames Courtier-Dutton change = (emu->capture_mic_line_in != val); 322ed144f3cSJames Courtier-Dutton if (change) { 323ed144f3cSJames Courtier-Dutton emu->capture_mic_line_in = val; 3245da95273STakashi Iwai ca0106_set_capture_mic_line_in(emu); 325ed144f3cSJames Courtier-Dutton } 326ed144f3cSJames Courtier-Dutton return change; 327ed144f3cSJames Courtier-Dutton } 328ed144f3cSJames Courtier-Dutton 329e4a3d145STakashi Iwai static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata = 330ed144f3cSJames Courtier-Dutton { 331ed144f3cSJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3326129daaaSJames Courtier-Dutton .name = "Shared Mic/Line in Capture Switch", 333ed144f3cSJames Courtier-Dutton .info = snd_ca0106_capture_mic_line_in_info, 334ed144f3cSJames Courtier-Dutton .get = snd_ca0106_capture_mic_line_in_get, 335ed144f3cSJames Courtier-Dutton .put = snd_ca0106_capture_mic_line_in_put 336ed144f3cSJames Courtier-Dutton }; 337ed144f3cSJames Courtier-Dutton 338be0b7b01SJames Courtier-Dutton static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out __devinitdata = 339be0b7b01SJames Courtier-Dutton { 340be0b7b01SJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 341be0b7b01SJames Courtier-Dutton .name = "Shared Line in/Side out Capture Switch", 342be0b7b01SJames Courtier-Dutton .info = snd_ca0106_capture_line_in_side_out_info, 343be0b7b01SJames Courtier-Dutton .get = snd_ca0106_capture_mic_line_in_get, 344be0b7b01SJames Courtier-Dutton .put = snd_ca0106_capture_mic_line_in_put 345be0b7b01SJames Courtier-Dutton }; 346be0b7b01SJames Courtier-Dutton 347be0b7b01SJames Courtier-Dutton 348e4a3d145STakashi Iwai static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol, 349e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 3501da177e4SLinus Torvalds { 3511da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 3521da177e4SLinus Torvalds uinfo->count = 1; 3531da177e4SLinus Torvalds return 0; 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds 356*3d475829STakashi Iwai static void decode_spdif_bits(unsigned char *status, unsigned int bits) 357*3d475829STakashi Iwai { 358*3d475829STakashi Iwai status[0] = (bits >> 0) & 0xff; 359*3d475829STakashi Iwai status[1] = (bits >> 8) & 0xff; 360*3d475829STakashi Iwai status[2] = (bits >> 16) & 0xff; 361*3d475829STakashi Iwai status[3] = (bits >> 24) & 0xff; 362*3d475829STakashi Iwai } 363*3d475829STakashi Iwai 364*3d475829STakashi Iwai static int snd_ca0106_spdif_get_default(struct snd_kcontrol *kcontrol, 365e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3661da177e4SLinus Torvalds { 367e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 3681da177e4SLinus Torvalds unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 3691da177e4SLinus Torvalds 370*3d475829STakashi Iwai decode_spdif_bits(ucontrol->value.iec958.status, 371*3d475829STakashi Iwai emu->spdif_bits[idx]); 372*3d475829STakashi Iwai return 0; 373*3d475829STakashi Iwai } 374*3d475829STakashi Iwai 375*3d475829STakashi Iwai static int snd_ca0106_spdif_get_stream(struct snd_kcontrol *kcontrol, 376*3d475829STakashi Iwai struct snd_ctl_elem_value *ucontrol) 377*3d475829STakashi Iwai { 378*3d475829STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 379*3d475829STakashi Iwai unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 380*3d475829STakashi Iwai 381*3d475829STakashi Iwai decode_spdif_bits(ucontrol->value.iec958.status, 382*3d475829STakashi Iwai emu->spdif_str_bits[idx]); 3831da177e4SLinus Torvalds return 0; 3841da177e4SLinus Torvalds } 3851da177e4SLinus Torvalds 386e4a3d145STakashi Iwai static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol, 387e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3881da177e4SLinus Torvalds { 3891da177e4SLinus Torvalds ucontrol->value.iec958.status[0] = 0xff; 3901da177e4SLinus Torvalds ucontrol->value.iec958.status[1] = 0xff; 3911da177e4SLinus Torvalds ucontrol->value.iec958.status[2] = 0xff; 3921da177e4SLinus Torvalds ucontrol->value.iec958.status[3] = 0xff; 3931da177e4SLinus Torvalds return 0; 3941da177e4SLinus Torvalds } 3951da177e4SLinus Torvalds 396*3d475829STakashi Iwai static unsigned int encode_spdif_bits(unsigned char *status) 397*3d475829STakashi Iwai { 398*3d475829STakashi Iwai return ((unsigned int)status[0] << 0) | 399*3d475829STakashi Iwai ((unsigned int)status[1] << 8) | 400*3d475829STakashi Iwai ((unsigned int)status[2] << 16) | 401*3d475829STakashi Iwai ((unsigned int)status[3] << 24); 402*3d475829STakashi Iwai } 403*3d475829STakashi Iwai 404*3d475829STakashi Iwai static int snd_ca0106_spdif_put_default(struct snd_kcontrol *kcontrol, 405e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 4061da177e4SLinus Torvalds { 407e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 4081da177e4SLinus Torvalds unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 4091da177e4SLinus Torvalds unsigned int val; 4101da177e4SLinus Torvalds 411*3d475829STakashi Iwai val = encode_spdif_bits(ucontrol->value.iec958.status); 412*3d475829STakashi Iwai if (val != emu->spdif_bits[idx]) { 4131da177e4SLinus Torvalds emu->spdif_bits[idx] = val; 414*3d475829STakashi Iwai /* FIXME: this isn't safe, but needed to keep the compatibility 415*3d475829STakashi Iwai * with older alsa-lib config 416*3d475829STakashi Iwai */ 417*3d475829STakashi Iwai emu->spdif_str_bits[idx] = val; 4185da95273STakashi Iwai ca0106_set_spdif_bits(emu, idx); 419*3d475829STakashi Iwai return 1; 4201da177e4SLinus Torvalds } 421*3d475829STakashi Iwai return 0; 422*3d475829STakashi Iwai } 423*3d475829STakashi Iwai 424*3d475829STakashi Iwai static int snd_ca0106_spdif_put_stream(struct snd_kcontrol *kcontrol, 425*3d475829STakashi Iwai struct snd_ctl_elem_value *ucontrol) 426*3d475829STakashi Iwai { 427*3d475829STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 428*3d475829STakashi Iwai unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 429*3d475829STakashi Iwai unsigned int val; 430*3d475829STakashi Iwai 431*3d475829STakashi Iwai val = encode_spdif_bits(ucontrol->value.iec958.status); 432*3d475829STakashi Iwai if (val != emu->spdif_str_bits[idx]) { 433*3d475829STakashi Iwai emu->spdif_str_bits[idx] = val; 434*3d475829STakashi Iwai ca0106_set_spdif_bits(emu, idx); 435*3d475829STakashi Iwai return 1; 436*3d475829STakashi Iwai } 437*3d475829STakashi Iwai return 0; 4381da177e4SLinus Torvalds } 4391da177e4SLinus Torvalds 440e4a3d145STakashi Iwai static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol, 441e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 4421da177e4SLinus Torvalds { 4431da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 4441da177e4SLinus Torvalds uinfo->count = 2; 4451da177e4SLinus Torvalds uinfo->value.integer.min = 0; 4461da177e4SLinus Torvalds uinfo->value.integer.max = 255; 4471da177e4SLinus Torvalds return 0; 4481da177e4SLinus Torvalds } 4491da177e4SLinus Torvalds 450e4a3d145STakashi Iwai static int snd_ca0106_volume_get(struct snd_kcontrol *kcontrol, 451e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 4521da177e4SLinus Torvalds { 453e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 4541da177e4SLinus Torvalds unsigned int value; 45595a98265STakashi Iwai int channel_id, reg; 45695a98265STakashi Iwai 45795a98265STakashi Iwai channel_id = (kcontrol->private_value >> 8) & 0xff; 45895a98265STakashi Iwai reg = kcontrol->private_value & 0xff; 4591da177e4SLinus Torvalds 4601da177e4SLinus Torvalds value = snd_ca0106_ptr_read(emu, reg, channel_id); 4611da177e4SLinus Torvalds ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */ 4621da177e4SLinus Torvalds ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */ 4631da177e4SLinus Torvalds return 0; 4641da177e4SLinus Torvalds } 4651da177e4SLinus Torvalds 466e4a3d145STakashi Iwai static int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol, 467e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 4681da177e4SLinus Torvalds { 469e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 47095a98265STakashi Iwai unsigned int oval, nval; 47195a98265STakashi Iwai int channel_id, reg; 47295a98265STakashi Iwai 47395a98265STakashi Iwai channel_id = (kcontrol->private_value >> 8) & 0xff; 47495a98265STakashi Iwai reg = kcontrol->private_value & 0xff; 47595a98265STakashi Iwai 47695a98265STakashi Iwai oval = snd_ca0106_ptr_read(emu, reg, channel_id); 47795a98265STakashi Iwai nval = ((0xff - ucontrol->value.integer.value[0]) << 24) | 47895a98265STakashi Iwai ((0xff - ucontrol->value.integer.value[1]) << 16); 47995a98265STakashi Iwai nval |= ((0xff - ucontrol->value.integer.value[0]) << 8) | 48095a98265STakashi Iwai ((0xff - ucontrol->value.integer.value[1]) ); 48195a98265STakashi Iwai if (oval == nval) 48295a98265STakashi Iwai return 0; 48395a98265STakashi Iwai snd_ca0106_ptr_write(emu, reg, channel_id, nval); 4841da177e4SLinus Torvalds return 1; 4851da177e4SLinus Torvalds } 48695a98265STakashi Iwai 4876129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_info(struct snd_kcontrol *kcontrol, 4886129daaaSJames Courtier-Dutton struct snd_ctl_elem_info *uinfo) 4896129daaaSJames Courtier-Dutton { 4906129daaaSJames Courtier-Dutton uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 4916129daaaSJames Courtier-Dutton uinfo->count = 2; 4926129daaaSJames Courtier-Dutton uinfo->value.integer.min = 0; 4936129daaaSJames Courtier-Dutton uinfo->value.integer.max = 255; 4946129daaaSJames Courtier-Dutton return 0; 4956129daaaSJames Courtier-Dutton } 4966129daaaSJames Courtier-Dutton 4976129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol, 4986129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 4996129daaaSJames Courtier-Dutton { 5006129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 5016129daaaSJames Courtier-Dutton int source_id; 5026129daaaSJames Courtier-Dutton 5036129daaaSJames Courtier-Dutton source_id = kcontrol->private_value; 5046129daaaSJames Courtier-Dutton 5056129daaaSJames Courtier-Dutton ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0]; 5066129daaaSJames Courtier-Dutton ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1]; 5076129daaaSJames Courtier-Dutton return 0; 5086129daaaSJames Courtier-Dutton } 5096129daaaSJames Courtier-Dutton 5106129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol, 5116129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 5126129daaaSJames Courtier-Dutton { 5136129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 5146129daaaSJames Courtier-Dutton unsigned int ogain; 5156129daaaSJames Courtier-Dutton unsigned int ngain; 5166129daaaSJames Courtier-Dutton int source_id; 5176129daaaSJames Courtier-Dutton int change = 0; 5186129daaaSJames Courtier-Dutton 5196129daaaSJames Courtier-Dutton source_id = kcontrol->private_value; 5206129daaaSJames Courtier-Dutton ogain = emu->i2c_capture_volume[source_id][0]; /* Left */ 5216129daaaSJames Courtier-Dutton ngain = ucontrol->value.integer.value[0]; 5226129daaaSJames Courtier-Dutton if (ngain > 0xff) 5235fe619f9STakashi Iwai return -EINVAL; 5246129daaaSJames Courtier-Dutton if (ogain != ngain) { 5256129daaaSJames Courtier-Dutton if (emu->i2c_capture_source == source_id) 5266129daaaSJames Courtier-Dutton snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) ); 5276129daaaSJames Courtier-Dutton emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0]; 5286129daaaSJames Courtier-Dutton change = 1; 5296129daaaSJames Courtier-Dutton } 5306129daaaSJames Courtier-Dutton ogain = emu->i2c_capture_volume[source_id][1]; /* Right */ 5316129daaaSJames Courtier-Dutton ngain = ucontrol->value.integer.value[1]; 5326129daaaSJames Courtier-Dutton if (ngain > 0xff) 5335fe619f9STakashi Iwai return -EINVAL; 5346129daaaSJames Courtier-Dutton if (ogain != ngain) { 5356129daaaSJames Courtier-Dutton if (emu->i2c_capture_source == source_id) 5366129daaaSJames Courtier-Dutton snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff)); 5376129daaaSJames Courtier-Dutton emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1]; 5386129daaaSJames Courtier-Dutton change = 1; 5396129daaaSJames Courtier-Dutton } 5406129daaaSJames Courtier-Dutton 5416129daaaSJames Courtier-Dutton return change; 5426129daaaSJames Courtier-Dutton } 5436129daaaSJames Courtier-Dutton 544b18cd538STrent Piepho #define spi_mute_info snd_ctl_boolean_mono_info 545b18cd538STrent Piepho 546b18cd538STrent Piepho static int spi_mute_get(struct snd_kcontrol *kcontrol, 547b18cd538STrent Piepho struct snd_ctl_elem_value *ucontrol) 548b18cd538STrent Piepho { 549b18cd538STrent Piepho struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 550b18cd538STrent Piepho unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT; 551b18cd538STrent Piepho unsigned int bit = kcontrol->private_value & SPI_REG_MASK; 552b18cd538STrent Piepho 553b18cd538STrent Piepho ucontrol->value.integer.value[0] = !(emu->spi_dac_reg[reg] & bit); 554b18cd538STrent Piepho return 0; 555b18cd538STrent Piepho } 556b18cd538STrent Piepho 557b18cd538STrent Piepho static int spi_mute_put(struct snd_kcontrol *kcontrol, 558b18cd538STrent Piepho struct snd_ctl_elem_value *ucontrol) 559b18cd538STrent Piepho { 560b18cd538STrent Piepho struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 561b18cd538STrent Piepho unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT; 562b18cd538STrent Piepho unsigned int bit = kcontrol->private_value & SPI_REG_MASK; 563b18cd538STrent Piepho int ret; 564b18cd538STrent Piepho 565b18cd538STrent Piepho ret = emu->spi_dac_reg[reg] & bit; 566b18cd538STrent Piepho if (ucontrol->value.integer.value[0]) { 567b18cd538STrent Piepho if (!ret) /* bit already cleared, do nothing */ 568b18cd538STrent Piepho return 0; 569b18cd538STrent Piepho emu->spi_dac_reg[reg] &= ~bit; 570b18cd538STrent Piepho } else { 571b18cd538STrent Piepho if (ret) /* bit already set, do nothing */ 572b18cd538STrent Piepho return 0; 573b18cd538STrent Piepho emu->spi_dac_reg[reg] |= bit; 574b18cd538STrent Piepho } 575b18cd538STrent Piepho 576b18cd538STrent Piepho ret = snd_ca0106_spi_write(emu, emu->spi_dac_reg[reg]); 5775fe619f9STakashi Iwai return ret ? -EINVAL : 1; 578b18cd538STrent Piepho } 579b18cd538STrent Piepho 58095a98265STakashi Iwai #define CA_VOLUME(xname,chid,reg) \ 58195a98265STakashi Iwai { \ 58295a98265STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 583302e9c5aSJaroslav Kysela .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ 584302e9c5aSJaroslav Kysela SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 58595a98265STakashi Iwai .info = snd_ca0106_volume_info, \ 58695a98265STakashi Iwai .get = snd_ca0106_volume_get, \ 58795a98265STakashi Iwai .put = snd_ca0106_volume_put, \ 5887cf0a953STakashi Iwai .tlv = { .p = snd_ca0106_db_scale1 }, \ 58995a98265STakashi Iwai .private_value = ((chid) << 8) | (reg) \ 5901da177e4SLinus Torvalds } 5911da177e4SLinus Torvalds 592e4a3d145STakashi Iwai static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = { 59395a98265STakashi Iwai CA_VOLUME("Analog Front Playback Volume", 59495a98265STakashi Iwai CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2), 59595a98265STakashi Iwai CA_VOLUME("Analog Rear Playback Volume", 59695a98265STakashi Iwai CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2), 59795a98265STakashi Iwai CA_VOLUME("Analog Center/LFE Playback Volume", 59895a98265STakashi Iwai CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2), 59995a98265STakashi Iwai CA_VOLUME("Analog Side Playback Volume", 60095a98265STakashi Iwai CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2), 60195a98265STakashi Iwai 60239596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Front Playback Volume", 60395a98265STakashi Iwai CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1), 60439596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Rear Playback Volume", 60595a98265STakashi Iwai CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1), 60639596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Center/LFE Playback Volume", 60795a98265STakashi Iwai CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1), 60839596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Unknown Playback Volume", 60995a98265STakashi Iwai CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1), 61095a98265STakashi Iwai 61195a98265STakashi Iwai CA_VOLUME("CAPTURE feedback Playback Volume", 61295a98265STakashi Iwai 1, CAPTURE_CONTROL), 61395a98265STakashi Iwai 61495a98265STakashi Iwai { 61595a98265STakashi Iwai .access = SNDRV_CTL_ELEM_ACCESS_READ, 61695a98265STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_PCM, 61795a98265STakashi Iwai .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), 61895a98265STakashi Iwai .count = 4, 61995a98265STakashi Iwai .info = snd_ca0106_spdif_info, 62095a98265STakashi Iwai .get = snd_ca0106_spdif_get_mask 62195a98265STakashi Iwai }, 6221da177e4SLinus Torvalds { 6231da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 62439596dc8SJames Courtier-Dutton .name = "IEC958 Playback Switch", 62595a98265STakashi Iwai .info = snd_ca0106_shared_spdif_info, 62695a98265STakashi Iwai .get = snd_ca0106_shared_spdif_get, 62795a98265STakashi Iwai .put = snd_ca0106_shared_spdif_put 62895a98265STakashi Iwai }, 6291da177e4SLinus Torvalds { 6301da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 631e6327cf9SJames Courtier-Dutton .name = "Digital Source Capture Enum", 63295a98265STakashi Iwai .info = snd_ca0106_capture_source_info, 63395a98265STakashi Iwai .get = snd_ca0106_capture_source_get, 63495a98265STakashi Iwai .put = snd_ca0106_capture_source_put 63595a98265STakashi Iwai }, 6361da177e4SLinus Torvalds { 6376129daaaSJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 638e6327cf9SJames Courtier-Dutton .name = "Analog Source Capture Enum", 6396129daaaSJames Courtier-Dutton .info = snd_ca0106_i2c_capture_source_info, 6406129daaaSJames Courtier-Dutton .get = snd_ca0106_i2c_capture_source_get, 6416129daaaSJames Courtier-Dutton .put = snd_ca0106_i2c_capture_source_put 6426129daaaSJames Courtier-Dutton }, 6436129daaaSJames Courtier-Dutton { 64495a98265STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_PCM, 64595a98265STakashi Iwai .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), 64695a98265STakashi Iwai .count = 4, 64795a98265STakashi Iwai .info = snd_ca0106_spdif_info, 648*3d475829STakashi Iwai .get = snd_ca0106_spdif_get_default, 649*3d475829STakashi Iwai .put = snd_ca0106_spdif_put_default 650*3d475829STakashi Iwai }, 651*3d475829STakashi Iwai { 652*3d475829STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_PCM, 653*3d475829STakashi Iwai .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), 654*3d475829STakashi Iwai .count = 4, 655*3d475829STakashi Iwai .info = snd_ca0106_spdif_info, 656*3d475829STakashi Iwai .get = snd_ca0106_spdif_get_stream, 657*3d475829STakashi Iwai .put = snd_ca0106_spdif_put_stream 65895a98265STakashi Iwai }, 6591da177e4SLinus Torvalds }; 6601da177e4SLinus Torvalds 6617c157069SJames Courtier-Dutton #define I2C_VOLUME(xname,chid) \ 6627c157069SJames Courtier-Dutton { \ 6637c157069SJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 6647c157069SJames Courtier-Dutton .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ 6657c157069SJames Courtier-Dutton SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 6667c157069SJames Courtier-Dutton .info = snd_ca0106_i2c_volume_info, \ 6677c157069SJames Courtier-Dutton .get = snd_ca0106_i2c_volume_get, \ 6687c157069SJames Courtier-Dutton .put = snd_ca0106_i2c_volume_put, \ 6697c157069SJames Courtier-Dutton .tlv = { .p = snd_ca0106_db_scale2 }, \ 6707c157069SJames Courtier-Dutton .private_value = chid \ 6717c157069SJames Courtier-Dutton } 6727c157069SJames Courtier-Dutton 6737c157069SJames Courtier-Dutton static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata = { 6747c157069SJames Courtier-Dutton I2C_VOLUME("Phone Capture Volume", 0), 6757c157069SJames Courtier-Dutton I2C_VOLUME("Mic Capture Volume", 1), 6767c157069SJames Courtier-Dutton I2C_VOLUME("Line in Capture Volume", 2), 6777c157069SJames Courtier-Dutton I2C_VOLUME("Aux Capture Volume", 3), 6787c157069SJames Courtier-Dutton }; 6797c157069SJames Courtier-Dutton 680b18cd538STrent Piepho #define SPI_SWITCH(xname,reg,bit) \ 681b18cd538STrent Piepho { \ 682b18cd538STrent Piepho .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 683b18cd538STrent Piepho .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ 684b18cd538STrent Piepho .info = spi_mute_info, \ 685b18cd538STrent Piepho .get = spi_mute_get, \ 686b18cd538STrent Piepho .put = spi_mute_put, \ 68718b5d32fSTrent Piepho .private_value = (reg<<SPI_REG_SHIFT) | (bit) \ 688b18cd538STrent Piepho } 689b18cd538STrent Piepho 690b18cd538STrent Piepho static struct snd_kcontrol_new snd_ca0106_volume_spi_dac_ctls[] 691b18cd538STrent Piepho __devinitdata = { 692b18cd538STrent Piepho SPI_SWITCH("Analog Front Playback Switch", 693b18cd538STrent Piepho SPI_DMUTE4_REG, SPI_DMUTE4_BIT), 694b18cd538STrent Piepho SPI_SWITCH("Analog Rear Playback Switch", 695b18cd538STrent Piepho SPI_DMUTE0_REG, SPI_DMUTE0_BIT), 696b18cd538STrent Piepho SPI_SWITCH("Analog Center/LFE Playback Switch", 697b18cd538STrent Piepho SPI_DMUTE2_REG, SPI_DMUTE2_BIT), 698b18cd538STrent Piepho SPI_SWITCH("Analog Side Playback Switch", 699b18cd538STrent Piepho SPI_DMUTE1_REG, SPI_DMUTE1_BIT), 700b18cd538STrent Piepho }; 701b18cd538STrent Piepho 702e4a3d145STakashi Iwai static int __devinit remove_ctl(struct snd_card *card, const char *name) 7031da177e4SLinus Torvalds { 704e4a3d145STakashi Iwai struct snd_ctl_elem_id id; 7051da177e4SLinus Torvalds memset(&id, 0, sizeof(id)); 7061da177e4SLinus Torvalds strcpy(id.name, name); 7071da177e4SLinus Torvalds id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 7081da177e4SLinus Torvalds return snd_ctl_remove_id(card, &id); 7091da177e4SLinus Torvalds } 7101da177e4SLinus Torvalds 711e4a3d145STakashi Iwai static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, const char *name) 7121da177e4SLinus Torvalds { 713e4a3d145STakashi Iwai struct snd_ctl_elem_id sid; 7141da177e4SLinus Torvalds memset(&sid, 0, sizeof(sid)); 7151da177e4SLinus Torvalds /* FIXME: strcpy is bad. */ 7161da177e4SLinus Torvalds strcpy(sid.name, name); 7171da177e4SLinus Torvalds sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 7181da177e4SLinus Torvalds return snd_ctl_find_id(card, &sid); 7191da177e4SLinus Torvalds } 7201da177e4SLinus Torvalds 721e4a3d145STakashi Iwai static int __devinit rename_ctl(struct snd_card *card, const char *src, const char *dst) 7221da177e4SLinus Torvalds { 723e4a3d145STakashi Iwai struct snd_kcontrol *kctl = ctl_find(card, src); 7241da177e4SLinus Torvalds if (kctl) { 7251da177e4SLinus Torvalds strcpy(kctl->id.name, dst); 7261da177e4SLinus Torvalds return 0; 7271da177e4SLinus Torvalds } 7281da177e4SLinus Torvalds return -ENOENT; 7291da177e4SLinus Torvalds } 7301da177e4SLinus Torvalds 731fca7f388STrent Piepho #define ADD_CTLS(emu, ctls) \ 732fca7f388STrent Piepho do { \ 733bed515b0SHarvey Harrison int i, _err; \ 734fca7f388STrent Piepho for (i = 0; i < ARRAY_SIZE(ctls); i++) { \ 735bed515b0SHarvey Harrison _err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \ 736bed515b0SHarvey Harrison if (_err < 0) \ 737bed515b0SHarvey Harrison return _err; \ 738fca7f388STrent Piepho } \ 739fca7f388STrent Piepho } while (0) 740fca7f388STrent Piepho 74149c88b85STakashi Iwai static __devinitdata 74249c88b85STakashi Iwai DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 50, 1); 74349c88b85STakashi Iwai 74449c88b85STakashi Iwai static char *slave_vols[] __devinitdata = { 74549c88b85STakashi Iwai "Analog Front Playback Volume", 74649c88b85STakashi Iwai "Analog Rear Playback Volume", 74749c88b85STakashi Iwai "Analog Center/LFE Playback Volume", 74849c88b85STakashi Iwai "Analog Side Playback Volume", 74949c88b85STakashi Iwai "IEC958 Front Playback Volume", 75049c88b85STakashi Iwai "IEC958 Rear Playback Volume", 75149c88b85STakashi Iwai "IEC958 Center/LFE Playback Volume", 75249c88b85STakashi Iwai "IEC958 Unknown Playback Volume", 75349c88b85STakashi Iwai "CAPTURE feedback Playback Volume", 75449c88b85STakashi Iwai NULL 75549c88b85STakashi Iwai }; 75649c88b85STakashi Iwai 75749c88b85STakashi Iwai static char *slave_sws[] __devinitdata = { 75849c88b85STakashi Iwai "Analog Front Playback Switch", 75949c88b85STakashi Iwai "Analog Rear Playback Switch", 76049c88b85STakashi Iwai "Analog Center/LFE Playback Switch", 76149c88b85STakashi Iwai "Analog Side Playback Switch", 76249c88b85STakashi Iwai "IEC958 Playback Switch", 76349c88b85STakashi Iwai NULL 76449c88b85STakashi Iwai }; 76549c88b85STakashi Iwai 76649c88b85STakashi Iwai static void __devinit add_slaves(struct snd_card *card, 76749c88b85STakashi Iwai struct snd_kcontrol *master, char **list) 76849c88b85STakashi Iwai { 76949c88b85STakashi Iwai for (; *list; list++) { 77049c88b85STakashi Iwai struct snd_kcontrol *slave = ctl_find(card, *list); 77149c88b85STakashi Iwai if (slave) 77249c88b85STakashi Iwai snd_ctl_add_slave(master, slave); 77349c88b85STakashi Iwai } 77449c88b85STakashi Iwai } 77549c88b85STakashi Iwai 776e4a3d145STakashi Iwai int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) 7771da177e4SLinus Torvalds { 778fca7f388STrent Piepho int err; 779e4a3d145STakashi Iwai struct snd_card *card = emu->card; 7801da177e4SLinus Torvalds char **c; 78149c88b85STakashi Iwai struct snd_kcontrol *vmaster; 7821da177e4SLinus Torvalds static char *ca0106_remove_ctls[] = { 7831da177e4SLinus Torvalds "Master Mono Playback Switch", 7841da177e4SLinus Torvalds "Master Mono Playback Volume", 7851da177e4SLinus Torvalds "3D Control - Switch", 7861da177e4SLinus Torvalds "3D Control Sigmatel - Depth", 7871da177e4SLinus Torvalds "PCM Playback Switch", 7881da177e4SLinus Torvalds "PCM Playback Volume", 7891da177e4SLinus Torvalds "CD Playback Switch", 7901da177e4SLinus Torvalds "CD Playback Volume", 7911da177e4SLinus Torvalds "Phone Playback Switch", 7921da177e4SLinus Torvalds "Phone Playback Volume", 7931da177e4SLinus Torvalds "Video Playback Switch", 7941da177e4SLinus Torvalds "Video Playback Volume", 7951da177e4SLinus Torvalds "PC Speaker Playback Switch", 7961da177e4SLinus Torvalds "PC Speaker Playback Volume", 7971da177e4SLinus Torvalds "Mono Output Select", 7981da177e4SLinus Torvalds "Capture Source", 7991da177e4SLinus Torvalds "Capture Switch", 8001da177e4SLinus Torvalds "Capture Volume", 8011da177e4SLinus Torvalds "External Amplifier", 8021da177e4SLinus Torvalds "Sigmatel 4-Speaker Stereo Playback Switch", 8031da177e4SLinus Torvalds "Sigmatel Surround Phase Inversion Playback ", 8041da177e4SLinus Torvalds NULL 8051da177e4SLinus Torvalds }; 8061da177e4SLinus Torvalds static char *ca0106_rename_ctls[] = { 8071da177e4SLinus Torvalds "Master Playback Switch", "Capture Switch", 8081da177e4SLinus Torvalds "Master Playback Volume", "Capture Volume", 8091da177e4SLinus Torvalds "Line Playback Switch", "AC97 Line Capture Switch", 8101da177e4SLinus Torvalds "Line Playback Volume", "AC97 Line Capture Volume", 8111da177e4SLinus Torvalds "Aux Playback Switch", "AC97 Aux Capture Switch", 8121da177e4SLinus Torvalds "Aux Playback Volume", "AC97 Aux Capture Volume", 8131da177e4SLinus Torvalds "Mic Playback Switch", "AC97 Mic Capture Switch", 8141da177e4SLinus Torvalds "Mic Playback Volume", "AC97 Mic Capture Volume", 8151da177e4SLinus Torvalds "Mic Select", "AC97 Mic Select", 8161da177e4SLinus Torvalds "Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)", 8171da177e4SLinus Torvalds NULL 8181da177e4SLinus Torvalds }; 8191da177e4SLinus Torvalds #if 1 8201da177e4SLinus Torvalds for (c = ca0106_remove_ctls; *c; c++) 8211da177e4SLinus Torvalds remove_ctl(card, *c); 8221da177e4SLinus Torvalds for (c = ca0106_rename_ctls; *c; c += 2) 8231da177e4SLinus Torvalds rename_ctl(card, c[0], c[1]); 8241da177e4SLinus Torvalds #endif 8251da177e4SLinus Torvalds 826fca7f388STrent Piepho ADD_CTLS(emu, snd_ca0106_volume_ctls); 82795a98265STakashi Iwai if (emu->details->i2c_adc == 1) { 828fca7f388STrent Piepho ADD_CTLS(emu, snd_ca0106_volume_i2c_adc_ctls); 829be0b7b01SJames Courtier-Dutton if (emu->details->gpio_type == 1) 83095a98265STakashi Iwai err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu)); 831be0b7b01SJames Courtier-Dutton else /* gpio_type == 2 */ 832be0b7b01SJames Courtier-Dutton err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu)); 83395a98265STakashi Iwai if (err < 0) 8341da177e4SLinus Torvalds return err; 83595a98265STakashi Iwai } 836fca7f388STrent Piepho if (emu->details->spi_dac == 1) 837fca7f388STrent Piepho ADD_CTLS(emu, snd_ca0106_volume_spi_dac_ctls); 83849c88b85STakashi Iwai 83949c88b85STakashi Iwai /* Create virtual master controls */ 84049c88b85STakashi Iwai vmaster = snd_ctl_make_virtual_master("Master Playback Volume", 84149c88b85STakashi Iwai snd_ca0106_master_db_scale); 84249c88b85STakashi Iwai if (!vmaster) 84349c88b85STakashi Iwai return -ENOMEM; 84449c88b85STakashi Iwai add_slaves(card, vmaster, slave_vols); 84549c88b85STakashi Iwai 84649c88b85STakashi Iwai if (emu->details->spi_dac == 1) { 84749c88b85STakashi Iwai vmaster = snd_ctl_make_virtual_master("Master Playback Switch", 84849c88b85STakashi Iwai NULL); 84949c88b85STakashi Iwai if (!vmaster) 85049c88b85STakashi Iwai return -ENOMEM; 85149c88b85STakashi Iwai add_slaves(card, vmaster, slave_sws); 85249c88b85STakashi Iwai } 8531da177e4SLinus Torvalds return 0; 8541da177e4SLinus Torvalds } 8551da177e4SLinus Torvalds 8565da95273STakashi Iwai #ifdef CONFIG_PM 8575da95273STakashi Iwai struct ca0106_vol_tbl { 8585da95273STakashi Iwai unsigned int reg; 8595da95273STakashi Iwai unsigned int channel_id; 8605da95273STakashi Iwai }; 8615da95273STakashi Iwai 8625da95273STakashi Iwai static struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = { 8635da95273STakashi Iwai { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2 }, 8645da95273STakashi Iwai { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2 }, 8655da95273STakashi Iwai { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2 }, 8665da95273STakashi Iwai { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2 }, 8675da95273STakashi Iwai { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1 }, 8685da95273STakashi Iwai { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1 }, 8695da95273STakashi Iwai { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1 }, 8705da95273STakashi Iwai { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1 }, 8715da95273STakashi Iwai { 1, CAPTURE_CONTROL }, 8725da95273STakashi Iwai }; 8735da95273STakashi Iwai 8745da95273STakashi Iwai void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip) 8755da95273STakashi Iwai { 8765da95273STakashi Iwai int i; 8775da95273STakashi Iwai 8785da95273STakashi Iwai /* save volumes */ 8795da95273STakashi Iwai for (i = 0; i < NUM_SAVED_VOLUMES; i++) 8805da95273STakashi Iwai chip->saved_vol[i] = 8815da95273STakashi Iwai snd_ca0106_ptr_read(chip, saved_volumes[i].reg, 8825da95273STakashi Iwai saved_volumes[i].channel_id); 8835da95273STakashi Iwai } 8845da95273STakashi Iwai 8855da95273STakashi Iwai void snd_ca0106_mixer_resume(struct snd_ca0106 *chip) 8865da95273STakashi Iwai { 8875da95273STakashi Iwai int i; 8885da95273STakashi Iwai 8895da95273STakashi Iwai for (i = 0; i < NUM_SAVED_VOLUMES; i++) 8905da95273STakashi Iwai snd_ca0106_ptr_write(chip, saved_volumes[i].reg, 8915da95273STakashi Iwai saved_volumes[i].channel_id, 8925da95273STakashi Iwai chip->saved_vol[i]); 8935da95273STakashi Iwai 8945da95273STakashi Iwai ca0106_spdif_enable(chip); 8955da95273STakashi Iwai ca0106_set_capture_source(chip); 8965da95273STakashi Iwai ca0106_set_i2c_capture_source(chip, chip->i2c_capture_source, 1); 8975da95273STakashi Iwai for (i = 0; i < 4; i++) 8985da95273STakashi Iwai ca0106_set_spdif_bits(chip, i); 8995da95273STakashi Iwai if (chip->details->i2c_adc) 9005da95273STakashi Iwai ca0106_set_capture_mic_line_in(chip); 9015da95273STakashi Iwai } 9025da95273STakashi Iwai #endif /* CONFIG_PM */ 903