blob: a135b9c4c3c87ec2de0751cdac68310847fdc881 [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
James Courtier-Duttoned144f32005-05-27 23:28:27 +02004 * Version: 0.0.17
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.
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 *
43 * This code was initally based on code from ALSA's emu10k1x.c which is:
44 * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
45 *
46 * This program is free software; you can redistribute it and/or modify
47 * it under the terms of the GNU General Public License as published by
48 * the Free Software Foundation; either version 2 of the License, or
49 * (at your option) any later version.
50 *
51 * This program is distributed in the hope that it will be useful,
52 * but WITHOUT ANY WARRANTY; without even the implied warranty of
53 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
54 * GNU General Public License for more details.
55 *
56 * You should have received a copy of the GNU General Public License
57 * along with this program; if not, write to the Free Software
58 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
59 *
60 */
61#include <sound/driver.h>
62#include <linux/delay.h>
63#include <linux/init.h>
64#include <linux/interrupt.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070065#include <linux/slab.h>
66#include <linux/moduleparam.h>
67#include <sound/core.h>
68#include <sound/initval.h>
69#include <sound/pcm.h>
70#include <sound/ac97_codec.h>
71#include <sound/info.h>
Jaroslav Kysela42750b02006-06-01 18:34:01 +020072#include <sound/tlv.h>
Jean Delvare6473d162007-03-06 02:45:12 -080073#include <asm/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070074
75#include "ca0106.h"
76
Takashi Iwai0cb29ea2007-01-29 15:33:49 +010077static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
78static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
Jaroslav Kysela42750b02006-06-01 18:34:01 +020079
Takashi Iwaia5ce8892007-07-23 15:42:26 +020080#define snd_ca0106_shared_spdif_info snd_ctl_boolean_mono_info
Linus Torvalds1da177e2005-04-16 15:20:36 -070081
Takashi Iwaie4a3d142005-11-17 14:55:40 +010082static int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol,
83 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -070084{
Takashi Iwaie4a3d142005-11-17 14:55:40 +010085 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -070086
87 ucontrol->value.enumerated.item[0] = emu->spdif_enable;
88 return 0;
89}
90
Takashi Iwaie4a3d142005-11-17 14:55:40 +010091static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol,
92 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -070093{
Takashi Iwaie4a3d142005-11-17 14:55:40 +010094 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 unsigned int val;
96 int change = 0;
97 u32 mask;
98
99 val = ucontrol->value.enumerated.item[0] ;
100 change = (emu->spdif_enable != val);
101 if (change) {
102 emu->spdif_enable = val;
103 if (val == 1) {
104 /* Digital */
105 snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
106 snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
107 snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
108 snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000);
109 mask = inl(emu->port + GPIO) & ~0x101;
110 outl(mask, emu->port + GPIO);
111
112 } else {
113 /* Analog */
114 snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
James Courtier-Dutton1f829412005-05-21 16:23:37 +0200115 snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
117 snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000);
118 mask = inl(emu->port + GPIO) | 0x101;
119 outl(mask, emu->port + GPIO);
120 }
121 }
122 return change;
123}
124
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100125static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol,
126 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127{
Takashi Iwai95a98262005-11-17 10:40:18 +0100128 static char *texts[6] = {
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100129 "IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out"
Takashi Iwai95a98262005-11-17 10:40:18 +0100130 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131
132 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
133 uinfo->count = 1;
134 uinfo->value.enumerated.items = 6;
135 if (uinfo->value.enumerated.item > 5)
136 uinfo->value.enumerated.item = 5;
137 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
138 return 0;
139}
140
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100141static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol,
142 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100144 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145
146 ucontrol->value.enumerated.item[0] = emu->capture_source;
147 return 0;
148}
149
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100150static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
151 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100153 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 unsigned int val;
155 int change = 0;
156 u32 mask;
157 u32 source;
158
159 val = ucontrol->value.enumerated.item[0] ;
160 change = (emu->capture_source != val);
161 if (change) {
162 emu->capture_source = val;
163 source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
164 mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
165 snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
166 }
167 return change;
168}
169
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100170static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
171 struct snd_ctl_elem_info *uinfo)
172{
173 static char *texts[6] = {
174 "Phone", "Mic", "Line in", "Aux"
175 };
176
177 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
178 uinfo->count = 1;
179 uinfo->value.enumerated.items = 4;
180 if (uinfo->value.enumerated.item > 3)
181 uinfo->value.enumerated.item = 3;
182 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
183 return 0;
184}
185
186static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
187 struct snd_ctl_elem_value *ucontrol)
188{
189 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
190
191 ucontrol->value.enumerated.item[0] = emu->i2c_capture_source;
192 return 0;
193}
194
195static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
196 struct snd_ctl_elem_value *ucontrol)
197{
198 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
199 unsigned int source_id;
200 unsigned int ngain, ogain;
201 int change = 0;
202 u32 source;
203 /* If the capture source has changed,
204 * update the capture volume from the cached value
205 * for the particular source.
206 */
207 source_id = ucontrol->value.enumerated.item[0] ;
208 change = (emu->i2c_capture_source != source_id);
209 if (change) {
210 snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
211 ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
212 ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
213 if (ngain != ogain)
214 snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff));
215 ngain = emu->i2c_capture_volume[source_id][1]; /* Left */
216 ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Left */
217 if (ngain != ogain)
218 snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
219 source = 1 << source_id;
220 snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */
221 emu->i2c_capture_source = source_id;
222 }
223 return change;
224}
225
James Courtier-Duttonbe0b7b02006-04-09 20:48:44 +0100226static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol,
227 struct snd_ctl_elem_info *uinfo)
228{
229 static char *texts[2] = { "Side out", "Line in" };
230
231 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
232 uinfo->count = 1;
233 uinfo->value.enumerated.items = 2;
234 if (uinfo->value.enumerated.item > 1)
235 uinfo->value.enumerated.item = 1;
236 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
237 return 0;
238}
239
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100240static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol,
241 struct snd_ctl_elem_info *uinfo)
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200242{
243 static char *texts[2] = { "Line in", "Mic in" };
244
245 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
246 uinfo->count = 1;
247 uinfo->value.enumerated.items = 2;
248 if (uinfo->value.enumerated.item > 1)
249 uinfo->value.enumerated.item = 1;
250 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
251 return 0;
252}
253
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100254static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol,
255 struct snd_ctl_elem_value *ucontrol)
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200256{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100257 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200258
259 ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in;
260 return 0;
261}
262
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100263static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
264 struct snd_ctl_elem_value *ucontrol)
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200265{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100266 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200267 unsigned int val;
268 int change = 0;
269 u32 tmp;
270
271 val = ucontrol->value.enumerated.item[0] ;
272 change = (emu->capture_mic_line_in != val);
273 if (change) {
274 emu->capture_mic_line_in = val;
275 if (val) {
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100276 //snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200277 tmp = inl(emu->port+GPIO) & ~0x400;
278 tmp = tmp | 0x400;
279 outl(tmp, emu->port+GPIO);
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100280 //snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC);
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200281 } else {
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100282 //snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200283 tmp = inl(emu->port+GPIO) & ~0x400;
284 outl(tmp, emu->port+GPIO);
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100285 //snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN);
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200286 }
287 }
288 return change;
289}
290
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100291static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata =
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200292{
293 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100294 .name = "Shared Mic/Line in Capture Switch",
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200295 .info = snd_ca0106_capture_mic_line_in_info,
296 .get = snd_ca0106_capture_mic_line_in_get,
297 .put = snd_ca0106_capture_mic_line_in_put
298};
299
James Courtier-Duttonbe0b7b02006-04-09 20:48:44 +0100300static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out __devinitdata =
301{
302 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
303 .name = "Shared Line in/Side out Capture Switch",
304 .info = snd_ca0106_capture_line_in_side_out_info,
305 .get = snd_ca0106_capture_mic_line_in_get,
306 .put = snd_ca0106_capture_mic_line_in_put
307};
308
309
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100310static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol,
311 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312{
313 uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
314 uinfo->count = 1;
315 return 0;
316}
317
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100318static int snd_ca0106_spdif_get(struct snd_kcontrol *kcontrol,
319 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100321 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
323
324 ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
325 ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
326 ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
327 ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
328 return 0;
329}
330
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100331static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol,
332 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333{
334 ucontrol->value.iec958.status[0] = 0xff;
335 ucontrol->value.iec958.status[1] = 0xff;
336 ucontrol->value.iec958.status[2] = 0xff;
337 ucontrol->value.iec958.status[3] = 0xff;
338 return 0;
339}
340
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100341static int snd_ca0106_spdif_put(struct snd_kcontrol *kcontrol,
342 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100344 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
346 int change;
347 unsigned int val;
348
349 val = (ucontrol->value.iec958.status[0] << 0) |
350 (ucontrol->value.iec958.status[1] << 8) |
351 (ucontrol->value.iec958.status[2] << 16) |
352 (ucontrol->value.iec958.status[3] << 24);
353 change = val != emu->spdif_bits[idx];
354 if (change) {
355 snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, val);
356 emu->spdif_bits[idx] = val;
357 }
358 return change;
359}
360
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100361static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol,
362 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363{
364 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
365 uinfo->count = 2;
366 uinfo->value.integer.min = 0;
367 uinfo->value.integer.max = 255;
368 return 0;
369}
370
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100371static int snd_ca0106_volume_get(struct snd_kcontrol *kcontrol,
372 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100374 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 unsigned int value;
Takashi Iwai95a98262005-11-17 10:40:18 +0100376 int channel_id, reg;
377
378 channel_id = (kcontrol->private_value >> 8) & 0xff;
379 reg = kcontrol->private_value & 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380
381 value = snd_ca0106_ptr_read(emu, reg, channel_id);
382 ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
383 ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
384 return 0;
385}
386
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100387static int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol,
388 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100390 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Takashi Iwai95a98262005-11-17 10:40:18 +0100391 unsigned int oval, nval;
392 int channel_id, reg;
393
394 channel_id = (kcontrol->private_value >> 8) & 0xff;
395 reg = kcontrol->private_value & 0xff;
396
397 oval = snd_ca0106_ptr_read(emu, reg, channel_id);
398 nval = ((0xff - ucontrol->value.integer.value[0]) << 24) |
399 ((0xff - ucontrol->value.integer.value[1]) << 16);
400 nval |= ((0xff - ucontrol->value.integer.value[0]) << 8) |
401 ((0xff - ucontrol->value.integer.value[1]) );
402 if (oval == nval)
403 return 0;
404 snd_ca0106_ptr_write(emu, reg, channel_id, nval);
405 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406}
407
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100408static int snd_ca0106_i2c_volume_info(struct snd_kcontrol *kcontrol,
409 struct snd_ctl_elem_info *uinfo)
410{
411 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
412 uinfo->count = 2;
413 uinfo->value.integer.min = 0;
414 uinfo->value.integer.max = 255;
415 return 0;
416}
417
418static int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol,
419 struct snd_ctl_elem_value *ucontrol)
420{
421 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
422 int source_id;
423
424 source_id = kcontrol->private_value;
425
426 ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0];
427 ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1];
428 return 0;
429}
430
431static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol,
432 struct snd_ctl_elem_value *ucontrol)
433{
434 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
435 unsigned int ogain;
436 unsigned int ngain;
437 int source_id;
438 int change = 0;
439
440 source_id = kcontrol->private_value;
441 ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
442 ngain = ucontrol->value.integer.value[0];
443 if (ngain > 0xff)
444 return 0;
445 if (ogain != ngain) {
446 if (emu->i2c_capture_source == source_id)
447 snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
448 emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0];
449 change = 1;
450 }
451 ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
452 ngain = ucontrol->value.integer.value[1];
453 if (ngain > 0xff)
454 return 0;
455 if (ogain != ngain) {
456 if (emu->i2c_capture_source == source_id)
457 snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
458 emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1];
459 change = 1;
460 }
461
462 return change;
463}
464
Takashi Iwai95a98262005-11-17 10:40:18 +0100465#define CA_VOLUME(xname,chid,reg) \
466{ \
467 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
Jaroslav Kysela302e9c52006-07-05 17:39:49 +0200468 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
469 SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100470 .info = snd_ca0106_volume_info, \
471 .get = snd_ca0106_volume_get, \
472 .put = snd_ca0106_volume_put, \
Takashi Iwai7cf0a952006-08-17 16:23:07 +0200473 .tlv = { .p = snd_ca0106_db_scale1 }, \
Takashi Iwai95a98262005-11-17 10:40:18 +0100474 .private_value = ((chid) << 8) | (reg) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475}
476
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100477static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
Takashi Iwai95a98262005-11-17 10:40:18 +0100478 CA_VOLUME("Analog Front Playback Volume",
479 CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2),
480 CA_VOLUME("Analog Rear Playback Volume",
481 CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2),
482 CA_VOLUME("Analog Center/LFE Playback Volume",
483 CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2),
484 CA_VOLUME("Analog Side Playback Volume",
485 CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2),
486
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100487 CA_VOLUME("IEC958 Front Playback Volume",
Takashi Iwai95a98262005-11-17 10:40:18 +0100488 CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1),
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100489 CA_VOLUME("IEC958 Rear Playback Volume",
Takashi Iwai95a98262005-11-17 10:40:18 +0100490 CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1),
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100491 CA_VOLUME("IEC958 Center/LFE Playback Volume",
Takashi Iwai95a98262005-11-17 10:40:18 +0100492 CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1),
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100493 CA_VOLUME("IEC958 Unknown Playback Volume",
Takashi Iwai95a98262005-11-17 10:40:18 +0100494 CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1),
495
496 CA_VOLUME("CAPTURE feedback Playback Volume",
497 1, CAPTURE_CONTROL),
498
499 {
500 .access = SNDRV_CTL_ELEM_ACCESS_READ,
501 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
502 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
503 .count = 4,
504 .info = snd_ca0106_spdif_info,
505 .get = snd_ca0106_spdif_get_mask
506 },
507 {
508 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100509 .name = "IEC958 Playback Switch",
Takashi Iwai95a98262005-11-17 10:40:18 +0100510 .info = snd_ca0106_shared_spdif_info,
511 .get = snd_ca0106_shared_spdif_get,
512 .put = snd_ca0106_shared_spdif_put
513 },
514 {
515 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
James Courtier-Duttone6327cf2006-11-11 10:52:06 +0000516 .name = "Digital Source Capture Enum",
Takashi Iwai95a98262005-11-17 10:40:18 +0100517 .info = snd_ca0106_capture_source_info,
518 .get = snd_ca0106_capture_source_get,
519 .put = snd_ca0106_capture_source_put
520 },
521 {
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100522 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
James Courtier-Duttone6327cf2006-11-11 10:52:06 +0000523 .name = "Analog Source Capture Enum",
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100524 .info = snd_ca0106_i2c_capture_source_info,
525 .get = snd_ca0106_i2c_capture_source_get,
526 .put = snd_ca0106_i2c_capture_source_put
527 },
528 {
Takashi Iwai95a98262005-11-17 10:40:18 +0100529 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
530 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
531 .count = 4,
532 .info = snd_ca0106_spdif_info,
533 .get = snd_ca0106_spdif_get,
534 .put = snd_ca0106_spdif_put
535 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536};
537
James Courtier-Dutton7c157062006-12-10 00:00:38 +0000538#define I2C_VOLUME(xname,chid) \
539{ \
540 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
541 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
542 SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
543 .info = snd_ca0106_i2c_volume_info, \
544 .get = snd_ca0106_i2c_volume_get, \
545 .put = snd_ca0106_i2c_volume_put, \
546 .tlv = { .p = snd_ca0106_db_scale2 }, \
547 .private_value = chid \
548}
549
550static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata = {
551 I2C_VOLUME("Phone Capture Volume", 0),
552 I2C_VOLUME("Mic Capture Volume", 1),
553 I2C_VOLUME("Line in Capture Volume", 2),
554 I2C_VOLUME("Aux Capture Volume", 3),
555};
556
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100557static int __devinit remove_ctl(struct snd_card *card, const char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100559 struct snd_ctl_elem_id id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 memset(&id, 0, sizeof(id));
561 strcpy(id.name, name);
562 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
563 return snd_ctl_remove_id(card, &id);
564}
565
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100566static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, const char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100568 struct snd_ctl_elem_id sid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 memset(&sid, 0, sizeof(sid));
570 /* FIXME: strcpy is bad. */
571 strcpy(sid.name, name);
572 sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
573 return snd_ctl_find_id(card, &sid);
574}
575
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100576static int __devinit rename_ctl(struct snd_card *card, const char *src, const char *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100578 struct snd_kcontrol *kctl = ctl_find(card, src);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 if (kctl) {
580 strcpy(kctl->id.name, dst);
581 return 0;
582 }
583 return -ENOENT;
584}
585
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100586int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587{
Takashi Iwai95a98262005-11-17 10:40:18 +0100588 int i, err;
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100589 struct snd_card *card = emu->card;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 char **c;
591 static char *ca0106_remove_ctls[] = {
592 "Master Mono Playback Switch",
593 "Master Mono Playback Volume",
594 "3D Control - Switch",
595 "3D Control Sigmatel - Depth",
596 "PCM Playback Switch",
597 "PCM Playback Volume",
598 "CD Playback Switch",
599 "CD Playback Volume",
600 "Phone Playback Switch",
601 "Phone Playback Volume",
602 "Video Playback Switch",
603 "Video Playback Volume",
604 "PC Speaker Playback Switch",
605 "PC Speaker Playback Volume",
606 "Mono Output Select",
607 "Capture Source",
608 "Capture Switch",
609 "Capture Volume",
610 "External Amplifier",
611 "Sigmatel 4-Speaker Stereo Playback Switch",
612 "Sigmatel Surround Phase Inversion Playback ",
613 NULL
614 };
615 static char *ca0106_rename_ctls[] = {
616 "Master Playback Switch", "Capture Switch",
617 "Master Playback Volume", "Capture Volume",
618 "Line Playback Switch", "AC97 Line Capture Switch",
619 "Line Playback Volume", "AC97 Line Capture Volume",
620 "Aux Playback Switch", "AC97 Aux Capture Switch",
621 "Aux Playback Volume", "AC97 Aux Capture Volume",
622 "Mic Playback Switch", "AC97 Mic Capture Switch",
623 "Mic Playback Volume", "AC97 Mic Capture Volume",
624 "Mic Select", "AC97 Mic Select",
625 "Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)",
626 NULL
627 };
628#if 1
Takashi Iwai95a98262005-11-17 10:40:18 +0100629 for (c = ca0106_remove_ctls; *c; c++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 remove_ctl(card, *c);
Takashi Iwai95a98262005-11-17 10:40:18 +0100631 for (c = ca0106_rename_ctls; *c; c += 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 rename_ctl(card, c[0], c[1]);
633#endif
634
Takashi Iwai95a98262005-11-17 10:40:18 +0100635 for (i = 0; i < ARRAY_SIZE(snd_ca0106_volume_ctls); i++) {
636 err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_volume_ctls[i], emu));
637 if (err < 0)
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200638 return err;
639 }
Takashi Iwai95a98262005-11-17 10:40:18 +0100640 if (emu->details->i2c_adc == 1) {
James Courtier-Dutton7c157062006-12-10 00:00:38 +0000641 for (i = 0; i < ARRAY_SIZE(snd_ca0106_volume_i2c_adc_ctls); i++) {
642 err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_volume_i2c_adc_ctls[i], emu));
643 if (err < 0)
644 return err;
645 }
James Courtier-Duttonbe0b7b02006-04-09 20:48:44 +0100646 if (emu->details->gpio_type == 1)
647 err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu));
648 else /* gpio_type == 2 */
649 err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu));
Takashi Iwai95a98262005-11-17 10:40:18 +0100650 if (err < 0)
651 return err;
652 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 return 0;
654}
655