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 <sound/driver.h> 641da177e4SLinus Torvalds #include <linux/delay.h> 651da177e4SLinus Torvalds #include <linux/init.h> 661da177e4SLinus Torvalds #include <linux/interrupt.h> 671da177e4SLinus Torvalds #include <linux/slab.h> 681da177e4SLinus Torvalds #include <linux/moduleparam.h> 691da177e4SLinus Torvalds #include <sound/core.h> 701da177e4SLinus Torvalds #include <sound/initval.h> 711da177e4SLinus Torvalds #include <sound/pcm.h> 721da177e4SLinus Torvalds #include <sound/ac97_codec.h> 731da177e4SLinus Torvalds #include <sound/info.h> 7442750b04SJaroslav Kysela #include <sound/tlv.h> 756473d160SJean Delvare #include <asm/io.h> 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds #include "ca0106.h" 781da177e4SLinus Torvalds 790cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1); 800cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1); 8142750b04SJaroslav Kysela 82a5ce8890STakashi Iwai #define snd_ca0106_shared_spdif_info snd_ctl_boolean_mono_info 831da177e4SLinus Torvalds 84e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol, 85e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 861da177e4SLinus Torvalds { 87e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds ucontrol->value.enumerated.item[0] = emu->spdif_enable; 901da177e4SLinus Torvalds return 0; 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds 93e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol, 94e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 951da177e4SLinus Torvalds { 96e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 971da177e4SLinus Torvalds unsigned int val; 981da177e4SLinus Torvalds int change = 0; 991da177e4SLinus Torvalds u32 mask; 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds val = ucontrol->value.enumerated.item[0] ; 1021da177e4SLinus Torvalds change = (emu->spdif_enable != val); 1031da177e4SLinus Torvalds if (change) { 1041da177e4SLinus Torvalds emu->spdif_enable = val; 1051da177e4SLinus Torvalds if (val == 1) { 1061da177e4SLinus Torvalds /* Digital */ 1071da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); 1081da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000); 1091da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, 1101da177e4SLinus Torvalds snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000); 1111da177e4SLinus Torvalds mask = inl(emu->port + GPIO) & ~0x101; 1121da177e4SLinus Torvalds outl(mask, emu->port + GPIO); 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds } else { 1151da177e4SLinus Torvalds /* Analog */ 1161da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); 1171f82941eSJames Courtier-Dutton snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000); 1181da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, 1191da177e4SLinus Torvalds snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000); 1201da177e4SLinus Torvalds mask = inl(emu->port + GPIO) | 0x101; 1211da177e4SLinus Torvalds outl(mask, emu->port + GPIO); 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds } 1241da177e4SLinus Torvalds return change; 1251da177e4SLinus Torvalds } 1261da177e4SLinus Torvalds 127e4a3d145STakashi Iwai static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol, 128e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 1291da177e4SLinus Torvalds { 13095a98265STakashi Iwai static char *texts[6] = { 13139596dc8SJames Courtier-Dutton "IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out" 13295a98265STakashi Iwai }; 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 1351da177e4SLinus Torvalds uinfo->count = 1; 1361da177e4SLinus Torvalds uinfo->value.enumerated.items = 6; 1371da177e4SLinus Torvalds if (uinfo->value.enumerated.item > 5) 1381da177e4SLinus Torvalds uinfo->value.enumerated.item = 5; 1391da177e4SLinus Torvalds strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); 1401da177e4SLinus Torvalds return 0; 1411da177e4SLinus Torvalds } 1421da177e4SLinus Torvalds 143e4a3d145STakashi Iwai static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol, 144e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 1451da177e4SLinus Torvalds { 146e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds ucontrol->value.enumerated.item[0] = emu->capture_source; 1491da177e4SLinus Torvalds return 0; 1501da177e4SLinus Torvalds } 1511da177e4SLinus Torvalds 152e4a3d145STakashi Iwai static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol, 153e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 1541da177e4SLinus Torvalds { 155e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 1561da177e4SLinus Torvalds unsigned int val; 1571da177e4SLinus Torvalds int change = 0; 1581da177e4SLinus Torvalds u32 mask; 1591da177e4SLinus Torvalds u32 source; 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds val = ucontrol->value.enumerated.item[0] ; 1621da177e4SLinus Torvalds change = (emu->capture_source != val); 1631da177e4SLinus Torvalds if (change) { 1641da177e4SLinus Torvalds emu->capture_source = val; 1651da177e4SLinus Torvalds source = (val << 28) | (val << 24) | (val << 20) | (val << 16); 1661da177e4SLinus Torvalds mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff; 1671da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask); 1681da177e4SLinus Torvalds } 1691da177e4SLinus Torvalds return change; 1701da177e4SLinus Torvalds } 1711da177e4SLinus Torvalds 1726129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol, 1736129daaaSJames Courtier-Dutton struct snd_ctl_elem_info *uinfo) 1746129daaaSJames Courtier-Dutton { 1756129daaaSJames Courtier-Dutton static char *texts[6] = { 1766129daaaSJames Courtier-Dutton "Phone", "Mic", "Line in", "Aux" 1776129daaaSJames Courtier-Dutton }; 1786129daaaSJames Courtier-Dutton 1796129daaaSJames Courtier-Dutton uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 1806129daaaSJames Courtier-Dutton uinfo->count = 1; 1816129daaaSJames Courtier-Dutton uinfo->value.enumerated.items = 4; 1826129daaaSJames Courtier-Dutton if (uinfo->value.enumerated.item > 3) 1836129daaaSJames Courtier-Dutton uinfo->value.enumerated.item = 3; 1846129daaaSJames Courtier-Dutton strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); 1856129daaaSJames Courtier-Dutton return 0; 1866129daaaSJames Courtier-Dutton } 1876129daaaSJames Courtier-Dutton 1886129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol, 1896129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 1906129daaaSJames Courtier-Dutton { 1916129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 1926129daaaSJames Courtier-Dutton 1936129daaaSJames Courtier-Dutton ucontrol->value.enumerated.item[0] = emu->i2c_capture_source; 1946129daaaSJames Courtier-Dutton return 0; 1956129daaaSJames Courtier-Dutton } 1966129daaaSJames Courtier-Dutton 1976129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol, 1986129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 1996129daaaSJames Courtier-Dutton { 2006129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 2016129daaaSJames Courtier-Dutton unsigned int source_id; 2026129daaaSJames Courtier-Dutton unsigned int ngain, ogain; 2036129daaaSJames Courtier-Dutton int change = 0; 2046129daaaSJames Courtier-Dutton u32 source; 2056129daaaSJames Courtier-Dutton /* If the capture source has changed, 2066129daaaSJames Courtier-Dutton * update the capture volume from the cached value 2076129daaaSJames Courtier-Dutton * for the particular source. 2086129daaaSJames Courtier-Dutton */ 2096129daaaSJames Courtier-Dutton source_id = ucontrol->value.enumerated.item[0] ; 2106129daaaSJames Courtier-Dutton change = (emu->i2c_capture_source != source_id); 2116129daaaSJames Courtier-Dutton if (change) { 2126129daaaSJames Courtier-Dutton snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ 2136129daaaSJames Courtier-Dutton ngain = emu->i2c_capture_volume[source_id][0]; /* Left */ 2146129daaaSJames Courtier-Dutton ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */ 2156129daaaSJames Courtier-Dutton if (ngain != ogain) 2166129daaaSJames Courtier-Dutton snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff)); 2176129daaaSJames Courtier-Dutton ngain = emu->i2c_capture_volume[source_id][1]; /* Left */ 2186129daaaSJames Courtier-Dutton ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Left */ 2196129daaaSJames Courtier-Dutton if (ngain != ogain) 2206129daaaSJames Courtier-Dutton snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff)); 2216129daaaSJames Courtier-Dutton source = 1 << source_id; 2226129daaaSJames Courtier-Dutton snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */ 2236129daaaSJames Courtier-Dutton emu->i2c_capture_source = source_id; 2246129daaaSJames Courtier-Dutton } 2256129daaaSJames Courtier-Dutton return change; 2266129daaaSJames Courtier-Dutton } 2276129daaaSJames Courtier-Dutton 228be0b7b01SJames Courtier-Dutton static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol, 229be0b7b01SJames Courtier-Dutton struct snd_ctl_elem_info *uinfo) 230be0b7b01SJames Courtier-Dutton { 231be0b7b01SJames Courtier-Dutton static char *texts[2] = { "Side out", "Line in" }; 232be0b7b01SJames Courtier-Dutton 233be0b7b01SJames Courtier-Dutton uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 234be0b7b01SJames Courtier-Dutton uinfo->count = 1; 235be0b7b01SJames Courtier-Dutton uinfo->value.enumerated.items = 2; 236be0b7b01SJames Courtier-Dutton if (uinfo->value.enumerated.item > 1) 237be0b7b01SJames Courtier-Dutton uinfo->value.enumerated.item = 1; 238be0b7b01SJames Courtier-Dutton strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); 239be0b7b01SJames Courtier-Dutton return 0; 240be0b7b01SJames Courtier-Dutton } 241be0b7b01SJames Courtier-Dutton 242e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol, 243e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 244ed144f3cSJames Courtier-Dutton { 245ed144f3cSJames Courtier-Dutton static char *texts[2] = { "Line in", "Mic in" }; 246ed144f3cSJames Courtier-Dutton 247ed144f3cSJames Courtier-Dutton uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 248ed144f3cSJames Courtier-Dutton uinfo->count = 1; 249ed144f3cSJames Courtier-Dutton uinfo->value.enumerated.items = 2; 250ed144f3cSJames Courtier-Dutton if (uinfo->value.enumerated.item > 1) 251ed144f3cSJames Courtier-Dutton uinfo->value.enumerated.item = 1; 252ed144f3cSJames Courtier-Dutton strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); 253ed144f3cSJames Courtier-Dutton return 0; 254ed144f3cSJames Courtier-Dutton } 255ed144f3cSJames Courtier-Dutton 256e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol, 257e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 258ed144f3cSJames Courtier-Dutton { 259e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 260ed144f3cSJames Courtier-Dutton 261ed144f3cSJames Courtier-Dutton ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in; 262ed144f3cSJames Courtier-Dutton return 0; 263ed144f3cSJames Courtier-Dutton } 264ed144f3cSJames Courtier-Dutton 265e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol, 266e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 267ed144f3cSJames Courtier-Dutton { 268e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 269ed144f3cSJames Courtier-Dutton unsigned int val; 270ed144f3cSJames Courtier-Dutton int change = 0; 271ed144f3cSJames Courtier-Dutton u32 tmp; 272ed144f3cSJames Courtier-Dutton 273ed144f3cSJames Courtier-Dutton val = ucontrol->value.enumerated.item[0] ; 274ed144f3cSJames Courtier-Dutton change = (emu->capture_mic_line_in != val); 275ed144f3cSJames Courtier-Dutton if (change) { 276ed144f3cSJames Courtier-Dutton emu->capture_mic_line_in = val; 277ed144f3cSJames Courtier-Dutton if (val) { 2786129daaaSJames Courtier-Dutton //snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ 279ed144f3cSJames Courtier-Dutton tmp = inl(emu->port+GPIO) & ~0x400; 280ed144f3cSJames Courtier-Dutton tmp = tmp | 0x400; 281ed144f3cSJames Courtier-Dutton outl(tmp, emu->port+GPIO); 2826129daaaSJames Courtier-Dutton //snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); 283ed144f3cSJames Courtier-Dutton } else { 2846129daaaSJames Courtier-Dutton //snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ 285ed144f3cSJames Courtier-Dutton tmp = inl(emu->port+GPIO) & ~0x400; 286ed144f3cSJames Courtier-Dutton outl(tmp, emu->port+GPIO); 2876129daaaSJames Courtier-Dutton //snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); 288ed144f3cSJames Courtier-Dutton } 289ed144f3cSJames Courtier-Dutton } 290ed144f3cSJames Courtier-Dutton return change; 291ed144f3cSJames Courtier-Dutton } 292ed144f3cSJames Courtier-Dutton 293e4a3d145STakashi Iwai static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata = 294ed144f3cSJames Courtier-Dutton { 295ed144f3cSJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2966129daaaSJames Courtier-Dutton .name = "Shared Mic/Line in Capture Switch", 297ed144f3cSJames Courtier-Dutton .info = snd_ca0106_capture_mic_line_in_info, 298ed144f3cSJames Courtier-Dutton .get = snd_ca0106_capture_mic_line_in_get, 299ed144f3cSJames Courtier-Dutton .put = snd_ca0106_capture_mic_line_in_put 300ed144f3cSJames Courtier-Dutton }; 301ed144f3cSJames Courtier-Dutton 302be0b7b01SJames Courtier-Dutton static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out __devinitdata = 303be0b7b01SJames Courtier-Dutton { 304be0b7b01SJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 305be0b7b01SJames Courtier-Dutton .name = "Shared Line in/Side out Capture Switch", 306be0b7b01SJames Courtier-Dutton .info = snd_ca0106_capture_line_in_side_out_info, 307be0b7b01SJames Courtier-Dutton .get = snd_ca0106_capture_mic_line_in_get, 308be0b7b01SJames Courtier-Dutton .put = snd_ca0106_capture_mic_line_in_put 309be0b7b01SJames Courtier-Dutton }; 310be0b7b01SJames Courtier-Dutton 311be0b7b01SJames Courtier-Dutton 312e4a3d145STakashi Iwai static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol, 313e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 3141da177e4SLinus Torvalds { 3151da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 3161da177e4SLinus Torvalds uinfo->count = 1; 3171da177e4SLinus Torvalds return 0; 3181da177e4SLinus Torvalds } 3191da177e4SLinus Torvalds 320e4a3d145STakashi Iwai static int snd_ca0106_spdif_get(struct snd_kcontrol *kcontrol, 321e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3221da177e4SLinus Torvalds { 323e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 3241da177e4SLinus Torvalds unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 3251da177e4SLinus Torvalds 3261da177e4SLinus Torvalds ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff; 3271da177e4SLinus Torvalds ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff; 3281da177e4SLinus Torvalds ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff; 3291da177e4SLinus Torvalds ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff; 3301da177e4SLinus Torvalds return 0; 3311da177e4SLinus Torvalds } 3321da177e4SLinus Torvalds 333e4a3d145STakashi Iwai static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol, 334e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3351da177e4SLinus Torvalds { 3361da177e4SLinus Torvalds ucontrol->value.iec958.status[0] = 0xff; 3371da177e4SLinus Torvalds ucontrol->value.iec958.status[1] = 0xff; 3381da177e4SLinus Torvalds ucontrol->value.iec958.status[2] = 0xff; 3391da177e4SLinus Torvalds ucontrol->value.iec958.status[3] = 0xff; 3401da177e4SLinus Torvalds return 0; 3411da177e4SLinus Torvalds } 3421da177e4SLinus Torvalds 343e4a3d145STakashi Iwai static int snd_ca0106_spdif_put(struct snd_kcontrol *kcontrol, 344e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3451da177e4SLinus Torvalds { 346e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 3471da177e4SLinus Torvalds unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 3481da177e4SLinus Torvalds int change; 3491da177e4SLinus Torvalds unsigned int val; 3501da177e4SLinus Torvalds 3511da177e4SLinus Torvalds val = (ucontrol->value.iec958.status[0] << 0) | 3521da177e4SLinus Torvalds (ucontrol->value.iec958.status[1] << 8) | 3531da177e4SLinus Torvalds (ucontrol->value.iec958.status[2] << 16) | 3541da177e4SLinus Torvalds (ucontrol->value.iec958.status[3] << 24); 3551da177e4SLinus Torvalds change = val != emu->spdif_bits[idx]; 3561da177e4SLinus Torvalds if (change) { 3571da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, val); 3581da177e4SLinus Torvalds emu->spdif_bits[idx] = val; 3591da177e4SLinus Torvalds } 3601da177e4SLinus Torvalds return change; 3611da177e4SLinus Torvalds } 3621da177e4SLinus Torvalds 363e4a3d145STakashi Iwai static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol, 364e4a3d145STakashi Iwai struct snd_ctl_elem_info *uinfo) 3651da177e4SLinus Torvalds { 3661da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 3671da177e4SLinus Torvalds uinfo->count = 2; 3681da177e4SLinus Torvalds uinfo->value.integer.min = 0; 3691da177e4SLinus Torvalds uinfo->value.integer.max = 255; 3701da177e4SLinus Torvalds return 0; 3711da177e4SLinus Torvalds } 3721da177e4SLinus Torvalds 373e4a3d145STakashi Iwai static int snd_ca0106_volume_get(struct snd_kcontrol *kcontrol, 374e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3751da177e4SLinus Torvalds { 376e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 3771da177e4SLinus Torvalds unsigned int value; 37895a98265STakashi Iwai int channel_id, reg; 37995a98265STakashi Iwai 38095a98265STakashi Iwai channel_id = (kcontrol->private_value >> 8) & 0xff; 38195a98265STakashi Iwai reg = kcontrol->private_value & 0xff; 3821da177e4SLinus Torvalds 3831da177e4SLinus Torvalds value = snd_ca0106_ptr_read(emu, reg, channel_id); 3841da177e4SLinus Torvalds ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */ 3851da177e4SLinus Torvalds ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */ 3861da177e4SLinus Torvalds return 0; 3871da177e4SLinus Torvalds } 3881da177e4SLinus Torvalds 389e4a3d145STakashi Iwai static int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol, 390e4a3d145STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3911da177e4SLinus Torvalds { 392e4a3d145STakashi Iwai struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 39395a98265STakashi Iwai unsigned int oval, nval; 39495a98265STakashi Iwai int channel_id, reg; 39595a98265STakashi Iwai 39695a98265STakashi Iwai channel_id = (kcontrol->private_value >> 8) & 0xff; 39795a98265STakashi Iwai reg = kcontrol->private_value & 0xff; 39895a98265STakashi Iwai 39995a98265STakashi Iwai oval = snd_ca0106_ptr_read(emu, reg, channel_id); 40095a98265STakashi Iwai nval = ((0xff - ucontrol->value.integer.value[0]) << 24) | 40195a98265STakashi Iwai ((0xff - ucontrol->value.integer.value[1]) << 16); 40295a98265STakashi Iwai nval |= ((0xff - ucontrol->value.integer.value[0]) << 8) | 40395a98265STakashi Iwai ((0xff - ucontrol->value.integer.value[1]) ); 40495a98265STakashi Iwai if (oval == nval) 40595a98265STakashi Iwai return 0; 40695a98265STakashi Iwai snd_ca0106_ptr_write(emu, reg, channel_id, nval); 4071da177e4SLinus Torvalds return 1; 4081da177e4SLinus Torvalds } 40995a98265STakashi Iwai 4106129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_info(struct snd_kcontrol *kcontrol, 4116129daaaSJames Courtier-Dutton struct snd_ctl_elem_info *uinfo) 4126129daaaSJames Courtier-Dutton { 4136129daaaSJames Courtier-Dutton uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 4146129daaaSJames Courtier-Dutton uinfo->count = 2; 4156129daaaSJames Courtier-Dutton uinfo->value.integer.min = 0; 4166129daaaSJames Courtier-Dutton uinfo->value.integer.max = 255; 4176129daaaSJames Courtier-Dutton return 0; 4186129daaaSJames Courtier-Dutton } 4196129daaaSJames Courtier-Dutton 4206129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol, 4216129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 4226129daaaSJames Courtier-Dutton { 4236129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 4246129daaaSJames Courtier-Dutton int source_id; 4256129daaaSJames Courtier-Dutton 4266129daaaSJames Courtier-Dutton source_id = kcontrol->private_value; 4276129daaaSJames Courtier-Dutton 4286129daaaSJames Courtier-Dutton ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0]; 4296129daaaSJames Courtier-Dutton ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1]; 4306129daaaSJames Courtier-Dutton return 0; 4316129daaaSJames Courtier-Dutton } 4326129daaaSJames Courtier-Dutton 4336129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol, 4346129daaaSJames Courtier-Dutton struct snd_ctl_elem_value *ucontrol) 4356129daaaSJames Courtier-Dutton { 4366129daaaSJames Courtier-Dutton struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 4376129daaaSJames Courtier-Dutton unsigned int ogain; 4386129daaaSJames Courtier-Dutton unsigned int ngain; 4396129daaaSJames Courtier-Dutton int source_id; 4406129daaaSJames Courtier-Dutton int change = 0; 4416129daaaSJames Courtier-Dutton 4426129daaaSJames Courtier-Dutton source_id = kcontrol->private_value; 4436129daaaSJames Courtier-Dutton ogain = emu->i2c_capture_volume[source_id][0]; /* Left */ 4446129daaaSJames Courtier-Dutton ngain = ucontrol->value.integer.value[0]; 4456129daaaSJames Courtier-Dutton if (ngain > 0xff) 4466129daaaSJames Courtier-Dutton return 0; 4476129daaaSJames Courtier-Dutton if (ogain != ngain) { 4486129daaaSJames Courtier-Dutton if (emu->i2c_capture_source == source_id) 4496129daaaSJames Courtier-Dutton snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) ); 4506129daaaSJames Courtier-Dutton emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0]; 4516129daaaSJames Courtier-Dutton change = 1; 4526129daaaSJames Courtier-Dutton } 4536129daaaSJames Courtier-Dutton ogain = emu->i2c_capture_volume[source_id][1]; /* Right */ 4546129daaaSJames Courtier-Dutton ngain = ucontrol->value.integer.value[1]; 4556129daaaSJames Courtier-Dutton if (ngain > 0xff) 4566129daaaSJames Courtier-Dutton return 0; 4576129daaaSJames Courtier-Dutton if (ogain != ngain) { 4586129daaaSJames Courtier-Dutton if (emu->i2c_capture_source == source_id) 4596129daaaSJames Courtier-Dutton snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff)); 4606129daaaSJames Courtier-Dutton emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1]; 4616129daaaSJames Courtier-Dutton change = 1; 4626129daaaSJames Courtier-Dutton } 4636129daaaSJames Courtier-Dutton 4646129daaaSJames Courtier-Dutton return change; 4656129daaaSJames Courtier-Dutton } 4666129daaaSJames Courtier-Dutton 467b18cd538STrent Piepho #define spi_mute_info snd_ctl_boolean_mono_info 468b18cd538STrent Piepho 469b18cd538STrent Piepho static int spi_mute_get(struct snd_kcontrol *kcontrol, 470b18cd538STrent Piepho struct snd_ctl_elem_value *ucontrol) 471b18cd538STrent Piepho { 472b18cd538STrent Piepho struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 473b18cd538STrent Piepho unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT; 474b18cd538STrent Piepho unsigned int bit = kcontrol->private_value & SPI_REG_MASK; 475b18cd538STrent Piepho 476b18cd538STrent Piepho ucontrol->value.integer.value[0] = !(emu->spi_dac_reg[reg] & bit); 477b18cd538STrent Piepho return 0; 478b18cd538STrent Piepho } 479b18cd538STrent Piepho 480b18cd538STrent Piepho static int spi_mute_put(struct snd_kcontrol *kcontrol, 481b18cd538STrent Piepho struct snd_ctl_elem_value *ucontrol) 482b18cd538STrent Piepho { 483b18cd538STrent Piepho struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 484b18cd538STrent Piepho unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT; 485b18cd538STrent Piepho unsigned int bit = kcontrol->private_value & SPI_REG_MASK; 486b18cd538STrent Piepho int ret; 487b18cd538STrent Piepho 488b18cd538STrent Piepho ret = emu->spi_dac_reg[reg] & bit; 489b18cd538STrent Piepho if (ucontrol->value.integer.value[0]) { 490b18cd538STrent Piepho if (!ret) /* bit already cleared, do nothing */ 491b18cd538STrent Piepho return 0; 492b18cd538STrent Piepho emu->spi_dac_reg[reg] &= ~bit; 493b18cd538STrent Piepho } else { 494b18cd538STrent Piepho if (ret) /* bit already set, do nothing */ 495b18cd538STrent Piepho return 0; 496b18cd538STrent Piepho emu->spi_dac_reg[reg] |= bit; 497b18cd538STrent Piepho } 498b18cd538STrent Piepho 499b18cd538STrent Piepho ret = snd_ca0106_spi_write(emu, emu->spi_dac_reg[reg]); 500b18cd538STrent Piepho return ret ? -1 : 1; 501b18cd538STrent Piepho } 502b18cd538STrent Piepho 50395a98265STakashi Iwai #define CA_VOLUME(xname,chid,reg) \ 50495a98265STakashi Iwai { \ 50595a98265STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 506302e9c5aSJaroslav Kysela .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ 507302e9c5aSJaroslav Kysela SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 50895a98265STakashi Iwai .info = snd_ca0106_volume_info, \ 50995a98265STakashi Iwai .get = snd_ca0106_volume_get, \ 51095a98265STakashi Iwai .put = snd_ca0106_volume_put, \ 5117cf0a953STakashi Iwai .tlv = { .p = snd_ca0106_db_scale1 }, \ 51295a98265STakashi Iwai .private_value = ((chid) << 8) | (reg) \ 5131da177e4SLinus Torvalds } 5141da177e4SLinus Torvalds 515e4a3d145STakashi Iwai static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = { 51695a98265STakashi Iwai CA_VOLUME("Analog Front Playback Volume", 51795a98265STakashi Iwai CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2), 51895a98265STakashi Iwai CA_VOLUME("Analog Rear Playback Volume", 51995a98265STakashi Iwai CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2), 52095a98265STakashi Iwai CA_VOLUME("Analog Center/LFE Playback Volume", 52195a98265STakashi Iwai CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2), 52295a98265STakashi Iwai CA_VOLUME("Analog Side Playback Volume", 52395a98265STakashi Iwai CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2), 52495a98265STakashi Iwai 52539596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Front Playback Volume", 52695a98265STakashi Iwai CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1), 52739596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Rear Playback Volume", 52895a98265STakashi Iwai CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1), 52939596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Center/LFE Playback Volume", 53095a98265STakashi Iwai CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1), 53139596dc8SJames Courtier-Dutton CA_VOLUME("IEC958 Unknown Playback Volume", 53295a98265STakashi Iwai CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1), 53395a98265STakashi Iwai 53495a98265STakashi Iwai CA_VOLUME("CAPTURE feedback Playback Volume", 53595a98265STakashi Iwai 1, CAPTURE_CONTROL), 53695a98265STakashi Iwai 53795a98265STakashi Iwai { 53895a98265STakashi Iwai .access = SNDRV_CTL_ELEM_ACCESS_READ, 53995a98265STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_PCM, 54095a98265STakashi Iwai .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), 54195a98265STakashi Iwai .count = 4, 54295a98265STakashi Iwai .info = snd_ca0106_spdif_info, 54395a98265STakashi Iwai .get = snd_ca0106_spdif_get_mask 54495a98265STakashi Iwai }, 5451da177e4SLinus Torvalds { 5461da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 54739596dc8SJames Courtier-Dutton .name = "IEC958 Playback Switch", 54895a98265STakashi Iwai .info = snd_ca0106_shared_spdif_info, 54995a98265STakashi Iwai .get = snd_ca0106_shared_spdif_get, 55095a98265STakashi Iwai .put = snd_ca0106_shared_spdif_put 55195a98265STakashi Iwai }, 5521da177e4SLinus Torvalds { 5531da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 554e6327cf9SJames Courtier-Dutton .name = "Digital Source Capture Enum", 55595a98265STakashi Iwai .info = snd_ca0106_capture_source_info, 55695a98265STakashi Iwai .get = snd_ca0106_capture_source_get, 55795a98265STakashi Iwai .put = snd_ca0106_capture_source_put 55895a98265STakashi Iwai }, 5591da177e4SLinus Torvalds { 5606129daaaSJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 561e6327cf9SJames Courtier-Dutton .name = "Analog Source Capture Enum", 5626129daaaSJames Courtier-Dutton .info = snd_ca0106_i2c_capture_source_info, 5636129daaaSJames Courtier-Dutton .get = snd_ca0106_i2c_capture_source_get, 5646129daaaSJames Courtier-Dutton .put = snd_ca0106_i2c_capture_source_put 5656129daaaSJames Courtier-Dutton }, 5666129daaaSJames Courtier-Dutton { 56795a98265STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_PCM, 56895a98265STakashi Iwai .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), 56995a98265STakashi Iwai .count = 4, 57095a98265STakashi Iwai .info = snd_ca0106_spdif_info, 57195a98265STakashi Iwai .get = snd_ca0106_spdif_get, 57295a98265STakashi Iwai .put = snd_ca0106_spdif_put 57395a98265STakashi Iwai }, 5741da177e4SLinus Torvalds }; 5751da177e4SLinus Torvalds 5767c157069SJames Courtier-Dutton #define I2C_VOLUME(xname,chid) \ 5777c157069SJames Courtier-Dutton { \ 5787c157069SJames Courtier-Dutton .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 5797c157069SJames Courtier-Dutton .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ 5807c157069SJames Courtier-Dutton SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 5817c157069SJames Courtier-Dutton .info = snd_ca0106_i2c_volume_info, \ 5827c157069SJames Courtier-Dutton .get = snd_ca0106_i2c_volume_get, \ 5837c157069SJames Courtier-Dutton .put = snd_ca0106_i2c_volume_put, \ 5847c157069SJames Courtier-Dutton .tlv = { .p = snd_ca0106_db_scale2 }, \ 5857c157069SJames Courtier-Dutton .private_value = chid \ 5867c157069SJames Courtier-Dutton } 5877c157069SJames Courtier-Dutton 5887c157069SJames Courtier-Dutton static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata = { 5897c157069SJames Courtier-Dutton I2C_VOLUME("Phone Capture Volume", 0), 5907c157069SJames Courtier-Dutton I2C_VOLUME("Mic Capture Volume", 1), 5917c157069SJames Courtier-Dutton I2C_VOLUME("Line in Capture Volume", 2), 5927c157069SJames Courtier-Dutton I2C_VOLUME("Aux Capture Volume", 3), 5937c157069SJames Courtier-Dutton }; 5947c157069SJames Courtier-Dutton 595b18cd538STrent Piepho #define SPI_SWITCH(xname,reg,bit) \ 596b18cd538STrent Piepho { \ 597b18cd538STrent Piepho .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 598b18cd538STrent Piepho .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ 599b18cd538STrent Piepho .info = spi_mute_info, \ 600b18cd538STrent Piepho .get = spi_mute_get, \ 601b18cd538STrent Piepho .put = spi_mute_put, \ 602*18b5d32fSTrent Piepho .private_value = (reg<<SPI_REG_SHIFT) | (bit) \ 603b18cd538STrent Piepho } 604b18cd538STrent Piepho 605b18cd538STrent Piepho static struct snd_kcontrol_new snd_ca0106_volume_spi_dac_ctls[] 606b18cd538STrent Piepho __devinitdata = { 607b18cd538STrent Piepho SPI_SWITCH("Analog Front Playback Switch", 608b18cd538STrent Piepho SPI_DMUTE4_REG, SPI_DMUTE4_BIT), 609b18cd538STrent Piepho SPI_SWITCH("Analog Rear Playback Switch", 610b18cd538STrent Piepho SPI_DMUTE0_REG, SPI_DMUTE0_BIT), 611b18cd538STrent Piepho SPI_SWITCH("Analog Center/LFE Playback Switch", 612b18cd538STrent Piepho SPI_DMUTE2_REG, SPI_DMUTE2_BIT), 613b18cd538STrent Piepho SPI_SWITCH("Analog Side Playback Switch", 614b18cd538STrent Piepho SPI_DMUTE1_REG, SPI_DMUTE1_BIT), 615b18cd538STrent Piepho }; 616b18cd538STrent Piepho 617e4a3d145STakashi Iwai static int __devinit remove_ctl(struct snd_card *card, const char *name) 6181da177e4SLinus Torvalds { 619e4a3d145STakashi Iwai struct snd_ctl_elem_id id; 6201da177e4SLinus Torvalds memset(&id, 0, sizeof(id)); 6211da177e4SLinus Torvalds strcpy(id.name, name); 6221da177e4SLinus Torvalds id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 6231da177e4SLinus Torvalds return snd_ctl_remove_id(card, &id); 6241da177e4SLinus Torvalds } 6251da177e4SLinus Torvalds 626e4a3d145STakashi Iwai static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, const char *name) 6271da177e4SLinus Torvalds { 628e4a3d145STakashi Iwai struct snd_ctl_elem_id sid; 6291da177e4SLinus Torvalds memset(&sid, 0, sizeof(sid)); 6301da177e4SLinus Torvalds /* FIXME: strcpy is bad. */ 6311da177e4SLinus Torvalds strcpy(sid.name, name); 6321da177e4SLinus Torvalds sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 6331da177e4SLinus Torvalds return snd_ctl_find_id(card, &sid); 6341da177e4SLinus Torvalds } 6351da177e4SLinus Torvalds 636e4a3d145STakashi Iwai static int __devinit rename_ctl(struct snd_card *card, const char *src, const char *dst) 6371da177e4SLinus Torvalds { 638e4a3d145STakashi Iwai struct snd_kcontrol *kctl = ctl_find(card, src); 6391da177e4SLinus Torvalds if (kctl) { 6401da177e4SLinus Torvalds strcpy(kctl->id.name, dst); 6411da177e4SLinus Torvalds return 0; 6421da177e4SLinus Torvalds } 6431da177e4SLinus Torvalds return -ENOENT; 6441da177e4SLinus Torvalds } 6451da177e4SLinus Torvalds 646fca7f388STrent Piepho #define ADD_CTLS(emu, ctls) \ 647fca7f388STrent Piepho do { \ 648fca7f388STrent Piepho int i, err; \ 649fca7f388STrent Piepho for (i = 0; i < ARRAY_SIZE(ctls); i++) { \ 650fca7f388STrent Piepho err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \ 651fca7f388STrent Piepho if (err < 0) \ 652fca7f388STrent Piepho return err; \ 653fca7f388STrent Piepho } \ 654fca7f388STrent Piepho } while (0) 655fca7f388STrent Piepho 656e4a3d145STakashi Iwai int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) 6571da177e4SLinus Torvalds { 658fca7f388STrent Piepho int err; 659e4a3d145STakashi Iwai struct snd_card *card = emu->card; 6601da177e4SLinus Torvalds char **c; 6611da177e4SLinus Torvalds static char *ca0106_remove_ctls[] = { 6621da177e4SLinus Torvalds "Master Mono Playback Switch", 6631da177e4SLinus Torvalds "Master Mono Playback Volume", 6641da177e4SLinus Torvalds "3D Control - Switch", 6651da177e4SLinus Torvalds "3D Control Sigmatel - Depth", 6661da177e4SLinus Torvalds "PCM Playback Switch", 6671da177e4SLinus Torvalds "PCM Playback Volume", 6681da177e4SLinus Torvalds "CD Playback Switch", 6691da177e4SLinus Torvalds "CD Playback Volume", 6701da177e4SLinus Torvalds "Phone Playback Switch", 6711da177e4SLinus Torvalds "Phone Playback Volume", 6721da177e4SLinus Torvalds "Video Playback Switch", 6731da177e4SLinus Torvalds "Video Playback Volume", 6741da177e4SLinus Torvalds "PC Speaker Playback Switch", 6751da177e4SLinus Torvalds "PC Speaker Playback Volume", 6761da177e4SLinus Torvalds "Mono Output Select", 6771da177e4SLinus Torvalds "Capture Source", 6781da177e4SLinus Torvalds "Capture Switch", 6791da177e4SLinus Torvalds "Capture Volume", 6801da177e4SLinus Torvalds "External Amplifier", 6811da177e4SLinus Torvalds "Sigmatel 4-Speaker Stereo Playback Switch", 6821da177e4SLinus Torvalds "Sigmatel Surround Phase Inversion Playback ", 6831da177e4SLinus Torvalds NULL 6841da177e4SLinus Torvalds }; 6851da177e4SLinus Torvalds static char *ca0106_rename_ctls[] = { 6861da177e4SLinus Torvalds "Master Playback Switch", "Capture Switch", 6871da177e4SLinus Torvalds "Master Playback Volume", "Capture Volume", 6881da177e4SLinus Torvalds "Line Playback Switch", "AC97 Line Capture Switch", 6891da177e4SLinus Torvalds "Line Playback Volume", "AC97 Line Capture Volume", 6901da177e4SLinus Torvalds "Aux Playback Switch", "AC97 Aux Capture Switch", 6911da177e4SLinus Torvalds "Aux Playback Volume", "AC97 Aux Capture Volume", 6921da177e4SLinus Torvalds "Mic Playback Switch", "AC97 Mic Capture Switch", 6931da177e4SLinus Torvalds "Mic Playback Volume", "AC97 Mic Capture Volume", 6941da177e4SLinus Torvalds "Mic Select", "AC97 Mic Select", 6951da177e4SLinus Torvalds "Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)", 6961da177e4SLinus Torvalds NULL 6971da177e4SLinus Torvalds }; 6981da177e4SLinus Torvalds #if 1 6991da177e4SLinus Torvalds for (c = ca0106_remove_ctls; *c; c++) 7001da177e4SLinus Torvalds remove_ctl(card, *c); 7011da177e4SLinus Torvalds for (c = ca0106_rename_ctls; *c; c += 2) 7021da177e4SLinus Torvalds rename_ctl(card, c[0], c[1]); 7031da177e4SLinus Torvalds #endif 7041da177e4SLinus Torvalds 705fca7f388STrent Piepho ADD_CTLS(emu, snd_ca0106_volume_ctls); 70695a98265STakashi Iwai if (emu->details->i2c_adc == 1) { 707fca7f388STrent Piepho ADD_CTLS(emu, snd_ca0106_volume_i2c_adc_ctls); 708be0b7b01SJames Courtier-Dutton if (emu->details->gpio_type == 1) 70995a98265STakashi Iwai err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu)); 710be0b7b01SJames Courtier-Dutton else /* gpio_type == 2 */ 711be0b7b01SJames Courtier-Dutton err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu)); 71295a98265STakashi Iwai if (err < 0) 7131da177e4SLinus Torvalds return err; 71495a98265STakashi Iwai } 715fca7f388STrent Piepho if (emu->details->spi_dac == 1) 716fca7f388STrent Piepho ADD_CTLS(emu, snd_ca0106_volume_spi_dac_ctls); 7171da177e4SLinus Torvalds return 0; 7181da177e4SLinus Torvalds } 7191da177e4SLinus Torvalds 720