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 780cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1); 790cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1); 8042750b04SJaroslav Kysela 81a5ce8890STakashi Iwai #define snd_ca0106_shared_spdif_info snd_ctl_boolean_mono_info 821da177e4SLinus Torvalds 83e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol, 84e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 851da177e4SLinus Torvalds { 86e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 871da177e4SLinus Torvalds 885fe619f9STakashi Iwai ucontrol->value.integer.value[0] = emu->spdif_enable; 891da177e4SLinus Torvalds return 0; 901da177e4SLinus Torvalds } 911da177e4SLinus Torvalds 92e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol, 93e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 941da177e4SLinus Torvalds { 95e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 961da177e4SLinus Torvalds unsigned int val; 971da177e4SLinus Torvalds int change = 0; 981da177e4SLinus Torvalds u32 mask; 991da177e4SLinus Torvalds 1005fe619f9STakashi Iwai val = !!ucontrol->value.integer.value[0]; 1011da177e4SLinus Torvalds change = (emu->spdif_enable != val); 1021da177e4SLinus Torvalds if (change) { 1031da177e4SLinus Torvalds emu->spdif_enable = val; 1045fe619f9STakashi Iwai if (val) { 1051da177e4SLinus Torvalds /* Digital */ 1061da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); 1071da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000); 1081da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, 1091da177e4SLinus Torvalds snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000); 1101da177e4SLinus Torvalds mask = inl(emu->port + GPIO) & ~0x101; 1111da177e4SLinus Torvalds outl(mask, emu->port + GPIO); 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds } else { 1141da177e4SLinus Torvalds /* Analog */ 1151da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); 1161f82941eSJames Courtier-Dutton snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000); 1171da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, 1181da177e4SLinus Torvalds snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000); 1191da177e4SLinus Torvalds mask = inl(emu->port + GPIO) | 0x101; 1201da177e4SLinus Torvalds outl(mask, emu->port + GPIO); 1211da177e4SLinus Torvalds } 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds return change; 1241da177e4SLinus Torvalds } 1251da177e4SLinus Torvalds 126e4a3d145STakashi Iwai static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol, 127e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 1281da177e4SLinus Torvalds { 12995a98265STakashi Iwai static char *texts[6] = { 13039596dc8SJames Courtier-Dutton "IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out" 13195a98265STakashi Iwai }; 1321da177e4SLinus Torvalds 1331da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 1341da177e4SLinus Torvalds uinfo->count = 1; 1351da177e4SLinus Torvalds uinfo->value.enumerated.items = 6; 1361da177e4SLinus Torvalds if (uinfo->value.enumerated.item > 5) 1371da177e4SLinus Torvalds uinfo->value.enumerated.item = 5; 1381da177e4SLinus Torvalds strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); 1391da177e4SLinus Torvalds return 0; 1401da177e4SLinus Torvalds } 1411da177e4SLinus Torvalds 142e4a3d145STakashi Iwai static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol, 143e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 1441da177e4SLinus Torvalds { 145e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds ucontrol->value.enumerated.item[0] = emu->capture_source; 1481da177e4SLinus Torvalds return 0; 1491da177e4SLinus Torvalds } 1501da177e4SLinus Torvalds 151e4a3d145STakashi Iwai static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol, 152e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 1531da177e4SLinus Torvalds { 154e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 1551da177e4SLinus Torvalds unsigned int val; 1561da177e4SLinus Torvalds int change = 0; 1571da177e4SLinus Torvalds u32 mask; 1581da177e4SLinus Torvalds u32 source; 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds val = ucontrol->value.enumerated.item[0] ; 1615fe619f9STakashi Iwai if (val >= 6) 1625fe619f9STakashi Iwai return -EINVAL; 1631da177e4SLinus Torvalds change = (emu->capture_source != val); 1641da177e4SLinus Torvalds if (change) { 1651da177e4SLinus Torvalds emu->capture_source = val; 1661da177e4SLinus Torvalds source = (val << 28) | (val << 24) | (val << 20) | (val << 16); 1671da177e4SLinus Torvalds mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff; 1681da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask); 1691da177e4SLinus Torvalds } 1701da177e4SLinus Torvalds return change; 1711da177e4SLinus Torvalds } 1721da177e4SLinus Torvalds 1736129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol, 1746129daaaSJames Courtier-Dutton struct snd_ctl_elem_info *uinfo) 1756129daaaSJames Courtier-Dutton { 1766129daaaSJames Courtier-Dutton static char *texts[6] = { 1776129daaaSJames Courtier-Dutton "Phone", "Mic", "Line in", "Aux" 1786129daaaSJames Courtier-Dutton }; 1796129daaaSJames Courtier-Dutton 1806129daaaSJames Courtier-Dutton uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 1816129daaaSJames Courtier-Dutton uinfo->count = 1; 1826129daaaSJames Courtier-Dutton uinfo->value.enumerated.items = 4; 1836129daaaSJames Courtier-Dutton if (uinfo->value.enumerated.item > 3) 1846129daaaSJames Courtier-Dutton uinfo->value.enumerated.item = 3; 1856129daaaSJames Courtier-Dutton strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); 1866129daaaSJames Courtier-Dutton return 0; 1876129daaaSJames Courtier-Dutton } 1886129daaaSJames Courtier-Dutton 1896129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol, 1906129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 1916129daaaSJames Courtier-Dutton { 1926129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 1936129daaaSJames Courtier-Dutton 1946129daaaSJames Courtier-Dutton ucontrol->value.enumerated.item[0] = emu->i2c_capture_source; 1956129daaaSJames Courtier-Dutton return 0; 1966129daaaSJames Courtier-Dutton } 1976129daaaSJames Courtier-Dutton 1986129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol, 1996129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 2006129daaaSJames Courtier-Dutton { 2016129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 2026129daaaSJames Courtier-Dutton unsigned int source_id; 2036129daaaSJames Courtier-Dutton unsigned int ngain, ogain; 2046129daaaSJames Courtier-Dutton int change = 0; 2056129daaaSJames Courtier-Dutton u32 source; 2066129daaaSJames Courtier-Dutton /* If the capture source has changed, 2076129daaaSJames Courtier-Dutton * update the capture volume from the cached value 2086129daaaSJames Courtier-Dutton * for the particular source. 2096129daaaSJames Courtier-Dutton */ 2106129daaaSJames Courtier-Dutton source_id = ucontrol->value.enumerated.item[0] ; 2115fe619f9STakashi Iwai if (source_id >= 4) 2125fe619f9STakashi Iwai return -EINVAL; 2136129daaaSJames Courtier-Dutton change = (emu->i2c_capture_source != source_id); 2146129daaaSJames Courtier-Dutton if (change) { 2156129daaaSJames Courtier-Dutton snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ 2166129daaaSJames Courtier-Dutton ngain = emu->i2c_capture_volume[source_id][0]; /* Left */ 2176129daaaSJames Courtier-Dutton ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */ 2186129daaaSJames Courtier-Dutton if (ngain != ogain) 2196129daaaSJames Courtier-Dutton snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff)); 2206129daaaSJames Courtier-Dutton ngain = emu->i2c_capture_volume[source_id][1]; /* Left */ 2216129daaaSJames Courtier-Dutton ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Left */ 2226129daaaSJames Courtier-Dutton if (ngain != ogain) 2236129daaaSJames Courtier-Dutton snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff)); 2246129daaaSJames Courtier-Dutton source = 1 << source_id; 2256129daaaSJames Courtier-Dutton snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */ 2266129daaaSJames Courtier-Dutton emu->i2c_capture_source = source_id; 2276129daaaSJames Courtier-Dutton } 2286129daaaSJames Courtier-Dutton return change; 2296129daaaSJames Courtier-Dutton } 2306129daaaSJames Courtier-Dutton 231be0b7b01SJames Courtier-Dutton static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol, 232be0b7b01SJames Courtier-Dutton struct snd_ctl_elem_info *uinfo) 233be0b7b01SJames Courtier-Dutton { 234be0b7b01SJames Courtier-Dutton static char *texts[2] = { "Side out", "Line in" }; 235be0b7b01SJames Courtier-Dutton 236be0b7b01SJames Courtier-Dutton uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 237be0b7b01SJames Courtier-Dutton uinfo->count = 1; 238be0b7b01SJames Courtier-Dutton uinfo->value.enumerated.items = 2; 239be0b7b01SJames Courtier-Dutton if (uinfo->value.enumerated.item > 1) 240be0b7b01SJames Courtier-Dutton uinfo->value.enumerated.item = 1; 241be0b7b01SJames Courtier-Dutton strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); 242be0b7b01SJames Courtier-Dutton return 0; 243be0b7b01SJames Courtier-Dutton } 244be0b7b01SJames Courtier-Dutton 245e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol, 246e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 247ed144f3cSJames Courtier-Dutton { 248ed144f3cSJames Courtier-Dutton static char *texts[2] = { "Line in", "Mic in" }; 249ed144f3cSJames Courtier-Dutton 250ed144f3cSJames Courtier-Dutton uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 251ed144f3cSJames Courtier-Dutton uinfo->count = 1; 252ed144f3cSJames Courtier-Dutton uinfo->value.enumerated.items = 2; 253ed144f3cSJames Courtier-Dutton if (uinfo->value.enumerated.item > 1) 254ed144f3cSJames Courtier-Dutton uinfo->value.enumerated.item = 1; 255ed144f3cSJames Courtier-Dutton strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); 256ed144f3cSJames Courtier-Dutton return 0; 257ed144f3cSJames Courtier-Dutton } 258ed144f3cSJames Courtier-Dutton 259e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol, 260e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 261ed144f3cSJames Courtier-Dutton { 262e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 263ed144f3cSJames Courtier-Dutton 264ed144f3cSJames Courtier-Dutton ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in; 265ed144f3cSJames Courtier-Dutton return 0; 266ed144f3cSJames Courtier-Dutton } 267ed144f3cSJames Courtier-Dutton 268e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol, 269e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 270ed144f3cSJames Courtier-Dutton { 271e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 272ed144f3cSJames Courtier-Dutton unsigned int val; 273ed144f3cSJames Courtier-Dutton int change = 0; 274ed144f3cSJames Courtier-Dutton u32 tmp; 275ed144f3cSJames Courtier-Dutton 276ed144f3cSJames Courtier-Dutton val = ucontrol->value.enumerated.item[0] ; 2775fe619f9STakashi Iwai if (val > 1) 2785fe619f9STakashi Iwai return -EINVAL; 279ed144f3cSJames Courtier-Dutton change = (emu->capture_mic_line_in != val); 280ed144f3cSJames Courtier-Dutton if (change) { 281ed144f3cSJames Courtier-Dutton emu->capture_mic_line_in = val; 282ed144f3cSJames Courtier-Dutton if (val) { 2836129daaaSJames Courtier-Dutton //snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ 284ed144f3cSJames Courtier-Dutton tmp = inl(emu->port+GPIO) & ~0x400; 285ed144f3cSJames Courtier-Dutton tmp = tmp | 0x400; 286ed144f3cSJames Courtier-Dutton outl(tmp, emu->port+GPIO); 2876129daaaSJames Courtier-Dutton //snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); 288ed144f3cSJames Courtier-Dutton } else { 2896129daaaSJames Courtier-Dutton //snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ 290ed144f3cSJames Courtier-Dutton tmp = inl(emu->port+GPIO) & ~0x400; 291ed144f3cSJames Courtier-Dutton outl(tmp, emu->port+GPIO); 2926129daaaSJames Courtier-Dutton //snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); 293ed144f3cSJames Courtier-Dutton } 294ed144f3cSJames Courtier-Dutton } 295ed144f3cSJames Courtier-Dutton return change; 296ed144f3cSJames Courtier-Dutton } 297ed144f3cSJames Courtier-Dutton 298e4a3d145STakashi Iwai static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata = 299ed144f3cSJames Courtier-Dutton { 300ed144f3cSJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3016129daaaSJames Courtier-Dutton .name = "Shared Mic/Line in Capture Switch", 302ed144f3cSJames Courtier-Dutton .info = snd_ca0106_capture_mic_line_in_info, 303ed144f3cSJames Courtier-Dutton .get = snd_ca0106_capture_mic_line_in_get, 304ed144f3cSJames Courtier-Dutton .put = snd_ca0106_capture_mic_line_in_put 305ed144f3cSJames Courtier-Dutton }; 306ed144f3cSJames Courtier-Dutton 307be0b7b01SJames Courtier-Dutton static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out __devinitdata = 308be0b7b01SJames Courtier-Dutton { 309be0b7b01SJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 310be0b7b01SJames Courtier-Dutton .name = "Shared Line in/Side out Capture Switch", 311be0b7b01SJames Courtier-Dutton .info = snd_ca0106_capture_line_in_side_out_info, 312be0b7b01SJames Courtier-Dutton .get = snd_ca0106_capture_mic_line_in_get, 313be0b7b01SJames Courtier-Dutton .put = snd_ca0106_capture_mic_line_in_put 314be0b7b01SJames Courtier-Dutton }; 315be0b7b01SJames Courtier-Dutton 316be0b7b01SJames Courtier-Dutton 317e4a3d145STakashi Iwai static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol, 318e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 3191da177e4SLinus Torvalds { 3201da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 3211da177e4SLinus Torvalds uinfo->count = 1; 3221da177e4SLinus Torvalds return 0; 3231da177e4SLinus Torvalds } 3241da177e4SLinus Torvalds 325e4a3d145STakashi Iwai static int snd_ca0106_spdif_get(struct snd_kcontrol *kcontrol, 326e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3271da177e4SLinus Torvalds { 328e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 3291da177e4SLinus Torvalds unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 3301da177e4SLinus Torvalds 3311da177e4SLinus Torvalds ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff; 3321da177e4SLinus Torvalds ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff; 3331da177e4SLinus Torvalds ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff; 3341da177e4SLinus Torvalds ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff; 3351da177e4SLinus Torvalds return 0; 3361da177e4SLinus Torvalds } 3371da177e4SLinus Torvalds 338e4a3d145STakashi Iwai static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol, 339e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3401da177e4SLinus Torvalds { 3411da177e4SLinus Torvalds ucontrol->value.iec958.status[0] = 0xff; 3421da177e4SLinus Torvalds ucontrol->value.iec958.status[1] = 0xff; 3431da177e4SLinus Torvalds ucontrol->value.iec958.status[2] = 0xff; 3441da177e4SLinus Torvalds ucontrol->value.iec958.status[3] = 0xff; 3451da177e4SLinus Torvalds return 0; 3461da177e4SLinus Torvalds } 3471da177e4SLinus Torvalds 348e4a3d145STakashi Iwai static int snd_ca0106_spdif_put(struct snd_kcontrol *kcontrol, 349e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3501da177e4SLinus Torvalds { 351e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 3521da177e4SLinus Torvalds unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 3531da177e4SLinus Torvalds int change; 3541da177e4SLinus Torvalds unsigned int val; 3551da177e4SLinus Torvalds 3561da177e4SLinus Torvalds val = (ucontrol->value.iec958.status[0] << 0) | 3571da177e4SLinus Torvalds (ucontrol->value.iec958.status[1] << 8) | 3581da177e4SLinus Torvalds (ucontrol->value.iec958.status[2] << 16) | 3591da177e4SLinus Torvalds (ucontrol->value.iec958.status[3] << 24); 3601da177e4SLinus Torvalds change = val != emu->spdif_bits[idx]; 3611da177e4SLinus Torvalds if (change) { 3621da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, val); 3631da177e4SLinus Torvalds emu->spdif_bits[idx] = val; 3641da177e4SLinus Torvalds } 3651da177e4SLinus Torvalds return change; 3661da177e4SLinus Torvalds } 3671da177e4SLinus Torvalds 368e4a3d145STakashi Iwai static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol, 369e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 3701da177e4SLinus Torvalds { 3711da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 3721da177e4SLinus Torvalds uinfo->count = 2; 3731da177e4SLinus Torvalds uinfo->value.integer.min = 0; 3741da177e4SLinus Torvalds uinfo->value.integer.max = 255; 3751da177e4SLinus Torvalds return 0; 3761da177e4SLinus Torvalds } 3771da177e4SLinus Torvalds 378e4a3d145STakashi Iwai static int snd_ca0106_volume_get(struct snd_kcontrol *kcontrol, 379e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3801da177e4SLinus Torvalds { 381e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 3821da177e4SLinus Torvalds unsigned int value; 38395a98265STakashi Iwai int channel_id, reg; 38495a98265STakashi Iwai 38595a98265STakashi Iwai channel_id = (kcontrol->private_value >> 8) & 0xff; 38695a98265STakashi Iwai reg = kcontrol->private_value & 0xff; 3871da177e4SLinus Torvalds 3881da177e4SLinus Torvalds value = snd_ca0106_ptr_read(emu, reg, channel_id); 3891da177e4SLinus Torvalds ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */ 3901da177e4SLinus Torvalds ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */ 3911da177e4SLinus Torvalds return 0; 3921da177e4SLinus Torvalds } 3931da177e4SLinus Torvalds 394e4a3d145STakashi Iwai static int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol, 395e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3961da177e4SLinus Torvalds { 397e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 39895a98265STakashi Iwai unsigned int oval, nval; 39995a98265STakashi Iwai int channel_id, reg; 40095a98265STakashi Iwai 40195a98265STakashi Iwai channel_id = (kcontrol->private_value >> 8) & 0xff; 40295a98265STakashi Iwai reg = kcontrol->private_value & 0xff; 40395a98265STakashi Iwai 40495a98265STakashi Iwai oval = snd_ca0106_ptr_read(emu, reg, channel_id); 40595a98265STakashi Iwai nval = ((0xff - ucontrol->value.integer.value[0]) << 24) | 40695a98265STakashi Iwai ((0xff - ucontrol->value.integer.value[1]) << 16); 40795a98265STakashi Iwai nval |= ((0xff - ucontrol->value.integer.value[0]) << 8) | 40895a98265STakashi Iwai ((0xff - ucontrol->value.integer.value[1]) ); 40995a98265STakashi Iwai if (oval == nval) 41095a98265STakashi Iwai return 0; 41195a98265STakashi Iwai snd_ca0106_ptr_write(emu, reg, channel_id, nval); 4121da177e4SLinus Torvalds return 1; 4131da177e4SLinus Torvalds } 41495a98265STakashi Iwai 4156129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_info(struct snd_kcontrol *kcontrol, 4166129daaaSJames Courtier-Dutton struct snd_ctl_elem_info *uinfo) 4176129daaaSJames Courtier-Dutton { 4186129daaaSJames Courtier-Dutton uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 4196129daaaSJames Courtier-Dutton uinfo->count = 2; 4206129daaaSJames Courtier-Dutton uinfo->value.integer.min = 0; 4216129daaaSJames Courtier-Dutton uinfo->value.integer.max = 255; 4226129daaaSJames Courtier-Dutton return 0; 4236129daaaSJames Courtier-Dutton } 4246129daaaSJames Courtier-Dutton 4256129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol, 4266129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 4276129daaaSJames Courtier-Dutton { 4286129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 4296129daaaSJames Courtier-Dutton int source_id; 4306129daaaSJames Courtier-Dutton 4316129daaaSJames Courtier-Dutton source_id = kcontrol->private_value; 4326129daaaSJames Courtier-Dutton 4336129daaaSJames Courtier-Dutton ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0]; 4346129daaaSJames Courtier-Dutton ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1]; 4356129daaaSJames Courtier-Dutton return 0; 4366129daaaSJames Courtier-Dutton } 4376129daaaSJames Courtier-Dutton 4386129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol, 4396129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 4406129daaaSJames Courtier-Dutton { 4416129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 4426129daaaSJames Courtier-Dutton unsigned int ogain; 4436129daaaSJames Courtier-Dutton unsigned int ngain; 4446129daaaSJames Courtier-Dutton int source_id; 4456129daaaSJames Courtier-Dutton int change = 0; 4466129daaaSJames Courtier-Dutton 4476129daaaSJames Courtier-Dutton source_id = kcontrol->private_value; 4486129daaaSJames Courtier-Dutton ogain = emu->i2c_capture_volume[source_id][0]; /* Left */ 4496129daaaSJames Courtier-Dutton ngain = ucontrol->value.integer.value[0]; 4506129daaaSJames Courtier-Dutton if (ngain > 0xff) 4515fe619f9STakashi Iwai return -EINVAL; 4526129daaaSJames Courtier-Dutton if (ogain != ngain) { 4536129daaaSJames Courtier-Dutton if (emu->i2c_capture_source == source_id) 4546129daaaSJames Courtier-Dutton snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) ); 4556129daaaSJames Courtier-Dutton emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0]; 4566129daaaSJames Courtier-Dutton change = 1; 4576129daaaSJames Courtier-Dutton } 4586129daaaSJames Courtier-Dutton ogain = emu->i2c_capture_volume[source_id][1]; /* Right */ 4596129daaaSJames Courtier-Dutton ngain = ucontrol->value.integer.value[1]; 4606129daaaSJames Courtier-Dutton if (ngain > 0xff) 4615fe619f9STakashi Iwai return -EINVAL; 4626129daaaSJames Courtier-Dutton if (ogain != ngain) { 4636129daaaSJames Courtier-Dutton if (emu->i2c_capture_source == source_id) 4646129daaaSJames Courtier-Dutton snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff)); 4656129daaaSJames Courtier-Dutton emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1]; 4666129daaaSJames Courtier-Dutton change = 1; 4676129daaaSJames Courtier-Dutton } 4686129daaaSJames Courtier-Dutton 4696129daaaSJames Courtier-Dutton return change; 4706129daaaSJames Courtier-Dutton } 4716129daaaSJames Courtier-Dutton 472b18cd538STrent Piepho #define spi_mute_info snd_ctl_boolean_mono_info 473b18cd538STrent Piepho 474b18cd538STrent Piepho static int spi_mute_get(struct snd_kcontrol *kcontrol, 475b18cd538STrent Piepho struct snd_ctl_elem_value *ucontrol) 476b18cd538STrent Piepho { 477b18cd538STrent Piepho struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 478b18cd538STrent Piepho unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT; 479b18cd538STrent Piepho unsigned int bit = kcontrol->private_value & SPI_REG_MASK; 480b18cd538STrent Piepho 481b18cd538STrent Piepho ucontrol->value.integer.value[0] = !(emu->spi_dac_reg[reg] & bit); 482b18cd538STrent Piepho return 0; 483b18cd538STrent Piepho } 484b18cd538STrent Piepho 485b18cd538STrent Piepho static int spi_mute_put(struct snd_kcontrol *kcontrol, 486b18cd538STrent Piepho struct snd_ctl_elem_value *ucontrol) 487b18cd538STrent Piepho { 488b18cd538STrent Piepho struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 489b18cd538STrent Piepho unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT; 490b18cd538STrent Piepho unsigned int bit = kcontrol->private_value & SPI_REG_MASK; 491b18cd538STrent Piepho int ret; 492b18cd538STrent Piepho 493b18cd538STrent Piepho ret = emu->spi_dac_reg[reg] & bit; 494b18cd538STrent Piepho if (ucontrol->value.integer.value[0]) { 495b18cd538STrent Piepho if (!ret) /* bit already cleared, do nothing */ 496b18cd538STrent Piepho return 0; 497b18cd538STrent Piepho emu->spi_dac_reg[reg] &= ~bit; 498b18cd538STrent Piepho } else { 499b18cd538STrent Piepho if (ret) /* bit already set, do nothing */ 500b18cd538STrent Piepho return 0; 501b18cd538STrent Piepho emu->spi_dac_reg[reg] |= bit; 502b18cd538STrent Piepho } 503b18cd538STrent Piepho 504b18cd538STrent Piepho ret = snd_ca0106_spi_write(emu, emu->spi_dac_reg[reg]); 5055fe619f9STakashi Iwai return ret ? -EINVAL : 1; 506b18cd538STrent Piepho } 507b18cd538STrent Piepho 50895a98265STakashi Iwai #define CA_VOLUME(xname,chid,reg) \ 50995a98265STakashi Iwai { \ 51095a98265STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 511302e9c5aSJaroslav Kysela .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ 512302e9c5aSJaroslav Kysela SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 51395a98265STakashi Iwai .info = snd_ca0106_volume_info, \ 51495a98265STakashi Iwai .get = snd_ca0106_volume_get, \ 51595a98265STakashi Iwai .put = snd_ca0106_volume_put, \ 5167cf0a953STakashi Iwai .tlv = { .p = snd_ca0106_db_scale1 }, \ 51795a98265STakashi Iwai .private_value = ((chid) << 8) | (reg) \ 5181da177e4SLinus Torvalds } 5191da177e4SLinus Torvalds 520e4a3d145STakashi Iwai static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = { 52195a98265STakashi Iwai CA_VOLUME("Analog Front Playback Volume", 52295a98265STakashi Iwai CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2), 52395a98265STakashi Iwai CA_VOLUME("Analog Rear Playback Volume", 52495a98265STakashi Iwai CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2), 52595a98265STakashi Iwai CA_VOLUME("Analog Center/LFE Playback Volume", 52695a98265STakashi Iwai CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2), 52795a98265STakashi Iwai CA_VOLUME("Analog Side Playback Volume", 52895a98265STakashi Iwai CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2), 52995a98265STakashi Iwai 53039596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Front Playback Volume", 53195a98265STakashi Iwai CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1), 53239596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Rear Playback Volume", 53395a98265STakashi Iwai CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1), 53439596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Center/LFE Playback Volume", 53595a98265STakashi Iwai CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1), 53639596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Unknown Playback Volume", 53795a98265STakashi Iwai CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1), 53895a98265STakashi Iwai 53995a98265STakashi Iwai CA_VOLUME("CAPTURE feedback Playback Volume", 54095a98265STakashi Iwai 1, CAPTURE_CONTROL), 54195a98265STakashi Iwai 54295a98265STakashi Iwai { 54395a98265STakashi Iwai .access = SNDRV_CTL_ELEM_ACCESS_READ, 54495a98265STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_PCM, 54595a98265STakashi Iwai .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), 54695a98265STakashi Iwai .count = 4, 54795a98265STakashi Iwai .info = snd_ca0106_spdif_info, 54895a98265STakashi Iwai .get = snd_ca0106_spdif_get_mask 54995a98265STakashi Iwai }, 5501da177e4SLinus Torvalds { 5511da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 55239596dc8SJames Courtier-Dutton .name = "IEC958 Playback Switch", 55395a98265STakashi Iwai .info = snd_ca0106_shared_spdif_info, 55495a98265STakashi Iwai .get = snd_ca0106_shared_spdif_get, 55595a98265STakashi Iwai .put = snd_ca0106_shared_spdif_put 55695a98265STakashi Iwai }, 5571da177e4SLinus Torvalds { 5581da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 559e6327cf9SJames Courtier-Dutton .name = "Digital Source Capture Enum", 56095a98265STakashi Iwai .info = snd_ca0106_capture_source_info, 56195a98265STakashi Iwai .get = snd_ca0106_capture_source_get, 56295a98265STakashi Iwai .put = snd_ca0106_capture_source_put 56395a98265STakashi Iwai }, 5641da177e4SLinus Torvalds { 5656129daaaSJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 566e6327cf9SJames Courtier-Dutton .name = "Analog Source Capture Enum", 5676129daaaSJames Courtier-Dutton .info = snd_ca0106_i2c_capture_source_info, 5686129daaaSJames Courtier-Dutton .get = snd_ca0106_i2c_capture_source_get, 5696129daaaSJames Courtier-Dutton .put = snd_ca0106_i2c_capture_source_put 5706129daaaSJames Courtier-Dutton }, 5716129daaaSJames Courtier-Dutton { 57295a98265STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_PCM, 57395a98265STakashi Iwai .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), 57495a98265STakashi Iwai .count = 4, 57595a98265STakashi Iwai .info = snd_ca0106_spdif_info, 57695a98265STakashi Iwai .get = snd_ca0106_spdif_get, 57795a98265STakashi Iwai .put = snd_ca0106_spdif_put 57895a98265STakashi Iwai }, 5791da177e4SLinus Torvalds }; 5801da177e4SLinus Torvalds 5817c157069SJames Courtier-Dutton #define I2C_VOLUME(xname,chid) \ 5827c157069SJames Courtier-Dutton { \ 5837c157069SJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 5847c157069SJames Courtier-Dutton .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ 5857c157069SJames Courtier-Dutton SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 5867c157069SJames Courtier-Dutton .info = snd_ca0106_i2c_volume_info, \ 5877c157069SJames Courtier-Dutton .get = snd_ca0106_i2c_volume_get, \ 5887c157069SJames Courtier-Dutton .put = snd_ca0106_i2c_volume_put, \ 5897c157069SJames Courtier-Dutton .tlv = { .p = snd_ca0106_db_scale2 }, \ 5907c157069SJames Courtier-Dutton .private_value = chid \ 5917c157069SJames Courtier-Dutton } 5927c157069SJames Courtier-Dutton 5937c157069SJames Courtier-Dutton static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata = { 5947c157069SJames Courtier-Dutton I2C_VOLUME("Phone Capture Volume", 0), 5957c157069SJames Courtier-Dutton I2C_VOLUME("Mic Capture Volume", 1), 5967c157069SJames Courtier-Dutton I2C_VOLUME("Line in Capture Volume", 2), 5977c157069SJames Courtier-Dutton I2C_VOLUME("Aux Capture Volume", 3), 5987c157069SJames Courtier-Dutton }; 5997c157069SJames Courtier-Dutton 600b18cd538STrent Piepho #define SPI_SWITCH(xname,reg,bit) \ 601b18cd538STrent Piepho { \ 602b18cd538STrent Piepho .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 603b18cd538STrent Piepho .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ 604b18cd538STrent Piepho .info = spi_mute_info, \ 605b18cd538STrent Piepho .get = spi_mute_get, \ 606b18cd538STrent Piepho .put = spi_mute_put, \ 60718b5d32fSTrent Piepho .private_value = (reg<<SPI_REG_SHIFT) | (bit) \ 608b18cd538STrent Piepho } 609b18cd538STrent Piepho 610b18cd538STrent Piepho static struct snd_kcontrol_new snd_ca0106_volume_spi_dac_ctls[] 611b18cd538STrent Piepho __devinitdata = { 612b18cd538STrent Piepho SPI_SWITCH("Analog Front Playback Switch", 613b18cd538STrent Piepho SPI_DMUTE4_REG, SPI_DMUTE4_BIT), 614b18cd538STrent Piepho SPI_SWITCH("Analog Rear Playback Switch", 615b18cd538STrent Piepho SPI_DMUTE0_REG, SPI_DMUTE0_BIT), 616b18cd538STrent Piepho SPI_SWITCH("Analog Center/LFE Playback Switch", 617b18cd538STrent Piepho SPI_DMUTE2_REG, SPI_DMUTE2_BIT), 618b18cd538STrent Piepho SPI_SWITCH("Analog Side Playback Switch", 619b18cd538STrent Piepho SPI_DMUTE1_REG, SPI_DMUTE1_BIT), 620b18cd538STrent Piepho }; 621b18cd538STrent Piepho 622e4a3d145STakashi Iwai static int __devinit remove_ctl(struct snd_card *card, const char *name) 6231da177e4SLinus Torvalds { 624e4a3d145STakashi Iwai struct snd_ctl_elem_id id; 6251da177e4SLinus Torvalds memset(&id, 0, sizeof(id)); 6261da177e4SLinus Torvalds strcpy(id.name, name); 6271da177e4SLinus Torvalds id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 6281da177e4SLinus Torvalds return snd_ctl_remove_id(card, &id); 6291da177e4SLinus Torvalds } 6301da177e4SLinus Torvalds 631e4a3d145STakashi Iwai static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, const char *name) 6321da177e4SLinus Torvalds { 633e4a3d145STakashi Iwai struct snd_ctl_elem_id sid; 6341da177e4SLinus Torvalds memset(&sid, 0, sizeof(sid)); 6351da177e4SLinus Torvalds /* FIXME: strcpy is bad. */ 6361da177e4SLinus Torvalds strcpy(sid.name, name); 6371da177e4SLinus Torvalds sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 6381da177e4SLinus Torvalds return snd_ctl_find_id(card, &sid); 6391da177e4SLinus Torvalds } 6401da177e4SLinus Torvalds 641e4a3d145STakashi Iwai static int __devinit rename_ctl(struct snd_card *card, const char *src, const char *dst) 6421da177e4SLinus Torvalds { 643e4a3d145STakashi Iwai struct snd_kcontrol *kctl = ctl_find(card, src); 6441da177e4SLinus Torvalds if (kctl) { 6451da177e4SLinus Torvalds strcpy(kctl->id.name, dst); 6461da177e4SLinus Torvalds return 0; 6471da177e4SLinus Torvalds } 6481da177e4SLinus Torvalds return -ENOENT; 6491da177e4SLinus Torvalds } 6501da177e4SLinus Torvalds 651fca7f388STrent Piepho #define ADD_CTLS(emu, ctls) \ 652fca7f388STrent Piepho do { \ 653fca7f388STrent Piepho int i, err; \ 654fca7f388STrent Piepho for (i = 0; i < ARRAY_SIZE(ctls); i++) { \ 655fca7f388STrent Piepho err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \ 656fca7f388STrent Piepho if (err < 0) \ 657fca7f388STrent Piepho return err; \ 658fca7f388STrent Piepho } \ 659fca7f388STrent Piepho } while (0) 660fca7f388STrent Piepho 661*49c88b85STakashi Iwai static __devinitdata 662*49c88b85STakashi Iwai DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 50, 1); 663*49c88b85STakashi Iwai 664*49c88b85STakashi Iwai static char *slave_vols[] __devinitdata = { 665*49c88b85STakashi Iwai "Analog Front Playback Volume", 666*49c88b85STakashi Iwai "Analog Rear Playback Volume", 667*49c88b85STakashi Iwai "Analog Center/LFE Playback Volume", 668*49c88b85STakashi Iwai "Analog Side Playback Volume", 669*49c88b85STakashi Iwai "IEC958 Front Playback Volume", 670*49c88b85STakashi Iwai "IEC958 Rear Playback Volume", 671*49c88b85STakashi Iwai "IEC958 Center/LFE Playback Volume", 672*49c88b85STakashi Iwai "IEC958 Unknown Playback Volume", 673*49c88b85STakashi Iwai "CAPTURE feedback Playback Volume", 674*49c88b85STakashi Iwai NULL 675*49c88b85STakashi Iwai }; 676*49c88b85STakashi Iwai 677*49c88b85STakashi Iwai static char *slave_sws[] __devinitdata = { 678*49c88b85STakashi Iwai "Analog Front Playback Switch", 679*49c88b85STakashi Iwai "Analog Rear Playback Switch", 680*49c88b85STakashi Iwai "Analog Center/LFE Playback Switch", 681*49c88b85STakashi Iwai "Analog Side Playback Switch", 682*49c88b85STakashi Iwai "IEC958 Playback Switch", 683*49c88b85STakashi Iwai NULL 684*49c88b85STakashi Iwai }; 685*49c88b85STakashi Iwai 686*49c88b85STakashi Iwai static void __devinit add_slaves(struct snd_card *card, 687*49c88b85STakashi Iwai struct snd_kcontrol *master, char **list) 688*49c88b85STakashi Iwai { 689*49c88b85STakashi Iwai for (; *list; list++) { 690*49c88b85STakashi Iwai struct snd_kcontrol *slave = ctl_find(card, *list); 691*49c88b85STakashi Iwai if (slave) 692*49c88b85STakashi Iwai snd_ctl_add_slave(master, slave); 693*49c88b85STakashi Iwai } 694*49c88b85STakashi Iwai } 695*49c88b85STakashi Iwai 696e4a3d145STakashi Iwai int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) 6971da177e4SLinus Torvalds { 698fca7f388STrent Piepho int err; 699e4a3d145STakashi Iwai struct snd_card *card = emu->card; 7001da177e4SLinus Torvalds char **c; 701*49c88b85STakashi Iwai struct snd_kcontrol *vmaster; 7021da177e4SLinus Torvalds static char *ca0106_remove_ctls[] = { 7031da177e4SLinus Torvalds "Master Mono Playback Switch", 7041da177e4SLinus Torvalds "Master Mono Playback Volume", 7051da177e4SLinus Torvalds "3D Control - Switch", 7061da177e4SLinus Torvalds "3D Control Sigmatel - Depth", 7071da177e4SLinus Torvalds "PCM Playback Switch", 7081da177e4SLinus Torvalds "PCM Playback Volume", 7091da177e4SLinus Torvalds "CD Playback Switch", 7101da177e4SLinus Torvalds "CD Playback Volume", 7111da177e4SLinus Torvalds "Phone Playback Switch", 7121da177e4SLinus Torvalds "Phone Playback Volume", 7131da177e4SLinus Torvalds "Video Playback Switch", 7141da177e4SLinus Torvalds "Video Playback Volume", 7151da177e4SLinus Torvalds "PC Speaker Playback Switch", 7161da177e4SLinus Torvalds "PC Speaker Playback Volume", 7171da177e4SLinus Torvalds "Mono Output Select", 7181da177e4SLinus Torvalds "Capture Source", 7191da177e4SLinus Torvalds "Capture Switch", 7201da177e4SLinus Torvalds "Capture Volume", 7211da177e4SLinus Torvalds "External Amplifier", 7221da177e4SLinus Torvalds "Sigmatel 4-Speaker Stereo Playback Switch", 7231da177e4SLinus Torvalds "Sigmatel Surround Phase Inversion Playback ", 7241da177e4SLinus Torvalds NULL 7251da177e4SLinus Torvalds }; 7261da177e4SLinus Torvalds static char *ca0106_rename_ctls[] = { 7271da177e4SLinus Torvalds "Master Playback Switch", "Capture Switch", 7281da177e4SLinus Torvalds "Master Playback Volume", "Capture Volume", 7291da177e4SLinus Torvalds "Line Playback Switch", "AC97 Line Capture Switch", 7301da177e4SLinus Torvalds "Line Playback Volume", "AC97 Line Capture Volume", 7311da177e4SLinus Torvalds "Aux Playback Switch", "AC97 Aux Capture Switch", 7321da177e4SLinus Torvalds "Aux Playback Volume", "AC97 Aux Capture Volume", 7331da177e4SLinus Torvalds "Mic Playback Switch", "AC97 Mic Capture Switch", 7341da177e4SLinus Torvalds "Mic Playback Volume", "AC97 Mic Capture Volume", 7351da177e4SLinus Torvalds "Mic Select", "AC97 Mic Select", 7361da177e4SLinus Torvalds "Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)", 7371da177e4SLinus Torvalds NULL 7381da177e4SLinus Torvalds }; 7391da177e4SLinus Torvalds #if 1 7401da177e4SLinus Torvalds for (c = ca0106_remove_ctls; *c; c++) 7411da177e4SLinus Torvalds remove_ctl(card, *c); 7421da177e4SLinus Torvalds for (c = ca0106_rename_ctls; *c; c += 2) 7431da177e4SLinus Torvalds rename_ctl(card, c[0], c[1]); 7441da177e4SLinus Torvalds #endif 7451da177e4SLinus Torvalds 746fca7f388STrent Piepho ADD_CTLS(emu, snd_ca0106_volume_ctls); 74795a98265STakashi Iwai if (emu->details->i2c_adc == 1) { 748fca7f388STrent Piepho ADD_CTLS(emu, snd_ca0106_volume_i2c_adc_ctls); 749be0b7b01SJames Courtier-Dutton if (emu->details->gpio_type == 1) 75095a98265STakashi Iwai err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu)); 751be0b7b01SJames Courtier-Dutton else /* gpio_type == 2 */ 752be0b7b01SJames Courtier-Dutton err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu)); 75395a98265STakashi Iwai if (err < 0) 7541da177e4SLinus Torvalds return err; 75595a98265STakashi Iwai } 756fca7f388STrent Piepho if (emu->details->spi_dac == 1) 757fca7f388STrent Piepho ADD_CTLS(emu, snd_ca0106_volume_spi_dac_ctls); 758*49c88b85STakashi Iwai 759*49c88b85STakashi Iwai /* Create virtual master controls */ 760*49c88b85STakashi Iwai vmaster = snd_ctl_make_virtual_master("Master Playback Volume", 761*49c88b85STakashi Iwai snd_ca0106_master_db_scale); 762*49c88b85STakashi Iwai if (!vmaster) 763*49c88b85STakashi Iwai return -ENOMEM; 764*49c88b85STakashi Iwai add_slaves(card, vmaster, slave_vols); 765*49c88b85STakashi Iwai 766*49c88b85STakashi Iwai if (emu->details->spi_dac == 1) { 767*49c88b85STakashi Iwai vmaster = snd_ctl_make_virtual_master("Master Playback Switch", 768*49c88b85STakashi Iwai NULL); 769*49c88b85STakashi Iwai if (!vmaster) 770*49c88b85STakashi Iwai return -ENOMEM; 771*49c88b85STakashi Iwai add_slaves(card, vmaster, slave_sws); 772*49c88b85STakashi Iwai } 7731da177e4SLinus Torvalds return 0; 7741da177e4SLinus Torvalds } 7751da177e4SLinus Torvalds 776