diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/pci/ac97/ak4531_codec.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'sound/pci/ac97/ak4531_codec.c')
-rw-r--r-- | sound/pci/ac97/ak4531_codec.c | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c new file mode 100644 index 000000000000..f9ce0fd2f52f --- /dev/null +++ b/sound/pci/ac97/ak4531_codec.c | |||
@@ -0,0 +1,437 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Universal routines for AK4531 codec | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/ak4531_codec.h> | ||
28 | |||
29 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
30 | MODULE_DESCRIPTION("Universal routines for AK4531 codec"); | ||
31 | MODULE_LICENSE("GPL"); | ||
32 | |||
33 | static void snd_ak4531_proc_init(snd_card_t * card, ak4531_t * ak4531); | ||
34 | |||
35 | /* | ||
36 | * | ||
37 | */ | ||
38 | |||
39 | #if 0 | ||
40 | |||
41 | static void snd_ak4531_dump(ak4531_t *ak4531) | ||
42 | { | ||
43 | int idx; | ||
44 | |||
45 | for (idx = 0; idx < 0x19; idx++) | ||
46 | printk("ak4531 0x%x: 0x%x\n", idx, ak4531->regs[idx]); | ||
47 | } | ||
48 | |||
49 | #endif | ||
50 | |||
51 | /* | ||
52 | * | ||
53 | */ | ||
54 | |||
55 | #define AK4531_SINGLE(xname, xindex, reg, shift, mask, invert) \ | ||
56 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
57 | .info = snd_ak4531_info_single, \ | ||
58 | .get = snd_ak4531_get_single, .put = snd_ak4531_put_single, \ | ||
59 | .private_value = reg | (shift << 16) | (mask << 24) | (invert << 22) } | ||
60 | |||
61 | static int snd_ak4531_info_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) | ||
62 | { | ||
63 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
64 | |||
65 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
66 | uinfo->count = 1; | ||
67 | uinfo->value.integer.min = 0; | ||
68 | uinfo->value.integer.max = mask; | ||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static int snd_ak4531_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
73 | { | ||
74 | ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); | ||
75 | int reg = kcontrol->private_value & 0xff; | ||
76 | int shift = (kcontrol->private_value >> 16) & 0x07; | ||
77 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
78 | int invert = (kcontrol->private_value >> 22) & 1; | ||
79 | int val; | ||
80 | |||
81 | down(&ak4531->reg_mutex); | ||
82 | val = (ak4531->regs[reg] >> shift) & mask; | ||
83 | up(&ak4531->reg_mutex); | ||
84 | if (invert) { | ||
85 | val = mask - val; | ||
86 | } | ||
87 | ucontrol->value.integer.value[0] = val; | ||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static int snd_ak4531_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
92 | { | ||
93 | ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); | ||
94 | int reg = kcontrol->private_value & 0xff; | ||
95 | int shift = (kcontrol->private_value >> 16) & 0x07; | ||
96 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
97 | int invert = (kcontrol->private_value >> 22) & 1; | ||
98 | int change; | ||
99 | int val; | ||
100 | |||
101 | val = ucontrol->value.integer.value[0] & mask; | ||
102 | if (invert) { | ||
103 | val = mask - val; | ||
104 | } | ||
105 | val <<= shift; | ||
106 | down(&ak4531->reg_mutex); | ||
107 | val = (ak4531->regs[reg] & ~(mask << shift)) | val; | ||
108 | change = val != ak4531->regs[reg]; | ||
109 | ak4531->write(ak4531, reg, ak4531->regs[reg] = val); | ||
110 | up(&ak4531->reg_mutex); | ||
111 | return change; | ||
112 | } | ||
113 | |||
114 | #define AK4531_DOUBLE(xname, xindex, left_reg, right_reg, left_shift, right_shift, mask, invert) \ | ||
115 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
116 | .info = snd_ak4531_info_double, \ | ||
117 | .get = snd_ak4531_get_double, .put = snd_ak4531_put_double, \ | ||
118 | .private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22) } | ||
119 | |||
120 | static int snd_ak4531_info_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) | ||
121 | { | ||
122 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
123 | |||
124 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
125 | uinfo->count = 2; | ||
126 | uinfo->value.integer.min = 0; | ||
127 | uinfo->value.integer.max = mask; | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static int snd_ak4531_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
132 | { | ||
133 | ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); | ||
134 | int left_reg = kcontrol->private_value & 0xff; | ||
135 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
136 | int left_shift = (kcontrol->private_value >> 16) & 0x07; | ||
137 | int right_shift = (kcontrol->private_value >> 19) & 0x07; | ||
138 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
139 | int invert = (kcontrol->private_value >> 22) & 1; | ||
140 | int left, right; | ||
141 | |||
142 | down(&ak4531->reg_mutex); | ||
143 | left = (ak4531->regs[left_reg] >> left_shift) & mask; | ||
144 | right = (ak4531->regs[right_reg] >> right_shift) & mask; | ||
145 | up(&ak4531->reg_mutex); | ||
146 | if (invert) { | ||
147 | left = mask - left; | ||
148 | right = mask - right; | ||
149 | } | ||
150 | ucontrol->value.integer.value[0] = left; | ||
151 | ucontrol->value.integer.value[1] = right; | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | static int snd_ak4531_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
156 | { | ||
157 | ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); | ||
158 | int left_reg = kcontrol->private_value & 0xff; | ||
159 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
160 | int left_shift = (kcontrol->private_value >> 16) & 0x07; | ||
161 | int right_shift = (kcontrol->private_value >> 19) & 0x07; | ||
162 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
163 | int invert = (kcontrol->private_value >> 22) & 1; | ||
164 | int change; | ||
165 | int left, right; | ||
166 | |||
167 | left = ucontrol->value.integer.value[0] & mask; | ||
168 | right = ucontrol->value.integer.value[1] & mask; | ||
169 | if (invert) { | ||
170 | left = mask - left; | ||
171 | right = mask - right; | ||
172 | } | ||
173 | left <<= left_shift; | ||
174 | right <<= right_shift; | ||
175 | down(&ak4531->reg_mutex); | ||
176 | if (left_reg == right_reg) { | ||
177 | left = (ak4531->regs[left_reg] & ~((mask << left_shift) | (mask << right_shift))) | left | right; | ||
178 | change = left != ak4531->regs[left_reg]; | ||
179 | ak4531->write(ak4531, left_reg, ak4531->regs[left_reg] = left); | ||
180 | } else { | ||
181 | left = (ak4531->regs[left_reg] & ~(mask << left_shift)) | left; | ||
182 | right = (ak4531->regs[right_reg] & ~(mask << right_shift)) | right; | ||
183 | change = left != ak4531->regs[left_reg] || right != ak4531->regs[right_reg]; | ||
184 | ak4531->write(ak4531, left_reg, ak4531->regs[left_reg] = left); | ||
185 | ak4531->write(ak4531, right_reg, ak4531->regs[right_reg] = right); | ||
186 | } | ||
187 | up(&ak4531->reg_mutex); | ||
188 | return change; | ||
189 | } | ||
190 | |||
191 | #define AK4531_INPUT_SW(xname, xindex, reg1, reg2, left_shift, right_shift) \ | ||
192 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
193 | .info = snd_ak4531_info_input_sw, \ | ||
194 | .get = snd_ak4531_get_input_sw, .put = snd_ak4531_put_input_sw, \ | ||
195 | .private_value = reg1 | (reg2 << 8) | (left_shift << 16) | (right_shift << 24) } | ||
196 | |||
197 | static int snd_ak4531_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) | ||
198 | { | ||
199 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
200 | uinfo->count = 4; | ||
201 | uinfo->value.integer.min = 0; | ||
202 | uinfo->value.integer.max = 1; | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static int snd_ak4531_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
207 | { | ||
208 | ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); | ||
209 | int reg1 = kcontrol->private_value & 0xff; | ||
210 | int reg2 = (kcontrol->private_value >> 8) & 0xff; | ||
211 | int left_shift = (kcontrol->private_value >> 16) & 0x0f; | ||
212 | int right_shift = (kcontrol->private_value >> 24) & 0x0f; | ||
213 | |||
214 | down(&ak4531->reg_mutex); | ||
215 | ucontrol->value.integer.value[0] = (ak4531->regs[reg1] >> left_shift) & 1; | ||
216 | ucontrol->value.integer.value[1] = (ak4531->regs[reg2] >> left_shift) & 1; | ||
217 | ucontrol->value.integer.value[2] = (ak4531->regs[reg1] >> right_shift) & 1; | ||
218 | ucontrol->value.integer.value[3] = (ak4531->regs[reg2] >> right_shift) & 1; | ||
219 | up(&ak4531->reg_mutex); | ||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | static int snd_ak4531_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
224 | { | ||
225 | ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); | ||
226 | int reg1 = kcontrol->private_value & 0xff; | ||
227 | int reg2 = (kcontrol->private_value >> 8) & 0xff; | ||
228 | int left_shift = (kcontrol->private_value >> 16) & 0x0f; | ||
229 | int right_shift = (kcontrol->private_value >> 24) & 0x0f; | ||
230 | int change; | ||
231 | int val1, val2; | ||
232 | |||
233 | down(&ak4531->reg_mutex); | ||
234 | val1 = ak4531->regs[reg1] & ~((1 << left_shift) | (1 << right_shift)); | ||
235 | val2 = ak4531->regs[reg2] & ~((1 << left_shift) | (1 << right_shift)); | ||
236 | val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift; | ||
237 | val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift; | ||
238 | val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift; | ||
239 | val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift; | ||
240 | change = val1 != ak4531->regs[reg1] || val2 != ak4531->regs[reg2]; | ||
241 | ak4531->write(ak4531, reg1, ak4531->regs[reg1] = val1); | ||
242 | ak4531->write(ak4531, reg2, ak4531->regs[reg2] = val2); | ||
243 | up(&ak4531->reg_mutex); | ||
244 | return change; | ||
245 | } | ||
246 | |||
247 | static snd_kcontrol_new_t snd_ak4531_controls[] = { | ||
248 | |||
249 | AK4531_DOUBLE("Master Playback Switch", 0, AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1), | ||
250 | AK4531_DOUBLE("Master Playback Volume", 0, AK4531_LMASTER, AK4531_RMASTER, 0, 0, 0x1f, 1), | ||
251 | |||
252 | AK4531_SINGLE("Master Mono Playback Switch", 0, AK4531_MONO_OUT, 7, 1, 1), | ||
253 | AK4531_SINGLE("Master Mono Playback Volume", 0, AK4531_MONO_OUT, 0, 0x07, 1), | ||
254 | |||
255 | AK4531_DOUBLE("PCM Switch", 0, AK4531_LVOICE, AK4531_RVOICE, 7, 7, 1, 1), | ||
256 | AK4531_DOUBLE("PCM Volume", 0, AK4531_LVOICE, AK4531_RVOICE, 0, 0, 0x1f, 1), | ||
257 | AK4531_DOUBLE("PCM Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 3, 2, 1, 0), | ||
258 | AK4531_DOUBLE("PCM Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 2, 2, 1, 0), | ||
259 | |||
260 | AK4531_DOUBLE("PCM Switch", 1, AK4531_LFM, AK4531_RFM, 7, 7, 1, 1), | ||
261 | AK4531_DOUBLE("PCM Volume", 1, AK4531_LFM, AK4531_RFM, 0, 0, 0x1f, 1), | ||
262 | AK4531_DOUBLE("PCM Playback Switch", 1, AK4531_OUT_SW1, AK4531_OUT_SW1, 6, 5, 1, 0), | ||
263 | AK4531_INPUT_SW("PCM Capture Route", 1, AK4531_LIN_SW1, AK4531_RIN_SW1, 6, 5), | ||
264 | |||
265 | AK4531_DOUBLE("CD Switch", 0, AK4531_LCD, AK4531_RCD, 7, 7, 1, 1), | ||
266 | AK4531_DOUBLE("CD Volume", 0, AK4531_LCD, AK4531_RCD, 0, 0, 0x1f, 1), | ||
267 | AK4531_DOUBLE("CD Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 2, 1, 1, 0), | ||
268 | AK4531_INPUT_SW("CD Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 2, 1), | ||
269 | |||
270 | AK4531_DOUBLE("Line Switch", 0, AK4531_LLINE, AK4531_RLINE, 7, 7, 1, 1), | ||
271 | AK4531_DOUBLE("Line Volume", 0, AK4531_LLINE, AK4531_RLINE, 0, 0, 0x1f, 1), | ||
272 | AK4531_DOUBLE("Line Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 4, 3, 1, 0), | ||
273 | AK4531_INPUT_SW("Line Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 4, 3), | ||
274 | |||
275 | AK4531_DOUBLE("Aux Switch", 0, AK4531_LAUXA, AK4531_RAUXA, 7, 7, 1, 1), | ||
276 | AK4531_DOUBLE("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1), | ||
277 | AK4531_DOUBLE("Aux Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 5, 4, 1, 0), | ||
278 | AK4531_INPUT_SW("Aux Capture Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3), | ||
279 | |||
280 | AK4531_SINGLE("Mono Switch", 0, AK4531_MONO1, 7, 1, 1), | ||
281 | AK4531_SINGLE("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1), | ||
282 | AK4531_SINGLE("Mono Playback Switch", 0, AK4531_OUT_SW2, 0, 1, 0), | ||
283 | AK4531_DOUBLE("Mono Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 0, 0, 1, 0), | ||
284 | |||
285 | AK4531_SINGLE("Mono Switch", 1, AK4531_MONO2, 7, 1, 1), | ||
286 | AK4531_SINGLE("Mono Volume", 1, AK4531_MONO2, 0, 0x1f, 1), | ||
287 | AK4531_SINGLE("Mono Playback Switch", 1, AK4531_OUT_SW2, 1, 1, 0), | ||
288 | AK4531_DOUBLE("Mono Capture Switch", 1, AK4531_LIN_SW2, AK4531_RIN_SW2, 1, 1, 1, 0), | ||
289 | |||
290 | AK4531_SINGLE("Mic Volume", 0, AK4531_MIC, 0, 0x1f, 1), | ||
291 | AK4531_SINGLE("Mic Switch", 0, AK4531_MIC, 7, 1, 1), | ||
292 | AK4531_SINGLE("Mic Playback Switch", 0, AK4531_OUT_SW1, 0, 1, 0), | ||
293 | AK4531_DOUBLE("Mic Capture Switch", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 0, 0, 1, 0), | ||
294 | |||
295 | AK4531_DOUBLE("Mic Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 7, 7, 1, 0), | ||
296 | AK4531_DOUBLE("Mono1 Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 6, 6, 1, 0), | ||
297 | AK4531_DOUBLE("Mono2 Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 5, 5, 1, 0), | ||
298 | |||
299 | AK4531_SINGLE("AD Input Select", 0, AK4531_AD_IN, 0, 1, 0), | ||
300 | AK4531_SINGLE("Mic Boost (+30dB)", 0, AK4531_MIC_GAIN, 0, 1, 0) | ||
301 | }; | ||
302 | |||
303 | static int snd_ak4531_free(ak4531_t *ak4531) | ||
304 | { | ||
305 | if (ak4531) { | ||
306 | if (ak4531->private_free) | ||
307 | ak4531->private_free(ak4531); | ||
308 | kfree(ak4531); | ||
309 | } | ||
310 | return 0; | ||
311 | } | ||
312 | |||
313 | static int snd_ak4531_dev_free(snd_device_t *device) | ||
314 | { | ||
315 | ak4531_t *ak4531 = device->device_data; | ||
316 | return snd_ak4531_free(ak4531); | ||
317 | } | ||
318 | |||
319 | static u8 snd_ak4531_initial_map[0x19 + 1] = { | ||
320 | 0x9f, /* 00: Master Volume Lch */ | ||
321 | 0x9f, /* 01: Master Volume Rch */ | ||
322 | 0x9f, /* 02: Voice Volume Lch */ | ||
323 | 0x9f, /* 03: Voice Volume Rch */ | ||
324 | 0x9f, /* 04: FM Volume Lch */ | ||
325 | 0x9f, /* 05: FM Volume Rch */ | ||
326 | 0x9f, /* 06: CD Audio Volume Lch */ | ||
327 | 0x9f, /* 07: CD Audio Volume Rch */ | ||
328 | 0x9f, /* 08: Line Volume Lch */ | ||
329 | 0x9f, /* 09: Line Volume Rch */ | ||
330 | 0x9f, /* 0a: Aux Volume Lch */ | ||
331 | 0x9f, /* 0b: Aux Volume Rch */ | ||
332 | 0x9f, /* 0c: Mono1 Volume */ | ||
333 | 0x9f, /* 0d: Mono2 Volume */ | ||
334 | 0x9f, /* 0e: Mic Volume */ | ||
335 | 0x87, /* 0f: Mono-out Volume */ | ||
336 | 0x00, /* 10: Output Mixer SW1 */ | ||
337 | 0x00, /* 11: Output Mixer SW2 */ | ||
338 | 0x00, /* 12: Lch Input Mixer SW1 */ | ||
339 | 0x00, /* 13: Rch Input Mixer SW1 */ | ||
340 | 0x00, /* 14: Lch Input Mixer SW2 */ | ||
341 | 0x00, /* 15: Rch Input Mixer SW2 */ | ||
342 | 0x00, /* 16: Reset & Power Down */ | ||
343 | 0x00, /* 17: Clock Select */ | ||
344 | 0x00, /* 18: AD Input Select */ | ||
345 | 0x01 /* 19: Mic Amp Setup */ | ||
346 | }; | ||
347 | |||
348 | int snd_ak4531_mixer(snd_card_t * card, ak4531_t * _ak4531, ak4531_t ** rak4531) | ||
349 | { | ||
350 | unsigned int idx; | ||
351 | int err; | ||
352 | ak4531_t * ak4531; | ||
353 | static snd_device_ops_t ops = { | ||
354 | .dev_free = snd_ak4531_dev_free, | ||
355 | }; | ||
356 | |||
357 | snd_assert(rak4531 != NULL, return -EINVAL); | ||
358 | *rak4531 = NULL; | ||
359 | snd_assert(card != NULL && _ak4531 != NULL, return -EINVAL); | ||
360 | ak4531 = kcalloc(1, sizeof(*ak4531), GFP_KERNEL); | ||
361 | if (ak4531 == NULL) | ||
362 | return -ENOMEM; | ||
363 | *ak4531 = *_ak4531; | ||
364 | init_MUTEX(&ak4531->reg_mutex); | ||
365 | if ((err = snd_component_add(card, "AK4531")) < 0) { | ||
366 | snd_ak4531_free(ak4531); | ||
367 | return err; | ||
368 | } | ||
369 | strcpy(card->mixername, "Asahi Kasei AK4531"); | ||
370 | ak4531->write(ak4531, AK4531_RESET, 0x03); /* no RST, PD */ | ||
371 | udelay(100); | ||
372 | ak4531->write(ak4531, AK4531_CLOCK, 0x00); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off LRCLK2 PLL */ | ||
373 | for (idx = 0; idx < 0x19; idx++) { | ||
374 | if (idx == AK4531_RESET || idx == AK4531_CLOCK) | ||
375 | continue; | ||
376 | ak4531->write(ak4531, idx, ak4531->regs[idx] = snd_ak4531_initial_map[idx]); /* recording source is mixer */ | ||
377 | } | ||
378 | for (idx = 0; idx < ARRAY_SIZE(snd_ak4531_controls); idx++) { | ||
379 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ak4531_controls[idx], ak4531))) < 0) { | ||
380 | snd_ak4531_free(ak4531); | ||
381 | return err; | ||
382 | } | ||
383 | } | ||
384 | snd_ak4531_proc_init(card, ak4531); | ||
385 | if ((err = snd_device_new(card, SNDRV_DEV_CODEC, ak4531, &ops)) < 0) { | ||
386 | snd_ak4531_free(ak4531); | ||
387 | return err; | ||
388 | } | ||
389 | |||
390 | #if 0 | ||
391 | snd_ak4531_dump(ak4531); | ||
392 | #endif | ||
393 | *rak4531 = ak4531; | ||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | /* | ||
398 | |||
399 | */ | ||
400 | |||
401 | static void snd_ak4531_proc_read(snd_info_entry_t *entry, | ||
402 | snd_info_buffer_t * buffer) | ||
403 | { | ||
404 | ak4531_t *ak4531 = entry->private_data; | ||
405 | |||
406 | snd_iprintf(buffer, "Asahi Kasei AK4531\n\n"); | ||
407 | snd_iprintf(buffer, "Recording source : %s\n" | ||
408 | "MIC gain : %s\n", | ||
409 | ak4531->regs[AK4531_AD_IN] & 1 ? "external" : "mixer", | ||
410 | ak4531->regs[AK4531_MIC_GAIN] & 1 ? "+30dB" : "+0dB"); | ||
411 | } | ||
412 | |||
413 | static void snd_ak4531_proc_init(snd_card_t * card, ak4531_t * ak4531) | ||
414 | { | ||
415 | snd_info_entry_t *entry; | ||
416 | |||
417 | if (! snd_card_proc_new(card, "ak4531", &entry)) | ||
418 | snd_info_set_text_ops(entry, ak4531, 1024, snd_ak4531_proc_read); | ||
419 | } | ||
420 | |||
421 | EXPORT_SYMBOL(snd_ak4531_mixer); | ||
422 | |||
423 | /* | ||
424 | * INIT part | ||
425 | */ | ||
426 | |||
427 | static int __init alsa_ak4531_init(void) | ||
428 | { | ||
429 | return 0; | ||
430 | } | ||
431 | |||
432 | static void __exit alsa_ak4531_exit(void) | ||
433 | { | ||
434 | } | ||
435 | |||
436 | module_init(alsa_ak4531_init) | ||
437 | module_exit(alsa_ak4531_exit) | ||