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 41da177e4SLinus Torvalds * Version: 0.0.16 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. 401da177e4SLinus Torvalds * 411da177e4SLinus Torvalds * This code was initally based on code from ALSA's emu10k1x.c which is: 421da177e4SLinus Torvalds * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com> 431da177e4SLinus Torvalds * 441da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 451da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 461da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 471da177e4SLinus Torvalds * (at your option) any later version. 481da177e4SLinus Torvalds * 491da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 501da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 511da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 521da177e4SLinus Torvalds * GNU General Public License for more details. 531da177e4SLinus Torvalds * 541da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 551da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 561da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 571da177e4SLinus Torvalds * 581da177e4SLinus Torvalds */ 591da177e4SLinus Torvalds #include <sound/driver.h> 601da177e4SLinus Torvalds #include <linux/delay.h> 611da177e4SLinus Torvalds #include <linux/init.h> 621da177e4SLinus Torvalds #include <linux/interrupt.h> 631da177e4SLinus Torvalds #include <linux/pci.h> 641da177e4SLinus Torvalds #include <linux/slab.h> 651da177e4SLinus Torvalds #include <linux/moduleparam.h> 661da177e4SLinus Torvalds #include <sound/core.h> 671da177e4SLinus Torvalds #include <sound/initval.h> 681da177e4SLinus Torvalds #include <sound/pcm.h> 691da177e4SLinus Torvalds #include <sound/ac97_codec.h> 701da177e4SLinus Torvalds #include <sound/info.h> 711da177e4SLinus Torvalds 721da177e4SLinus Torvalds #include "ca0106.h" 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds static int snd_ca0106_shared_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) 751da177e4SLinus Torvalds { 761da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 771da177e4SLinus Torvalds uinfo->count = 1; 781da177e4SLinus Torvalds uinfo->value.integer.min = 0; 791da177e4SLinus Torvalds uinfo->value.integer.max = 1; 801da177e4SLinus Torvalds return 0; 811da177e4SLinus Torvalds } 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds static int snd_ca0106_shared_spdif_get(snd_kcontrol_t * kcontrol, 841da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 851da177e4SLinus Torvalds { 861da177e4SLinus Torvalds ca0106_t *emu = snd_kcontrol_chip(kcontrol); 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds ucontrol->value.enumerated.item[0] = emu->spdif_enable; 891da177e4SLinus Torvalds return 0; 901da177e4SLinus Torvalds } 911da177e4SLinus Torvalds 921da177e4SLinus Torvalds static int snd_ca0106_shared_spdif_put(snd_kcontrol_t * kcontrol, 931da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 941da177e4SLinus Torvalds { 951da177e4SLinus Torvalds ca0106_t *emu = snd_kcontrol_chip(kcontrol); 961da177e4SLinus Torvalds unsigned int val; 971da177e4SLinus Torvalds int change = 0; 981da177e4SLinus Torvalds u32 mask; 991da177e4SLinus Torvalds 1001da177e4SLinus Torvalds val = ucontrol->value.enumerated.item[0] ; 1011da177e4SLinus Torvalds change = (emu->spdif_enable != val); 1021da177e4SLinus Torvalds if (change) { 1031da177e4SLinus Torvalds emu->spdif_enable = val; 1041da177e4SLinus Torvalds if (val == 1) { 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); 116*1f82941eSJames 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 1261da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_shared_spdif __devinitdata = 1271da177e4SLinus Torvalds { 1281da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1291da177e4SLinus Torvalds .name = "SPDIF Out", 1301da177e4SLinus Torvalds .info = snd_ca0106_shared_spdif_info, 1311da177e4SLinus Torvalds .get = snd_ca0106_shared_spdif_get, 1321da177e4SLinus Torvalds .put = snd_ca0106_shared_spdif_put 1331da177e4SLinus Torvalds }; 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds static int snd_ca0106_capture_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) 1361da177e4SLinus Torvalds { 1371da177e4SLinus Torvalds static char *texts[6] = { "SPDIF out", "i2s mixer out", "SPDIF in", "i2s in", "AC97 in", "SRC out" }; 1381da177e4SLinus Torvalds 1391da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 1401da177e4SLinus Torvalds uinfo->count = 1; 1411da177e4SLinus Torvalds uinfo->value.enumerated.items = 6; 1421da177e4SLinus Torvalds if (uinfo->value.enumerated.item > 5) 1431da177e4SLinus Torvalds uinfo->value.enumerated.item = 5; 1441da177e4SLinus Torvalds strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); 1451da177e4SLinus Torvalds return 0; 1461da177e4SLinus Torvalds } 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds static int snd_ca0106_capture_source_get(snd_kcontrol_t * kcontrol, 1491da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 1501da177e4SLinus Torvalds { 1511da177e4SLinus Torvalds ca0106_t *emu = snd_kcontrol_chip(kcontrol); 1521da177e4SLinus Torvalds 1531da177e4SLinus Torvalds ucontrol->value.enumerated.item[0] = emu->capture_source; 1541da177e4SLinus Torvalds return 0; 1551da177e4SLinus Torvalds } 1561da177e4SLinus Torvalds 1571da177e4SLinus Torvalds static int snd_ca0106_capture_source_put(snd_kcontrol_t * kcontrol, 1581da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 1591da177e4SLinus Torvalds { 1601da177e4SLinus Torvalds ca0106_t *emu = snd_kcontrol_chip(kcontrol); 1611da177e4SLinus Torvalds unsigned int val; 1621da177e4SLinus Torvalds int change = 0; 1631da177e4SLinus Torvalds u32 mask; 1641da177e4SLinus Torvalds u32 source; 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds val = ucontrol->value.enumerated.item[0] ; 1671da177e4SLinus Torvalds change = (emu->capture_source != val); 1681da177e4SLinus Torvalds if (change) { 1691da177e4SLinus Torvalds emu->capture_source = val; 1701da177e4SLinus Torvalds source = (val << 28) | (val << 24) | (val << 20) | (val << 16); 1711da177e4SLinus Torvalds mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff; 1721da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask); 1731da177e4SLinus Torvalds } 1741da177e4SLinus Torvalds return change; 1751da177e4SLinus Torvalds } 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_capture_source __devinitdata = 1781da177e4SLinus Torvalds { 1791da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1801da177e4SLinus Torvalds .name = "Capture Source", 1811da177e4SLinus Torvalds .info = snd_ca0106_capture_source_info, 1821da177e4SLinus Torvalds .get = snd_ca0106_capture_source_get, 1831da177e4SLinus Torvalds .put = snd_ca0106_capture_source_put 1841da177e4SLinus Torvalds }; 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds static int snd_ca0106_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) 1871da177e4SLinus Torvalds { 1881da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 1891da177e4SLinus Torvalds uinfo->count = 1; 1901da177e4SLinus Torvalds return 0; 1911da177e4SLinus Torvalds } 1921da177e4SLinus Torvalds 1931da177e4SLinus Torvalds static int snd_ca0106_spdif_get(snd_kcontrol_t * kcontrol, 1941da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 1951da177e4SLinus Torvalds { 1961da177e4SLinus Torvalds ca0106_t *emu = snd_kcontrol_chip(kcontrol); 1971da177e4SLinus Torvalds unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 1981da177e4SLinus Torvalds 1991da177e4SLinus Torvalds ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff; 2001da177e4SLinus Torvalds ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff; 2011da177e4SLinus Torvalds ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff; 2021da177e4SLinus Torvalds ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff; 2031da177e4SLinus Torvalds return 0; 2041da177e4SLinus Torvalds } 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds static int snd_ca0106_spdif_get_mask(snd_kcontrol_t * kcontrol, 2071da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 2081da177e4SLinus Torvalds { 2091da177e4SLinus Torvalds ucontrol->value.iec958.status[0] = 0xff; 2101da177e4SLinus Torvalds ucontrol->value.iec958.status[1] = 0xff; 2111da177e4SLinus Torvalds ucontrol->value.iec958.status[2] = 0xff; 2121da177e4SLinus Torvalds ucontrol->value.iec958.status[3] = 0xff; 2131da177e4SLinus Torvalds return 0; 2141da177e4SLinus Torvalds } 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds static int snd_ca0106_spdif_put(snd_kcontrol_t * kcontrol, 2171da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 2181da177e4SLinus Torvalds { 2191da177e4SLinus Torvalds ca0106_t *emu = snd_kcontrol_chip(kcontrol); 2201da177e4SLinus Torvalds unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 2211da177e4SLinus Torvalds int change; 2221da177e4SLinus Torvalds unsigned int val; 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds val = (ucontrol->value.iec958.status[0] << 0) | 2251da177e4SLinus Torvalds (ucontrol->value.iec958.status[1] << 8) | 2261da177e4SLinus Torvalds (ucontrol->value.iec958.status[2] << 16) | 2271da177e4SLinus Torvalds (ucontrol->value.iec958.status[3] << 24); 2281da177e4SLinus Torvalds change = val != emu->spdif_bits[idx]; 2291da177e4SLinus Torvalds if (change) { 2301da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, val); 2311da177e4SLinus Torvalds emu->spdif_bits[idx] = val; 2321da177e4SLinus Torvalds } 2331da177e4SLinus Torvalds return change; 2341da177e4SLinus Torvalds } 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_spdif_mask_control = 2371da177e4SLinus Torvalds { 2381da177e4SLinus Torvalds .access = SNDRV_CTL_ELEM_ACCESS_READ, 2391da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2401da177e4SLinus Torvalds .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), 2411da177e4SLinus Torvalds .count = 4, 2421da177e4SLinus Torvalds .info = snd_ca0106_spdif_info, 2431da177e4SLinus Torvalds .get = snd_ca0106_spdif_get_mask 2441da177e4SLinus Torvalds }; 2451da177e4SLinus Torvalds 2461da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_spdif_control = 2471da177e4SLinus Torvalds { 2481da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2491da177e4SLinus Torvalds .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), 2501da177e4SLinus Torvalds .count = 4, 2511da177e4SLinus Torvalds .info = snd_ca0106_spdif_info, 2521da177e4SLinus Torvalds .get = snd_ca0106_spdif_get, 2531da177e4SLinus Torvalds .put = snd_ca0106_spdif_put 2541da177e4SLinus Torvalds }; 2551da177e4SLinus Torvalds 2561da177e4SLinus Torvalds static int snd_ca0106_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) 2571da177e4SLinus Torvalds { 2581da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 2591da177e4SLinus Torvalds uinfo->count = 2; 2601da177e4SLinus Torvalds uinfo->value.integer.min = 0; 2611da177e4SLinus Torvalds uinfo->value.integer.max = 255; 2621da177e4SLinus Torvalds return 0; 2631da177e4SLinus Torvalds } 2641da177e4SLinus Torvalds 2651da177e4SLinus Torvalds static int snd_ca0106_volume_get(snd_kcontrol_t * kcontrol, 2661da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol, int reg, int channel_id) 2671da177e4SLinus Torvalds { 2681da177e4SLinus Torvalds ca0106_t *emu = snd_kcontrol_chip(kcontrol); 2691da177e4SLinus Torvalds unsigned int value; 2701da177e4SLinus Torvalds 2711da177e4SLinus Torvalds value = snd_ca0106_ptr_read(emu, reg, channel_id); 2721da177e4SLinus Torvalds ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */ 2731da177e4SLinus Torvalds ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */ 2741da177e4SLinus Torvalds return 0; 2751da177e4SLinus Torvalds } 2761da177e4SLinus Torvalds 2771da177e4SLinus Torvalds static int snd_ca0106_volume_get_spdif_front(snd_kcontrol_t * kcontrol, 2781da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 2791da177e4SLinus Torvalds { 2801da177e4SLinus Torvalds int channel_id = CONTROL_FRONT_CHANNEL; 2811da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME1; 2821da177e4SLinus Torvalds return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id); 2831da177e4SLinus Torvalds } 2841da177e4SLinus Torvalds 2851da177e4SLinus Torvalds static int snd_ca0106_volume_get_spdif_center_lfe(snd_kcontrol_t * kcontrol, 2861da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 2871da177e4SLinus Torvalds { 2881da177e4SLinus Torvalds int channel_id = CONTROL_CENTER_LFE_CHANNEL; 2891da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME1; 2901da177e4SLinus Torvalds return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id); 2911da177e4SLinus Torvalds } 2921da177e4SLinus Torvalds static int snd_ca0106_volume_get_spdif_unknown(snd_kcontrol_t * kcontrol, 2931da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 2941da177e4SLinus Torvalds { 2951da177e4SLinus Torvalds int channel_id = CONTROL_UNKNOWN_CHANNEL; 2961da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME1; 2971da177e4SLinus Torvalds return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id); 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds static int snd_ca0106_volume_get_spdif_rear(snd_kcontrol_t * kcontrol, 3001da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 3011da177e4SLinus Torvalds { 3021da177e4SLinus Torvalds int channel_id = CONTROL_REAR_CHANNEL; 3031da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME1; 3041da177e4SLinus Torvalds return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id); 3051da177e4SLinus Torvalds } 3061da177e4SLinus Torvalds static int snd_ca0106_volume_get_analog_front(snd_kcontrol_t * kcontrol, 3071da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 3081da177e4SLinus Torvalds { 3091da177e4SLinus Torvalds int channel_id = CONTROL_FRONT_CHANNEL; 3101da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME2; 3111da177e4SLinus Torvalds return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id); 3121da177e4SLinus Torvalds } 3131da177e4SLinus Torvalds 3141da177e4SLinus Torvalds static int snd_ca0106_volume_get_analog_center_lfe(snd_kcontrol_t * kcontrol, 3151da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 3161da177e4SLinus Torvalds { 3171da177e4SLinus Torvalds int channel_id = CONTROL_CENTER_LFE_CHANNEL; 3181da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME2; 3191da177e4SLinus Torvalds return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id); 3201da177e4SLinus Torvalds } 3211da177e4SLinus Torvalds static int snd_ca0106_volume_get_analog_unknown(snd_kcontrol_t * kcontrol, 3221da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 3231da177e4SLinus Torvalds { 3241da177e4SLinus Torvalds int channel_id = CONTROL_UNKNOWN_CHANNEL; 3251da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME2; 3261da177e4SLinus Torvalds return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id); 3271da177e4SLinus Torvalds } 3281da177e4SLinus Torvalds static int snd_ca0106_volume_get_analog_rear(snd_kcontrol_t * kcontrol, 3291da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 3301da177e4SLinus Torvalds { 3311da177e4SLinus Torvalds int channel_id = CONTROL_REAR_CHANNEL; 3321da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME2; 3331da177e4SLinus Torvalds return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id); 3341da177e4SLinus Torvalds } 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds static int snd_ca0106_volume_get_feedback(snd_kcontrol_t * kcontrol, 3371da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 3381da177e4SLinus Torvalds { 3391da177e4SLinus Torvalds int channel_id = 1; 3401da177e4SLinus Torvalds int reg = CAPTURE_CONTROL; 3411da177e4SLinus Torvalds return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id); 3421da177e4SLinus Torvalds } 3431da177e4SLinus Torvalds 3441da177e4SLinus Torvalds static int snd_ca0106_volume_put(snd_kcontrol_t * kcontrol, 3451da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol, int reg, int channel_id) 3461da177e4SLinus Torvalds { 3471da177e4SLinus Torvalds ca0106_t *emu = snd_kcontrol_chip(kcontrol); 3481da177e4SLinus Torvalds unsigned int value; 3491da177e4SLinus Torvalds //value = snd_ca0106_ptr_read(emu, reg, channel_id); 3501da177e4SLinus Torvalds //value = value & 0xffff; 3511da177e4SLinus Torvalds value = ((0xff - ucontrol->value.integer.value[0]) << 24) | ((0xff - ucontrol->value.integer.value[1]) << 16); 3521da177e4SLinus Torvalds value = value | ((0xff - ucontrol->value.integer.value[0]) << 8) | ((0xff - ucontrol->value.integer.value[1]) ); 3531da177e4SLinus Torvalds snd_ca0106_ptr_write(emu, reg, channel_id, value); 3541da177e4SLinus Torvalds return 1; 3551da177e4SLinus Torvalds } 3561da177e4SLinus Torvalds static int snd_ca0106_volume_put_spdif_front(snd_kcontrol_t * kcontrol, 3571da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 3581da177e4SLinus Torvalds { 3591da177e4SLinus Torvalds int channel_id = CONTROL_FRONT_CHANNEL; 3601da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME1; 3611da177e4SLinus Torvalds return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id); 3621da177e4SLinus Torvalds } 3631da177e4SLinus Torvalds static int snd_ca0106_volume_put_spdif_center_lfe(snd_kcontrol_t * kcontrol, 3641da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 3651da177e4SLinus Torvalds { 3661da177e4SLinus Torvalds int channel_id = CONTROL_CENTER_LFE_CHANNEL; 3671da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME1; 3681da177e4SLinus Torvalds return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id); 3691da177e4SLinus Torvalds } 3701da177e4SLinus Torvalds static int snd_ca0106_volume_put_spdif_unknown(snd_kcontrol_t * kcontrol, 3711da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 3721da177e4SLinus Torvalds { 3731da177e4SLinus Torvalds int channel_id = CONTROL_UNKNOWN_CHANNEL; 3741da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME1; 3751da177e4SLinus Torvalds return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id); 3761da177e4SLinus Torvalds } 3771da177e4SLinus Torvalds static int snd_ca0106_volume_put_spdif_rear(snd_kcontrol_t * kcontrol, 3781da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 3791da177e4SLinus Torvalds { 3801da177e4SLinus Torvalds int channel_id = CONTROL_REAR_CHANNEL; 3811da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME1; 3821da177e4SLinus Torvalds return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id); 3831da177e4SLinus Torvalds } 3841da177e4SLinus Torvalds static int snd_ca0106_volume_put_analog_front(snd_kcontrol_t * kcontrol, 3851da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 3861da177e4SLinus Torvalds { 3871da177e4SLinus Torvalds int channel_id = CONTROL_FRONT_CHANNEL; 3881da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME2; 3891da177e4SLinus Torvalds return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id); 3901da177e4SLinus Torvalds } 3911da177e4SLinus Torvalds static int snd_ca0106_volume_put_analog_center_lfe(snd_kcontrol_t * kcontrol, 3921da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 3931da177e4SLinus Torvalds { 3941da177e4SLinus Torvalds int channel_id = CONTROL_CENTER_LFE_CHANNEL; 3951da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME2; 3961da177e4SLinus Torvalds return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id); 3971da177e4SLinus Torvalds } 3981da177e4SLinus Torvalds static int snd_ca0106_volume_put_analog_unknown(snd_kcontrol_t * kcontrol, 3991da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 4001da177e4SLinus Torvalds { 4011da177e4SLinus Torvalds int channel_id = CONTROL_UNKNOWN_CHANNEL; 4021da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME2; 4031da177e4SLinus Torvalds return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id); 4041da177e4SLinus Torvalds } 4051da177e4SLinus Torvalds static int snd_ca0106_volume_put_analog_rear(snd_kcontrol_t * kcontrol, 4061da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 4071da177e4SLinus Torvalds { 4081da177e4SLinus Torvalds int channel_id = CONTROL_REAR_CHANNEL; 4091da177e4SLinus Torvalds int reg = PLAYBACK_VOLUME2; 4101da177e4SLinus Torvalds return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id); 4111da177e4SLinus Torvalds } 4121da177e4SLinus Torvalds 4131da177e4SLinus Torvalds static int snd_ca0106_volume_put_feedback(snd_kcontrol_t * kcontrol, 4141da177e4SLinus Torvalds snd_ctl_elem_value_t * ucontrol) 4151da177e4SLinus Torvalds { 4161da177e4SLinus Torvalds int channel_id = 1; 4171da177e4SLinus Torvalds int reg = CAPTURE_CONTROL; 4181da177e4SLinus Torvalds return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id); 4191da177e4SLinus Torvalds } 4201da177e4SLinus Torvalds 4211da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_volume_control_analog_front = 4221da177e4SLinus Torvalds { 4231da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4241da177e4SLinus Torvalds .name = "Analog Front Volume", 4251da177e4SLinus Torvalds .info = snd_ca0106_volume_info, 4261da177e4SLinus Torvalds .get = snd_ca0106_volume_get_analog_front, 4271da177e4SLinus Torvalds .put = snd_ca0106_volume_put_analog_front 4281da177e4SLinus Torvalds }; 4291da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_volume_control_analog_center_lfe = 4301da177e4SLinus Torvalds { 4311da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4321da177e4SLinus Torvalds .name = "Analog Center/LFE Volume", 4331da177e4SLinus Torvalds .info = snd_ca0106_volume_info, 4341da177e4SLinus Torvalds .get = snd_ca0106_volume_get_analog_center_lfe, 4351da177e4SLinus Torvalds .put = snd_ca0106_volume_put_analog_center_lfe 4361da177e4SLinus Torvalds }; 4371da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_volume_control_analog_unknown = 4381da177e4SLinus Torvalds { 4391da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 440*1f82941eSJames Courtier-Dutton .name = "Analog Side Volume", 4411da177e4SLinus Torvalds .info = snd_ca0106_volume_info, 4421da177e4SLinus Torvalds .get = snd_ca0106_volume_get_analog_unknown, 4431da177e4SLinus Torvalds .put = snd_ca0106_volume_put_analog_unknown 4441da177e4SLinus Torvalds }; 4451da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_volume_control_analog_rear = 4461da177e4SLinus Torvalds { 4471da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4481da177e4SLinus Torvalds .name = "Analog Rear Volume", 4491da177e4SLinus Torvalds .info = snd_ca0106_volume_info, 4501da177e4SLinus Torvalds .get = snd_ca0106_volume_get_analog_rear, 4511da177e4SLinus Torvalds .put = snd_ca0106_volume_put_analog_rear 4521da177e4SLinus Torvalds }; 4531da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_front = 4541da177e4SLinus Torvalds { 4551da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4561da177e4SLinus Torvalds .name = "SPDIF Front Volume", 4571da177e4SLinus Torvalds .info = snd_ca0106_volume_info, 4581da177e4SLinus Torvalds .get = snd_ca0106_volume_get_spdif_front, 4591da177e4SLinus Torvalds .put = snd_ca0106_volume_put_spdif_front 4601da177e4SLinus Torvalds }; 4611da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_center_lfe = 4621da177e4SLinus Torvalds { 4631da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4641da177e4SLinus Torvalds .name = "SPDIF Center/LFE Volume", 4651da177e4SLinus Torvalds .info = snd_ca0106_volume_info, 4661da177e4SLinus Torvalds .get = snd_ca0106_volume_get_spdif_center_lfe, 4671da177e4SLinus Torvalds .put = snd_ca0106_volume_put_spdif_center_lfe 4681da177e4SLinus Torvalds }; 4691da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_unknown = 4701da177e4SLinus Torvalds { 4711da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4721da177e4SLinus Torvalds .name = "SPDIF Unknown Volume", 4731da177e4SLinus Torvalds .info = snd_ca0106_volume_info, 4741da177e4SLinus Torvalds .get = snd_ca0106_volume_get_spdif_unknown, 4751da177e4SLinus Torvalds .put = snd_ca0106_volume_put_spdif_unknown 4761da177e4SLinus Torvalds }; 4771da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_rear = 4781da177e4SLinus Torvalds { 4791da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4801da177e4SLinus Torvalds .name = "SPDIF Rear Volume", 4811da177e4SLinus Torvalds .info = snd_ca0106_volume_info, 4821da177e4SLinus Torvalds .get = snd_ca0106_volume_get_spdif_rear, 4831da177e4SLinus Torvalds .put = snd_ca0106_volume_put_spdif_rear 4841da177e4SLinus Torvalds }; 4851da177e4SLinus Torvalds 4861da177e4SLinus Torvalds static snd_kcontrol_new_t snd_ca0106_volume_control_feedback = 4871da177e4SLinus Torvalds { 4881da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4891da177e4SLinus Torvalds .name = "CAPTURE feedback into PLAYBACK", 4901da177e4SLinus Torvalds .info = snd_ca0106_volume_info, 4911da177e4SLinus Torvalds .get = snd_ca0106_volume_get_feedback, 4921da177e4SLinus Torvalds .put = snd_ca0106_volume_put_feedback 4931da177e4SLinus Torvalds }; 4941da177e4SLinus Torvalds 4951da177e4SLinus Torvalds 4961da177e4SLinus Torvalds static int remove_ctl(snd_card_t *card, const char *name) 4971da177e4SLinus Torvalds { 4981da177e4SLinus Torvalds snd_ctl_elem_id_t id; 4991da177e4SLinus Torvalds memset(&id, 0, sizeof(id)); 5001da177e4SLinus Torvalds strcpy(id.name, name); 5011da177e4SLinus Torvalds id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 5021da177e4SLinus Torvalds return snd_ctl_remove_id(card, &id); 5031da177e4SLinus Torvalds } 5041da177e4SLinus Torvalds 5051da177e4SLinus Torvalds static snd_kcontrol_t *ctl_find(snd_card_t *card, const char *name) 5061da177e4SLinus Torvalds { 5071da177e4SLinus Torvalds snd_ctl_elem_id_t sid; 5081da177e4SLinus Torvalds memset(&sid, 0, sizeof(sid)); 5091da177e4SLinus Torvalds /* FIXME: strcpy is bad. */ 5101da177e4SLinus Torvalds strcpy(sid.name, name); 5111da177e4SLinus Torvalds sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 5121da177e4SLinus Torvalds return snd_ctl_find_id(card, &sid); 5131da177e4SLinus Torvalds } 5141da177e4SLinus Torvalds 5151da177e4SLinus Torvalds static int rename_ctl(snd_card_t *card, const char *src, const char *dst) 5161da177e4SLinus Torvalds { 5171da177e4SLinus Torvalds snd_kcontrol_t *kctl = ctl_find(card, src); 5181da177e4SLinus Torvalds if (kctl) { 5191da177e4SLinus Torvalds strcpy(kctl->id.name, dst); 5201da177e4SLinus Torvalds return 0; 5211da177e4SLinus Torvalds } 5221da177e4SLinus Torvalds return -ENOENT; 5231da177e4SLinus Torvalds } 5241da177e4SLinus Torvalds 5251da177e4SLinus Torvalds int __devinit snd_ca0106_mixer(ca0106_t *emu) 5261da177e4SLinus Torvalds { 5271da177e4SLinus Torvalds int err; 5281da177e4SLinus Torvalds snd_kcontrol_t *kctl; 5291da177e4SLinus Torvalds snd_card_t *card = emu->card; 5301da177e4SLinus Torvalds char **c; 5311da177e4SLinus Torvalds static char *ca0106_remove_ctls[] = { 5321da177e4SLinus Torvalds "Master Mono Playback Switch", 5331da177e4SLinus Torvalds "Master Mono Playback Volume", 5341da177e4SLinus Torvalds "3D Control - Switch", 5351da177e4SLinus Torvalds "3D Control Sigmatel - Depth", 5361da177e4SLinus Torvalds "PCM Playback Switch", 5371da177e4SLinus Torvalds "PCM Playback Volume", 5381da177e4SLinus Torvalds "CD Playback Switch", 5391da177e4SLinus Torvalds "CD Playback Volume", 5401da177e4SLinus Torvalds "Phone Playback Switch", 5411da177e4SLinus Torvalds "Phone Playback Volume", 5421da177e4SLinus Torvalds "Video Playback Switch", 5431da177e4SLinus Torvalds "Video Playback Volume", 5441da177e4SLinus Torvalds "PC Speaker Playback Switch", 5451da177e4SLinus Torvalds "PC Speaker Playback Volume", 5461da177e4SLinus Torvalds "Mono Output Select", 5471da177e4SLinus Torvalds "Capture Source", 5481da177e4SLinus Torvalds "Capture Switch", 5491da177e4SLinus Torvalds "Capture Volume", 5501da177e4SLinus Torvalds "External Amplifier", 5511da177e4SLinus Torvalds "Sigmatel 4-Speaker Stereo Playback Switch", 5521da177e4SLinus Torvalds "Sigmatel Surround Phase Inversion Playback ", 5531da177e4SLinus Torvalds NULL 5541da177e4SLinus Torvalds }; 5551da177e4SLinus Torvalds static char *ca0106_rename_ctls[] = { 5561da177e4SLinus Torvalds "Master Playback Switch", "Capture Switch", 5571da177e4SLinus Torvalds "Master Playback Volume", "Capture Volume", 5581da177e4SLinus Torvalds "Line Playback Switch", "AC97 Line Capture Switch", 5591da177e4SLinus Torvalds "Line Playback Volume", "AC97 Line Capture Volume", 5601da177e4SLinus Torvalds "Aux Playback Switch", "AC97 Aux Capture Switch", 5611da177e4SLinus Torvalds "Aux Playback Volume", "AC97 Aux Capture Volume", 5621da177e4SLinus Torvalds "Mic Playback Switch", "AC97 Mic Capture Switch", 5631da177e4SLinus Torvalds "Mic Playback Volume", "AC97 Mic Capture Volume", 5641da177e4SLinus Torvalds "Mic Select", "AC97 Mic Select", 5651da177e4SLinus Torvalds "Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)", 5661da177e4SLinus Torvalds NULL 5671da177e4SLinus Torvalds }; 5681da177e4SLinus Torvalds #if 1 5691da177e4SLinus Torvalds for (c=ca0106_remove_ctls; *c; c++) 5701da177e4SLinus Torvalds remove_ctl(card, *c); 5711da177e4SLinus Torvalds for (c=ca0106_rename_ctls; *c; c += 2) 5721da177e4SLinus Torvalds rename_ctl(card, c[0], c[1]); 5731da177e4SLinus Torvalds #endif 5741da177e4SLinus Torvalds 5751da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_front, emu)) == NULL) 5761da177e4SLinus Torvalds return -ENOMEM; 5771da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 5781da177e4SLinus Torvalds return err; 5791da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_rear, emu)) == NULL) 5801da177e4SLinus Torvalds return -ENOMEM; 5811da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 5821da177e4SLinus Torvalds return err; 5831da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_center_lfe, emu)) == NULL) 5841da177e4SLinus Torvalds return -ENOMEM; 5851da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 5861da177e4SLinus Torvalds return err; 5871da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_unknown, emu)) == NULL) 5881da177e4SLinus Torvalds return -ENOMEM; 5891da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 5901da177e4SLinus Torvalds return err; 5911da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_front, emu)) == NULL) 5921da177e4SLinus Torvalds return -ENOMEM; 5931da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 5941da177e4SLinus Torvalds return err; 5951da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_rear, emu)) == NULL) 5961da177e4SLinus Torvalds return -ENOMEM; 5971da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 5981da177e4SLinus Torvalds return err; 5991da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_center_lfe, emu)) == NULL) 6001da177e4SLinus Torvalds return -ENOMEM; 6011da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 6021da177e4SLinus Torvalds return err; 6031da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_unknown, emu)) == NULL) 6041da177e4SLinus Torvalds return -ENOMEM; 6051da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 6061da177e4SLinus Torvalds return err; 6071da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_feedback, emu)) == NULL) 6081da177e4SLinus Torvalds return -ENOMEM; 6091da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 6101da177e4SLinus Torvalds return err; 6111da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_spdif_mask_control, emu)) == NULL) 6121da177e4SLinus Torvalds return -ENOMEM; 6131da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 6141da177e4SLinus Torvalds return err; 6151da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_shared_spdif, emu)) == NULL) 6161da177e4SLinus Torvalds return -ENOMEM; 6171da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 6181da177e4SLinus Torvalds return err; 6191da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_capture_source, emu)) == NULL) 6201da177e4SLinus Torvalds return -ENOMEM; 6211da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 6221da177e4SLinus Torvalds return err; 6231da177e4SLinus Torvalds if ((kctl = snd_ctl_new1(&snd_ca0106_spdif_control, emu)) == NULL) 6241da177e4SLinus Torvalds return -ENOMEM; 6251da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl))) 6261da177e4SLinus Torvalds return err; 6271da177e4SLinus Torvalds return 0; 6281da177e4SLinus Torvalds } 6291da177e4SLinus Torvalds 630