blob: 68eacf7002d6ee87b848818b06a6997236d00426 [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 *
Lucas De Marchi25985ed2011-03-30 22:57:33 -030045 * This code was initially based on code from ALSA's emu10k1x.c which is:
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 * 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/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 Iwai5da95272008-11-24 14:06:08 +010077static void ca0106_spdif_enable(struct snd_ca0106 *emu)
78{
79 unsigned int val;
80
81 if (emu->spdif_enable) {
82 /* Digital */
83 snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
84 snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
85 val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000;
86 snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
87 val = inl(emu->port + GPIO) & ~0x101;
88 outl(val, emu->port + GPIO);
89
90 } else {
91 /* Analog */
92 snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
93 snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
94 val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000;
95 snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
96 val = inl(emu->port + GPIO) | 0x101;
97 outl(val, emu->port + GPIO);
98 }
99}
100
101static void ca0106_set_capture_source(struct snd_ca0106 *emu)
102{
103 unsigned int val = emu->capture_source;
104 unsigned int source, mask;
105 source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
106 mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
107 snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
108}
109
110static void ca0106_set_i2c_capture_source(struct snd_ca0106 *emu,
111 unsigned int val, int force)
112{
113 unsigned int ngain, ogain;
114 u32 source;
115
116 snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
117 ngain = emu->i2c_capture_volume[val][0]; /* Left */
118 ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
119 if (force || ngain != ogain)
120 snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ngain & 0xff);
121 ngain = emu->i2c_capture_volume[val][1]; /* Right */
122 ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */
123 if (force || ngain != ogain)
124 snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ngain & 0xff);
125 source = 1 << val;
126 snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */
127 emu->i2c_capture_source = val;
128}
129
130static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu)
131{
132 u32 tmp;
133
134 if (emu->capture_mic_line_in) {
135 /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
136 tmp = inl(emu->port+GPIO) & ~0x400;
137 tmp = tmp | 0x400;
138 outl(tmp, emu->port+GPIO);
139 /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */
140 } else {
141 /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
142 tmp = inl(emu->port+GPIO) & ~0x400;
143 outl(tmp, emu->port+GPIO);
144 /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */
145 }
146}
147
148static void ca0106_set_spdif_bits(struct snd_ca0106 *emu, int idx)
149{
Takashi Iwai3d4758292008-12-19 12:13:18 +0100150 snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, emu->spdif_str_bits[idx]);
Takashi Iwai5da95272008-11-24 14:06:08 +0100151}
152
153/*
154 */
Takashi Iwai0cb29ea2007-01-29 15:33:49 +0100155static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
156static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
Jaroslav Kysela42750b02006-06-01 18:34:01 +0200157
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200158#define snd_ca0106_shared_spdif_info snd_ctl_boolean_mono_info
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100160static int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol,
161 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100163 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100165 ucontrol->value.integer.value[0] = emu->spdif_enable;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 return 0;
167}
168
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100169static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol,
170 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100172 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 unsigned int val;
174 int change = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100176 val = !!ucontrol->value.integer.value[0];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 change = (emu->spdif_enable != val);
178 if (change) {
179 emu->spdif_enable = val;
Takashi Iwai5da95272008-11-24 14:06:08 +0100180 ca0106_spdif_enable(emu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 }
182 return change;
183}
184
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100185static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol,
186 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187{
Takashi Iwai95a98262005-11-17 10:40:18 +0100188 static char *texts[6] = {
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100189 "IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out"
Takashi Iwai95a98262005-11-17 10:40:18 +0100190 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191
192 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
193 uinfo->count = 1;
194 uinfo->value.enumerated.items = 6;
195 if (uinfo->value.enumerated.item > 5)
196 uinfo->value.enumerated.item = 5;
197 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
198 return 0;
199}
200
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100201static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol,
202 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100204 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205
206 ucontrol->value.enumerated.item[0] = emu->capture_source;
207 return 0;
208}
209
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100210static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
211 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100213 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 unsigned int val;
215 int change = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216
217 val = ucontrol->value.enumerated.item[0] ;
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100218 if (val >= 6)
219 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 change = (emu->capture_source != val);
221 if (change) {
222 emu->capture_source = val;
Takashi Iwai5da95272008-11-24 14:06:08 +0100223 ca0106_set_capture_source(emu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 }
225 return change;
226}
227
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100228static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
229 struct snd_ctl_elem_info *uinfo)
230{
231 static char *texts[6] = {
232 "Phone", "Mic", "Line in", "Aux"
233 };
234
235 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
236 uinfo->count = 1;
237 uinfo->value.enumerated.items = 4;
238 if (uinfo->value.enumerated.item > 3)
239 uinfo->value.enumerated.item = 3;
240 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
241 return 0;
242}
243
244static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
245 struct snd_ctl_elem_value *ucontrol)
246{
247 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
248
249 ucontrol->value.enumerated.item[0] = emu->i2c_capture_source;
250 return 0;
251}
252
253static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
254 struct snd_ctl_elem_value *ucontrol)
255{
256 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
257 unsigned int source_id;
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100258 int change = 0;
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100259 /* If the capture source has changed,
260 * update the capture volume from the cached value
261 * for the particular source.
262 */
263 source_id = ucontrol->value.enumerated.item[0] ;
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100264 if (source_id >= 4)
265 return -EINVAL;
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100266 change = (emu->i2c_capture_source != source_id);
267 if (change) {
Takashi Iwai5da95272008-11-24 14:06:08 +0100268 ca0106_set_i2c_capture_source(emu, source_id, 0);
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100269 }
270 return change;
271}
272
James Courtier-Duttonbe0b7b02006-04-09 20:48:44 +0100273static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol,
274 struct snd_ctl_elem_info *uinfo)
275{
276 static char *texts[2] = { "Side out", "Line in" };
277
278 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
279 uinfo->count = 1;
280 uinfo->value.enumerated.items = 2;
281 if (uinfo->value.enumerated.item > 1)
282 uinfo->value.enumerated.item = 1;
283 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
284 return 0;
285}
286
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100287static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol,
288 struct snd_ctl_elem_info *uinfo)
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200289{
290 static char *texts[2] = { "Line in", "Mic in" };
291
292 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
293 uinfo->count = 1;
294 uinfo->value.enumerated.items = 2;
295 if (uinfo->value.enumerated.item > 1)
296 uinfo->value.enumerated.item = 1;
297 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
298 return 0;
299}
300
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100301static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol,
302 struct snd_ctl_elem_value *ucontrol)
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200303{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100304 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200305
306 ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in;
307 return 0;
308}
309
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100310static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
311 struct snd_ctl_elem_value *ucontrol)
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200312{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100313 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200314 unsigned int val;
315 int change = 0;
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200316
317 val = ucontrol->value.enumerated.item[0] ;
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100318 if (val > 1)
319 return -EINVAL;
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200320 change = (emu->capture_mic_line_in != val);
321 if (change) {
322 emu->capture_mic_line_in = val;
Takashi Iwai5da95272008-11-24 14:06:08 +0100323 ca0106_set_capture_mic_line_in(emu);
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200324 }
325 return change;
326}
327
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100328static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata =
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200329{
330 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100331 .name = "Shared Mic/Line in Capture Switch",
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200332 .info = snd_ca0106_capture_mic_line_in_info,
333 .get = snd_ca0106_capture_mic_line_in_get,
334 .put = snd_ca0106_capture_mic_line_in_put
335};
336
James Courtier-Duttonbe0b7b02006-04-09 20:48:44 +0100337static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out __devinitdata =
338{
339 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
340 .name = "Shared Line in/Side out Capture Switch",
341 .info = snd_ca0106_capture_line_in_side_out_info,
342 .get = snd_ca0106_capture_mic_line_in_get,
343 .put = snd_ca0106_capture_mic_line_in_put
344};
345
346
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100347static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol,
348 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349{
350 uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
351 uinfo->count = 1;
352 return 0;
353}
354
Takashi Iwai3d4758292008-12-19 12:13:18 +0100355static void decode_spdif_bits(unsigned char *status, unsigned int bits)
356{
357 status[0] = (bits >> 0) & 0xff;
358 status[1] = (bits >> 8) & 0xff;
359 status[2] = (bits >> 16) & 0xff;
360 status[3] = (bits >> 24) & 0xff;
361}
362
363static int snd_ca0106_spdif_get_default(struct snd_kcontrol *kcontrol,
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100364 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100366 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
368
Takashi Iwai3d4758292008-12-19 12:13:18 +0100369 decode_spdif_bits(ucontrol->value.iec958.status,
370 emu->spdif_bits[idx]);
371 return 0;
372}
373
374static int snd_ca0106_spdif_get_stream(struct snd_kcontrol *kcontrol,
375 struct snd_ctl_elem_value *ucontrol)
376{
377 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
378 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
379
380 decode_spdif_bits(ucontrol->value.iec958.status,
381 emu->spdif_str_bits[idx]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 return 0;
383}
384
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100385static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol,
386 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387{
388 ucontrol->value.iec958.status[0] = 0xff;
389 ucontrol->value.iec958.status[1] = 0xff;
390 ucontrol->value.iec958.status[2] = 0xff;
391 ucontrol->value.iec958.status[3] = 0xff;
392 return 0;
393}
394
Takashi Iwai3d4758292008-12-19 12:13:18 +0100395static unsigned int encode_spdif_bits(unsigned char *status)
396{
397 return ((unsigned int)status[0] << 0) |
398 ((unsigned int)status[1] << 8) |
399 ((unsigned int)status[2] << 16) |
400 ((unsigned int)status[3] << 24);
401}
402
403static int snd_ca0106_spdif_put_default(struct snd_kcontrol *kcontrol,
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100404 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100406 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 unsigned int val;
409
Takashi Iwai3d4758292008-12-19 12:13:18 +0100410 val = encode_spdif_bits(ucontrol->value.iec958.status);
411 if (val != emu->spdif_bits[idx]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 emu->spdif_bits[idx] = val;
Takashi Iwai3d4758292008-12-19 12:13:18 +0100413 /* FIXME: this isn't safe, but needed to keep the compatibility
414 * with older alsa-lib config
415 */
416 emu->spdif_str_bits[idx] = val;
Takashi Iwai5da95272008-11-24 14:06:08 +0100417 ca0106_set_spdif_bits(emu, idx);
Takashi Iwai3d4758292008-12-19 12:13:18 +0100418 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 }
Takashi Iwai3d4758292008-12-19 12:13:18 +0100420 return 0;
421}
422
423static int snd_ca0106_spdif_put_stream(struct snd_kcontrol *kcontrol,
424 struct snd_ctl_elem_value *ucontrol)
425{
426 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
427 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
428 unsigned int val;
429
430 val = encode_spdif_bits(ucontrol->value.iec958.status);
431 if (val != emu->spdif_str_bits[idx]) {
432 emu->spdif_str_bits[idx] = val;
433 ca0106_set_spdif_bits(emu, idx);
434 return 1;
435 }
436 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437}
438
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100439static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol,
440 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441{
442 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
443 uinfo->count = 2;
444 uinfo->value.integer.min = 0;
445 uinfo->value.integer.max = 255;
446 return 0;
447}
448
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100449static int snd_ca0106_volume_get(struct snd_kcontrol *kcontrol,
450 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100452 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 unsigned int value;
Takashi Iwai95a98262005-11-17 10:40:18 +0100454 int channel_id, reg;
455
456 channel_id = (kcontrol->private_value >> 8) & 0xff;
457 reg = kcontrol->private_value & 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458
459 value = snd_ca0106_ptr_read(emu, reg, channel_id);
460 ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
461 ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
462 return 0;
463}
464
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100465static int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol,
466 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100468 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Takashi Iwai95a98262005-11-17 10:40:18 +0100469 unsigned int oval, nval;
470 int channel_id, reg;
471
472 channel_id = (kcontrol->private_value >> 8) & 0xff;
473 reg = kcontrol->private_value & 0xff;
474
475 oval = snd_ca0106_ptr_read(emu, reg, channel_id);
476 nval = ((0xff - ucontrol->value.integer.value[0]) << 24) |
477 ((0xff - ucontrol->value.integer.value[1]) << 16);
478 nval |= ((0xff - ucontrol->value.integer.value[0]) << 8) |
479 ((0xff - ucontrol->value.integer.value[1]) );
480 if (oval == nval)
481 return 0;
482 snd_ca0106_ptr_write(emu, reg, channel_id, nval);
483 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484}
485
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100486static int snd_ca0106_i2c_volume_info(struct snd_kcontrol *kcontrol,
487 struct snd_ctl_elem_info *uinfo)
488{
489 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
490 uinfo->count = 2;
491 uinfo->value.integer.min = 0;
492 uinfo->value.integer.max = 255;
493 return 0;
494}
495
496static int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol,
497 struct snd_ctl_elem_value *ucontrol)
498{
499 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
500 int source_id;
501
502 source_id = kcontrol->private_value;
503
504 ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0];
505 ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1];
506 return 0;
507}
508
509static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol,
510 struct snd_ctl_elem_value *ucontrol)
511{
512 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
513 unsigned int ogain;
514 unsigned int ngain;
515 int source_id;
516 int change = 0;
517
518 source_id = kcontrol->private_value;
519 ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
520 ngain = ucontrol->value.integer.value[0];
521 if (ngain > 0xff)
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100522 return -EINVAL;
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100523 if (ogain != ngain) {
524 if (emu->i2c_capture_source == source_id)
525 snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
526 emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0];
527 change = 1;
528 }
529 ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
530 ngain = ucontrol->value.integer.value[1];
531 if (ngain > 0xff)
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100532 return -EINVAL;
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100533 if (ogain != ngain) {
534 if (emu->i2c_capture_source == source_id)
535 snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
536 emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1];
537 change = 1;
538 }
539
540 return change;
541}
542
Trent Piephob18cd532007-07-24 12:06:16 +0200543#define spi_mute_info snd_ctl_boolean_mono_info
544
545static int spi_mute_get(struct snd_kcontrol *kcontrol,
546 struct snd_ctl_elem_value *ucontrol)
547{
548 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
549 unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
550 unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
551
552 ucontrol->value.integer.value[0] = !(emu->spi_dac_reg[reg] & bit);
553 return 0;
554}
555
556static int spi_mute_put(struct snd_kcontrol *kcontrol,
557 struct snd_ctl_elem_value *ucontrol)
558{
559 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
560 unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
561 unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
562 int ret;
563
564 ret = emu->spi_dac_reg[reg] & bit;
565 if (ucontrol->value.integer.value[0]) {
566 if (!ret) /* bit already cleared, do nothing */
567 return 0;
568 emu->spi_dac_reg[reg] &= ~bit;
569 } else {
570 if (ret) /* bit already set, do nothing */
571 return 0;
572 emu->spi_dac_reg[reg] |= bit;
573 }
574
575 ret = snd_ca0106_spi_write(emu, emu->spi_dac_reg[reg]);
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100576 return ret ? -EINVAL : 1;
Trent Piephob18cd532007-07-24 12:06:16 +0200577}
578
Takashi Iwai95a98262005-11-17 10:40:18 +0100579#define CA_VOLUME(xname,chid,reg) \
580{ \
581 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
Jaroslav Kysela302e9c52006-07-05 17:39:49 +0200582 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
583 SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100584 .info = snd_ca0106_volume_info, \
585 .get = snd_ca0106_volume_get, \
586 .put = snd_ca0106_volume_put, \
Takashi Iwai7cf0a952006-08-17 16:23:07 +0200587 .tlv = { .p = snd_ca0106_db_scale1 }, \
Takashi Iwai95a98262005-11-17 10:40:18 +0100588 .private_value = ((chid) << 8) | (reg) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589}
590
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100591static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
Takashi Iwai95a98262005-11-17 10:40:18 +0100592 CA_VOLUME("Analog Front Playback Volume",
593 CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2),
594 CA_VOLUME("Analog Rear Playback Volume",
595 CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2),
596 CA_VOLUME("Analog Center/LFE Playback Volume",
597 CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2),
598 CA_VOLUME("Analog Side Playback Volume",
599 CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2),
600
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100601 CA_VOLUME("IEC958 Front Playback Volume",
Takashi Iwai95a98262005-11-17 10:40:18 +0100602 CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1),
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100603 CA_VOLUME("IEC958 Rear Playback Volume",
Takashi Iwai95a98262005-11-17 10:40:18 +0100604 CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1),
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100605 CA_VOLUME("IEC958 Center/LFE Playback Volume",
Takashi Iwai95a98262005-11-17 10:40:18 +0100606 CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1),
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100607 CA_VOLUME("IEC958 Unknown Playback Volume",
Takashi Iwai95a98262005-11-17 10:40:18 +0100608 CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1),
609
610 CA_VOLUME("CAPTURE feedback Playback Volume",
611 1, CAPTURE_CONTROL),
612
613 {
614 .access = SNDRV_CTL_ELEM_ACCESS_READ,
615 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
616 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
617 .count = 4,
618 .info = snd_ca0106_spdif_info,
619 .get = snd_ca0106_spdif_get_mask
620 },
621 {
622 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100623 .name = "IEC958 Playback Switch",
Takashi Iwai95a98262005-11-17 10:40:18 +0100624 .info = snd_ca0106_shared_spdif_info,
625 .get = snd_ca0106_shared_spdif_get,
626 .put = snd_ca0106_shared_spdif_put
627 },
628 {
629 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
James Courtier-Duttone6327cf2006-11-11 10:52:06 +0000630 .name = "Digital Source Capture Enum",
Takashi Iwai95a98262005-11-17 10:40:18 +0100631 .info = snd_ca0106_capture_source_info,
632 .get = snd_ca0106_capture_source_get,
633 .put = snd_ca0106_capture_source_put
634 },
635 {
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100636 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
James Courtier-Duttone6327cf2006-11-11 10:52:06 +0000637 .name = "Analog Source Capture Enum",
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100638 .info = snd_ca0106_i2c_capture_source_info,
639 .get = snd_ca0106_i2c_capture_source_get,
640 .put = snd_ca0106_i2c_capture_source_put
641 },
642 {
Takashi Iwai95a98262005-11-17 10:40:18 +0100643 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
644 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
645 .count = 4,
646 .info = snd_ca0106_spdif_info,
Takashi Iwai3d4758292008-12-19 12:13:18 +0100647 .get = snd_ca0106_spdif_get_default,
648 .put = snd_ca0106_spdif_put_default
649 },
650 {
651 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
652 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
653 .count = 4,
654 .info = snd_ca0106_spdif_info,
655 .get = snd_ca0106_spdif_get_stream,
656 .put = snd_ca0106_spdif_put_stream
Takashi Iwai95a98262005-11-17 10:40:18 +0100657 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658};
659
James Courtier-Dutton7c157062006-12-10 00:00:38 +0000660#define I2C_VOLUME(xname,chid) \
661{ \
662 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
663 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
664 SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
665 .info = snd_ca0106_i2c_volume_info, \
666 .get = snd_ca0106_i2c_volume_get, \
667 .put = snd_ca0106_i2c_volume_put, \
668 .tlv = { .p = snd_ca0106_db_scale2 }, \
669 .private_value = chid \
670}
671
672static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata = {
673 I2C_VOLUME("Phone Capture Volume", 0),
674 I2C_VOLUME("Mic Capture Volume", 1),
675 I2C_VOLUME("Line in Capture Volume", 2),
676 I2C_VOLUME("Aux Capture Volume", 3),
677};
678
Andy Owen64e53102010-10-23 22:12:33 +1100679static const int spi_dmute_reg[] = {
680 SPI_DMUTE0_REG,
681 SPI_DMUTE1_REG,
682 SPI_DMUTE2_REG,
683 0,
684 SPI_DMUTE4_REG,
Trent Piephob18cd532007-07-24 12:06:16 +0200685};
Andy Owen64e53102010-10-23 22:12:33 +1100686static const int spi_dmute_bit[] = {
687 SPI_DMUTE0_BIT,
688 SPI_DMUTE1_BIT,
689 SPI_DMUTE2_BIT,
690 0,
691 SPI_DMUTE4_BIT,
692};
693
694static struct snd_kcontrol_new __devinit
695snd_ca0106_volume_spi_dac_ctl(struct snd_ca0106_details *details,
696 int channel_id)
697{
698 struct snd_kcontrol_new spi_switch = {0};
699 int reg, bit;
700 int dac_id;
701
702 spi_switch.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
703 spi_switch.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
704 spi_switch.info = spi_mute_info;
705 spi_switch.get = spi_mute_get;
706 spi_switch.put = spi_mute_put;
707
708 switch (channel_id) {
709 case PCM_FRONT_CHANNEL:
710 spi_switch.name = "Analog Front Playback Switch";
711 dac_id = (details->spi_dac & 0xf000) >> (4 * 3);
712 break;
713 case PCM_REAR_CHANNEL:
714 spi_switch.name = "Analog Rear Playback Switch";
715 dac_id = (details->spi_dac & 0x0f00) >> (4 * 2);
716 break;
717 case PCM_CENTER_LFE_CHANNEL:
718 spi_switch.name = "Analog Center/LFE Playback Switch";
719 dac_id = (details->spi_dac & 0x00f0) >> (4 * 1);
720 break;
721 case PCM_UNKNOWN_CHANNEL:
722 spi_switch.name = "Analog Side Playback Switch";
723 dac_id = (details->spi_dac & 0x000f) >> (4 * 0);
724 break;
725 default:
726 /* Unused channel */
727 spi_switch.name = NULL;
728 dac_id = 0;
729 }
730 reg = spi_dmute_reg[dac_id];
731 bit = spi_dmute_bit[dac_id];
732
733 spi_switch.private_value = (reg << SPI_REG_SHIFT) | bit;
734
735 return spi_switch;
736}
Trent Piephob18cd532007-07-24 12:06:16 +0200737
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100738static int __devinit remove_ctl(struct snd_card *card, const char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100740 struct snd_ctl_elem_id id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 memset(&id, 0, sizeof(id));
742 strcpy(id.name, name);
743 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
744 return snd_ctl_remove_id(card, &id);
745}
746
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100747static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, const char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100749 struct snd_ctl_elem_id sid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 memset(&sid, 0, sizeof(sid));
751 /* FIXME: strcpy is bad. */
752 strcpy(sid.name, name);
753 sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
754 return snd_ctl_find_id(card, &sid);
755}
756
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100757static int __devinit rename_ctl(struct snd_card *card, const char *src, const char *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100759 struct snd_kcontrol *kctl = ctl_find(card, src);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 if (kctl) {
761 strcpy(kctl->id.name, dst);
762 return 0;
763 }
764 return -ENOENT;
765}
766
Trent Piephofca7f382007-07-24 12:10:34 +0200767#define ADD_CTLS(emu, ctls) \
768 do { \
Harvey Harrisonbed515b2008-02-28 12:02:56 +0100769 int i, _err; \
Trent Piephofca7f382007-07-24 12:10:34 +0200770 for (i = 0; i < ARRAY_SIZE(ctls); i++) { \
Harvey Harrisonbed515b2008-02-28 12:02:56 +0100771 _err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \
772 if (_err < 0) \
773 return _err; \
Trent Piephofca7f382007-07-24 12:10:34 +0200774 } \
775 } while (0)
776
Takashi Iwai49c88b82008-02-18 13:06:49 +0100777static __devinitdata
Takashi Iwaic4865672009-06-08 12:57:17 +0200778DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 25, 1);
Takashi Iwai49c88b82008-02-18 13:06:49 +0100779
780static char *slave_vols[] __devinitdata = {
781 "Analog Front Playback Volume",
782 "Analog Rear Playback Volume",
783 "Analog Center/LFE Playback Volume",
784 "Analog Side Playback Volume",
785 "IEC958 Front Playback Volume",
786 "IEC958 Rear Playback Volume",
787 "IEC958 Center/LFE Playback Volume",
788 "IEC958 Unknown Playback Volume",
789 "CAPTURE feedback Playback Volume",
790 NULL
791};
792
793static char *slave_sws[] __devinitdata = {
794 "Analog Front Playback Switch",
795 "Analog Rear Playback Switch",
796 "Analog Center/LFE Playback Switch",
797 "Analog Side Playback Switch",
798 "IEC958 Playback Switch",
799 NULL
800};
801
802static void __devinit add_slaves(struct snd_card *card,
803 struct snd_kcontrol *master, char **list)
804{
805 for (; *list; list++) {
806 struct snd_kcontrol *slave = ctl_find(card, *list);
807 if (slave)
808 snd_ctl_add_slave(master, slave);
809 }
810}
811
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100812int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813{
Trent Piephofca7f382007-07-24 12:10:34 +0200814 int err;
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100815 struct snd_card *card = emu->card;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 char **c;
Takashi Iwai49c88b82008-02-18 13:06:49 +0100817 struct snd_kcontrol *vmaster;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 static char *ca0106_remove_ctls[] = {
819 "Master Mono Playback Switch",
820 "Master Mono Playback Volume",
821 "3D Control - Switch",
822 "3D Control Sigmatel - Depth",
823 "PCM Playback Switch",
824 "PCM Playback Volume",
825 "CD Playback Switch",
826 "CD Playback Volume",
827 "Phone Playback Switch",
828 "Phone Playback Volume",
829 "Video Playback Switch",
830 "Video Playback Volume",
Jaroslav Kyselad355c82a2009-11-03 15:47:25 +0100831 "Beep Playback Switch",
832 "Beep Playback Volume",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 "Mono Output Select",
834 "Capture Source",
835 "Capture Switch",
836 "Capture Volume",
837 "External Amplifier",
838 "Sigmatel 4-Speaker Stereo Playback Switch",
Andreas Mohrafe6d7e2009-05-22 17:48:58 +0200839 "Surround Phase Inversion Playback Switch",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 NULL
841 };
842 static char *ca0106_rename_ctls[] = {
843 "Master Playback Switch", "Capture Switch",
844 "Master Playback Volume", "Capture Volume",
845 "Line Playback Switch", "AC97 Line Capture Switch",
846 "Line Playback Volume", "AC97 Line Capture Volume",
847 "Aux Playback Switch", "AC97 Aux Capture Switch",
848 "Aux Playback Volume", "AC97 Aux Capture Volume",
849 "Mic Playback Switch", "AC97 Mic Capture Switch",
850 "Mic Playback Volume", "AC97 Mic Capture Volume",
851 "Mic Select", "AC97 Mic Select",
852 "Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)",
853 NULL
854 };
855#if 1
Takashi Iwai95a98262005-11-17 10:40:18 +0100856 for (c = ca0106_remove_ctls; *c; c++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 remove_ctl(card, *c);
Takashi Iwai95a98262005-11-17 10:40:18 +0100858 for (c = ca0106_rename_ctls; *c; c += 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 rename_ctl(card, c[0], c[1]);
860#endif
861
Trent Piephofca7f382007-07-24 12:10:34 +0200862 ADD_CTLS(emu, snd_ca0106_volume_ctls);
Takashi Iwai95a98262005-11-17 10:40:18 +0100863 if (emu->details->i2c_adc == 1) {
Trent Piephofca7f382007-07-24 12:10:34 +0200864 ADD_CTLS(emu, snd_ca0106_volume_i2c_adc_ctls);
James Courtier-Duttonbe0b7b02006-04-09 20:48:44 +0100865 if (emu->details->gpio_type == 1)
866 err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu));
867 else /* gpio_type == 2 */
868 err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu));
Takashi Iwai95a98262005-11-17 10:40:18 +0100869 if (err < 0)
870 return err;
871 }
Andy Owen64e53102010-10-23 22:12:33 +1100872 if (emu->details->spi_dac) {
873 int i;
874 for (i = 0;; i++) {
875 struct snd_kcontrol_new ctl;
876 ctl = snd_ca0106_volume_spi_dac_ctl(emu->details, i);
877 if (!ctl.name)
878 break;
879 err = snd_ctl_add(card, snd_ctl_new1(&ctl, emu));
880 if (err < 0)
881 return err;
882 }
883 }
Takashi Iwai49c88b82008-02-18 13:06:49 +0100884
885 /* Create virtual master controls */
886 vmaster = snd_ctl_make_virtual_master("Master Playback Volume",
887 snd_ca0106_master_db_scale);
888 if (!vmaster)
889 return -ENOMEM;
Takashi Iwai601e1cc2009-06-02 11:37:01 +0200890 err = snd_ctl_add(card, vmaster);
891 if (err < 0)
892 return err;
Takashi Iwai49c88b82008-02-18 13:06:49 +0100893 add_slaves(card, vmaster, slave_vols);
894
Andy Owen6fef1532010-10-23 22:12:32 +1100895 if (emu->details->spi_dac) {
Takashi Iwai49c88b82008-02-18 13:06:49 +0100896 vmaster = snd_ctl_make_virtual_master("Master Playback Switch",
897 NULL);
898 if (!vmaster)
899 return -ENOMEM;
Takashi Iwai601e1cc2009-06-02 11:37:01 +0200900 err = snd_ctl_add(card, vmaster);
901 if (err < 0)
902 return err;
Takashi Iwai49c88b82008-02-18 13:06:49 +0100903 add_slaves(card, vmaster, slave_sws);
904 }
Takashi Iwaieeaf1002009-06-02 16:06:02 +0200905
906 strcpy(card->mixername, "CA0106");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 return 0;
908}
909
Takashi Iwaic7561cd2012-08-14 18:12:04 +0200910#ifdef CONFIG_PM_SLEEP
Takashi Iwai5da95272008-11-24 14:06:08 +0100911struct ca0106_vol_tbl {
Takashi Iwai5da95272008-11-24 14:06:08 +0100912 unsigned int channel_id;
Takashi Iwai8df0f702008-12-19 13:55:17 +0100913 unsigned int reg;
Takashi Iwai5da95272008-11-24 14:06:08 +0100914};
915
916static struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = {
917 { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2 },
918 { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2 },
919 { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2 },
920 { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2 },
921 { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1 },
922 { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1 },
923 { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1 },
924 { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1 },
925 { 1, CAPTURE_CONTROL },
926};
927
928void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip)
929{
930 int i;
931
932 /* save volumes */
933 for (i = 0; i < NUM_SAVED_VOLUMES; i++)
934 chip->saved_vol[i] =
935 snd_ca0106_ptr_read(chip, saved_volumes[i].reg,
936 saved_volumes[i].channel_id);
937}
938
939void snd_ca0106_mixer_resume(struct snd_ca0106 *chip)
940{
941 int i;
942
943 for (i = 0; i < NUM_SAVED_VOLUMES; i++)
944 snd_ca0106_ptr_write(chip, saved_volumes[i].reg,
945 saved_volumes[i].channel_id,
946 chip->saved_vol[i]);
947
948 ca0106_spdif_enable(chip);
949 ca0106_set_capture_source(chip);
950 ca0106_set_i2c_capture_source(chip, chip->i2c_capture_source, 1);
951 for (i = 0; i < 4; i++)
952 ca0106_set_spdif_bits(chip, i);
953 if (chip->details->i2c_adc)
954 ca0106_set_capture_mic_line_in(chip);
955}
Takashi Iwaic7561cd2012-08-14 18:12:04 +0200956#endif /* CONFIG_PM_SLEEP */