blob: cccc32cdb943029ff4beee1b938cd11dace16c66 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
3 * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
Trent Piephob18cd532007-07-24 12:06:16 +02004 * Version: 0.0.18
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 *
6 * FEATURES currently supported:
7 * See ca0106_main.c for features.
8 *
9 * Changelog:
10 * Support interrupts per period.
11 * Removed noise from Center/LFE channel when in Analog mode.
12 * Rename and remove mixer controls.
13 * 0.0.6
14 * Use separate card based DMA buffer for periods table list.
15 * 0.0.7
16 * Change remove and rename ctrls into lists.
17 * 0.0.8
18 * Try to fix capture sources.
19 * 0.0.9
20 * Fix AC3 output.
21 * Enable S32_LE format support.
22 * 0.0.10
23 * Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
24 * 0.0.11
25 * Add Model name recognition.
26 * 0.0.12
27 * Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
28 * Remove redundent "voice" handling.
29 * 0.0.13
30 * Single trigger call for multi channels.
31 * 0.0.14
32 * Set limits based on what the sound card hardware can do.
33 * playback periods_min=2, periods_max=8
34 * capture hw constraints require period_size = n * 64 bytes.
35 * playback hw constraints require period_size = n * 64 bytes.
36 * 0.0.15
37 * Separated ca0106.c into separate functional .c files.
38 * 0.0.16
39 * Modified Copyright message.
James Courtier-Duttoned144f32005-05-27 23:28:27 +020040 * 0.0.17
41 * Implement Mic and Line in Capture.
Trent Piephob18cd532007-07-24 12:06:16 +020042 * 0.0.18
43 * Add support for mute control on SB Live 24bit (cards w/ SPI DAC)
Linus Torvalds1da177e2005-04-16 15:20:36 -070044 *
45 * This code was initally based on code from ALSA's emu10k1x.c which is:
46 * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
47 *
48 * This program is free software; you can redistribute it and/or modify
49 * it under the terms of the GNU General Public License as published by
50 * the Free Software Foundation; either version 2 of the License, or
51 * (at your option) any later version.
52 *
53 * This program is distributed in the hope that it will be useful,
54 * but WITHOUT ANY WARRANTY; without even the implied warranty of
55 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
56 * GNU General Public License for more details.
57 *
58 * You should have received a copy of the GNU General Public License
59 * along with this program; if not, write to the Free Software
60 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
61 *
62 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070063#include <linux/delay.h>
64#include <linux/init.h>
65#include <linux/interrupt.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070066#include <linux/slab.h>
67#include <linux/moduleparam.h>
68#include <sound/core.h>
69#include <sound/initval.h>
70#include <sound/pcm.h>
71#include <sound/ac97_codec.h>
72#include <sound/info.h>
Jaroslav Kysela42750b02006-06-01 18:34:01 +020073#include <sound/tlv.h>
Jean Delvare6473d162007-03-06 02:45:12 -080074#include <asm/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070075
76#include "ca0106.h"
77
Takashi Iwai5da95272008-11-24 14:06:08 +010078static void ca0106_spdif_enable(struct snd_ca0106 *emu)
79{
80 unsigned int val;
81
82 if (emu->spdif_enable) {
83 /* Digital */
84 snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
85 snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
86 val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000;
87 snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
88 val = inl(emu->port + GPIO) & ~0x101;
89 outl(val, emu->port + GPIO);
90
91 } else {
92 /* Analog */
93 snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
94 snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
95 val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000;
96 snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
97 val = inl(emu->port + GPIO) | 0x101;
98 outl(val, emu->port + GPIO);
99 }
100}
101
102static void ca0106_set_capture_source(struct snd_ca0106 *emu)
103{
104 unsigned int val = emu->capture_source;
105 unsigned int source, mask;
106 source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
107 mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
108 snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
109}
110
111static void ca0106_set_i2c_capture_source(struct snd_ca0106 *emu,
112 unsigned int val, int force)
113{
114 unsigned int ngain, ogain;
115 u32 source;
116
117 snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
118 ngain = emu->i2c_capture_volume[val][0]; /* Left */
119 ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
120 if (force || ngain != ogain)
121 snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ngain & 0xff);
122 ngain = emu->i2c_capture_volume[val][1]; /* Right */
123 ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */
124 if (force || ngain != ogain)
125 snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ngain & 0xff);
126 source = 1 << val;
127 snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */
128 emu->i2c_capture_source = val;
129}
130
131static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu)
132{
133 u32 tmp;
134
135 if (emu->capture_mic_line_in) {
136 /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
137 tmp = inl(emu->port+GPIO) & ~0x400;
138 tmp = tmp | 0x400;
139 outl(tmp, emu->port+GPIO);
140 /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */
141 } else {
142 /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
143 tmp = inl(emu->port+GPIO) & ~0x400;
144 outl(tmp, emu->port+GPIO);
145 /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */
146 }
147}
148
149static void ca0106_set_spdif_bits(struct snd_ca0106 *emu, int idx)
150{
151 snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, emu->spdif_bits[idx]);
152}
153
154/*
155 */
Takashi Iwai0cb29ea2007-01-29 15:33:49 +0100156static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
157static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
Jaroslav Kysela42750b02006-06-01 18:34:01 +0200158
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200159#define snd_ca0106_shared_spdif_info snd_ctl_boolean_mono_info
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100161static int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol,
162 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100164 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100166 ucontrol->value.integer.value[0] = emu->spdif_enable;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 return 0;
168}
169
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100170static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol,
171 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100173 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 unsigned int val;
175 int change = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100177 val = !!ucontrol->value.integer.value[0];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 change = (emu->spdif_enable != val);
179 if (change) {
180 emu->spdif_enable = val;
Takashi Iwai5da95272008-11-24 14:06:08 +0100181 ca0106_spdif_enable(emu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 }
183 return change;
184}
185
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100186static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol,
187 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188{
Takashi Iwai95a98262005-11-17 10:40:18 +0100189 static char *texts[6] = {
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100190 "IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out"
Takashi Iwai95a98262005-11-17 10:40:18 +0100191 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192
193 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
194 uinfo->count = 1;
195 uinfo->value.enumerated.items = 6;
196 if (uinfo->value.enumerated.item > 5)
197 uinfo->value.enumerated.item = 5;
198 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
199 return 0;
200}
201
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100202static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol,
203 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100205 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206
207 ucontrol->value.enumerated.item[0] = emu->capture_source;
208 return 0;
209}
210
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100211static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
212 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100214 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 unsigned int val;
216 int change = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217
218 val = ucontrol->value.enumerated.item[0] ;
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100219 if (val >= 6)
220 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 change = (emu->capture_source != val);
222 if (change) {
223 emu->capture_source = val;
Takashi Iwai5da95272008-11-24 14:06:08 +0100224 ca0106_set_capture_source(emu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 }
226 return change;
227}
228
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100229static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
230 struct snd_ctl_elem_info *uinfo)
231{
232 static char *texts[6] = {
233 "Phone", "Mic", "Line in", "Aux"
234 };
235
236 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
237 uinfo->count = 1;
238 uinfo->value.enumerated.items = 4;
239 if (uinfo->value.enumerated.item > 3)
240 uinfo->value.enumerated.item = 3;
241 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
242 return 0;
243}
244
245static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
246 struct snd_ctl_elem_value *ucontrol)
247{
248 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
249
250 ucontrol->value.enumerated.item[0] = emu->i2c_capture_source;
251 return 0;
252}
253
254static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
255 struct snd_ctl_elem_value *ucontrol)
256{
257 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
258 unsigned int source_id;
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100259 int change = 0;
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100260 /* If the capture source has changed,
261 * update the capture volume from the cached value
262 * for the particular source.
263 */
264 source_id = ucontrol->value.enumerated.item[0] ;
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100265 if (source_id >= 4)
266 return -EINVAL;
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100267 change = (emu->i2c_capture_source != source_id);
268 if (change) {
Takashi Iwai5da95272008-11-24 14:06:08 +0100269 ca0106_set_i2c_capture_source(emu, source_id, 0);
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100270 }
271 return change;
272}
273
James Courtier-Duttonbe0b7b02006-04-09 20:48:44 +0100274static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol,
275 struct snd_ctl_elem_info *uinfo)
276{
277 static char *texts[2] = { "Side out", "Line in" };
278
279 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
280 uinfo->count = 1;
281 uinfo->value.enumerated.items = 2;
282 if (uinfo->value.enumerated.item > 1)
283 uinfo->value.enumerated.item = 1;
284 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
285 return 0;
286}
287
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100288static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol,
289 struct snd_ctl_elem_info *uinfo)
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200290{
291 static char *texts[2] = { "Line in", "Mic in" };
292
293 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
294 uinfo->count = 1;
295 uinfo->value.enumerated.items = 2;
296 if (uinfo->value.enumerated.item > 1)
297 uinfo->value.enumerated.item = 1;
298 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
299 return 0;
300}
301
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100302static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol,
303 struct snd_ctl_elem_value *ucontrol)
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200304{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100305 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200306
307 ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in;
308 return 0;
309}
310
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100311static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
312 struct snd_ctl_elem_value *ucontrol)
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200313{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100314 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200315 unsigned int val;
316 int change = 0;
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200317
318 val = ucontrol->value.enumerated.item[0] ;
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100319 if (val > 1)
320 return -EINVAL;
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200321 change = (emu->capture_mic_line_in != val);
322 if (change) {
323 emu->capture_mic_line_in = val;
Takashi Iwai5da95272008-11-24 14:06:08 +0100324 ca0106_set_capture_mic_line_in(emu);
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200325 }
326 return change;
327}
328
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100329static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata =
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200330{
331 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100332 .name = "Shared Mic/Line in Capture Switch",
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200333 .info = snd_ca0106_capture_mic_line_in_info,
334 .get = snd_ca0106_capture_mic_line_in_get,
335 .put = snd_ca0106_capture_mic_line_in_put
336};
337
James Courtier-Duttonbe0b7b02006-04-09 20:48:44 +0100338static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out __devinitdata =
339{
340 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
341 .name = "Shared Line in/Side out Capture Switch",
342 .info = snd_ca0106_capture_line_in_side_out_info,
343 .get = snd_ca0106_capture_mic_line_in_get,
344 .put = snd_ca0106_capture_mic_line_in_put
345};
346
347
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100348static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol,
349 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350{
351 uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
352 uinfo->count = 1;
353 return 0;
354}
355
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100356static int snd_ca0106_spdif_get(struct snd_kcontrol *kcontrol,
357 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100359 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
361
362 ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
363 ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
364 ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
365 ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
366 return 0;
367}
368
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100369static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol,
370 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371{
372 ucontrol->value.iec958.status[0] = 0xff;
373 ucontrol->value.iec958.status[1] = 0xff;
374 ucontrol->value.iec958.status[2] = 0xff;
375 ucontrol->value.iec958.status[3] = 0xff;
376 return 0;
377}
378
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100379static int snd_ca0106_spdif_put(struct snd_kcontrol *kcontrol,
380 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100382 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
384 int change;
385 unsigned int val;
386
387 val = (ucontrol->value.iec958.status[0] << 0) |
388 (ucontrol->value.iec958.status[1] << 8) |
389 (ucontrol->value.iec958.status[2] << 16) |
390 (ucontrol->value.iec958.status[3] << 24);
391 change = val != emu->spdif_bits[idx];
392 if (change) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 emu->spdif_bits[idx] = val;
Takashi Iwai5da95272008-11-24 14:06:08 +0100394 ca0106_set_spdif_bits(emu, idx);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 }
396 return change;
397}
398
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100399static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol,
400 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401{
402 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
403 uinfo->count = 2;
404 uinfo->value.integer.min = 0;
405 uinfo->value.integer.max = 255;
406 return 0;
407}
408
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100409static int snd_ca0106_volume_get(struct snd_kcontrol *kcontrol,
410 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100412 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 unsigned int value;
Takashi Iwai95a98262005-11-17 10:40:18 +0100414 int channel_id, reg;
415
416 channel_id = (kcontrol->private_value >> 8) & 0xff;
417 reg = kcontrol->private_value & 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418
419 value = snd_ca0106_ptr_read(emu, reg, channel_id);
420 ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
421 ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
422 return 0;
423}
424
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100425static int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol,
426 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100428 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Takashi Iwai95a98262005-11-17 10:40:18 +0100429 unsigned int oval, nval;
430 int channel_id, reg;
431
432 channel_id = (kcontrol->private_value >> 8) & 0xff;
433 reg = kcontrol->private_value & 0xff;
434
435 oval = snd_ca0106_ptr_read(emu, reg, channel_id);
436 nval = ((0xff - ucontrol->value.integer.value[0]) << 24) |
437 ((0xff - ucontrol->value.integer.value[1]) << 16);
438 nval |= ((0xff - ucontrol->value.integer.value[0]) << 8) |
439 ((0xff - ucontrol->value.integer.value[1]) );
440 if (oval == nval)
441 return 0;
442 snd_ca0106_ptr_write(emu, reg, channel_id, nval);
443 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444}
445
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100446static int snd_ca0106_i2c_volume_info(struct snd_kcontrol *kcontrol,
447 struct snd_ctl_elem_info *uinfo)
448{
449 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
450 uinfo->count = 2;
451 uinfo->value.integer.min = 0;
452 uinfo->value.integer.max = 255;
453 return 0;
454}
455
456static int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol,
457 struct snd_ctl_elem_value *ucontrol)
458{
459 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
460 int source_id;
461
462 source_id = kcontrol->private_value;
463
464 ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0];
465 ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1];
466 return 0;
467}
468
469static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol,
470 struct snd_ctl_elem_value *ucontrol)
471{
472 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
473 unsigned int ogain;
474 unsigned int ngain;
475 int source_id;
476 int change = 0;
477
478 source_id = kcontrol->private_value;
479 ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
480 ngain = ucontrol->value.integer.value[0];
481 if (ngain > 0xff)
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100482 return -EINVAL;
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100483 if (ogain != ngain) {
484 if (emu->i2c_capture_source == source_id)
485 snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
486 emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0];
487 change = 1;
488 }
489 ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
490 ngain = ucontrol->value.integer.value[1];
491 if (ngain > 0xff)
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100492 return -EINVAL;
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100493 if (ogain != ngain) {
494 if (emu->i2c_capture_source == source_id)
495 snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
496 emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1];
497 change = 1;
498 }
499
500 return change;
501}
502
Trent Piephob18cd532007-07-24 12:06:16 +0200503#define spi_mute_info snd_ctl_boolean_mono_info
504
505static int spi_mute_get(struct snd_kcontrol *kcontrol,
506 struct snd_ctl_elem_value *ucontrol)
507{
508 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
509 unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
510 unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
511
512 ucontrol->value.integer.value[0] = !(emu->spi_dac_reg[reg] & bit);
513 return 0;
514}
515
516static int spi_mute_put(struct snd_kcontrol *kcontrol,
517 struct snd_ctl_elem_value *ucontrol)
518{
519 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
520 unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
521 unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
522 int ret;
523
524 ret = emu->spi_dac_reg[reg] & bit;
525 if (ucontrol->value.integer.value[0]) {
526 if (!ret) /* bit already cleared, do nothing */
527 return 0;
528 emu->spi_dac_reg[reg] &= ~bit;
529 } else {
530 if (ret) /* bit already set, do nothing */
531 return 0;
532 emu->spi_dac_reg[reg] |= bit;
533 }
534
535 ret = snd_ca0106_spi_write(emu, emu->spi_dac_reg[reg]);
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100536 return ret ? -EINVAL : 1;
Trent Piephob18cd532007-07-24 12:06:16 +0200537}
538
Takashi Iwai95a98262005-11-17 10:40:18 +0100539#define CA_VOLUME(xname,chid,reg) \
540{ \
541 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
Jaroslav Kysela302e9c52006-07-05 17:39:49 +0200542 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
543 SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100544 .info = snd_ca0106_volume_info, \
545 .get = snd_ca0106_volume_get, \
546 .put = snd_ca0106_volume_put, \
Takashi Iwai7cf0a952006-08-17 16:23:07 +0200547 .tlv = { .p = snd_ca0106_db_scale1 }, \
Takashi Iwai95a98262005-11-17 10:40:18 +0100548 .private_value = ((chid) << 8) | (reg) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549}
550
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100551static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
Takashi Iwai95a98262005-11-17 10:40:18 +0100552 CA_VOLUME("Analog Front Playback Volume",
553 CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2),
554 CA_VOLUME("Analog Rear Playback Volume",
555 CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2),
556 CA_VOLUME("Analog Center/LFE Playback Volume",
557 CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2),
558 CA_VOLUME("Analog Side Playback Volume",
559 CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2),
560
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100561 CA_VOLUME("IEC958 Front Playback Volume",
Takashi Iwai95a98262005-11-17 10:40:18 +0100562 CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1),
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100563 CA_VOLUME("IEC958 Rear Playback Volume",
Takashi Iwai95a98262005-11-17 10:40:18 +0100564 CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1),
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100565 CA_VOLUME("IEC958 Center/LFE Playback Volume",
Takashi Iwai95a98262005-11-17 10:40:18 +0100566 CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1),
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100567 CA_VOLUME("IEC958 Unknown Playback Volume",
Takashi Iwai95a98262005-11-17 10:40:18 +0100568 CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1),
569
570 CA_VOLUME("CAPTURE feedback Playback Volume",
571 1, CAPTURE_CONTROL),
572
573 {
574 .access = SNDRV_CTL_ELEM_ACCESS_READ,
575 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
576 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
577 .count = 4,
578 .info = snd_ca0106_spdif_info,
579 .get = snd_ca0106_spdif_get_mask
580 },
581 {
582 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100583 .name = "IEC958 Playback Switch",
Takashi Iwai95a98262005-11-17 10:40:18 +0100584 .info = snd_ca0106_shared_spdif_info,
585 .get = snd_ca0106_shared_spdif_get,
586 .put = snd_ca0106_shared_spdif_put
587 },
588 {
589 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
James Courtier-Duttone6327cf2006-11-11 10:52:06 +0000590 .name = "Digital Source Capture Enum",
Takashi Iwai95a98262005-11-17 10:40:18 +0100591 .info = snd_ca0106_capture_source_info,
592 .get = snd_ca0106_capture_source_get,
593 .put = snd_ca0106_capture_source_put
594 },
595 {
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100596 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
James Courtier-Duttone6327cf2006-11-11 10:52:06 +0000597 .name = "Analog Source Capture Enum",
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100598 .info = snd_ca0106_i2c_capture_source_info,
599 .get = snd_ca0106_i2c_capture_source_get,
600 .put = snd_ca0106_i2c_capture_source_put
601 },
602 {
Takashi Iwai95a98262005-11-17 10:40:18 +0100603 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
604 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
605 .count = 4,
606 .info = snd_ca0106_spdif_info,
607 .get = snd_ca0106_spdif_get,
608 .put = snd_ca0106_spdif_put
609 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610};
611
James Courtier-Dutton7c157062006-12-10 00:00:38 +0000612#define I2C_VOLUME(xname,chid) \
613{ \
614 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
615 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
616 SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
617 .info = snd_ca0106_i2c_volume_info, \
618 .get = snd_ca0106_i2c_volume_get, \
619 .put = snd_ca0106_i2c_volume_put, \
620 .tlv = { .p = snd_ca0106_db_scale2 }, \
621 .private_value = chid \
622}
623
624static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata = {
625 I2C_VOLUME("Phone Capture Volume", 0),
626 I2C_VOLUME("Mic Capture Volume", 1),
627 I2C_VOLUME("Line in Capture Volume", 2),
628 I2C_VOLUME("Aux Capture Volume", 3),
629};
630
Trent Piephob18cd532007-07-24 12:06:16 +0200631#define SPI_SWITCH(xname,reg,bit) \
632{ \
633 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
634 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
635 .info = spi_mute_info, \
636 .get = spi_mute_get, \
637 .put = spi_mute_put, \
Trent Piepho18b5d322007-07-25 18:40:39 +0200638 .private_value = (reg<<SPI_REG_SHIFT) | (bit) \
Trent Piephob18cd532007-07-24 12:06:16 +0200639}
640
641static struct snd_kcontrol_new snd_ca0106_volume_spi_dac_ctls[]
642__devinitdata = {
643 SPI_SWITCH("Analog Front Playback Switch",
644 SPI_DMUTE4_REG, SPI_DMUTE4_BIT),
645 SPI_SWITCH("Analog Rear Playback Switch",
646 SPI_DMUTE0_REG, SPI_DMUTE0_BIT),
647 SPI_SWITCH("Analog Center/LFE Playback Switch",
648 SPI_DMUTE2_REG, SPI_DMUTE2_BIT),
649 SPI_SWITCH("Analog Side Playback Switch",
650 SPI_DMUTE1_REG, SPI_DMUTE1_BIT),
651};
652
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100653static int __devinit remove_ctl(struct snd_card *card, const char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100655 struct snd_ctl_elem_id id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 memset(&id, 0, sizeof(id));
657 strcpy(id.name, name);
658 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
659 return snd_ctl_remove_id(card, &id);
660}
661
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100662static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, const char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100664 struct snd_ctl_elem_id sid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 memset(&sid, 0, sizeof(sid));
666 /* FIXME: strcpy is bad. */
667 strcpy(sid.name, name);
668 sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
669 return snd_ctl_find_id(card, &sid);
670}
671
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100672static int __devinit rename_ctl(struct snd_card *card, const char *src, const char *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100674 struct snd_kcontrol *kctl = ctl_find(card, src);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 if (kctl) {
676 strcpy(kctl->id.name, dst);
677 return 0;
678 }
679 return -ENOENT;
680}
681
Trent Piephofca7f382007-07-24 12:10:34 +0200682#define ADD_CTLS(emu, ctls) \
683 do { \
Harvey Harrisonbed515b2008-02-28 12:02:56 +0100684 int i, _err; \
Trent Piephofca7f382007-07-24 12:10:34 +0200685 for (i = 0; i < ARRAY_SIZE(ctls); i++) { \
Harvey Harrisonbed515b2008-02-28 12:02:56 +0100686 _err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \
687 if (_err < 0) \
688 return _err; \
Trent Piephofca7f382007-07-24 12:10:34 +0200689 } \
690 } while (0)
691
Takashi Iwai49c88b82008-02-18 13:06:49 +0100692static __devinitdata
693DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 50, 1);
694
695static char *slave_vols[] __devinitdata = {
696 "Analog Front Playback Volume",
697 "Analog Rear Playback Volume",
698 "Analog Center/LFE Playback Volume",
699 "Analog Side Playback Volume",
700 "IEC958 Front Playback Volume",
701 "IEC958 Rear Playback Volume",
702 "IEC958 Center/LFE Playback Volume",
703 "IEC958 Unknown Playback Volume",
704 "CAPTURE feedback Playback Volume",
705 NULL
706};
707
708static char *slave_sws[] __devinitdata = {
709 "Analog Front Playback Switch",
710 "Analog Rear Playback Switch",
711 "Analog Center/LFE Playback Switch",
712 "Analog Side Playback Switch",
713 "IEC958 Playback Switch",
714 NULL
715};
716
717static void __devinit add_slaves(struct snd_card *card,
718 struct snd_kcontrol *master, char **list)
719{
720 for (; *list; list++) {
721 struct snd_kcontrol *slave = ctl_find(card, *list);
722 if (slave)
723 snd_ctl_add_slave(master, slave);
724 }
725}
726
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100727int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728{
Trent Piephofca7f382007-07-24 12:10:34 +0200729 int err;
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100730 struct snd_card *card = emu->card;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 char **c;
Takashi Iwai49c88b82008-02-18 13:06:49 +0100732 struct snd_kcontrol *vmaster;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 static char *ca0106_remove_ctls[] = {
734 "Master Mono Playback Switch",
735 "Master Mono Playback Volume",
736 "3D Control - Switch",
737 "3D Control Sigmatel - Depth",
738 "PCM Playback Switch",
739 "PCM Playback Volume",
740 "CD Playback Switch",
741 "CD Playback Volume",
742 "Phone Playback Switch",
743 "Phone Playback Volume",
744 "Video Playback Switch",
745 "Video Playback Volume",
746 "PC Speaker Playback Switch",
747 "PC Speaker Playback Volume",
748 "Mono Output Select",
749 "Capture Source",
750 "Capture Switch",
751 "Capture Volume",
752 "External Amplifier",
753 "Sigmatel 4-Speaker Stereo Playback Switch",
754 "Sigmatel Surround Phase Inversion Playback ",
755 NULL
756 };
757 static char *ca0106_rename_ctls[] = {
758 "Master Playback Switch", "Capture Switch",
759 "Master Playback Volume", "Capture Volume",
760 "Line Playback Switch", "AC97 Line Capture Switch",
761 "Line Playback Volume", "AC97 Line Capture Volume",
762 "Aux Playback Switch", "AC97 Aux Capture Switch",
763 "Aux Playback Volume", "AC97 Aux Capture Volume",
764 "Mic Playback Switch", "AC97 Mic Capture Switch",
765 "Mic Playback Volume", "AC97 Mic Capture Volume",
766 "Mic Select", "AC97 Mic Select",
767 "Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)",
768 NULL
769 };
770#if 1
Takashi Iwai95a98262005-11-17 10:40:18 +0100771 for (c = ca0106_remove_ctls; *c; c++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 remove_ctl(card, *c);
Takashi Iwai95a98262005-11-17 10:40:18 +0100773 for (c = ca0106_rename_ctls; *c; c += 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 rename_ctl(card, c[0], c[1]);
775#endif
776
Trent Piephofca7f382007-07-24 12:10:34 +0200777 ADD_CTLS(emu, snd_ca0106_volume_ctls);
Takashi Iwai95a98262005-11-17 10:40:18 +0100778 if (emu->details->i2c_adc == 1) {
Trent Piephofca7f382007-07-24 12:10:34 +0200779 ADD_CTLS(emu, snd_ca0106_volume_i2c_adc_ctls);
James Courtier-Duttonbe0b7b02006-04-09 20:48:44 +0100780 if (emu->details->gpio_type == 1)
781 err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu));
782 else /* gpio_type == 2 */
783 err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu));
Takashi Iwai95a98262005-11-17 10:40:18 +0100784 if (err < 0)
785 return err;
786 }
Trent Piephofca7f382007-07-24 12:10:34 +0200787 if (emu->details->spi_dac == 1)
788 ADD_CTLS(emu, snd_ca0106_volume_spi_dac_ctls);
Takashi Iwai49c88b82008-02-18 13:06:49 +0100789
790 /* Create virtual master controls */
791 vmaster = snd_ctl_make_virtual_master("Master Playback Volume",
792 snd_ca0106_master_db_scale);
793 if (!vmaster)
794 return -ENOMEM;
795 add_slaves(card, vmaster, slave_vols);
796
797 if (emu->details->spi_dac == 1) {
798 vmaster = snd_ctl_make_virtual_master("Master Playback Switch",
799 NULL);
800 if (!vmaster)
801 return -ENOMEM;
802 add_slaves(card, vmaster, slave_sws);
803 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 return 0;
805}
806
Takashi Iwai5da95272008-11-24 14:06:08 +0100807#ifdef CONFIG_PM
808struct ca0106_vol_tbl {
809 unsigned int reg;
810 unsigned int channel_id;
811};
812
813static struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = {
814 { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2 },
815 { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2 },
816 { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2 },
817 { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2 },
818 { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1 },
819 { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1 },
820 { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1 },
821 { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1 },
822 { 1, CAPTURE_CONTROL },
823};
824
825void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip)
826{
827 int i;
828
829 /* save volumes */
830 for (i = 0; i < NUM_SAVED_VOLUMES; i++)
831 chip->saved_vol[i] =
832 snd_ca0106_ptr_read(chip, saved_volumes[i].reg,
833 saved_volumes[i].channel_id);
834}
835
836void snd_ca0106_mixer_resume(struct snd_ca0106 *chip)
837{
838 int i;
839
840 for (i = 0; i < NUM_SAVED_VOLUMES; i++)
841 snd_ca0106_ptr_write(chip, saved_volumes[i].reg,
842 saved_volumes[i].channel_id,
843 chip->saved_vol[i]);
844
845 ca0106_spdif_enable(chip);
846 ca0106_set_capture_source(chip);
847 ca0106_set_i2c_capture_source(chip, chip->i2c_capture_source, 1);
848 for (i = 0; i < 4; i++)
849 ca0106_set_spdif_bits(chip, i);
850 if (chip->details->i2c_adc)
851 ca0106_set_capture_mic_line_in(chip);
852}
853#endif /* CONFIG_PM */