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 * 4525985edcSLucas De Marchi * This code was initially 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/moduleparam.h> 671da177e4SLinus Torvalds #include <sound/core.h> 681da177e4SLinus Torvalds #include <sound/initval.h> 691da177e4SLinus Torvalds #include <sound/pcm.h> 701da177e4SLinus Torvalds #include <sound/ac97_codec.h> 711da177e4SLinus Torvalds #include <sound/info.h> 7242750b04SJaroslav Kysela #include <sound/tlv.h> 736cbbfe1cSTakashi Iwai #include <linux/io.h> 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds #include "ca0106.h" 761da177e4SLinus Torvalds 775da95273STakashi Iwai static void ca0106_spdif_enable(struct snd_ca0106 *emu) 785da95273STakashi Iwai { 795da95273STakashi Iwai unsigned int val; 805da95273STakashi Iwai 815da95273STakashi Iwai if (emu->spdif_enable) { 825da95273STakashi Iwai /* Digital */ 835da95273STakashi Iwai snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); 845da95273STakashi Iwai snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000); 855da95273STakashi Iwai val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000; 865da95273STakashi Iwai snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val); 875da95273STakashi Iwai val = inl(emu->port + GPIO) & ~0x101; 885da95273STakashi Iwai outl(val, emu->port + GPIO); 895da95273STakashi Iwai 905da95273STakashi Iwai } else { 915da95273STakashi Iwai /* Analog */ 925da95273STakashi Iwai snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); 935da95273STakashi Iwai snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000); 945da95273STakashi Iwai val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000; 955da95273STakashi Iwai snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val); 965da95273STakashi Iwai val = inl(emu->port + GPIO) | 0x101; 975da95273STakashi Iwai outl(val, emu->port + GPIO); 985da95273STakashi Iwai } 995da95273STakashi Iwai } 1005da95273STakashi Iwai 1015da95273STakashi Iwai static void ca0106_set_capture_source(struct snd_ca0106 *emu) 1025da95273STakashi Iwai { 1035da95273STakashi Iwai unsigned int val = emu->capture_source; 1045da95273STakashi Iwai unsigned int source, mask; 1055da95273STakashi Iwai source = (val << 28) | (val << 24) | (val << 20) | (val << 16); 1065da95273STakashi Iwai mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff; 1075da95273STakashi Iwai snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask); 1085da95273STakashi Iwai } 1095da95273STakashi Iwai 1105da95273STakashi Iwai static void ca0106_set_i2c_capture_source(struct snd_ca0106 *emu, 1115da95273STakashi Iwai unsigned int val, int force) 1125da95273STakashi Iwai { 1135da95273STakashi Iwai unsigned int ngain, ogain; 1145da95273STakashi Iwai u32 source; 1155da95273STakashi Iwai 1165da95273STakashi Iwai snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ 1175da95273STakashi Iwai ngain = emu->i2c_capture_volume[val][0]; /* Left */ 1185da95273STakashi Iwai ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */ 1195da95273STakashi Iwai if (force || ngain != ogain) 1205da95273STakashi Iwai snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ngain & 0xff); 1215da95273STakashi Iwai ngain = emu->i2c_capture_volume[val][1]; /* Right */ 1225da95273STakashi Iwai ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */ 1235da95273STakashi Iwai if (force || ngain != ogain) 1245da95273STakashi Iwai snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ngain & 0xff); 1255da95273STakashi Iwai source = 1 << val; 1265da95273STakashi Iwai snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */ 1275da95273STakashi Iwai emu->i2c_capture_source = val; 1285da95273STakashi Iwai } 1295da95273STakashi Iwai 1305da95273STakashi Iwai static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu) 1315da95273STakashi Iwai { 1325da95273STakashi Iwai u32 tmp; 1335da95273STakashi Iwai 1345da95273STakashi Iwai if (emu->capture_mic_line_in) { 1355da95273STakashi Iwai /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */ 1365da95273STakashi Iwai tmp = inl(emu->port+GPIO) & ~0x400; 1375da95273STakashi Iwai tmp = tmp | 0x400; 1385da95273STakashi Iwai outl(tmp, emu->port+GPIO); 1395da95273STakashi Iwai /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */ 1405da95273STakashi Iwai } else { 1415da95273STakashi Iwai /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */ 1425da95273STakashi Iwai tmp = inl(emu->port+GPIO) & ~0x400; 1435da95273STakashi Iwai outl(tmp, emu->port+GPIO); 1445da95273STakashi Iwai /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */ 1455da95273STakashi Iwai } 1465da95273STakashi Iwai } 1475da95273STakashi Iwai 1485da95273STakashi Iwai static void ca0106_set_spdif_bits(struct snd_ca0106 *emu, int idx) 1495da95273STakashi Iwai { 1503d475829STakashi Iwai snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, emu->spdif_str_bits[idx]); 1515da95273STakashi Iwai } 1525da95273STakashi Iwai 1535da95273STakashi Iwai /* 1545da95273STakashi Iwai */ 1550cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1); 1560cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1); 15742750b04SJaroslav Kysela 158a5ce8890STakashi Iwai #define snd_ca0106_shared_spdif_info snd_ctl_boolean_mono_info 1591da177e4SLinus Torvalds 160e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol, 161e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 1621da177e4SLinus Torvalds { 163e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 1641da177e4SLinus Torvalds 1655fe619f9STakashi Iwai ucontrol->value.integer.value[0] = emu->spdif_enable; 1661da177e4SLinus Torvalds return 0; 1671da177e4SLinus Torvalds } 1681da177e4SLinus Torvalds 169e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol, 170e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 1711da177e4SLinus Torvalds { 172e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 1731da177e4SLinus Torvalds unsigned int val; 1741da177e4SLinus Torvalds int change = 0; 1751da177e4SLinus Torvalds 1765fe619f9STakashi Iwai val = !!ucontrol->value.integer.value[0]; 1771da177e4SLinus Torvalds change = (emu->spdif_enable != val); 1781da177e4SLinus Torvalds if (change) { 1791da177e4SLinus Torvalds emu->spdif_enable = val; 1805da95273STakashi Iwai ca0106_spdif_enable(emu); 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds return change; 1831da177e4SLinus Torvalds } 1841da177e4SLinus Torvalds 185e4a3d145STakashi Iwai static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol, 186e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 1871da177e4SLinus Torvalds { 188de95eae2STakashi Iwai static const char * const texts[6] = { 18939596dc8SJames Courtier-Dutton "IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out" 19095a98265STakashi Iwai }; 1911da177e4SLinus Torvalds 192de95eae2STakashi Iwai return snd_ctl_enum_info(uinfo, 1, 6, texts); 1931da177e4SLinus Torvalds } 1941da177e4SLinus Torvalds 195e4a3d145STakashi Iwai static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol, 196e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 1971da177e4SLinus Torvalds { 198e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds ucontrol->value.enumerated.item[0] = emu->capture_source; 2011da177e4SLinus Torvalds return 0; 2021da177e4SLinus Torvalds } 2031da177e4SLinus Torvalds 204e4a3d145STakashi Iwai static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol, 205e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 2061da177e4SLinus Torvalds { 207e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 2081da177e4SLinus Torvalds unsigned int val; 2091da177e4SLinus Torvalds int change = 0; 2101da177e4SLinus Torvalds 2111da177e4SLinus Torvalds val = ucontrol->value.enumerated.item[0] ; 2125fe619f9STakashi Iwai if (val >= 6) 2135fe619f9STakashi Iwai return -EINVAL; 2141da177e4SLinus Torvalds change = (emu->capture_source != val); 2151da177e4SLinus Torvalds if (change) { 2161da177e4SLinus Torvalds emu->capture_source = val; 2175da95273STakashi Iwai ca0106_set_capture_source(emu); 2181da177e4SLinus Torvalds } 2191da177e4SLinus Torvalds return change; 2201da177e4SLinus Torvalds } 2211da177e4SLinus Torvalds 2226129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol, 2236129daaaSJames Courtier-Dutton struct snd_ctl_elem_info *uinfo) 2246129daaaSJames Courtier-Dutton { 225de95eae2STakashi Iwai static const char * const texts[4] = { 2266129daaaSJames Courtier-Dutton "Phone", "Mic", "Line in", "Aux" 2276129daaaSJames Courtier-Dutton }; 2286129daaaSJames Courtier-Dutton 229de95eae2STakashi Iwai return snd_ctl_enum_info(uinfo, 1, 4, texts); 2306129daaaSJames Courtier-Dutton } 2316129daaaSJames Courtier-Dutton 2326129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol, 2336129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 2346129daaaSJames Courtier-Dutton { 2356129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 2366129daaaSJames Courtier-Dutton 2376129daaaSJames Courtier-Dutton ucontrol->value.enumerated.item[0] = emu->i2c_capture_source; 2386129daaaSJames Courtier-Dutton return 0; 2396129daaaSJames Courtier-Dutton } 2406129daaaSJames Courtier-Dutton 2416129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol, 2426129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 2436129daaaSJames Courtier-Dutton { 2446129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 2456129daaaSJames Courtier-Dutton unsigned int source_id; 2466129daaaSJames Courtier-Dutton int change = 0; 2476129daaaSJames Courtier-Dutton /* If the capture source has changed, 2486129daaaSJames Courtier-Dutton * update the capture volume from the cached value 2496129daaaSJames Courtier-Dutton * for the particular source. 2506129daaaSJames Courtier-Dutton */ 2516129daaaSJames Courtier-Dutton source_id = ucontrol->value.enumerated.item[0] ; 2525fe619f9STakashi Iwai if (source_id >= 4) 2535fe619f9STakashi Iwai return -EINVAL; 2546129daaaSJames Courtier-Dutton change = (emu->i2c_capture_source != source_id); 2556129daaaSJames Courtier-Dutton if (change) { 2565da95273STakashi Iwai ca0106_set_i2c_capture_source(emu, source_id, 0); 2576129daaaSJames Courtier-Dutton } 2586129daaaSJames Courtier-Dutton return change; 2596129daaaSJames Courtier-Dutton } 2606129daaaSJames Courtier-Dutton 261be0b7b01SJames Courtier-Dutton static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol, 262be0b7b01SJames Courtier-Dutton struct snd_ctl_elem_info *uinfo) 263be0b7b01SJames Courtier-Dutton { 264de95eae2STakashi Iwai static const char * const texts[2] = { "Side out", "Line in" }; 265be0b7b01SJames Courtier-Dutton 266de95eae2STakashi Iwai return snd_ctl_enum_info(uinfo, 1, 2, texts); 267be0b7b01SJames Courtier-Dutton } 268be0b7b01SJames Courtier-Dutton 269e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol, 270e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 271ed144f3cSJames Courtier-Dutton { 272de95eae2STakashi Iwai static const char * const texts[2] = { "Line in", "Mic in" }; 273ed144f3cSJames Courtier-Dutton 274de95eae2STakashi Iwai return snd_ctl_enum_info(uinfo, 1, 2, texts); 275ed144f3cSJames Courtier-Dutton } 276ed144f3cSJames Courtier-Dutton 277e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol, 278e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 279ed144f3cSJames Courtier-Dutton { 280e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 281ed144f3cSJames Courtier-Dutton 282ed144f3cSJames Courtier-Dutton ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in; 283ed144f3cSJames Courtier-Dutton return 0; 284ed144f3cSJames Courtier-Dutton } 285ed144f3cSJames Courtier-Dutton 286e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol, 287e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 288ed144f3cSJames Courtier-Dutton { 289e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 290ed144f3cSJames Courtier-Dutton unsigned int val; 291ed144f3cSJames Courtier-Dutton int change = 0; 292ed144f3cSJames Courtier-Dutton 293ed144f3cSJames Courtier-Dutton val = ucontrol->value.enumerated.item[0] ; 2945fe619f9STakashi Iwai if (val > 1) 2955fe619f9STakashi Iwai return -EINVAL; 296ed144f3cSJames Courtier-Dutton change = (emu->capture_mic_line_in != val); 297ed144f3cSJames Courtier-Dutton if (change) { 298ed144f3cSJames Courtier-Dutton emu->capture_mic_line_in = val; 2995da95273STakashi Iwai ca0106_set_capture_mic_line_in(emu); 300ed144f3cSJames Courtier-Dutton } 301ed144f3cSJames Courtier-Dutton return change; 302ed144f3cSJames Courtier-Dutton } 303ed144f3cSJames Courtier-Dutton 304*f3b827e0SBhumika Goyal static const struct snd_kcontrol_new snd_ca0106_capture_mic_line_in = 305ed144f3cSJames Courtier-Dutton { 306ed144f3cSJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3076129daaaSJames Courtier-Dutton .name = "Shared Mic/Line in Capture Switch", 308ed144f3cSJames Courtier-Dutton .info = snd_ca0106_capture_mic_line_in_info, 309ed144f3cSJames Courtier-Dutton .get = snd_ca0106_capture_mic_line_in_get, 310ed144f3cSJames Courtier-Dutton .put = snd_ca0106_capture_mic_line_in_put 311ed144f3cSJames Courtier-Dutton }; 312ed144f3cSJames Courtier-Dutton 313*f3b827e0SBhumika Goyal static const struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out = 314be0b7b01SJames Courtier-Dutton { 315be0b7b01SJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 316be0b7b01SJames Courtier-Dutton .name = "Shared Line in/Side out Capture Switch", 317be0b7b01SJames Courtier-Dutton .info = snd_ca0106_capture_line_in_side_out_info, 318be0b7b01SJames Courtier-Dutton .get = snd_ca0106_capture_mic_line_in_get, 319be0b7b01SJames Courtier-Dutton .put = snd_ca0106_capture_mic_line_in_put 320be0b7b01SJames Courtier-Dutton }; 321be0b7b01SJames Courtier-Dutton 322be0b7b01SJames Courtier-Dutton 323e4a3d145STakashi Iwai static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol, 324e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 3251da177e4SLinus Torvalds { 3261da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 3271da177e4SLinus Torvalds uinfo->count = 1; 3281da177e4SLinus Torvalds return 0; 3291da177e4SLinus Torvalds } 3301da177e4SLinus Torvalds 3313d475829STakashi Iwai static void decode_spdif_bits(unsigned char *status, unsigned int bits) 3323d475829STakashi Iwai { 3333d475829STakashi Iwai status[0] = (bits >> 0) & 0xff; 3343d475829STakashi Iwai status[1] = (bits >> 8) & 0xff; 3353d475829STakashi Iwai status[2] = (bits >> 16) & 0xff; 3363d475829STakashi Iwai status[3] = (bits >> 24) & 0xff; 3373d475829STakashi Iwai } 3383d475829STakashi Iwai 3393d475829STakashi Iwai static int snd_ca0106_spdif_get_default(struct snd_kcontrol *kcontrol, 340e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3411da177e4SLinus Torvalds { 342e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 3431da177e4SLinus Torvalds unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 3441da177e4SLinus Torvalds 3453d475829STakashi Iwai decode_spdif_bits(ucontrol->value.iec958.status, 3463d475829STakashi Iwai emu->spdif_bits[idx]); 3473d475829STakashi Iwai return 0; 3483d475829STakashi Iwai } 3493d475829STakashi Iwai 3503d475829STakashi Iwai static int snd_ca0106_spdif_get_stream(struct snd_kcontrol *kcontrol, 3513d475829STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3523d475829STakashi Iwai { 3533d475829STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 3543d475829STakashi Iwai unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 3553d475829STakashi Iwai 3563d475829STakashi Iwai decode_spdif_bits(ucontrol->value.iec958.status, 3573d475829STakashi Iwai emu->spdif_str_bits[idx]); 3581da177e4SLinus Torvalds return 0; 3591da177e4SLinus Torvalds } 3601da177e4SLinus Torvalds 361e4a3d145STakashi Iwai static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol, 362e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3631da177e4SLinus Torvalds { 3641da177e4SLinus Torvalds ucontrol->value.iec958.status[0] = 0xff; 3651da177e4SLinus Torvalds ucontrol->value.iec958.status[1] = 0xff; 3661da177e4SLinus Torvalds ucontrol->value.iec958.status[2] = 0xff; 3671da177e4SLinus Torvalds ucontrol->value.iec958.status[3] = 0xff; 3681da177e4SLinus Torvalds return 0; 3691da177e4SLinus Torvalds } 3701da177e4SLinus Torvalds 3713d475829STakashi Iwai static unsigned int encode_spdif_bits(unsigned char *status) 3723d475829STakashi Iwai { 3733d475829STakashi Iwai return ((unsigned int)status[0] << 0) | 3743d475829STakashi Iwai ((unsigned int)status[1] << 8) | 3753d475829STakashi Iwai ((unsigned int)status[2] << 16) | 3763d475829STakashi Iwai ((unsigned int)status[3] << 24); 3773d475829STakashi Iwai } 3783d475829STakashi Iwai 3793d475829STakashi Iwai static int snd_ca0106_spdif_put_default(struct snd_kcontrol *kcontrol, 380e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3811da177e4SLinus Torvalds { 382e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 3831da177e4SLinus Torvalds unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 3841da177e4SLinus Torvalds unsigned int val; 3851da177e4SLinus Torvalds 3863d475829STakashi Iwai val = encode_spdif_bits(ucontrol->value.iec958.status); 3873d475829STakashi Iwai if (val != emu->spdif_bits[idx]) { 3881da177e4SLinus Torvalds emu->spdif_bits[idx] = val; 3893d475829STakashi Iwai /* FIXME: this isn't safe, but needed to keep the compatibility 3903d475829STakashi Iwai * with older alsa-lib config 3913d475829STakashi Iwai */ 3923d475829STakashi Iwai emu->spdif_str_bits[idx] = val; 3935da95273STakashi Iwai ca0106_set_spdif_bits(emu, idx); 3943d475829STakashi Iwai return 1; 3951da177e4SLinus Torvalds } 3963d475829STakashi Iwai return 0; 3973d475829STakashi Iwai } 3983d475829STakashi Iwai 3993d475829STakashi Iwai static int snd_ca0106_spdif_put_stream(struct snd_kcontrol *kcontrol, 4003d475829STakashi Iwai struct snd_ctl_elem_value *ucontrol) 4013d475829STakashi Iwai { 4023d475829STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 4033d475829STakashi Iwai unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 4043d475829STakashi Iwai unsigned int val; 4053d475829STakashi Iwai 4063d475829STakashi Iwai val = encode_spdif_bits(ucontrol->value.iec958.status); 4073d475829STakashi Iwai if (val != emu->spdif_str_bits[idx]) { 4083d475829STakashi Iwai emu->spdif_str_bits[idx] = val; 4093d475829STakashi Iwai ca0106_set_spdif_bits(emu, idx); 4103d475829STakashi Iwai return 1; 4113d475829STakashi Iwai } 4123d475829STakashi Iwai return 0; 4131da177e4SLinus Torvalds } 4141da177e4SLinus Torvalds 415e4a3d145STakashi Iwai static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol, 416e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 4171da177e4SLinus Torvalds { 4181da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 4191da177e4SLinus Torvalds uinfo->count = 2; 4201da177e4SLinus Torvalds uinfo->value.integer.min = 0; 4211da177e4SLinus Torvalds uinfo->value.integer.max = 255; 4221da177e4SLinus Torvalds return 0; 4231da177e4SLinus Torvalds } 4241da177e4SLinus Torvalds 425e4a3d145STakashi Iwai static int snd_ca0106_volume_get(struct snd_kcontrol *kcontrol, 426e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 4271da177e4SLinus Torvalds { 428e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 4291da177e4SLinus Torvalds unsigned int value; 43095a98265STakashi Iwai int channel_id, reg; 43195a98265STakashi Iwai 43295a98265STakashi Iwai channel_id = (kcontrol->private_value >> 8) & 0xff; 43395a98265STakashi Iwai reg = kcontrol->private_value & 0xff; 4341da177e4SLinus Torvalds 4351da177e4SLinus Torvalds value = snd_ca0106_ptr_read(emu, reg, channel_id); 4361da177e4SLinus Torvalds ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */ 4371da177e4SLinus Torvalds ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */ 4381da177e4SLinus Torvalds return 0; 4391da177e4SLinus Torvalds } 4401da177e4SLinus Torvalds 441e4a3d145STakashi Iwai static int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol, 442e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 4431da177e4SLinus Torvalds { 444e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 44595a98265STakashi Iwai unsigned int oval, nval; 44695a98265STakashi Iwai int channel_id, reg; 44795a98265STakashi Iwai 44895a98265STakashi Iwai channel_id = (kcontrol->private_value >> 8) & 0xff; 44995a98265STakashi Iwai reg = kcontrol->private_value & 0xff; 45095a98265STakashi Iwai 45195a98265STakashi Iwai oval = snd_ca0106_ptr_read(emu, reg, channel_id); 45295a98265STakashi Iwai nval = ((0xff - ucontrol->value.integer.value[0]) << 24) | 45395a98265STakashi Iwai ((0xff - ucontrol->value.integer.value[1]) << 16); 45495a98265STakashi Iwai nval |= ((0xff - ucontrol->value.integer.value[0]) << 8) | 45595a98265STakashi Iwai ((0xff - ucontrol->value.integer.value[1]) ); 45695a98265STakashi Iwai if (oval == nval) 45795a98265STakashi Iwai return 0; 45895a98265STakashi Iwai snd_ca0106_ptr_write(emu, reg, channel_id, nval); 4591da177e4SLinus Torvalds return 1; 4601da177e4SLinus Torvalds } 46195a98265STakashi Iwai 4626129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_info(struct snd_kcontrol *kcontrol, 4636129daaaSJames Courtier-Dutton struct snd_ctl_elem_info *uinfo) 4646129daaaSJames Courtier-Dutton { 4656129daaaSJames Courtier-Dutton uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 4666129daaaSJames Courtier-Dutton uinfo->count = 2; 4676129daaaSJames Courtier-Dutton uinfo->value.integer.min = 0; 4686129daaaSJames Courtier-Dutton uinfo->value.integer.max = 255; 4696129daaaSJames Courtier-Dutton return 0; 4706129daaaSJames Courtier-Dutton } 4716129daaaSJames Courtier-Dutton 4726129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol, 4736129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 4746129daaaSJames Courtier-Dutton { 4756129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 4766129daaaSJames Courtier-Dutton int source_id; 4776129daaaSJames Courtier-Dutton 4786129daaaSJames Courtier-Dutton source_id = kcontrol->private_value; 4796129daaaSJames Courtier-Dutton 4806129daaaSJames Courtier-Dutton ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0]; 4816129daaaSJames Courtier-Dutton ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1]; 4826129daaaSJames Courtier-Dutton return 0; 4836129daaaSJames Courtier-Dutton } 4846129daaaSJames Courtier-Dutton 4856129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol, 4866129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 4876129daaaSJames Courtier-Dutton { 4886129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 4896129daaaSJames Courtier-Dutton unsigned int ogain; 4906129daaaSJames Courtier-Dutton unsigned int ngain; 4916129daaaSJames Courtier-Dutton int source_id; 4926129daaaSJames Courtier-Dutton int change = 0; 4936129daaaSJames Courtier-Dutton 4946129daaaSJames Courtier-Dutton source_id = kcontrol->private_value; 4956129daaaSJames Courtier-Dutton ogain = emu->i2c_capture_volume[source_id][0]; /* Left */ 4966129daaaSJames Courtier-Dutton ngain = ucontrol->value.integer.value[0]; 4976129daaaSJames Courtier-Dutton if (ngain > 0xff) 4985fe619f9STakashi Iwai return -EINVAL; 4996129daaaSJames Courtier-Dutton if (ogain != ngain) { 5006129daaaSJames Courtier-Dutton if (emu->i2c_capture_source == source_id) 5016129daaaSJames Courtier-Dutton snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) ); 5026129daaaSJames Courtier-Dutton emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0]; 5036129daaaSJames Courtier-Dutton change = 1; 5046129daaaSJames Courtier-Dutton } 5056129daaaSJames Courtier-Dutton ogain = emu->i2c_capture_volume[source_id][1]; /* Right */ 5066129daaaSJames Courtier-Dutton ngain = ucontrol->value.integer.value[1]; 5076129daaaSJames Courtier-Dutton if (ngain > 0xff) 5085fe619f9STakashi Iwai return -EINVAL; 5096129daaaSJames Courtier-Dutton if (ogain != ngain) { 5106129daaaSJames Courtier-Dutton if (emu->i2c_capture_source == source_id) 5116129daaaSJames Courtier-Dutton snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff)); 5126129daaaSJames Courtier-Dutton emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1]; 5136129daaaSJames Courtier-Dutton change = 1; 5146129daaaSJames Courtier-Dutton } 5156129daaaSJames Courtier-Dutton 5166129daaaSJames Courtier-Dutton return change; 5176129daaaSJames Courtier-Dutton } 5186129daaaSJames Courtier-Dutton 519b18cd538STrent Piepho #define spi_mute_info snd_ctl_boolean_mono_info 520b18cd538STrent Piepho 521b18cd538STrent Piepho static int spi_mute_get(struct snd_kcontrol *kcontrol, 522b18cd538STrent Piepho struct snd_ctl_elem_value *ucontrol) 523b18cd538STrent Piepho { 524b18cd538STrent Piepho struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 525b18cd538STrent Piepho unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT; 526b18cd538STrent Piepho unsigned int bit = kcontrol->private_value & SPI_REG_MASK; 527b18cd538STrent Piepho 528b18cd538STrent Piepho ucontrol->value.integer.value[0] = !(emu->spi_dac_reg[reg] & bit); 529b18cd538STrent Piepho return 0; 530b18cd538STrent Piepho } 531b18cd538STrent Piepho 532b18cd538STrent Piepho static int spi_mute_put(struct snd_kcontrol *kcontrol, 533b18cd538STrent Piepho struct snd_ctl_elem_value *ucontrol) 534b18cd538STrent Piepho { 535b18cd538STrent Piepho struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 536b18cd538STrent Piepho unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT; 537b18cd538STrent Piepho unsigned int bit = kcontrol->private_value & SPI_REG_MASK; 538b18cd538STrent Piepho int ret; 539b18cd538STrent Piepho 540b18cd538STrent Piepho ret = emu->spi_dac_reg[reg] & bit; 541b18cd538STrent Piepho if (ucontrol->value.integer.value[0]) { 542b18cd538STrent Piepho if (!ret) /* bit already cleared, do nothing */ 543b18cd538STrent Piepho return 0; 544b18cd538STrent Piepho emu->spi_dac_reg[reg] &= ~bit; 545b18cd538STrent Piepho } else { 546b18cd538STrent Piepho if (ret) /* bit already set, do nothing */ 547b18cd538STrent Piepho return 0; 548b18cd538STrent Piepho emu->spi_dac_reg[reg] |= bit; 549b18cd538STrent Piepho } 550b18cd538STrent Piepho 551b18cd538STrent Piepho ret = snd_ca0106_spi_write(emu, emu->spi_dac_reg[reg]); 5525fe619f9STakashi Iwai return ret ? -EINVAL : 1; 553b18cd538STrent Piepho } 554b18cd538STrent Piepho 55595a98265STakashi Iwai #define CA_VOLUME(xname,chid,reg) \ 55695a98265STakashi Iwai { \ 55795a98265STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 558302e9c5aSJaroslav Kysela .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ 559302e9c5aSJaroslav Kysela SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 56095a98265STakashi Iwai .info = snd_ca0106_volume_info, \ 56195a98265STakashi Iwai .get = snd_ca0106_volume_get, \ 56295a98265STakashi Iwai .put = snd_ca0106_volume_put, \ 5637cf0a953STakashi Iwai .tlv = { .p = snd_ca0106_db_scale1 }, \ 56495a98265STakashi Iwai .private_value = ((chid) << 8) | (reg) \ 5651da177e4SLinus Torvalds } 5661da177e4SLinus Torvalds 567e23e7a14SBill Pemberton static struct snd_kcontrol_new snd_ca0106_volume_ctls[] = { 56895a98265STakashi Iwai CA_VOLUME("Analog Front Playback Volume", 56995a98265STakashi Iwai CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2), 57095a98265STakashi Iwai CA_VOLUME("Analog Rear Playback Volume", 57195a98265STakashi Iwai CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2), 57295a98265STakashi Iwai CA_VOLUME("Analog Center/LFE Playback Volume", 57395a98265STakashi Iwai CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2), 57495a98265STakashi Iwai CA_VOLUME("Analog Side Playback Volume", 57595a98265STakashi Iwai CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2), 57695a98265STakashi Iwai 57739596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Front Playback Volume", 57895a98265STakashi Iwai CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1), 57939596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Rear Playback Volume", 58095a98265STakashi Iwai CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1), 58139596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Center/LFE Playback Volume", 58295a98265STakashi Iwai CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1), 58339596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Unknown Playback Volume", 58495a98265STakashi Iwai CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1), 58595a98265STakashi Iwai 58695a98265STakashi Iwai CA_VOLUME("CAPTURE feedback Playback Volume", 58795a98265STakashi Iwai 1, CAPTURE_CONTROL), 58895a98265STakashi Iwai 58995a98265STakashi Iwai { 59095a98265STakashi Iwai .access = SNDRV_CTL_ELEM_ACCESS_READ, 59195a98265STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_PCM, 59295a98265STakashi Iwai .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), 59395a98265STakashi Iwai .count = 4, 59495a98265STakashi Iwai .info = snd_ca0106_spdif_info, 59595a98265STakashi Iwai .get = snd_ca0106_spdif_get_mask 59695a98265STakashi Iwai }, 5971da177e4SLinus Torvalds { 5981da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 59939596dc8SJames Courtier-Dutton .name = "IEC958 Playback Switch", 60095a98265STakashi Iwai .info = snd_ca0106_shared_spdif_info, 60195a98265STakashi Iwai .get = snd_ca0106_shared_spdif_get, 60295a98265STakashi Iwai .put = snd_ca0106_shared_spdif_put 60395a98265STakashi Iwai }, 6041da177e4SLinus Torvalds { 6051da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 606e6327cf9SJames Courtier-Dutton .name = "Digital Source Capture Enum", 60795a98265STakashi Iwai .info = snd_ca0106_capture_source_info, 60895a98265STakashi Iwai .get = snd_ca0106_capture_source_get, 60995a98265STakashi Iwai .put = snd_ca0106_capture_source_put 61095a98265STakashi Iwai }, 6111da177e4SLinus Torvalds { 6126129daaaSJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 613e6327cf9SJames Courtier-Dutton .name = "Analog Source Capture Enum", 6146129daaaSJames Courtier-Dutton .info = snd_ca0106_i2c_capture_source_info, 6156129daaaSJames Courtier-Dutton .get = snd_ca0106_i2c_capture_source_get, 6166129daaaSJames Courtier-Dutton .put = snd_ca0106_i2c_capture_source_put 6176129daaaSJames Courtier-Dutton }, 6186129daaaSJames Courtier-Dutton { 61995a98265STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_PCM, 62095a98265STakashi Iwai .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), 62195a98265STakashi Iwai .count = 4, 62295a98265STakashi Iwai .info = snd_ca0106_spdif_info, 6233d475829STakashi Iwai .get = snd_ca0106_spdif_get_default, 6243d475829STakashi Iwai .put = snd_ca0106_spdif_put_default 6253d475829STakashi Iwai }, 6263d475829STakashi Iwai { 6273d475829STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_PCM, 6283d475829STakashi Iwai .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), 6293d475829STakashi Iwai .count = 4, 6303d475829STakashi Iwai .info = snd_ca0106_spdif_info, 6313d475829STakashi Iwai .get = snd_ca0106_spdif_get_stream, 6323d475829STakashi Iwai .put = snd_ca0106_spdif_put_stream 63395a98265STakashi Iwai }, 6341da177e4SLinus Torvalds }; 6351da177e4SLinus Torvalds 6367c157069SJames Courtier-Dutton #define I2C_VOLUME(xname,chid) \ 6377c157069SJames Courtier-Dutton { \ 6387c157069SJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 6397c157069SJames Courtier-Dutton .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ 6407c157069SJames Courtier-Dutton SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 6417c157069SJames Courtier-Dutton .info = snd_ca0106_i2c_volume_info, \ 6427c157069SJames Courtier-Dutton .get = snd_ca0106_i2c_volume_get, \ 6437c157069SJames Courtier-Dutton .put = snd_ca0106_i2c_volume_put, \ 6447c157069SJames Courtier-Dutton .tlv = { .p = snd_ca0106_db_scale2 }, \ 6457c157069SJames Courtier-Dutton .private_value = chid \ 6467c157069SJames Courtier-Dutton } 6477c157069SJames Courtier-Dutton 648e23e7a14SBill Pemberton static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] = { 6497c157069SJames Courtier-Dutton I2C_VOLUME("Phone Capture Volume", 0), 6507c157069SJames Courtier-Dutton I2C_VOLUME("Mic Capture Volume", 1), 6517c157069SJames Courtier-Dutton I2C_VOLUME("Line in Capture Volume", 2), 6527c157069SJames Courtier-Dutton I2C_VOLUME("Aux Capture Volume", 3), 6537c157069SJames Courtier-Dutton }; 6547c157069SJames Courtier-Dutton 65564e5310aSAndy Owen static const int spi_dmute_reg[] = { 65664e5310aSAndy Owen SPI_DMUTE0_REG, 65764e5310aSAndy Owen SPI_DMUTE1_REG, 65864e5310aSAndy Owen SPI_DMUTE2_REG, 65964e5310aSAndy Owen 0, 66064e5310aSAndy Owen SPI_DMUTE4_REG, 661b18cd538STrent Piepho }; 66264e5310aSAndy Owen static const int spi_dmute_bit[] = { 66364e5310aSAndy Owen SPI_DMUTE0_BIT, 66464e5310aSAndy Owen SPI_DMUTE1_BIT, 66564e5310aSAndy Owen SPI_DMUTE2_BIT, 66664e5310aSAndy Owen 0, 66764e5310aSAndy Owen SPI_DMUTE4_BIT, 66864e5310aSAndy Owen }; 66964e5310aSAndy Owen 670e23e7a14SBill Pemberton static struct snd_kcontrol_new 67164e5310aSAndy Owen snd_ca0106_volume_spi_dac_ctl(struct snd_ca0106_details *details, 67264e5310aSAndy Owen int channel_id) 67364e5310aSAndy Owen { 67464e5310aSAndy Owen struct snd_kcontrol_new spi_switch = {0}; 67564e5310aSAndy Owen int reg, bit; 67664e5310aSAndy Owen int dac_id; 67764e5310aSAndy Owen 67864e5310aSAndy Owen spi_switch.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 67964e5310aSAndy Owen spi_switch.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 68064e5310aSAndy Owen spi_switch.info = spi_mute_info; 68164e5310aSAndy Owen spi_switch.get = spi_mute_get; 68264e5310aSAndy Owen spi_switch.put = spi_mute_put; 68364e5310aSAndy Owen 68464e5310aSAndy Owen switch (channel_id) { 68564e5310aSAndy Owen case PCM_FRONT_CHANNEL: 68664e5310aSAndy Owen spi_switch.name = "Analog Front Playback Switch"; 68764e5310aSAndy Owen dac_id = (details->spi_dac & 0xf000) >> (4 * 3); 68864e5310aSAndy Owen break; 68964e5310aSAndy Owen case PCM_REAR_CHANNEL: 69064e5310aSAndy Owen spi_switch.name = "Analog Rear Playback Switch"; 69164e5310aSAndy Owen dac_id = (details->spi_dac & 0x0f00) >> (4 * 2); 69264e5310aSAndy Owen break; 69364e5310aSAndy Owen case PCM_CENTER_LFE_CHANNEL: 69464e5310aSAndy Owen spi_switch.name = "Analog Center/LFE Playback Switch"; 69564e5310aSAndy Owen dac_id = (details->spi_dac & 0x00f0) >> (4 * 1); 69664e5310aSAndy Owen break; 69764e5310aSAndy Owen case PCM_UNKNOWN_CHANNEL: 69864e5310aSAndy Owen spi_switch.name = "Analog Side Playback Switch"; 69964e5310aSAndy Owen dac_id = (details->spi_dac & 0x000f) >> (4 * 0); 70064e5310aSAndy Owen break; 70164e5310aSAndy Owen default: 70264e5310aSAndy Owen /* Unused channel */ 70364e5310aSAndy Owen spi_switch.name = NULL; 70464e5310aSAndy Owen dac_id = 0; 70564e5310aSAndy Owen } 70664e5310aSAndy Owen reg = spi_dmute_reg[dac_id]; 70764e5310aSAndy Owen bit = spi_dmute_bit[dac_id]; 70864e5310aSAndy Owen 70964e5310aSAndy Owen spi_switch.private_value = (reg << SPI_REG_SHIFT) | bit; 71064e5310aSAndy Owen 71164e5310aSAndy Owen return spi_switch; 71264e5310aSAndy Owen } 713b18cd538STrent Piepho 714e23e7a14SBill Pemberton static int remove_ctl(struct snd_card *card, const char *name) 7151da177e4SLinus Torvalds { 716e4a3d145STakashi Iwai struct snd_ctl_elem_id id; 7171da177e4SLinus Torvalds memset(&id, 0, sizeof(id)); 7181da177e4SLinus Torvalds strcpy(id.name, name); 7191da177e4SLinus Torvalds id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 7201da177e4SLinus Torvalds return snd_ctl_remove_id(card, &id); 7211da177e4SLinus Torvalds } 7221da177e4SLinus Torvalds 723e23e7a14SBill Pemberton static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name) 7241da177e4SLinus Torvalds { 725e4a3d145STakashi Iwai struct snd_ctl_elem_id sid; 7261da177e4SLinus Torvalds memset(&sid, 0, sizeof(sid)); 7271da177e4SLinus Torvalds /* FIXME: strcpy is bad. */ 7281da177e4SLinus Torvalds strcpy(sid.name, name); 7291da177e4SLinus Torvalds sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 7301da177e4SLinus Torvalds return snd_ctl_find_id(card, &sid); 7311da177e4SLinus Torvalds } 7321da177e4SLinus Torvalds 733e23e7a14SBill Pemberton static int rename_ctl(struct snd_card *card, const char *src, const char *dst) 7341da177e4SLinus Torvalds { 735e4a3d145STakashi Iwai struct snd_kcontrol *kctl = ctl_find(card, src); 7361da177e4SLinus Torvalds if (kctl) { 7371da177e4SLinus Torvalds strcpy(kctl->id.name, dst); 7381da177e4SLinus Torvalds return 0; 7391da177e4SLinus Torvalds } 7401da177e4SLinus Torvalds return -ENOENT; 7411da177e4SLinus Torvalds } 7421da177e4SLinus Torvalds 743fca7f388STrent Piepho #define ADD_CTLS(emu, ctls) \ 744fca7f388STrent Piepho do { \ 745bed515b0SHarvey Harrison int i, _err; \ 746fca7f388STrent Piepho for (i = 0; i < ARRAY_SIZE(ctls); i++) { \ 747bed515b0SHarvey Harrison _err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \ 748bed515b0SHarvey Harrison if (_err < 0) \ 749bed515b0SHarvey Harrison return _err; \ 750fca7f388STrent Piepho } \ 751fca7f388STrent Piepho } while (0) 752fca7f388STrent Piepho 753e23e7a14SBill Pemberton static 754c4865679STakashi Iwai DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 25, 1); 75549c88b85STakashi Iwai 756e23e7a14SBill Pemberton static char *slave_vols[] = { 75749c88b85STakashi Iwai "Analog Front Playback Volume", 75849c88b85STakashi Iwai "Analog Rear Playback Volume", 75949c88b85STakashi Iwai "Analog Center/LFE Playback Volume", 76049c88b85STakashi Iwai "Analog Side Playback Volume", 76149c88b85STakashi Iwai "IEC958 Front Playback Volume", 76249c88b85STakashi Iwai "IEC958 Rear Playback Volume", 76349c88b85STakashi Iwai "IEC958 Center/LFE Playback Volume", 76449c88b85STakashi Iwai "IEC958 Unknown Playback Volume", 76549c88b85STakashi Iwai "CAPTURE feedback Playback Volume", 76649c88b85STakashi Iwai NULL 76749c88b85STakashi Iwai }; 76849c88b85STakashi Iwai 769e23e7a14SBill Pemberton static char *slave_sws[] = { 77049c88b85STakashi Iwai "Analog Front Playback Switch", 77149c88b85STakashi Iwai "Analog Rear Playback Switch", 77249c88b85STakashi Iwai "Analog Center/LFE Playback Switch", 77349c88b85STakashi Iwai "Analog Side Playback Switch", 77449c88b85STakashi Iwai "IEC958 Playback Switch", 77549c88b85STakashi Iwai NULL 77649c88b85STakashi Iwai }; 77749c88b85STakashi Iwai 778e23e7a14SBill Pemberton static void add_slaves(struct snd_card *card, 77949c88b85STakashi Iwai struct snd_kcontrol *master, char **list) 78049c88b85STakashi Iwai { 78149c88b85STakashi Iwai for (; *list; list++) { 78249c88b85STakashi Iwai struct snd_kcontrol *slave = ctl_find(card, *list); 78349c88b85STakashi Iwai if (slave) 78449c88b85STakashi Iwai snd_ctl_add_slave(master, slave); 78549c88b85STakashi Iwai } 78649c88b85STakashi Iwai } 78749c88b85STakashi Iwai 788e23e7a14SBill Pemberton int snd_ca0106_mixer(struct snd_ca0106 *emu) 7891da177e4SLinus Torvalds { 790fca7f388STrent Piepho int err; 791e4a3d145STakashi Iwai struct snd_card *card = emu->card; 7921da177e4SLinus Torvalds char **c; 79349c88b85STakashi Iwai struct snd_kcontrol *vmaster; 7941da177e4SLinus Torvalds static char *ca0106_remove_ctls[] = { 7951da177e4SLinus Torvalds "Master Mono Playback Switch", 7961da177e4SLinus Torvalds "Master Mono Playback Volume", 7971da177e4SLinus Torvalds "3D Control - Switch", 7981da177e4SLinus Torvalds "3D Control Sigmatel - Depth", 7991da177e4SLinus Torvalds "PCM Playback Switch", 8001da177e4SLinus Torvalds "PCM Playback Volume", 8011da177e4SLinus Torvalds "CD Playback Switch", 8021da177e4SLinus Torvalds "CD Playback Volume", 8031da177e4SLinus Torvalds "Phone Playback Switch", 8041da177e4SLinus Torvalds "Phone Playback Volume", 8051da177e4SLinus Torvalds "Video Playback Switch", 8061da177e4SLinus Torvalds "Video Playback Volume", 807d355c82aSJaroslav Kysela "Beep Playback Switch", 808d355c82aSJaroslav Kysela "Beep Playback Volume", 8091da177e4SLinus Torvalds "Mono Output Select", 8101da177e4SLinus Torvalds "Capture Source", 8111da177e4SLinus Torvalds "Capture Switch", 8121da177e4SLinus Torvalds "Capture Volume", 8131da177e4SLinus Torvalds "External Amplifier", 8141da177e4SLinus Torvalds "Sigmatel 4-Speaker Stereo Playback Switch", 815afe6d7e3SAndreas Mohr "Surround Phase Inversion Playback Switch", 8161da177e4SLinus Torvalds NULL 8171da177e4SLinus Torvalds }; 8181da177e4SLinus Torvalds static char *ca0106_rename_ctls[] = { 8191da177e4SLinus Torvalds "Master Playback Switch", "Capture Switch", 8201da177e4SLinus Torvalds "Master Playback Volume", "Capture Volume", 8211da177e4SLinus Torvalds "Line Playback Switch", "AC97 Line Capture Switch", 8221da177e4SLinus Torvalds "Line Playback Volume", "AC97 Line Capture Volume", 8231da177e4SLinus Torvalds "Aux Playback Switch", "AC97 Aux Capture Switch", 8241da177e4SLinus Torvalds "Aux Playback Volume", "AC97 Aux Capture Volume", 8251da177e4SLinus Torvalds "Mic Playback Switch", "AC97 Mic Capture Switch", 8261da177e4SLinus Torvalds "Mic Playback Volume", "AC97 Mic Capture Volume", 8271da177e4SLinus Torvalds "Mic Select", "AC97 Mic Select", 8281da177e4SLinus Torvalds "Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)", 8291da177e4SLinus Torvalds NULL 8301da177e4SLinus Torvalds }; 8311da177e4SLinus Torvalds #if 1 8321da177e4SLinus Torvalds for (c = ca0106_remove_ctls; *c; c++) 8331da177e4SLinus Torvalds remove_ctl(card, *c); 8341da177e4SLinus Torvalds for (c = ca0106_rename_ctls; *c; c += 2) 8351da177e4SLinus Torvalds rename_ctl(card, c[0], c[1]); 8361da177e4SLinus Torvalds #endif 8371da177e4SLinus Torvalds 838fca7f388STrent Piepho ADD_CTLS(emu, snd_ca0106_volume_ctls); 83995a98265STakashi Iwai if (emu->details->i2c_adc == 1) { 840fca7f388STrent Piepho ADD_CTLS(emu, snd_ca0106_volume_i2c_adc_ctls); 841be0b7b01SJames Courtier-Dutton if (emu->details->gpio_type == 1) 84295a98265STakashi Iwai err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu)); 843be0b7b01SJames Courtier-Dutton else /* gpio_type == 2 */ 844be0b7b01SJames Courtier-Dutton err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu)); 84595a98265STakashi Iwai if (err < 0) 8461da177e4SLinus Torvalds return err; 84795a98265STakashi Iwai } 84864e5310aSAndy Owen if (emu->details->spi_dac) { 84964e5310aSAndy Owen int i; 85064e5310aSAndy Owen for (i = 0;; i++) { 85164e5310aSAndy Owen struct snd_kcontrol_new ctl; 85264e5310aSAndy Owen ctl = snd_ca0106_volume_spi_dac_ctl(emu->details, i); 85364e5310aSAndy Owen if (!ctl.name) 85464e5310aSAndy Owen break; 85564e5310aSAndy Owen err = snd_ctl_add(card, snd_ctl_new1(&ctl, emu)); 85664e5310aSAndy Owen if (err < 0) 85764e5310aSAndy Owen return err; 85864e5310aSAndy Owen } 85964e5310aSAndy Owen } 86049c88b85STakashi Iwai 86149c88b85STakashi Iwai /* Create virtual master controls */ 86249c88b85STakashi Iwai vmaster = snd_ctl_make_virtual_master("Master Playback Volume", 86349c88b85STakashi Iwai snd_ca0106_master_db_scale); 86449c88b85STakashi Iwai if (!vmaster) 86549c88b85STakashi Iwai return -ENOMEM; 866601e1cc5STakashi Iwai err = snd_ctl_add(card, vmaster); 867601e1cc5STakashi Iwai if (err < 0) 868601e1cc5STakashi Iwai return err; 86949c88b85STakashi Iwai add_slaves(card, vmaster, slave_vols); 87049c88b85STakashi Iwai 8716fef153aSAndy Owen if (emu->details->spi_dac) { 87249c88b85STakashi Iwai vmaster = snd_ctl_make_virtual_master("Master Playback Switch", 87349c88b85STakashi Iwai NULL); 87449c88b85STakashi Iwai if (!vmaster) 87549c88b85STakashi Iwai return -ENOMEM; 876601e1cc5STakashi Iwai err = snd_ctl_add(card, vmaster); 877601e1cc5STakashi Iwai if (err < 0) 878601e1cc5STakashi Iwai return err; 87949c88b85STakashi Iwai add_slaves(card, vmaster, slave_sws); 88049c88b85STakashi Iwai } 881eeaf100dSTakashi Iwai 882eeaf100dSTakashi Iwai strcpy(card->mixername, "CA0106"); 8831da177e4SLinus Torvalds return 0; 8841da177e4SLinus Torvalds } 8851da177e4SLinus Torvalds 886c7561cd8STakashi Iwai #ifdef CONFIG_PM_SLEEP 8875da95273STakashi Iwai struct ca0106_vol_tbl { 8885da95273STakashi Iwai unsigned int channel_id; 8898df0f707STakashi Iwai unsigned int reg; 8905da95273STakashi Iwai }; 8915da95273STakashi Iwai 8925da95273STakashi Iwai static struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = { 8935da95273STakashi Iwai { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2 }, 8945da95273STakashi Iwai { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2 }, 8955da95273STakashi Iwai { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2 }, 8965da95273STakashi Iwai { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2 }, 8975da95273STakashi Iwai { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1 }, 8985da95273STakashi Iwai { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1 }, 8995da95273STakashi Iwai { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1 }, 9005da95273STakashi Iwai { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1 }, 9015da95273STakashi Iwai { 1, CAPTURE_CONTROL }, 9025da95273STakashi Iwai }; 9035da95273STakashi Iwai 9045da95273STakashi Iwai void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip) 9055da95273STakashi Iwai { 9065da95273STakashi Iwai int i; 9075da95273STakashi Iwai 9085da95273STakashi Iwai /* save volumes */ 9095da95273STakashi Iwai for (i = 0; i < NUM_SAVED_VOLUMES; i++) 9105da95273STakashi Iwai chip->saved_vol[i] = 9115da95273STakashi Iwai snd_ca0106_ptr_read(chip, saved_volumes[i].reg, 9125da95273STakashi Iwai saved_volumes[i].channel_id); 9135da95273STakashi Iwai } 9145da95273STakashi Iwai 9155da95273STakashi Iwai void snd_ca0106_mixer_resume(struct snd_ca0106 *chip) 9165da95273STakashi Iwai { 9175da95273STakashi Iwai int i; 9185da95273STakashi Iwai 9195da95273STakashi Iwai for (i = 0; i < NUM_SAVED_VOLUMES; i++) 9205da95273STakashi Iwai snd_ca0106_ptr_write(chip, saved_volumes[i].reg, 9215da95273STakashi Iwai saved_volumes[i].channel_id, 9225da95273STakashi Iwai chip->saved_vol[i]); 9235da95273STakashi Iwai 9245da95273STakashi Iwai ca0106_spdif_enable(chip); 9255da95273STakashi Iwai ca0106_set_capture_source(chip); 9265da95273STakashi Iwai ca0106_set_i2c_capture_source(chip, chip->i2c_capture_source, 1); 9275da95273STakashi Iwai for (i = 0; i < 4; i++) 9285da95273STakashi Iwai ca0106_set_spdif_bits(chip, i); 9295da95273STakashi Iwai if (chip->details->i2c_adc) 9305da95273STakashi Iwai ca0106_set_capture_mic_line_in(chip); 9315da95273STakashi Iwai } 932c7561cd8STakashi Iwai #endif /* CONFIG_PM_SLEEP */ 933