blob: 025805cba7795e27ab365fcf7dd7f5495ace0ee7 [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>
Takashi Iwai6cbbfe12015-01-28 16:49:33 +010073#include <linux/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 Iwaide95eae2014-10-20 18:16:26 +0200188 static const char * const 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
Takashi Iwaide95eae2014-10-20 18:16:26 +0200192 return snd_ctl_enum_info(uinfo, 1, 6, texts);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193}
194
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100195static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol,
196 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100198 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199
200 ucontrol->value.enumerated.item[0] = emu->capture_source;
201 return 0;
202}
203
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100204static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
205 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100207 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 unsigned int val;
209 int change = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210
211 val = ucontrol->value.enumerated.item[0] ;
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100212 if (val >= 6)
213 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 change = (emu->capture_source != val);
215 if (change) {
216 emu->capture_source = val;
Takashi Iwai5da95272008-11-24 14:06:08 +0100217 ca0106_set_capture_source(emu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 }
219 return change;
220}
221
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100222static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
223 struct snd_ctl_elem_info *uinfo)
224{
Takashi Iwaide95eae2014-10-20 18:16:26 +0200225 static const char * const texts[4] = {
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100226 "Phone", "Mic", "Line in", "Aux"
227 };
228
Takashi Iwaide95eae2014-10-20 18:16:26 +0200229 return snd_ctl_enum_info(uinfo, 1, 4, texts);
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100230}
231
232static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
233 struct snd_ctl_elem_value *ucontrol)
234{
235 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
236
237 ucontrol->value.enumerated.item[0] = emu->i2c_capture_source;
238 return 0;
239}
240
241static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
242 struct snd_ctl_elem_value *ucontrol)
243{
244 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
245 unsigned int source_id;
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100246 int change = 0;
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100247 /* If the capture source has changed,
248 * update the capture volume from the cached value
249 * for the particular source.
250 */
251 source_id = ucontrol->value.enumerated.item[0] ;
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100252 if (source_id >= 4)
253 return -EINVAL;
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100254 change = (emu->i2c_capture_source != source_id);
255 if (change) {
Takashi Iwai5da95272008-11-24 14:06:08 +0100256 ca0106_set_i2c_capture_source(emu, source_id, 0);
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100257 }
258 return change;
259}
260
James Courtier-Duttonbe0b7b02006-04-09 20:48:44 +0100261static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol,
262 struct snd_ctl_elem_info *uinfo)
263{
Takashi Iwaide95eae2014-10-20 18:16:26 +0200264 static const char * const texts[2] = { "Side out", "Line in" };
James Courtier-Duttonbe0b7b02006-04-09 20:48:44 +0100265
Takashi Iwaide95eae2014-10-20 18:16:26 +0200266 return snd_ctl_enum_info(uinfo, 1, 2, texts);
James Courtier-Duttonbe0b7b02006-04-09 20:48:44 +0100267}
268
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100269static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol,
270 struct snd_ctl_elem_info *uinfo)
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200271{
Takashi Iwaide95eae2014-10-20 18:16:26 +0200272 static const char * const texts[2] = { "Line in", "Mic in" };
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200273
Takashi Iwaide95eae2014-10-20 18:16:26 +0200274 return snd_ctl_enum_info(uinfo, 1, 2, texts);
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200275}
276
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100277static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol,
278 struct snd_ctl_elem_value *ucontrol)
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200279{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100280 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200281
282 ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in;
283 return 0;
284}
285
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100286static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
287 struct snd_ctl_elem_value *ucontrol)
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200288{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100289 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200290 unsigned int val;
291 int change = 0;
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200292
293 val = ucontrol->value.enumerated.item[0] ;
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100294 if (val > 1)
295 return -EINVAL;
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200296 change = (emu->capture_mic_line_in != val);
297 if (change) {
298 emu->capture_mic_line_in = val;
Takashi Iwai5da95272008-11-24 14:06:08 +0100299 ca0106_set_capture_mic_line_in(emu);
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200300 }
301 return change;
302}
303
Bill Pembertone23e7a12012-12-06 12:35:10 -0500304static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in =
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200305{
306 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100307 .name = "Shared Mic/Line in Capture Switch",
James Courtier-Duttoned144f32005-05-27 23:28:27 +0200308 .info = snd_ca0106_capture_mic_line_in_info,
309 .get = snd_ca0106_capture_mic_line_in_get,
310 .put = snd_ca0106_capture_mic_line_in_put
311};
312
Bill Pembertone23e7a12012-12-06 12:35:10 -0500313static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out =
James Courtier-Duttonbe0b7b02006-04-09 20:48:44 +0100314{
315 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
316 .name = "Shared Line in/Side out Capture Switch",
317 .info = snd_ca0106_capture_line_in_side_out_info,
318 .get = snd_ca0106_capture_mic_line_in_get,
319 .put = snd_ca0106_capture_mic_line_in_put
320};
321
322
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100323static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol,
324 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325{
326 uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
327 uinfo->count = 1;
328 return 0;
329}
330
Takashi Iwai3d4758292008-12-19 12:13:18 +0100331static void decode_spdif_bits(unsigned char *status, unsigned int bits)
332{
333 status[0] = (bits >> 0) & 0xff;
334 status[1] = (bits >> 8) & 0xff;
335 status[2] = (bits >> 16) & 0xff;
336 status[3] = (bits >> 24) & 0xff;
337}
338
339static int snd_ca0106_spdif_get_default(struct snd_kcontrol *kcontrol,
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100340 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100342 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
344
Takashi Iwai3d4758292008-12-19 12:13:18 +0100345 decode_spdif_bits(ucontrol->value.iec958.status,
346 emu->spdif_bits[idx]);
347 return 0;
348}
349
350static int snd_ca0106_spdif_get_stream(struct snd_kcontrol *kcontrol,
351 struct snd_ctl_elem_value *ucontrol)
352{
353 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
354 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
355
356 decode_spdif_bits(ucontrol->value.iec958.status,
357 emu->spdif_str_bits[idx]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 return 0;
359}
360
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100361static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol,
362 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363{
364 ucontrol->value.iec958.status[0] = 0xff;
365 ucontrol->value.iec958.status[1] = 0xff;
366 ucontrol->value.iec958.status[2] = 0xff;
367 ucontrol->value.iec958.status[3] = 0xff;
368 return 0;
369}
370
Takashi Iwai3d4758292008-12-19 12:13:18 +0100371static unsigned int encode_spdif_bits(unsigned char *status)
372{
373 return ((unsigned int)status[0] << 0) |
374 ((unsigned int)status[1] << 8) |
375 ((unsigned int)status[2] << 16) |
376 ((unsigned int)status[3] << 24);
377}
378
379static int snd_ca0106_spdif_put_default(struct snd_kcontrol *kcontrol,
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100380 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 unsigned int val;
385
Takashi Iwai3d4758292008-12-19 12:13:18 +0100386 val = encode_spdif_bits(ucontrol->value.iec958.status);
387 if (val != emu->spdif_bits[idx]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 emu->spdif_bits[idx] = val;
Takashi Iwai3d4758292008-12-19 12:13:18 +0100389 /* FIXME: this isn't safe, but needed to keep the compatibility
390 * with older alsa-lib config
391 */
392 emu->spdif_str_bits[idx] = val;
Takashi Iwai5da95272008-11-24 14:06:08 +0100393 ca0106_set_spdif_bits(emu, idx);
Takashi Iwai3d4758292008-12-19 12:13:18 +0100394 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 }
Takashi Iwai3d4758292008-12-19 12:13:18 +0100396 return 0;
397}
398
399static int snd_ca0106_spdif_put_stream(struct snd_kcontrol *kcontrol,
400 struct snd_ctl_elem_value *ucontrol)
401{
402 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
403 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
404 unsigned int val;
405
406 val = encode_spdif_bits(ucontrol->value.iec958.status);
407 if (val != emu->spdif_str_bits[idx]) {
408 emu->spdif_str_bits[idx] = val;
409 ca0106_set_spdif_bits(emu, idx);
410 return 1;
411 }
412 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413}
414
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100415static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol,
416 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417{
418 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
419 uinfo->count = 2;
420 uinfo->value.integer.min = 0;
421 uinfo->value.integer.max = 255;
422 return 0;
423}
424
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100425static int snd_ca0106_volume_get(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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 unsigned int value;
Takashi Iwai95a98262005-11-17 10:40:18 +0100430 int channel_id, reg;
431
432 channel_id = (kcontrol->private_value >> 8) & 0xff;
433 reg = kcontrol->private_value & 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434
435 value = snd_ca0106_ptr_read(emu, reg, channel_id);
436 ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
437 ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
438 return 0;
439}
440
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100441static int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol,
442 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100444 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
Takashi Iwai95a98262005-11-17 10:40:18 +0100445 unsigned int oval, nval;
446 int channel_id, reg;
447
448 channel_id = (kcontrol->private_value >> 8) & 0xff;
449 reg = kcontrol->private_value & 0xff;
450
451 oval = snd_ca0106_ptr_read(emu, reg, channel_id);
452 nval = ((0xff - ucontrol->value.integer.value[0]) << 24) |
453 ((0xff - ucontrol->value.integer.value[1]) << 16);
454 nval |= ((0xff - ucontrol->value.integer.value[0]) << 8) |
455 ((0xff - ucontrol->value.integer.value[1]) );
456 if (oval == nval)
457 return 0;
458 snd_ca0106_ptr_write(emu, reg, channel_id, nval);
459 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460}
461
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100462static int snd_ca0106_i2c_volume_info(struct snd_kcontrol *kcontrol,
463 struct snd_ctl_elem_info *uinfo)
464{
465 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
466 uinfo->count = 2;
467 uinfo->value.integer.min = 0;
468 uinfo->value.integer.max = 255;
469 return 0;
470}
471
472static int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol,
473 struct snd_ctl_elem_value *ucontrol)
474{
475 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
476 int source_id;
477
478 source_id = kcontrol->private_value;
479
480 ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0];
481 ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1];
482 return 0;
483}
484
485static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol,
486 struct snd_ctl_elem_value *ucontrol)
487{
488 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
489 unsigned int ogain;
490 unsigned int ngain;
491 int source_id;
492 int change = 0;
493
494 source_id = kcontrol->private_value;
495 ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
496 ngain = ucontrol->value.integer.value[0];
497 if (ngain > 0xff)
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100498 return -EINVAL;
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100499 if (ogain != ngain) {
500 if (emu->i2c_capture_source == source_id)
501 snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
502 emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0];
503 change = 1;
504 }
505 ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
506 ngain = ucontrol->value.integer.value[1];
507 if (ngain > 0xff)
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100508 return -EINVAL;
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100509 if (ogain != ngain) {
510 if (emu->i2c_capture_source == source_id)
511 snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
512 emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1];
513 change = 1;
514 }
515
516 return change;
517}
518
Trent Piephob18cd532007-07-24 12:06:16 +0200519#define spi_mute_info snd_ctl_boolean_mono_info
520
521static int spi_mute_get(struct snd_kcontrol *kcontrol,
522 struct snd_ctl_elem_value *ucontrol)
523{
524 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
525 unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
526 unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
527
528 ucontrol->value.integer.value[0] = !(emu->spi_dac_reg[reg] & bit);
529 return 0;
530}
531
532static int spi_mute_put(struct snd_kcontrol *kcontrol,
533 struct snd_ctl_elem_value *ucontrol)
534{
535 struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
536 unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
537 unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
538 int ret;
539
540 ret = emu->spi_dac_reg[reg] & bit;
541 if (ucontrol->value.integer.value[0]) {
542 if (!ret) /* bit already cleared, do nothing */
543 return 0;
544 emu->spi_dac_reg[reg] &= ~bit;
545 } else {
546 if (ret) /* bit already set, do nothing */
547 return 0;
548 emu->spi_dac_reg[reg] |= bit;
549 }
550
551 ret = snd_ca0106_spi_write(emu, emu->spi_dac_reg[reg]);
Takashi Iwai5fe619f2007-11-15 14:42:34 +0100552 return ret ? -EINVAL : 1;
Trent Piephob18cd532007-07-24 12:06:16 +0200553}
554
Takashi Iwai95a98262005-11-17 10:40:18 +0100555#define CA_VOLUME(xname,chid,reg) \
556{ \
557 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
Jaroslav Kysela302e9c52006-07-05 17:39:49 +0200558 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
559 SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100560 .info = snd_ca0106_volume_info, \
561 .get = snd_ca0106_volume_get, \
562 .put = snd_ca0106_volume_put, \
Takashi Iwai7cf0a952006-08-17 16:23:07 +0200563 .tlv = { .p = snd_ca0106_db_scale1 }, \
Takashi Iwai95a98262005-11-17 10:40:18 +0100564 .private_value = ((chid) << 8) | (reg) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565}
566
Bill Pembertone23e7a12012-12-06 12:35:10 -0500567static struct snd_kcontrol_new snd_ca0106_volume_ctls[] = {
Takashi Iwai95a98262005-11-17 10:40:18 +0100568 CA_VOLUME("Analog Front Playback Volume",
569 CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2),
570 CA_VOLUME("Analog Rear Playback Volume",
571 CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2),
572 CA_VOLUME("Analog Center/LFE Playback Volume",
573 CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2),
574 CA_VOLUME("Analog Side Playback Volume",
575 CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2),
576
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100577 CA_VOLUME("IEC958 Front Playback Volume",
Takashi Iwai95a98262005-11-17 10:40:18 +0100578 CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1),
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100579 CA_VOLUME("IEC958 Rear Playback Volume",
Takashi Iwai95a98262005-11-17 10:40:18 +0100580 CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1),
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100581 CA_VOLUME("IEC958 Center/LFE Playback Volume",
Takashi Iwai95a98262005-11-17 10:40:18 +0100582 CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1),
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100583 CA_VOLUME("IEC958 Unknown Playback Volume",
Takashi Iwai95a98262005-11-17 10:40:18 +0100584 CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1),
585
586 CA_VOLUME("CAPTURE feedback Playback Volume",
587 1, CAPTURE_CONTROL),
588
589 {
590 .access = SNDRV_CTL_ELEM_ACCESS_READ,
591 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
592 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
593 .count = 4,
594 .info = snd_ca0106_spdif_info,
595 .get = snd_ca0106_spdif_get_mask
596 },
597 {
598 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
James Courtier-Dutton39596dc2005-12-16 21:59:59 +0100599 .name = "IEC958 Playback Switch",
Takashi Iwai95a98262005-11-17 10:40:18 +0100600 .info = snd_ca0106_shared_spdif_info,
601 .get = snd_ca0106_shared_spdif_get,
602 .put = snd_ca0106_shared_spdif_put
603 },
604 {
605 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
James Courtier-Duttone6327cf2006-11-11 10:52:06 +0000606 .name = "Digital Source Capture Enum",
Takashi Iwai95a98262005-11-17 10:40:18 +0100607 .info = snd_ca0106_capture_source_info,
608 .get = snd_ca0106_capture_source_get,
609 .put = snd_ca0106_capture_source_put
610 },
611 {
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100612 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
James Courtier-Duttone6327cf2006-11-11 10:52:06 +0000613 .name = "Analog Source Capture Enum",
James Courtier-Dutton6129daa2006-04-09 13:01:34 +0100614 .info = snd_ca0106_i2c_capture_source_info,
615 .get = snd_ca0106_i2c_capture_source_get,
616 .put = snd_ca0106_i2c_capture_source_put
617 },
618 {
Takashi Iwai95a98262005-11-17 10:40:18 +0100619 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
620 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
621 .count = 4,
622 .info = snd_ca0106_spdif_info,
Takashi Iwai3d4758292008-12-19 12:13:18 +0100623 .get = snd_ca0106_spdif_get_default,
624 .put = snd_ca0106_spdif_put_default
625 },
626 {
627 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
628 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
629 .count = 4,
630 .info = snd_ca0106_spdif_info,
631 .get = snd_ca0106_spdif_get_stream,
632 .put = snd_ca0106_spdif_put_stream
Takashi Iwai95a98262005-11-17 10:40:18 +0100633 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634};
635
James Courtier-Dutton7c157062006-12-10 00:00:38 +0000636#define I2C_VOLUME(xname,chid) \
637{ \
638 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
639 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
640 SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
641 .info = snd_ca0106_i2c_volume_info, \
642 .get = snd_ca0106_i2c_volume_get, \
643 .put = snd_ca0106_i2c_volume_put, \
644 .tlv = { .p = snd_ca0106_db_scale2 }, \
645 .private_value = chid \
646}
647
Bill Pembertone23e7a12012-12-06 12:35:10 -0500648static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] = {
James Courtier-Dutton7c157062006-12-10 00:00:38 +0000649 I2C_VOLUME("Phone Capture Volume", 0),
650 I2C_VOLUME("Mic Capture Volume", 1),
651 I2C_VOLUME("Line in Capture Volume", 2),
652 I2C_VOLUME("Aux Capture Volume", 3),
653};
654
Andy Owen64e53102010-10-23 22:12:33 +1100655static const int spi_dmute_reg[] = {
656 SPI_DMUTE0_REG,
657 SPI_DMUTE1_REG,
658 SPI_DMUTE2_REG,
659 0,
660 SPI_DMUTE4_REG,
Trent Piephob18cd532007-07-24 12:06:16 +0200661};
Andy Owen64e53102010-10-23 22:12:33 +1100662static const int spi_dmute_bit[] = {
663 SPI_DMUTE0_BIT,
664 SPI_DMUTE1_BIT,
665 SPI_DMUTE2_BIT,
666 0,
667 SPI_DMUTE4_BIT,
668};
669
Bill Pembertone23e7a12012-12-06 12:35:10 -0500670static struct snd_kcontrol_new
Andy Owen64e53102010-10-23 22:12:33 +1100671snd_ca0106_volume_spi_dac_ctl(struct snd_ca0106_details *details,
672 int channel_id)
673{
674 struct snd_kcontrol_new spi_switch = {0};
675 int reg, bit;
676 int dac_id;
677
678 spi_switch.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
679 spi_switch.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
680 spi_switch.info = spi_mute_info;
681 spi_switch.get = spi_mute_get;
682 spi_switch.put = spi_mute_put;
683
684 switch (channel_id) {
685 case PCM_FRONT_CHANNEL:
686 spi_switch.name = "Analog Front Playback Switch";
687 dac_id = (details->spi_dac & 0xf000) >> (4 * 3);
688 break;
689 case PCM_REAR_CHANNEL:
690 spi_switch.name = "Analog Rear Playback Switch";
691 dac_id = (details->spi_dac & 0x0f00) >> (4 * 2);
692 break;
693 case PCM_CENTER_LFE_CHANNEL:
694 spi_switch.name = "Analog Center/LFE Playback Switch";
695 dac_id = (details->spi_dac & 0x00f0) >> (4 * 1);
696 break;
697 case PCM_UNKNOWN_CHANNEL:
698 spi_switch.name = "Analog Side Playback Switch";
699 dac_id = (details->spi_dac & 0x000f) >> (4 * 0);
700 break;
701 default:
702 /* Unused channel */
703 spi_switch.name = NULL;
704 dac_id = 0;
705 }
706 reg = spi_dmute_reg[dac_id];
707 bit = spi_dmute_bit[dac_id];
708
709 spi_switch.private_value = (reg << SPI_REG_SHIFT) | bit;
710
711 return spi_switch;
712}
Trent Piephob18cd532007-07-24 12:06:16 +0200713
Bill Pembertone23e7a12012-12-06 12:35:10 -0500714static int remove_ctl(struct snd_card *card, const char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100716 struct snd_ctl_elem_id id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 memset(&id, 0, sizeof(id));
718 strcpy(id.name, name);
719 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
720 return snd_ctl_remove_id(card, &id);
721}
722
Bill Pembertone23e7a12012-12-06 12:35:10 -0500723static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100725 struct snd_ctl_elem_id sid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 memset(&sid, 0, sizeof(sid));
727 /* FIXME: strcpy is bad. */
728 strcpy(sid.name, name);
729 sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
730 return snd_ctl_find_id(card, &sid);
731}
732
Bill Pembertone23e7a12012-12-06 12:35:10 -0500733static int rename_ctl(struct snd_card *card, const char *src, const char *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734{
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100735 struct snd_kcontrol *kctl = ctl_find(card, src);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 if (kctl) {
737 strcpy(kctl->id.name, dst);
738 return 0;
739 }
740 return -ENOENT;
741}
742
Trent Piephofca7f382007-07-24 12:10:34 +0200743#define ADD_CTLS(emu, ctls) \
744 do { \
Harvey Harrisonbed515b2008-02-28 12:02:56 +0100745 int i, _err; \
Trent Piephofca7f382007-07-24 12:10:34 +0200746 for (i = 0; i < ARRAY_SIZE(ctls); i++) { \
Harvey Harrisonbed515b2008-02-28 12:02:56 +0100747 _err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \
748 if (_err < 0) \
749 return _err; \
Trent Piephofca7f382007-07-24 12:10:34 +0200750 } \
751 } while (0)
752
Bill Pembertone23e7a12012-12-06 12:35:10 -0500753static
Takashi Iwaic4865672009-06-08 12:57:17 +0200754DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 25, 1);
Takashi Iwai49c88b82008-02-18 13:06:49 +0100755
Bill Pembertone23e7a12012-12-06 12:35:10 -0500756static char *slave_vols[] = {
Takashi Iwai49c88b82008-02-18 13:06:49 +0100757 "Analog Front Playback Volume",
758 "Analog Rear Playback Volume",
759 "Analog Center/LFE Playback Volume",
760 "Analog Side Playback Volume",
761 "IEC958 Front Playback Volume",
762 "IEC958 Rear Playback Volume",
763 "IEC958 Center/LFE Playback Volume",
764 "IEC958 Unknown Playback Volume",
765 "CAPTURE feedback Playback Volume",
766 NULL
767};
768
Bill Pembertone23e7a12012-12-06 12:35:10 -0500769static char *slave_sws[] = {
Takashi Iwai49c88b82008-02-18 13:06:49 +0100770 "Analog Front Playback Switch",
771 "Analog Rear Playback Switch",
772 "Analog Center/LFE Playback Switch",
773 "Analog Side Playback Switch",
774 "IEC958 Playback Switch",
775 NULL
776};
777
Bill Pembertone23e7a12012-12-06 12:35:10 -0500778static void add_slaves(struct snd_card *card,
Takashi Iwai49c88b82008-02-18 13:06:49 +0100779 struct snd_kcontrol *master, char **list)
780{
781 for (; *list; list++) {
782 struct snd_kcontrol *slave = ctl_find(card, *list);
783 if (slave)
784 snd_ctl_add_slave(master, slave);
785 }
786}
787
Bill Pembertone23e7a12012-12-06 12:35:10 -0500788int snd_ca0106_mixer(struct snd_ca0106 *emu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789{
Trent Piephofca7f382007-07-24 12:10:34 +0200790 int err;
Takashi Iwaie4a3d142005-11-17 14:55:40 +0100791 struct snd_card *card = emu->card;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 char **c;
Takashi Iwai49c88b82008-02-18 13:06:49 +0100793 struct snd_kcontrol *vmaster;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 static char *ca0106_remove_ctls[] = {
795 "Master Mono Playback Switch",
796 "Master Mono Playback Volume",
797 "3D Control - Switch",
798 "3D Control Sigmatel - Depth",
799 "PCM Playback Switch",
800 "PCM Playback Volume",
801 "CD Playback Switch",
802 "CD Playback Volume",
803 "Phone Playback Switch",
804 "Phone Playback Volume",
805 "Video Playback Switch",
806 "Video Playback Volume",
Jaroslav Kyselad355c82a2009-11-03 15:47:25 +0100807 "Beep Playback Switch",
808 "Beep Playback Volume",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 "Mono Output Select",
810 "Capture Source",
811 "Capture Switch",
812 "Capture Volume",
813 "External Amplifier",
814 "Sigmatel 4-Speaker Stereo Playback Switch",
Andreas Mohrafe6d7e2009-05-22 17:48:58 +0200815 "Surround Phase Inversion Playback Switch",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 NULL
817 };
818 static char *ca0106_rename_ctls[] = {
819 "Master Playback Switch", "Capture Switch",
820 "Master Playback Volume", "Capture Volume",
821 "Line Playback Switch", "AC97 Line Capture Switch",
822 "Line Playback Volume", "AC97 Line Capture Volume",
823 "Aux Playback Switch", "AC97 Aux Capture Switch",
824 "Aux Playback Volume", "AC97 Aux Capture Volume",
825 "Mic Playback Switch", "AC97 Mic Capture Switch",
826 "Mic Playback Volume", "AC97 Mic Capture Volume",
827 "Mic Select", "AC97 Mic Select",
828 "Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)",
829 NULL
830 };
831#if 1
Takashi Iwai95a98262005-11-17 10:40:18 +0100832 for (c = ca0106_remove_ctls; *c; c++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 remove_ctl(card, *c);
Takashi Iwai95a98262005-11-17 10:40:18 +0100834 for (c = ca0106_rename_ctls; *c; c += 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 rename_ctl(card, c[0], c[1]);
836#endif
837
Trent Piephofca7f382007-07-24 12:10:34 +0200838 ADD_CTLS(emu, snd_ca0106_volume_ctls);
Takashi Iwai95a98262005-11-17 10:40:18 +0100839 if (emu->details->i2c_adc == 1) {
Trent Piephofca7f382007-07-24 12:10:34 +0200840 ADD_CTLS(emu, snd_ca0106_volume_i2c_adc_ctls);
James Courtier-Duttonbe0b7b02006-04-09 20:48:44 +0100841 if (emu->details->gpio_type == 1)
842 err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu));
843 else /* gpio_type == 2 */
844 err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu));
Takashi Iwai95a98262005-11-17 10:40:18 +0100845 if (err < 0)
846 return err;
847 }
Andy Owen64e53102010-10-23 22:12:33 +1100848 if (emu->details->spi_dac) {
849 int i;
850 for (i = 0;; i++) {
851 struct snd_kcontrol_new ctl;
852 ctl = snd_ca0106_volume_spi_dac_ctl(emu->details, i);
853 if (!ctl.name)
854 break;
855 err = snd_ctl_add(card, snd_ctl_new1(&ctl, emu));
856 if (err < 0)
857 return err;
858 }
859 }
Takashi Iwai49c88b82008-02-18 13:06:49 +0100860
861 /* Create virtual master controls */
862 vmaster = snd_ctl_make_virtual_master("Master Playback Volume",
863 snd_ca0106_master_db_scale);
864 if (!vmaster)
865 return -ENOMEM;
Takashi Iwai601e1cc2009-06-02 11:37:01 +0200866 err = snd_ctl_add(card, vmaster);
867 if (err < 0)
868 return err;
Takashi Iwai49c88b82008-02-18 13:06:49 +0100869 add_slaves(card, vmaster, slave_vols);
870
Andy Owen6fef1532010-10-23 22:12:32 +1100871 if (emu->details->spi_dac) {
Takashi Iwai49c88b82008-02-18 13:06:49 +0100872 vmaster = snd_ctl_make_virtual_master("Master Playback Switch",
873 NULL);
874 if (!vmaster)
875 return -ENOMEM;
Takashi Iwai601e1cc2009-06-02 11:37:01 +0200876 err = snd_ctl_add(card, vmaster);
877 if (err < 0)
878 return err;
Takashi Iwai49c88b82008-02-18 13:06:49 +0100879 add_slaves(card, vmaster, slave_sws);
880 }
Takashi Iwaieeaf1002009-06-02 16:06:02 +0200881
882 strcpy(card->mixername, "CA0106");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 return 0;
884}
885
Takashi Iwaic7561cd2012-08-14 18:12:04 +0200886#ifdef CONFIG_PM_SLEEP
Takashi Iwai5da95272008-11-24 14:06:08 +0100887struct ca0106_vol_tbl {
Takashi Iwai5da95272008-11-24 14:06:08 +0100888 unsigned int channel_id;
Takashi Iwai8df0f702008-12-19 13:55:17 +0100889 unsigned int reg;
Takashi Iwai5da95272008-11-24 14:06:08 +0100890};
891
892static struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = {
893 { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2 },
894 { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2 },
895 { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2 },
896 { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2 },
897 { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1 },
898 { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1 },
899 { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1 },
900 { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1 },
901 { 1, CAPTURE_CONTROL },
902};
903
904void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip)
905{
906 int i;
907
908 /* save volumes */
909 for (i = 0; i < NUM_SAVED_VOLUMES; i++)
910 chip->saved_vol[i] =
911 snd_ca0106_ptr_read(chip, saved_volumes[i].reg,
912 saved_volumes[i].channel_id);
913}
914
915void snd_ca0106_mixer_resume(struct snd_ca0106 *chip)
916{
917 int i;
918
919 for (i = 0; i < NUM_SAVED_VOLUMES; i++)
920 snd_ca0106_ptr_write(chip, saved_volumes[i].reg,
921 saved_volumes[i].channel_id,
922 chip->saved_vol[i]);
923
924 ca0106_spdif_enable(chip);
925 ca0106_set_capture_source(chip);
926 ca0106_set_i2c_capture_source(chip, chip->i2c_capture_source, 1);
927 for (i = 0; i < 4; i++)
928 ca0106_set_spdif_bits(chip, i);
929 if (chip->details->i2c_adc)
930 ca0106_set_capture_mic_line_in(chip);
931}
Takashi Iwaic7561cd2012-08-14 18:12:04 +0200932#endif /* CONFIG_PM_SLEEP */