1*1da177e4SLinus Torvalds /* 2*1da177e4SLinus Torvalds * Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk> 3*1da177e4SLinus Torvalds * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit 4*1da177e4SLinus Torvalds * Version: 0.0.16 5*1da177e4SLinus Torvalds * 6*1da177e4SLinus Torvalds * FEATURES currently supported: 7*1da177e4SLinus Torvalds * See ca0106_main.c for features. 8*1da177e4SLinus Torvalds * 9*1da177e4SLinus Torvalds * Changelog: 10*1da177e4SLinus Torvalds * Support interrupts per period. 11*1da177e4SLinus Torvalds * Removed noise from Center/LFE channel when in Analog mode. 12*1da177e4SLinus Torvalds * Rename and remove mixer controls. 13*1da177e4SLinus Torvalds * 0.0.6 14*1da177e4SLinus Torvalds * Use separate card based DMA buffer for periods table list. 15*1da177e4SLinus Torvalds * 0.0.7 16*1da177e4SLinus Torvalds * Change remove and rename ctrls into lists. 17*1da177e4SLinus Torvalds * 0.0.8 18*1da177e4SLinus Torvalds * Try to fix capture sources. 19*1da177e4SLinus Torvalds * 0.0.9 20*1da177e4SLinus Torvalds * Fix AC3 output. 21*1da177e4SLinus Torvalds * Enable S32_LE format support. 22*1da177e4SLinus Torvalds * 0.0.10 23*1da177e4SLinus Torvalds * Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".) 24*1da177e4SLinus Torvalds * 0.0.11 25*1da177e4SLinus Torvalds * Add Model name recognition. 26*1da177e4SLinus Torvalds * 0.0.12 27*1da177e4SLinus Torvalds * Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period. 28*1da177e4SLinus Torvalds * Remove redundent "voice" handling. 29*1da177e4SLinus Torvalds * 0.0.13 30*1da177e4SLinus Torvalds * Single trigger call for multi channels. 31*1da177e4SLinus Torvalds * 0.0.14 32*1da177e4SLinus Torvalds * Set limits based on what the sound card hardware can do. 33*1da177e4SLinus Torvalds * playback periods_min=2, periods_max=8 34*1da177e4SLinus Torvalds * capture hw constraints require period_size = n * 64 bytes. 35*1da177e4SLinus Torvalds * playback hw constraints require period_size = n * 64 bytes. 36*1da177e4SLinus Torvalds * 0.0.15 37*1da177e4SLinus Torvalds * Separated ca0106.c into separate functional .c files. 38*1da177e4SLinus Torvalds * 0.0.16 39*1da177e4SLinus Torvalds * Modified Copyright message. 40*1da177e4SLinus Torvalds * 41*1da177e4SLinus Torvalds * This code was initally based on code from ALSA's emu10k1x.c which is: 42*1da177e4SLinus Torvalds * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com> 43*1da177e4SLinus Torvalds * 44*1da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 45*1da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 46*1da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 47*1da177e4SLinus Torvalds * (at your option) any later version. 48*1da177e4SLinus Torvalds * 49*1da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 50*1da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 51*1da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 52*1da177e4SLinus Torvalds * GNU General Public License for more details. 53*1da177e4SLinus Torvalds * 54*1da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 55*1da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 56*1da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 57*1da177e4SLinus Torvalds * 58*1da177e4SLinus Torvalds */ 59*1da177e4SLinus Torvalds #include <sound/driver.h> 60*1da177e4SLinus Torvalds #include <linux/delay.h> 61*1da177e4SLinus Torvalds #include <linux/init.h> 62*1da177e4SLinus Torvalds #include <linux/interrupt.h> 63*1da177e4SLinus Torvalds #include <linux/pci.h> 64*1da177e4SLinus Torvalds #include <linux/slab.h> 65*1da177e4SLinus Torvalds #include <linux/moduleparam.h> 66*1da177e4SLinus Torvalds #include <sound/core.h> 67*1da177e4SLinus Torvalds #include <sound/initval.h> 68*1da177e4SLinus Torvalds #include <sound/pcm.h> 69*1da177e4SLinus Torvalds #include <sound/ac97_codec.h> 70*1da177e4SLinus Torvalds #include <sound/info.h> 71*1da177e4SLinus Torvalds 72*1da177e4SLinus Torvalds #include "ca0106.h" 73*1da177e4SLinus Torvalds 74*1da177e4SLinus Torvalds static int snd_ca0106_shared_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) 75*1da177e4SLinus Torvalds { 76*1da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 77*1da177e4SLinus Torvalds uinfo->count = 1; 78*1da177e4SLinus Torvalds uinfo->value.integer.min = 0; 79*1da177e4SLinus Torvalds uinfo->value.integer.max = 1; 80*1da177e4SLinus Torvalds return 0; 81*1da177e4SLinus Torvalds } 82*1da177e4SLinus Torvalds 83*1da177e4SLinus Torvalds static int snd_ca0106_shared_spdif_get(snd_kcontrol_t * kcontrol, 84*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 85*1da177e4SLinus Torvalds { 86*1da177e4SLinus Torvalds ca0106_t *emu = snd_kcontrol_chip(kcontrol); 87*1da177e4SLinus Torvalds 88*1da177e4SLinus Torvalds ucontrol->value.enumerated.item[0] = emu->spdif_enable; 89*1da177e4SLinus Torvalds return 0; 90*1da177e4SLinus Torvalds } 91*1da177e4SLinus Torvalds 92*1da177e4SLinus Torvalds static int snd_ca0106_shared_spdif_put(snd_kcontrol_t * kcontrol, 93*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 94*1da177e4SLinus Torvalds { 95*1da177e4SLinus Torvalds ca0106_t *emu = snd_kcontrol_chip(kcontrol); 96*1da177e4SLinus Torvalds unsigned int val; 97*1da177e4SLinus Torvalds int change = 0; 98*1da177e4SLinus Torvalds u32 mask; 99*1da177e4SLinus Torvalds 100*1da177e4SLinus Torvalds val = ucontrol->value.enumerated.item[0] ; 101*1da177e4SLinus Torvalds change = (emu->spdif_enable != val); 102*1da177e4SLinus Torvalds if (change) { 103*1da177e4SLinus Torvalds emu->spdif_enable = val; 104*1da177e4SLinus Torvalds if (val == 1) { 105*1da177e4SLinus Torvalds /* Digital */ 106*1da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); 107*1da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000); 108*1da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, 109*1da177e4SLinus Torvalds snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000); 110*1da177e4SLinus Torvalds mask = inl(emu->port + GPIO) & ~0x101; 111*1da177e4SLinus Torvalds outl(mask, emu->port + GPIO); 112*1da177e4SLinus Torvalds 113*1da177e4SLinus Torvalds } else { 114*1da177e4SLinus Torvalds /* Analog */ 115*1da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); 116*1da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000b0000); 117*1da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, 118*1da177e4SLinus Torvalds snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000); 119*1da177e4SLinus Torvalds mask = inl(emu->port + GPIO) | 0x101; 120*1da177e4SLinus Torvalds outl(mask, emu->port + GPIO); 121*1da177e4SLinus Torvalds } 122*1da177e4SLinus Torvalds } 123*1da177e4SLinus Torvalds return change; 124*1da177e4SLinus Torvalds } 125*1da177e4SLinus Torvalds 126*1da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_shared_spdif __devinitdata = 127*1da177e4SLinus Torvalds { 128*1da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 129*1da177e4SLinus Torvalds .name = "SPDIF Out", 130*1da177e4SLinus Torvalds .info = snd_ca0106_shared_spdif_info, 131*1da177e4SLinus Torvalds .get = snd_ca0106_shared_spdif_get, 132*1da177e4SLinus Torvalds .put = snd_ca0106_shared_spdif_put 133*1da177e4SLinus Torvalds }; 134*1da177e4SLinus Torvalds 135*1da177e4SLinus Torvalds static int snd_ca0106_capture_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) 136*1da177e4SLinus Torvalds { 137*1da177e4SLinus Torvalds static char *texts[6] = { "SPDIF out", "i2s mixer out", "SPDIF in", "i2s in", "AC97 in", "SRC out" }; 138*1da177e4SLinus Torvalds 139*1da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 140*1da177e4SLinus Torvalds uinfo->count = 1; 141*1da177e4SLinus Torvalds uinfo->value.enumerated.items = 6; 142*1da177e4SLinus Torvalds if (uinfo->value.enumerated.item > 5) 143*1da177e4SLinus Torvalds uinfo->value.enumerated.item = 5; 144*1da177e4SLinus Torvalds strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); 145*1da177e4SLinus Torvalds return 0; 146*1da177e4SLinus Torvalds } 147*1da177e4SLinus Torvalds 148*1da177e4SLinus Torvalds static int snd_ca0106_capture_source_get(snd_kcontrol_t * kcontrol, 149*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 150*1da177e4SLinus Torvalds { 151*1da177e4SLinus Torvalds ca0106_t *emu = snd_kcontrol_chip(kcontrol); 152*1da177e4SLinus Torvalds 153*1da177e4SLinus Torvalds ucontrol->value.enumerated.item[0] = emu->capture_source; 154*1da177e4SLinus Torvalds return 0; 155*1da177e4SLinus Torvalds } 156*1da177e4SLinus Torvalds 157*1da177e4SLinus Torvalds static int snd_ca0106_capture_source_put(snd_kcontrol_t * kcontrol, 158*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 159*1da177e4SLinus Torvalds { 160*1da177e4SLinus Torvalds ca0106_t *emu = snd_kcontrol_chip(kcontrol); 161*1da177e4SLinus Torvalds unsigned int val; 162*1da177e4SLinus Torvalds int change = 0; 163*1da177e4SLinus Torvalds u32 mask; 164*1da177e4SLinus Torvalds u32 source; 165*1da177e4SLinus Torvalds 166*1da177e4SLinus Torvalds val = ucontrol->value.enumerated.item[0] ; 167*1da177e4SLinus Torvalds change = (emu->capture_source != val); 168*1da177e4SLinus Torvalds if (change) { 169*1da177e4SLinus Torvalds emu->capture_source = val; 170*1da177e4SLinus Torvalds source = (val << 28) | (val << 24) | (val << 20) | (val << 16); 171*1da177e4SLinus Torvalds mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff; 172*1da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask); 173*1da177e4SLinus Torvalds } 174*1da177e4SLinus Torvalds return change; 175*1da177e4SLinus Torvalds } 176*1da177e4SLinus Torvalds 177*1da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_capture_source __devinitdata = 178*1da177e4SLinus Torvalds { 179*1da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 180*1da177e4SLinus Torvalds .name = "Capture Source", 181*1da177e4SLinus Torvalds .info = snd_ca0106_capture_source_info, 182*1da177e4SLinus Torvalds .get = snd_ca0106_capture_source_get, 183*1da177e4SLinus Torvalds .put = snd_ca0106_capture_source_put 184*1da177e4SLinus Torvalds }; 185*1da177e4SLinus Torvalds 186*1da177e4SLinus Torvalds static int snd_ca0106_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) 187*1da177e4SLinus Torvalds { 188*1da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 189*1da177e4SLinus Torvalds uinfo->count = 1; 190*1da177e4SLinus Torvalds return 0; 191*1da177e4SLinus Torvalds } 192*1da177e4SLinus Torvalds 193*1da177e4SLinus Torvalds static int snd_ca0106_spdif_get(snd_kcontrol_t * kcontrol, 194*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 195*1da177e4SLinus Torvalds { 196*1da177e4SLinus Torvalds ca0106_t *emu = snd_kcontrol_chip(kcontrol); 197*1da177e4SLinus Torvalds unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 198*1da177e4SLinus Torvalds 199*1da177e4SLinus Torvalds ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff; 200*1da177e4SLinus Torvalds ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff; 201*1da177e4SLinus Torvalds ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff; 202*1da177e4SLinus Torvalds ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff; 203*1da177e4SLinus Torvalds return 0; 204*1da177e4SLinus Torvalds } 205*1da177e4SLinus Torvalds 206*1da177e4SLinus Torvalds static int snd_ca0106_spdif_get_mask(snd_kcontrol_t * kcontrol, 207*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 208*1da177e4SLinus Torvalds { 209*1da177e4SLinus Torvalds ucontrol->value.iec958.status[0] = 0xff; 210*1da177e4SLinus Torvalds ucontrol->value.iec958.status[1] = 0xff; 211*1da177e4SLinus Torvalds ucontrol->value.iec958.status[2] = 0xff; 212*1da177e4SLinus Torvalds ucontrol->value.iec958.status[3] = 0xff; 213*1da177e4SLinus Torvalds return 0; 214*1da177e4SLinus Torvalds } 215*1da177e4SLinus Torvalds 216*1da177e4SLinus Torvalds static int snd_ca0106_spdif_put(snd_kcontrol_t * kcontrol, 217*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 218*1da177e4SLinus Torvalds { 219*1da177e4SLinus Torvalds ca0106_t *emu = snd_kcontrol_chip(kcontrol); 220*1da177e4SLinus Torvalds unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 221*1da177e4SLinus Torvalds int change; 222*1da177e4SLinus Torvalds unsigned int val; 223*1da177e4SLinus Torvalds 224*1da177e4SLinus Torvalds val = (ucontrol->value.iec958.status[0] << 0) | 225*1da177e4SLinus Torvalds (ucontrol->value.iec958.status[1] << 8) | 226*1da177e4SLinus Torvalds (ucontrol->value.iec958.status[2] << 16) | 227*1da177e4SLinus Torvalds (ucontrol->value.iec958.status[3] << 24); 228*1da177e4SLinus Torvalds change = val != emu->spdif_bits[idx]; 229*1da177e4SLinus Torvalds if (change) { 230*1da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, val); 231*1da177e4SLinus Torvalds emu->spdif_bits[idx] = val; 232*1da177e4SLinus Torvalds } 233*1da177e4SLinus Torvalds return change; 234*1da177e4SLinus Torvalds } 235*1da177e4SLinus Torvalds 236*1da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_spdif_mask_control = 237*1da177e4SLinus Torvalds { 238*1da177e4SLinus Torvalds .access = SNDRV_CTL_ELEM_ACCESS_READ, 239*1da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 240*1da177e4SLinus Torvalds .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), 241*1da177e4SLinus Torvalds .count = 4, 242*1da177e4SLinus Torvalds .info = snd_ca0106_spdif_info, 243*1da177e4SLinus Torvalds .get = snd_ca0106_spdif_get_mask 244*1da177e4SLinus Torvalds }; 245*1da177e4SLinus Torvalds 246*1da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_spdif_control = 247*1da177e4SLinus Torvalds { 248*1da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 249*1da177e4SLinus Torvalds .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), 250*1da177e4SLinus Torvalds .count = 4, 251*1da177e4SLinus Torvalds .info = snd_ca0106_spdif_info, 252*1da177e4SLinus Torvalds .get = snd_ca0106_spdif_get, 253*1da177e4SLinus Torvalds .put = snd_ca0106_spdif_put 254*1da177e4SLinus Torvalds }; 255*1da177e4SLinus Torvalds 256*1da177e4SLinus Torvalds static int snd_ca0106_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) 257*1da177e4SLinus Torvalds { 258*1da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 259*1da177e4SLinus Torvalds uinfo->count = 2; 260*1da177e4SLinus Torvalds uinfo->value.integer.min = 0; 261*1da177e4SLinus Torvalds uinfo->value.integer.max = 255; 262*1da177e4SLinus Torvalds return 0; 263*1da177e4SLinus Torvalds } 264*1da177e4SLinus Torvalds 265*1da177e4SLinus Torvalds static int snd_ca0106_volume_get(snd_kcontrol_t * kcontrol, 266*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol, int reg, int channel_id) 267*1da177e4SLinus Torvalds { 268*1da177e4SLinus Torvalds ca0106_t *emu = snd_kcontrol_chip(kcontrol); 269*1da177e4SLinus Torvalds unsigned int value; 270*1da177e4SLinus Torvalds 271*1da177e4SLinus Torvalds value = snd_ca0106_ptr_read(emu, reg, channel_id); 272*1da177e4SLinus Torvalds ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */ 273*1da177e4SLinus Torvalds ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */ 274*1da177e4SLinus Torvalds return 0; 275*1da177e4SLinus Torvalds } 276*1da177e4SLinus Torvalds 277*1da177e4SLinus Torvalds static int snd_ca0106_volume_get_spdif_front(snd_kcontrol_t * kcontrol, 278*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 279*1da177e4SLinus Torvalds { 280*1da177e4SLinus Torvalds int channel_id = CONTROL_FRONT_CHANNEL; 281*1da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME1; 282*1da177e4SLinus Torvalds return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id); 283*1da177e4SLinus Torvalds } 284*1da177e4SLinus Torvalds 285*1da177e4SLinus Torvalds static int snd_ca0106_volume_get_spdif_center_lfe(snd_kcontrol_t * kcontrol, 286*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 287*1da177e4SLinus Torvalds { 288*1da177e4SLinus Torvalds int channel_id = CONTROL_CENTER_LFE_CHANNEL; 289*1da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME1; 290*1da177e4SLinus Torvalds return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id); 291*1da177e4SLinus Torvalds } 292*1da177e4SLinus Torvalds static int snd_ca0106_volume_get_spdif_unknown(snd_kcontrol_t * kcontrol, 293*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 294*1da177e4SLinus Torvalds { 295*1da177e4SLinus Torvalds int channel_id = CONTROL_UNKNOWN_CHANNEL; 296*1da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME1; 297*1da177e4SLinus Torvalds return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id); 298*1da177e4SLinus Torvalds } 299*1da177e4SLinus Torvalds static int snd_ca0106_volume_get_spdif_rear(snd_kcontrol_t * kcontrol, 300*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 301*1da177e4SLinus Torvalds { 302*1da177e4SLinus Torvalds int channel_id = CONTROL_REAR_CHANNEL; 303*1da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME1; 304*1da177e4SLinus Torvalds return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id); 305*1da177e4SLinus Torvalds } 306*1da177e4SLinus Torvalds static int snd_ca0106_volume_get_analog_front(snd_kcontrol_t * kcontrol, 307*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 308*1da177e4SLinus Torvalds { 309*1da177e4SLinus Torvalds int channel_id = CONTROL_FRONT_CHANNEL; 310*1da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME2; 311*1da177e4SLinus Torvalds return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id); 312*1da177e4SLinus Torvalds } 313*1da177e4SLinus Torvalds 314*1da177e4SLinus Torvalds static int snd_ca0106_volume_get_analog_center_lfe(snd_kcontrol_t * kcontrol, 315*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 316*1da177e4SLinus Torvalds { 317*1da177e4SLinus Torvalds int channel_id = CONTROL_CENTER_LFE_CHANNEL; 318*1da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME2; 319*1da177e4SLinus Torvalds return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id); 320*1da177e4SLinus Torvalds } 321*1da177e4SLinus Torvalds static int snd_ca0106_volume_get_analog_unknown(snd_kcontrol_t * kcontrol, 322*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 323*1da177e4SLinus Torvalds { 324*1da177e4SLinus Torvalds int channel_id = CONTROL_UNKNOWN_CHANNEL; 325*1da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME2; 326*1da177e4SLinus Torvalds return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id); 327*1da177e4SLinus Torvalds } 328*1da177e4SLinus Torvalds static int snd_ca0106_volume_get_analog_rear(snd_kcontrol_t * kcontrol, 329*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 330*1da177e4SLinus Torvalds { 331*1da177e4SLinus Torvalds int channel_id = CONTROL_REAR_CHANNEL; 332*1da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME2; 333*1da177e4SLinus Torvalds return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id); 334*1da177e4SLinus Torvalds } 335*1da177e4SLinus Torvalds 336*1da177e4SLinus Torvalds static int snd_ca0106_volume_get_feedback(snd_kcontrol_t * kcontrol, 337*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 338*1da177e4SLinus Torvalds { 339*1da177e4SLinus Torvalds int channel_id = 1; 340*1da177e4SLinus Torvalds int reg = CAPTURE_CONTROL; 341*1da177e4SLinus Torvalds return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id); 342*1da177e4SLinus Torvalds } 343*1da177e4SLinus Torvalds 344*1da177e4SLinus Torvalds static int snd_ca0106_volume_put(snd_kcontrol_t * kcontrol, 345*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol, int reg, int channel_id) 346*1da177e4SLinus Torvalds { 347*1da177e4SLinus Torvalds ca0106_t *emu = snd_kcontrol_chip(kcontrol); 348*1da177e4SLinus Torvalds unsigned int value; 349*1da177e4SLinus Torvalds //value = snd_ca0106_ptr_read(emu, reg, channel_id); 350*1da177e4SLinus Torvalds //value = value & 0xffff; 351*1da177e4SLinus Torvalds value = ((0xff - ucontrol->value.integer.value[0]) << 24) | ((0xff - ucontrol->value.integer.value[1]) << 16); 352*1da177e4SLinus Torvalds value = value | ((0xff - ucontrol->value.integer.value[0]) << 8) | ((0xff - ucontrol->value.integer.value[1]) ); 353*1da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, reg, channel_id, value); 354*1da177e4SLinus Torvalds return 1; 355*1da177e4SLinus Torvalds } 356*1da177e4SLinus Torvalds static int snd_ca0106_volume_put_spdif_front(snd_kcontrol_t * kcontrol, 357*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 358*1da177e4SLinus Torvalds { 359*1da177e4SLinus Torvalds int channel_id = CONTROL_FRONT_CHANNEL; 360*1da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME1; 361*1da177e4SLinus Torvalds return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id); 362*1da177e4SLinus Torvalds } 363*1da177e4SLinus Torvalds static int snd_ca0106_volume_put_spdif_center_lfe(snd_kcontrol_t * kcontrol, 364*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 365*1da177e4SLinus Torvalds { 366*1da177e4SLinus Torvalds int channel_id = CONTROL_CENTER_LFE_CHANNEL; 367*1da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME1; 368*1da177e4SLinus Torvalds return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id); 369*1da177e4SLinus Torvalds } 370*1da177e4SLinus Torvalds static int snd_ca0106_volume_put_spdif_unknown(snd_kcontrol_t * kcontrol, 371*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 372*1da177e4SLinus Torvalds { 373*1da177e4SLinus Torvalds int channel_id = CONTROL_UNKNOWN_CHANNEL; 374*1da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME1; 375*1da177e4SLinus Torvalds return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id); 376*1da177e4SLinus Torvalds } 377*1da177e4SLinus Torvalds static int snd_ca0106_volume_put_spdif_rear(snd_kcontrol_t * kcontrol, 378*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 379*1da177e4SLinus Torvalds { 380*1da177e4SLinus Torvalds int channel_id = CONTROL_REAR_CHANNEL; 381*1da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME1; 382*1da177e4SLinus Torvalds return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id); 383*1da177e4SLinus Torvalds } 384*1da177e4SLinus Torvalds static int snd_ca0106_volume_put_analog_front(snd_kcontrol_t * kcontrol, 385*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 386*1da177e4SLinus Torvalds { 387*1da177e4SLinus Torvalds int channel_id = CONTROL_FRONT_CHANNEL; 388*1da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME2; 389*1da177e4SLinus Torvalds return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id); 390*1da177e4SLinus Torvalds } 391*1da177e4SLinus Torvalds static int snd_ca0106_volume_put_analog_center_lfe(snd_kcontrol_t * kcontrol, 392*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 393*1da177e4SLinus Torvalds { 394*1da177e4SLinus Torvalds int channel_id = CONTROL_CENTER_LFE_CHANNEL; 395*1da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME2; 396*1da177e4SLinus Torvalds return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id); 397*1da177e4SLinus Torvalds } 398*1da177e4SLinus Torvalds static int snd_ca0106_volume_put_analog_unknown(snd_kcontrol_t * kcontrol, 399*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 400*1da177e4SLinus Torvalds { 401*1da177e4SLinus Torvalds int channel_id = CONTROL_UNKNOWN_CHANNEL; 402*1da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME2; 403*1da177e4SLinus Torvalds return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id); 404*1da177e4SLinus Torvalds } 405*1da177e4SLinus Torvalds static int snd_ca0106_volume_put_analog_rear(snd_kcontrol_t * kcontrol, 406*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 407*1da177e4SLinus Torvalds { 408*1da177e4SLinus Torvalds int channel_id = CONTROL_REAR_CHANNEL; 409*1da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME2; 410*1da177e4SLinus Torvalds return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id); 411*1da177e4SLinus Torvalds } 412*1da177e4SLinus Torvalds 413*1da177e4SLinus Torvalds static int snd_ca0106_volume_put_feedback(snd_kcontrol_t * kcontrol, 414*1da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 415*1da177e4SLinus Torvalds { 416*1da177e4SLinus Torvalds int channel_id = 1; 417*1da177e4SLinus Torvalds int reg = CAPTURE_CONTROL; 418*1da177e4SLinus Torvalds return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id); 419*1da177e4SLinus Torvalds } 420*1da177e4SLinus Torvalds 421*1da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_volume_control_analog_front = 422*1da177e4SLinus Torvalds { 423*1da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 424*1da177e4SLinus Torvalds .name = "Analog Front Volume", 425*1da177e4SLinus Torvalds .info = snd_ca0106_volume_info, 426*1da177e4SLinus Torvalds .get = snd_ca0106_volume_get_analog_front, 427*1da177e4SLinus Torvalds .put = snd_ca0106_volume_put_analog_front 428*1da177e4SLinus Torvalds }; 429*1da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_volume_control_analog_center_lfe = 430*1da177e4SLinus Torvalds { 431*1da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 432*1da177e4SLinus Torvalds .name = "Analog Center/LFE Volume", 433*1da177e4SLinus Torvalds .info = snd_ca0106_volume_info, 434*1da177e4SLinus Torvalds .get = snd_ca0106_volume_get_analog_center_lfe, 435*1da177e4SLinus Torvalds .put = snd_ca0106_volume_put_analog_center_lfe 436*1da177e4SLinus Torvalds }; 437*1da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_volume_control_analog_unknown = 438*1da177e4SLinus Torvalds { 439*1da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 440*1da177e4SLinus Torvalds .name = "Analog Unknown Volume", 441*1da177e4SLinus Torvalds .info = snd_ca0106_volume_info, 442*1da177e4SLinus Torvalds .get = snd_ca0106_volume_get_analog_unknown, 443*1da177e4SLinus Torvalds .put = snd_ca0106_volume_put_analog_unknown 444*1da177e4SLinus Torvalds }; 445*1da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_volume_control_analog_rear = 446*1da177e4SLinus Torvalds { 447*1da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 448*1da177e4SLinus Torvalds .name = "Analog Rear Volume", 449*1da177e4SLinus Torvalds .info = snd_ca0106_volume_info, 450*1da177e4SLinus Torvalds .get = snd_ca0106_volume_get_analog_rear, 451*1da177e4SLinus Torvalds .put = snd_ca0106_volume_put_analog_rear 452*1da177e4SLinus Torvalds }; 453*1da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_front = 454*1da177e4SLinus Torvalds { 455*1da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 456*1da177e4SLinus Torvalds .name = "SPDIF Front Volume", 457*1da177e4SLinus Torvalds .info = snd_ca0106_volume_info, 458*1da177e4SLinus Torvalds .get = snd_ca0106_volume_get_spdif_front, 459*1da177e4SLinus Torvalds .put = snd_ca0106_volume_put_spdif_front 460*1da177e4SLinus Torvalds }; 461*1da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_center_lfe = 462*1da177e4SLinus Torvalds { 463*1da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 464*1da177e4SLinus Torvalds .name = "SPDIF Center/LFE Volume", 465*1da177e4SLinus Torvalds .info = snd_ca0106_volume_info, 466*1da177e4SLinus Torvalds .get = snd_ca0106_volume_get_spdif_center_lfe, 467*1da177e4SLinus Torvalds .put = snd_ca0106_volume_put_spdif_center_lfe 468*1da177e4SLinus Torvalds }; 469*1da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_unknown = 470*1da177e4SLinus Torvalds { 471*1da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 472*1da177e4SLinus Torvalds .name = "SPDIF Unknown Volume", 473*1da177e4SLinus Torvalds .info = snd_ca0106_volume_info, 474*1da177e4SLinus Torvalds .get = snd_ca0106_volume_get_spdif_unknown, 475*1da177e4SLinus Torvalds .put = snd_ca0106_volume_put_spdif_unknown 476*1da177e4SLinus Torvalds }; 477*1da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_rear = 478*1da177e4SLinus Torvalds { 479*1da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 480*1da177e4SLinus Torvalds .name = "SPDIF Rear Volume", 481*1da177e4SLinus Torvalds .info = snd_ca0106_volume_info, 482*1da177e4SLinus Torvalds .get = snd_ca0106_volume_get_spdif_rear, 483*1da177e4SLinus Torvalds .put = snd_ca0106_volume_put_spdif_rear 484*1da177e4SLinus Torvalds }; 485*1da177e4SLinus Torvalds 486*1da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_volume_control_feedback = 487*1da177e4SLinus Torvalds { 488*1da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 489*1da177e4SLinus Torvalds .name = "CAPTURE feedback into PLAYBACK", 490*1da177e4SLinus Torvalds .info = snd_ca0106_volume_info, 491*1da177e4SLinus Torvalds .get = snd_ca0106_volume_get_feedback, 492*1da177e4SLinus Torvalds .put = snd_ca0106_volume_put_feedback 493*1da177e4SLinus Torvalds }; 494*1da177e4SLinus Torvalds 495*1da177e4SLinus Torvalds 496*1da177e4SLinus Torvalds static int remove_ctl(snd_card_t *card, const char *name) 497*1da177e4SLinus Torvalds { 498*1da177e4SLinus Torvalds snd_ctl_elem_id_t id; 499*1da177e4SLinus Torvalds memset(&id, 0, sizeof(id)); 500*1da177e4SLinus Torvalds strcpy(id.name, name); 501*1da177e4SLinus Torvalds id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 502*1da177e4SLinus Torvalds return snd_ctl_remove_id(card, &id); 503*1da177e4SLinus Torvalds } 504*1da177e4SLinus Torvalds 505*1da177e4SLinus Torvalds static snd_kcontrol_t *ctl_find(snd_card_t *card, const char *name) 506*1da177e4SLinus Torvalds { 507*1da177e4SLinus Torvalds snd_ctl_elem_id_t sid; 508*1da177e4SLinus Torvalds memset(&sid, 0, sizeof(sid)); 509*1da177e4SLinus Torvalds /* FIXME: strcpy is bad. */ 510*1da177e4SLinus Torvalds strcpy(sid.name, name); 511*1da177e4SLinus Torvalds sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 512*1da177e4SLinus Torvalds return snd_ctl_find_id(card, &sid); 513*1da177e4SLinus Torvalds } 514*1da177e4SLinus Torvalds 515*1da177e4SLinus Torvalds static int rename_ctl(snd_card_t *card, const char *src, const char *dst) 516*1da177e4SLinus Torvalds { 517*1da177e4SLinus Torvalds snd_kcontrol_t *kctl = ctl_find(card, src); 518*1da177e4SLinus Torvalds if (kctl) { 519*1da177e4SLinus Torvalds strcpy(kctl->id.name, dst); 520*1da177e4SLinus Torvalds return 0; 521*1da177e4SLinus Torvalds } 522*1da177e4SLinus Torvalds return -ENOENT; 523*1da177e4SLinus Torvalds } 524*1da177e4SLinus Torvalds 525*1da177e4SLinus Torvalds int __devinit snd_ca0106_mixer(ca0106_t *emu) 526*1da177e4SLinus Torvalds { 527*1da177e4SLinus Torvalds int err; 528*1da177e4SLinus Torvalds snd_kcontrol_t *kctl; 529*1da177e4SLinus Torvalds snd_card_t *card = emu->card; 530*1da177e4SLinus Torvalds char **c; 531*1da177e4SLinus Torvalds static char *ca0106_remove_ctls[] = { 532*1da177e4SLinus Torvalds "Master Mono Playback Switch", 533*1da177e4SLinus Torvalds "Master Mono Playback Volume", 534*1da177e4SLinus Torvalds "3D Control - Switch", 535*1da177e4SLinus Torvalds "3D Control Sigmatel - Depth", 536*1da177e4SLinus Torvalds "PCM Playback Switch", 537*1da177e4SLinus Torvalds "PCM Playback Volume", 538*1da177e4SLinus Torvalds "CD Playback Switch", 539*1da177e4SLinus Torvalds "CD Playback Volume", 540*1da177e4SLinus Torvalds "Phone Playback Switch", 541*1da177e4SLinus Torvalds "Phone Playback Volume", 542*1da177e4SLinus Torvalds "Video Playback Switch", 543*1da177e4SLinus Torvalds "Video Playback Volume", 544*1da177e4SLinus Torvalds "PC Speaker Playback Switch", 545*1da177e4SLinus Torvalds "PC Speaker Playback Volume", 546*1da177e4SLinus Torvalds "Mono Output Select", 547*1da177e4SLinus Torvalds "Capture Source", 548*1da177e4SLinus Torvalds "Capture Switch", 549*1da177e4SLinus Torvalds "Capture Volume", 550*1da177e4SLinus Torvalds "External Amplifier", 551*1da177e4SLinus Torvalds "Sigmatel 4-Speaker Stereo Playback Switch", 552*1da177e4SLinus Torvalds "Sigmatel Surround Phase Inversion Playback ", 553*1da177e4SLinus Torvalds NULL 554*1da177e4SLinus Torvalds }; 555*1da177e4SLinus Torvalds static char *ca0106_rename_ctls[] = { 556*1da177e4SLinus Torvalds "Master Playback Switch", "Capture Switch", 557*1da177e4SLinus Torvalds "Master Playback Volume", "Capture Volume", 558*1da177e4SLinus Torvalds "Line Playback Switch", "AC97 Line Capture Switch", 559*1da177e4SLinus Torvalds "Line Playback Volume", "AC97 Line Capture Volume", 560*1da177e4SLinus Torvalds "Aux Playback Switch", "AC97 Aux Capture Switch", 561*1da177e4SLinus Torvalds "Aux Playback Volume", "AC97 Aux Capture Volume", 562*1da177e4SLinus Torvalds "Mic Playback Switch", "AC97 Mic Capture Switch", 563*1da177e4SLinus Torvalds "Mic Playback Volume", "AC97 Mic Capture Volume", 564*1da177e4SLinus Torvalds "Mic Select", "AC97 Mic Select", 565*1da177e4SLinus Torvalds "Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)", 566*1da177e4SLinus Torvalds NULL 567*1da177e4SLinus Torvalds }; 568*1da177e4SLinus Torvalds #if 1 569*1da177e4SLinus Torvalds for (c=ca0106_remove_ctls; *c; c++) 570*1da177e4SLinus Torvalds remove_ctl(card, *c); 571*1da177e4SLinus Torvalds for (c=ca0106_rename_ctls; *c; c += 2) 572*1da177e4SLinus Torvalds rename_ctl(card, c[0], c[1]); 573*1da177e4SLinus Torvalds #endif 574*1da177e4SLinus Torvalds 575*1da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_front, emu)) == NULL) 576*1da177e4SLinus Torvalds return -ENOMEM; 577*1da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 578*1da177e4SLinus Torvalds return err; 579*1da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_rear, emu)) == NULL) 580*1da177e4SLinus Torvalds return -ENOMEM; 581*1da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 582*1da177e4SLinus Torvalds return err; 583*1da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_center_lfe, emu)) == NULL) 584*1da177e4SLinus Torvalds return -ENOMEM; 585*1da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 586*1da177e4SLinus Torvalds return err; 587*1da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_unknown, emu)) == NULL) 588*1da177e4SLinus Torvalds return -ENOMEM; 589*1da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 590*1da177e4SLinus Torvalds return err; 591*1da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_front, emu)) == NULL) 592*1da177e4SLinus Torvalds return -ENOMEM; 593*1da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 594*1da177e4SLinus Torvalds return err; 595*1da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_rear, emu)) == NULL) 596*1da177e4SLinus Torvalds return -ENOMEM; 597*1da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 598*1da177e4SLinus Torvalds return err; 599*1da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_center_lfe, emu)) == NULL) 600*1da177e4SLinus Torvalds return -ENOMEM; 601*1da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 602*1da177e4SLinus Torvalds return err; 603*1da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_unknown, emu)) == NULL) 604*1da177e4SLinus Torvalds return -ENOMEM; 605*1da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 606*1da177e4SLinus Torvalds return err; 607*1da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_feedback, emu)) == NULL) 608*1da177e4SLinus Torvalds return -ENOMEM; 609*1da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 610*1da177e4SLinus Torvalds return err; 611*1da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_spdif_mask_control, emu)) == NULL) 612*1da177e4SLinus Torvalds return -ENOMEM; 613*1da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 614*1da177e4SLinus Torvalds return err; 615*1da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_shared_spdif, emu)) == NULL) 616*1da177e4SLinus Torvalds return -ENOMEM; 617*1da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 618*1da177e4SLinus Torvalds return err; 619*1da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_capture_source, emu)) == NULL) 620*1da177e4SLinus Torvalds return -ENOMEM; 621*1da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 622*1da177e4SLinus Torvalds return err; 623*1da177e4SLinus Torvalds if ((kctl = ctl_find(card, SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT))) != NULL) { 624*1da177e4SLinus Torvalds /* already defined by ac97, remove it */ 625*1da177e4SLinus Torvalds /* FIXME: or do we need both controls? */ 626*1da177e4SLinus Torvalds remove_ctl(card, SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT)); 627*1da177e4SLinus Torvalds } 628*1da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_spdif_control, emu)) == NULL) 629*1da177e4SLinus Torvalds return -ENOMEM; 630*1da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 631*1da177e4SLinus Torvalds return err; 632*1da177e4SLinus Torvalds return 0; 633*1da177e4SLinus Torvalds } 634*1da177e4SLinus Torvalds 635