diff options
Diffstat (limited to 'sound/drivers/vx/vx_mixer.c')
-rw-r--r-- | sound/drivers/vx/vx_mixer.c | 1000 |
1 files changed, 1000 insertions, 0 deletions
diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c new file mode 100644 index 000000000000..f00c88886460 --- /dev/null +++ b/sound/drivers/vx/vx_mixer.c | |||
@@ -0,0 +1,1000 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VX soundcards | ||
3 | * | ||
4 | * Common mixer part | ||
5 | * | ||
6 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/control.h> | ||
26 | #include <sound/vx_core.h> | ||
27 | #include "vx_cmd.h" | ||
28 | |||
29 | |||
30 | /* | ||
31 | * write a codec data (24bit) | ||
32 | */ | ||
33 | static void vx_write_codec_reg(vx_core_t *chip, int codec, unsigned int data) | ||
34 | { | ||
35 | unsigned long flags; | ||
36 | |||
37 | snd_assert(chip->ops->write_codec, return); | ||
38 | |||
39 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
40 | return; | ||
41 | |||
42 | spin_lock_irqsave(&chip->lock, flags); | ||
43 | chip->ops->write_codec(chip, codec, data); | ||
44 | spin_unlock_irqrestore(&chip->lock, flags); | ||
45 | } | ||
46 | |||
47 | /* | ||
48 | * Data type used to access the Codec | ||
49 | */ | ||
50 | typedef union { | ||
51 | u32 l; | ||
52 | #ifdef SNDRV_BIG_ENDIAN | ||
53 | struct w { | ||
54 | u16 h; | ||
55 | u16 l; | ||
56 | } w; | ||
57 | struct b { | ||
58 | u8 hh; | ||
59 | u8 mh; | ||
60 | u8 ml; | ||
61 | u8 ll; | ||
62 | } b; | ||
63 | #else /* LITTLE_ENDIAN */ | ||
64 | struct w { | ||
65 | u16 l; | ||
66 | u16 h; | ||
67 | } w; | ||
68 | struct b { | ||
69 | u8 ll; | ||
70 | u8 ml; | ||
71 | u8 mh; | ||
72 | u8 hh; | ||
73 | } b; | ||
74 | #endif | ||
75 | } vx_codec_data_t; | ||
76 | |||
77 | #define SET_CDC_DATA_SEL(di,s) ((di).b.mh = (u8) (s)) | ||
78 | #define SET_CDC_DATA_REG(di,r) ((di).b.ml = (u8) (r)) | ||
79 | #define SET_CDC_DATA_VAL(di,d) ((di).b.ll = (u8) (d)) | ||
80 | #define SET_CDC_DATA_INIT(di) ((di).l = 0L, SET_CDC_DATA_SEL(di,XX_CODEC_SELECTOR)) | ||
81 | |||
82 | /* | ||
83 | * set up codec register and write the value | ||
84 | * @codec: the codec id, 0 or 1 | ||
85 | * @reg: register index | ||
86 | * @val: data value | ||
87 | */ | ||
88 | static void vx_set_codec_reg(vx_core_t *chip, int codec, int reg, int val) | ||
89 | { | ||
90 | vx_codec_data_t data; | ||
91 | /* DAC control register */ | ||
92 | SET_CDC_DATA_INIT(data); | ||
93 | SET_CDC_DATA_REG(data, reg); | ||
94 | SET_CDC_DATA_VAL(data, val); | ||
95 | vx_write_codec_reg(chip, codec, data.l); | ||
96 | } | ||
97 | |||
98 | |||
99 | /* | ||
100 | * vx_set_analog_output_level - set the output attenuation level | ||
101 | * @codec: the output codec, 0 or 1. (1 for VXP440 only) | ||
102 | * @left: left output level, 0 = mute | ||
103 | * @right: right output level | ||
104 | */ | ||
105 | static void vx_set_analog_output_level(vx_core_t *chip, int codec, int left, int right) | ||
106 | { | ||
107 | left = chip->hw->output_level_max - left; | ||
108 | right = chip->hw->output_level_max - right; | ||
109 | |||
110 | if (chip->ops->akm_write) { | ||
111 | chip->ops->akm_write(chip, XX_CODEC_LEVEL_LEFT_REGISTER, left); | ||
112 | chip->ops->akm_write(chip, XX_CODEC_LEVEL_RIGHT_REGISTER, right); | ||
113 | } else { | ||
114 | /* convert to attenuation level: 0 = 0dB (max), 0xe3 = -113.5 dB (min) */ | ||
115 | vx_set_codec_reg(chip, codec, XX_CODEC_LEVEL_LEFT_REGISTER, left); | ||
116 | vx_set_codec_reg(chip, codec, XX_CODEC_LEVEL_RIGHT_REGISTER, right); | ||
117 | } | ||
118 | } | ||
119 | |||
120 | |||
121 | /* | ||
122 | * vx_toggle_dac_mute - mute/unmute DAC | ||
123 | * @mute: 0 = unmute, 1 = mute | ||
124 | */ | ||
125 | |||
126 | #define DAC_ATTEN_MIN 0x08 | ||
127 | #define DAC_ATTEN_MAX 0x38 | ||
128 | |||
129 | void vx_toggle_dac_mute(vx_core_t *chip, int mute) | ||
130 | { | ||
131 | unsigned int i; | ||
132 | for (i = 0; i < chip->hw->num_codecs; i++) { | ||
133 | if (chip->ops->akm_write) | ||
134 | chip->ops->akm_write(chip, XX_CODEC_DAC_CONTROL_REGISTER, mute); /* XXX */ | ||
135 | else | ||
136 | vx_set_codec_reg(chip, i, XX_CODEC_DAC_CONTROL_REGISTER, | ||
137 | mute ? DAC_ATTEN_MAX : DAC_ATTEN_MIN); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | /* | ||
142 | * vx_reset_codec - reset and initialize the codecs | ||
143 | */ | ||
144 | void vx_reset_codec(vx_core_t *chip, int cold_reset) | ||
145 | { | ||
146 | unsigned int i; | ||
147 | int port = chip->type >= VX_TYPE_VXPOCKET ? 0x75 : 0x65; | ||
148 | |||
149 | chip->ops->reset_codec(chip); | ||
150 | |||
151 | /* AKM codecs should be initialized in reset_codec callback */ | ||
152 | if (! chip->ops->akm_write) { | ||
153 | /* initialize old codecs */ | ||
154 | for (i = 0; i < chip->hw->num_codecs; i++) { | ||
155 | /* DAC control register (change level when zero crossing + mute) */ | ||
156 | vx_set_codec_reg(chip, i, XX_CODEC_DAC_CONTROL_REGISTER, DAC_ATTEN_MAX); | ||
157 | /* ADC control register */ | ||
158 | vx_set_codec_reg(chip, i, XX_CODEC_ADC_CONTROL_REGISTER, 0x00); | ||
159 | /* Port mode register */ | ||
160 | vx_set_codec_reg(chip, i, XX_CODEC_PORT_MODE_REGISTER, port); | ||
161 | /* Clock control register */ | ||
162 | vx_set_codec_reg(chip, i, XX_CODEC_CLOCK_CONTROL_REGISTER, 0x00); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | /* mute analog output */ | ||
167 | for (i = 0; i < chip->hw->num_codecs; i++) { | ||
168 | chip->output_level[i][0] = 0; | ||
169 | chip->output_level[i][1] = 0; | ||
170 | vx_set_analog_output_level(chip, i, 0, 0); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | /* | ||
175 | * change the audio input source | ||
176 | * @src: the target source (VX_AUDIO_SRC_XXX) | ||
177 | */ | ||
178 | static void vx_change_audio_source(vx_core_t *chip, int src) | ||
179 | { | ||
180 | unsigned long flags; | ||
181 | |||
182 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
183 | return; | ||
184 | |||
185 | spin_lock_irqsave(&chip->lock, flags); | ||
186 | chip->ops->change_audio_source(chip, src); | ||
187 | spin_unlock_irqrestore(&chip->lock, flags); | ||
188 | } | ||
189 | |||
190 | |||
191 | /* | ||
192 | * change the audio source if necessary and possible | ||
193 | * returns 1 if the source is actually changed. | ||
194 | */ | ||
195 | int vx_sync_audio_source(vx_core_t *chip) | ||
196 | { | ||
197 | if (chip->audio_source_target == chip->audio_source || | ||
198 | chip->pcm_running) | ||
199 | return 0; | ||
200 | vx_change_audio_source(chip, chip->audio_source_target); | ||
201 | chip->audio_source = chip->audio_source_target; | ||
202 | return 1; | ||
203 | } | ||
204 | |||
205 | |||
206 | /* | ||
207 | * audio level, mute, monitoring | ||
208 | */ | ||
209 | struct vx_audio_level { | ||
210 | unsigned int has_level: 1; | ||
211 | unsigned int has_monitor_level: 1; | ||
212 | unsigned int has_mute: 1; | ||
213 | unsigned int has_monitor_mute: 1; | ||
214 | unsigned int mute; | ||
215 | unsigned int monitor_mute; | ||
216 | short level; | ||
217 | short monitor_level; | ||
218 | }; | ||
219 | |||
220 | static int vx_adjust_audio_level(vx_core_t *chip, int audio, int capture, | ||
221 | struct vx_audio_level *info) | ||
222 | { | ||
223 | struct vx_rmh rmh; | ||
224 | |||
225 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
226 | return -EBUSY; | ||
227 | |||
228 | vx_init_rmh(&rmh, CMD_AUDIO_LEVEL_ADJUST); | ||
229 | if (capture) | ||
230 | rmh.Cmd[0] |= COMMAND_RECORD_MASK; | ||
231 | /* Add Audio IO mask */ | ||
232 | rmh.Cmd[1] = 1 << audio; | ||
233 | rmh.Cmd[2] = 0; | ||
234 | if (info->has_level) { | ||
235 | rmh.Cmd[0] |= VALID_AUDIO_IO_DIGITAL_LEVEL; | ||
236 | rmh.Cmd[2] |= info->level; | ||
237 | } | ||
238 | if (info->has_monitor_level) { | ||
239 | rmh.Cmd[0] |= VALID_AUDIO_IO_MONITORING_LEVEL; | ||
240 | rmh.Cmd[2] |= ((unsigned int)info->monitor_level << 10); | ||
241 | } | ||
242 | if (info->has_mute) { | ||
243 | rmh.Cmd[0] |= VALID_AUDIO_IO_MUTE_LEVEL; | ||
244 | if (info->mute) | ||
245 | rmh.Cmd[2] |= AUDIO_IO_HAS_MUTE_LEVEL; | ||
246 | } | ||
247 | if (info->has_monitor_mute) { | ||
248 | /* validate flag for M2 at least to unmute it */ | ||
249 | rmh.Cmd[0] |= VALID_AUDIO_IO_MUTE_MONITORING_1 | VALID_AUDIO_IO_MUTE_MONITORING_2; | ||
250 | if (info->monitor_mute) | ||
251 | rmh.Cmd[2] |= AUDIO_IO_HAS_MUTE_MONITORING_1; | ||
252 | } | ||
253 | |||
254 | return vx_send_msg(chip, &rmh); | ||
255 | } | ||
256 | |||
257 | |||
258 | #if 0 // not used | ||
259 | static int vx_read_audio_level(vx_core_t *chip, int audio, int capture, | ||
260 | struct vx_audio_level *info) | ||
261 | { | ||
262 | int err; | ||
263 | struct vx_rmh rmh; | ||
264 | |||
265 | memset(info, 0, sizeof(*info)); | ||
266 | vx_init_rmh(&rmh, CMD_GET_AUDIO_LEVELS); | ||
267 | if (capture) | ||
268 | rmh.Cmd[0] |= COMMAND_RECORD_MASK; | ||
269 | /* Add Audio IO mask */ | ||
270 | rmh.Cmd[1] = 1 << audio; | ||
271 | err = vx_send_msg(chip, &rmh); | ||
272 | if (err < 0) | ||
273 | return err; | ||
274 | info.level = rmh.Stat[0] & MASK_DSP_WORD_LEVEL; | ||
275 | info.monitor_level = (rmh.Stat[0] >> 10) & MASK_DSP_WORD_LEVEL; | ||
276 | info.mute = (rmh.Stat[i] & AUDIO_IO_HAS_MUTE_LEVEL) ? 1 : 0; | ||
277 | info.monitor_mute = (rmh.Stat[i] & AUDIO_IO_HAS_MUTE_MONITORING_1) ? 1 : 0; | ||
278 | return 0; | ||
279 | } | ||
280 | #endif // not used | ||
281 | |||
282 | /* | ||
283 | * set the monitoring level and mute state of the given audio | ||
284 | * no more static, because must be called from vx_pcm to demute monitoring | ||
285 | */ | ||
286 | int vx_set_monitor_level(vx_core_t *chip, int audio, int level, int active) | ||
287 | { | ||
288 | struct vx_audio_level info; | ||
289 | |||
290 | memset(&info, 0, sizeof(info)); | ||
291 | info.has_monitor_level = 1; | ||
292 | info.monitor_level = level; | ||
293 | info.has_monitor_mute = 1; | ||
294 | info.monitor_mute = !active; | ||
295 | chip->audio_monitor[audio] = level; | ||
296 | chip->audio_monitor_active[audio] = active; | ||
297 | return vx_adjust_audio_level(chip, audio, 0, &info); /* playback only */ | ||
298 | } | ||
299 | |||
300 | |||
301 | /* | ||
302 | * set the mute status of the given audio | ||
303 | */ | ||
304 | static int vx_set_audio_switch(vx_core_t *chip, int audio, int active) | ||
305 | { | ||
306 | struct vx_audio_level info; | ||
307 | |||
308 | memset(&info, 0, sizeof(info)); | ||
309 | info.has_mute = 1; | ||
310 | info.mute = !active; | ||
311 | chip->audio_active[audio] = active; | ||
312 | return vx_adjust_audio_level(chip, audio, 0, &info); /* playback only */ | ||
313 | } | ||
314 | |||
315 | /* | ||
316 | * set the mute status of the given audio | ||
317 | */ | ||
318 | static int vx_set_audio_gain(vx_core_t *chip, int audio, int capture, int level) | ||
319 | { | ||
320 | struct vx_audio_level info; | ||
321 | |||
322 | memset(&info, 0, sizeof(info)); | ||
323 | info.has_level = 1; | ||
324 | info.level = level; | ||
325 | chip->audio_gain[capture][audio] = level; | ||
326 | return vx_adjust_audio_level(chip, audio, capture, &info); | ||
327 | } | ||
328 | |||
329 | /* | ||
330 | * reset all audio levels | ||
331 | */ | ||
332 | static void vx_reset_audio_levels(vx_core_t *chip) | ||
333 | { | ||
334 | unsigned int i, c; | ||
335 | struct vx_audio_level info; | ||
336 | |||
337 | memset(chip->audio_gain, 0, sizeof(chip->audio_gain)); | ||
338 | memset(chip->audio_active, 0, sizeof(chip->audio_active)); | ||
339 | memset(chip->audio_monitor, 0, sizeof(chip->audio_monitor)); | ||
340 | memset(chip->audio_monitor_active, 0, sizeof(chip->audio_monitor_active)); | ||
341 | |||
342 | for (c = 0; c < 2; c++) { | ||
343 | for (i = 0; i < chip->hw->num_ins * 2; i++) { | ||
344 | memset(&info, 0, sizeof(info)); | ||
345 | if (c == 0) { | ||
346 | info.has_monitor_level = 1; | ||
347 | info.has_mute = 1; | ||
348 | info.has_monitor_mute = 1; | ||
349 | } | ||
350 | info.has_level = 1; | ||
351 | info.level = CVAL_0DB; /* default: 0dB */ | ||
352 | vx_adjust_audio_level(chip, i, c, &info); | ||
353 | chip->audio_gain[c][i] = CVAL_0DB; | ||
354 | chip->audio_monitor[i] = CVAL_0DB; | ||
355 | } | ||
356 | } | ||
357 | } | ||
358 | |||
359 | |||
360 | /* | ||
361 | * VU, peak meter record | ||
362 | */ | ||
363 | |||
364 | #define VU_METER_CHANNELS 2 | ||
365 | |||
366 | struct vx_vu_meter { | ||
367 | int saturated; | ||
368 | int vu_level; | ||
369 | int peak_level; | ||
370 | }; | ||
371 | |||
372 | /* | ||
373 | * get the VU and peak meter values | ||
374 | * @audio: the audio index | ||
375 | * @capture: 0 = playback, 1 = capture operation | ||
376 | * @info: the array of vx_vu_meter records (size = 2). | ||
377 | */ | ||
378 | static int vx_get_audio_vu_meter(vx_core_t *chip, int audio, int capture, struct vx_vu_meter *info) | ||
379 | { | ||
380 | struct vx_rmh rmh; | ||
381 | int i, err; | ||
382 | |||
383 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
384 | return -EBUSY; | ||
385 | |||
386 | vx_init_rmh(&rmh, CMD_AUDIO_VU_PIC_METER); | ||
387 | rmh.LgStat += 2 * VU_METER_CHANNELS; | ||
388 | if (capture) | ||
389 | rmh.Cmd[0] |= COMMAND_RECORD_MASK; | ||
390 | |||
391 | /* Add Audio IO mask */ | ||
392 | rmh.Cmd[1] = 0; | ||
393 | for (i = 0; i < VU_METER_CHANNELS; i++) | ||
394 | rmh.Cmd[1] |= 1 << (audio + i); | ||
395 | err = vx_send_msg(chip, &rmh); | ||
396 | if (err < 0) | ||
397 | return err; | ||
398 | /* Read response */ | ||
399 | for (i = 0; i < 2 * VU_METER_CHANNELS; i +=2) { | ||
400 | info->saturated = (rmh.Stat[0] & (1 << (audio + i))) ? 1 : 0; | ||
401 | info->vu_level = rmh.Stat[i + 1]; | ||
402 | info->peak_level = rmh.Stat[i + 2]; | ||
403 | info++; | ||
404 | } | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | |||
409 | /* | ||
410 | * control API entries | ||
411 | */ | ||
412 | |||
413 | /* | ||
414 | * output level control | ||
415 | */ | ||
416 | static int vx_output_level_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
417 | { | ||
418 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
419 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
420 | uinfo->count = 2; | ||
421 | uinfo->value.integer.min = 0; | ||
422 | uinfo->value.integer.max = chip->hw->output_level_max; | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | static int vx_output_level_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
427 | { | ||
428 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
429 | int codec = kcontrol->id.index; | ||
430 | down(&chip->mixer_mutex); | ||
431 | ucontrol->value.integer.value[0] = chip->output_level[codec][0]; | ||
432 | ucontrol->value.integer.value[1] = chip->output_level[codec][1]; | ||
433 | up(&chip->mixer_mutex); | ||
434 | return 0; | ||
435 | } | ||
436 | |||
437 | static int vx_output_level_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
438 | { | ||
439 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
440 | int codec = kcontrol->id.index; | ||
441 | down(&chip->mixer_mutex); | ||
442 | if (ucontrol->value.integer.value[0] != chip->output_level[codec][0] || | ||
443 | ucontrol->value.integer.value[1] != chip->output_level[codec][1]) { | ||
444 | vx_set_analog_output_level(chip, codec, | ||
445 | ucontrol->value.integer.value[0], | ||
446 | ucontrol->value.integer.value[1]); | ||
447 | chip->output_level[codec][0] = ucontrol->value.integer.value[0]; | ||
448 | chip->output_level[codec][1] = ucontrol->value.integer.value[1]; | ||
449 | up(&chip->mixer_mutex); | ||
450 | return 1; | ||
451 | } | ||
452 | up(&chip->mixer_mutex); | ||
453 | return 0; | ||
454 | } | ||
455 | |||
456 | static snd_kcontrol_new_t vx_control_output_level = { | ||
457 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
458 | .name = "Master Playback Volume", | ||
459 | .info = vx_output_level_info, | ||
460 | .get = vx_output_level_get, | ||
461 | .put = vx_output_level_put, | ||
462 | }; | ||
463 | |||
464 | /* | ||
465 | * audio source select | ||
466 | */ | ||
467 | static int vx_audio_src_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
468 | { | ||
469 | static char *texts_mic[3] = { | ||
470 | "Digital", "Line", "Mic" | ||
471 | }; | ||
472 | static char *texts_vx2[2] = { | ||
473 | "Digital", "Analog" | ||
474 | }; | ||
475 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
476 | |||
477 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
478 | uinfo->count = 1; | ||
479 | if (chip->type >= VX_TYPE_VXPOCKET) { | ||
480 | uinfo->value.enumerated.items = 3; | ||
481 | if (uinfo->value.enumerated.item > 2) | ||
482 | uinfo->value.enumerated.item = 2; | ||
483 | strcpy(uinfo->value.enumerated.name, | ||
484 | texts_mic[uinfo->value.enumerated.item]); | ||
485 | } else { | ||
486 | uinfo->value.enumerated.items = 2; | ||
487 | if (uinfo->value.enumerated.item > 1) | ||
488 | uinfo->value.enumerated.item = 1; | ||
489 | strcpy(uinfo->value.enumerated.name, | ||
490 | texts_vx2[uinfo->value.enumerated.item]); | ||
491 | } | ||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | static int vx_audio_src_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
496 | { | ||
497 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
498 | ucontrol->value.enumerated.item[0] = chip->audio_source_target; | ||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | static int vx_audio_src_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
503 | { | ||
504 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
505 | down(&chip->mixer_mutex); | ||
506 | if (chip->audio_source_target != ucontrol->value.enumerated.item[0]) { | ||
507 | chip->audio_source_target = ucontrol->value.enumerated.item[0]; | ||
508 | vx_sync_audio_source(chip); | ||
509 | up(&chip->mixer_mutex); | ||
510 | return 1; | ||
511 | } | ||
512 | up(&chip->mixer_mutex); | ||
513 | return 0; | ||
514 | } | ||
515 | |||
516 | static snd_kcontrol_new_t vx_control_audio_src = { | ||
517 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
518 | .name = "Capture Source", | ||
519 | .info = vx_audio_src_info, | ||
520 | .get = vx_audio_src_get, | ||
521 | .put = vx_audio_src_put, | ||
522 | }; | ||
523 | |||
524 | /* | ||
525 | * clock mode selection | ||
526 | */ | ||
527 | static int vx_clock_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
528 | { | ||
529 | static char *texts[3] = { | ||
530 | "Auto", "Internal", "External" | ||
531 | }; | ||
532 | |||
533 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
534 | uinfo->count = 1; | ||
535 | uinfo->value.enumerated.items = 3; | ||
536 | if (uinfo->value.enumerated.item > 2) | ||
537 | uinfo->value.enumerated.item = 2; | ||
538 | strcpy(uinfo->value.enumerated.name, | ||
539 | texts[uinfo->value.enumerated.item]); | ||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | static int vx_clock_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
544 | { | ||
545 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
546 | ucontrol->value.enumerated.item[0] = chip->clock_mode; | ||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | static int vx_clock_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
551 | { | ||
552 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
553 | down(&chip->mixer_mutex); | ||
554 | if (chip->clock_mode != ucontrol->value.enumerated.item[0]) { | ||
555 | chip->clock_mode = ucontrol->value.enumerated.item[0]; | ||
556 | vx_set_clock(chip, chip->freq); | ||
557 | up(&chip->mixer_mutex); | ||
558 | return 1; | ||
559 | } | ||
560 | up(&chip->mixer_mutex); | ||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | static snd_kcontrol_new_t vx_control_clock_mode = { | ||
565 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
566 | .name = "Clock Mode", | ||
567 | .info = vx_clock_mode_info, | ||
568 | .get = vx_clock_mode_get, | ||
569 | .put = vx_clock_mode_put, | ||
570 | }; | ||
571 | |||
572 | /* | ||
573 | * Audio Gain | ||
574 | */ | ||
575 | static int vx_audio_gain_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
576 | { | ||
577 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
578 | uinfo->count = 2; | ||
579 | uinfo->value.integer.min = 0; | ||
580 | uinfo->value.integer.max = CVAL_MAX; | ||
581 | return 0; | ||
582 | } | ||
583 | |||
584 | static int vx_audio_gain_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
585 | { | ||
586 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
587 | int audio = kcontrol->private_value & 0xff; | ||
588 | int capture = (kcontrol->private_value >> 8) & 1; | ||
589 | |||
590 | down(&chip->mixer_mutex); | ||
591 | ucontrol->value.integer.value[0] = chip->audio_gain[capture][audio]; | ||
592 | ucontrol->value.integer.value[1] = chip->audio_gain[capture][audio+1]; | ||
593 | up(&chip->mixer_mutex); | ||
594 | return 0; | ||
595 | } | ||
596 | |||
597 | static int vx_audio_gain_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
598 | { | ||
599 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
600 | int audio = kcontrol->private_value & 0xff; | ||
601 | int capture = (kcontrol->private_value >> 8) & 1; | ||
602 | |||
603 | down(&chip->mixer_mutex); | ||
604 | if (ucontrol->value.integer.value[0] != chip->audio_gain[capture][audio] || | ||
605 | ucontrol->value.integer.value[1] != chip->audio_gain[capture][audio+1]) { | ||
606 | vx_set_audio_gain(chip, audio, capture, ucontrol->value.integer.value[0]); | ||
607 | vx_set_audio_gain(chip, audio+1, capture, ucontrol->value.integer.value[1]); | ||
608 | up(&chip->mixer_mutex); | ||
609 | return 1; | ||
610 | } | ||
611 | up(&chip->mixer_mutex); | ||
612 | return 0; | ||
613 | } | ||
614 | |||
615 | static int vx_audio_monitor_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
616 | { | ||
617 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
618 | int audio = kcontrol->private_value & 0xff; | ||
619 | |||
620 | down(&chip->mixer_mutex); | ||
621 | ucontrol->value.integer.value[0] = chip->audio_monitor[audio]; | ||
622 | ucontrol->value.integer.value[1] = chip->audio_monitor[audio+1]; | ||
623 | up(&chip->mixer_mutex); | ||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | static int vx_audio_monitor_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
628 | { | ||
629 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
630 | int audio = kcontrol->private_value & 0xff; | ||
631 | |||
632 | down(&chip->mixer_mutex); | ||
633 | if (ucontrol->value.integer.value[0] != chip->audio_monitor[audio] || | ||
634 | ucontrol->value.integer.value[1] != chip->audio_monitor[audio+1]) { | ||
635 | vx_set_monitor_level(chip, audio, ucontrol->value.integer.value[0], | ||
636 | chip->audio_monitor_active[audio]); | ||
637 | vx_set_monitor_level(chip, audio+1, ucontrol->value.integer.value[1], | ||
638 | chip->audio_monitor_active[audio+1]); | ||
639 | up(&chip->mixer_mutex); | ||
640 | return 1; | ||
641 | } | ||
642 | up(&chip->mixer_mutex); | ||
643 | return 0; | ||
644 | } | ||
645 | |||
646 | static int vx_audio_sw_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
647 | { | ||
648 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
649 | uinfo->count = 2; | ||
650 | uinfo->value.integer.min = 0; | ||
651 | uinfo->value.integer.max = 1; | ||
652 | return 0; | ||
653 | } | ||
654 | |||
655 | static int vx_audio_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
656 | { | ||
657 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
658 | int audio = kcontrol->private_value & 0xff; | ||
659 | |||
660 | down(&chip->mixer_mutex); | ||
661 | ucontrol->value.integer.value[0] = chip->audio_active[audio]; | ||
662 | ucontrol->value.integer.value[1] = chip->audio_active[audio+1]; | ||
663 | up(&chip->mixer_mutex); | ||
664 | return 0; | ||
665 | } | ||
666 | |||
667 | static int vx_audio_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
668 | { | ||
669 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
670 | int audio = kcontrol->private_value & 0xff; | ||
671 | |||
672 | down(&chip->mixer_mutex); | ||
673 | if (ucontrol->value.integer.value[0] != chip->audio_active[audio] || | ||
674 | ucontrol->value.integer.value[1] != chip->audio_active[audio+1]) { | ||
675 | vx_set_audio_switch(chip, audio, ucontrol->value.integer.value[0]); | ||
676 | vx_set_audio_switch(chip, audio+1, ucontrol->value.integer.value[1]); | ||
677 | up(&chip->mixer_mutex); | ||
678 | return 1; | ||
679 | } | ||
680 | up(&chip->mixer_mutex); | ||
681 | return 0; | ||
682 | } | ||
683 | |||
684 | static int vx_monitor_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
685 | { | ||
686 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
687 | int audio = kcontrol->private_value & 0xff; | ||
688 | |||
689 | down(&chip->mixer_mutex); | ||
690 | ucontrol->value.integer.value[0] = chip->audio_monitor_active[audio]; | ||
691 | ucontrol->value.integer.value[1] = chip->audio_monitor_active[audio+1]; | ||
692 | up(&chip->mixer_mutex); | ||
693 | return 0; | ||
694 | } | ||
695 | |||
696 | static int vx_monitor_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
697 | { | ||
698 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
699 | int audio = kcontrol->private_value & 0xff; | ||
700 | |||
701 | down(&chip->mixer_mutex); | ||
702 | if (ucontrol->value.integer.value[0] != chip->audio_monitor_active[audio] || | ||
703 | ucontrol->value.integer.value[1] != chip->audio_monitor_active[audio+1]) { | ||
704 | vx_set_monitor_level(chip, audio, chip->audio_monitor[audio], | ||
705 | ucontrol->value.integer.value[0]); | ||
706 | vx_set_monitor_level(chip, audio+1, chip->audio_monitor[audio+1], | ||
707 | ucontrol->value.integer.value[1]); | ||
708 | up(&chip->mixer_mutex); | ||
709 | return 1; | ||
710 | } | ||
711 | up(&chip->mixer_mutex); | ||
712 | return 0; | ||
713 | } | ||
714 | |||
715 | static snd_kcontrol_new_t vx_control_audio_gain = { | ||
716 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
717 | /* name will be filled later */ | ||
718 | .info = vx_audio_gain_info, | ||
719 | .get = vx_audio_gain_get, | ||
720 | .put = vx_audio_gain_put | ||
721 | }; | ||
722 | static snd_kcontrol_new_t vx_control_output_switch = { | ||
723 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
724 | .name = "PCM Playback Switch", | ||
725 | .info = vx_audio_sw_info, | ||
726 | .get = vx_audio_sw_get, | ||
727 | .put = vx_audio_sw_put | ||
728 | }; | ||
729 | static snd_kcontrol_new_t vx_control_monitor_gain = { | ||
730 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
731 | .name = "Monitoring Volume", | ||
732 | .info = vx_audio_gain_info, /* shared */ | ||
733 | .get = vx_audio_monitor_get, | ||
734 | .put = vx_audio_monitor_put | ||
735 | }; | ||
736 | static snd_kcontrol_new_t vx_control_monitor_switch = { | ||
737 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
738 | .name = "Monitoring Switch", | ||
739 | .info = vx_audio_sw_info, /* shared */ | ||
740 | .get = vx_monitor_sw_get, | ||
741 | .put = vx_monitor_sw_put | ||
742 | }; | ||
743 | |||
744 | |||
745 | /* | ||
746 | * IEC958 status bits | ||
747 | */ | ||
748 | static int vx_iec958_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
749 | { | ||
750 | uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; | ||
751 | uinfo->count = 1; | ||
752 | return 0; | ||
753 | } | ||
754 | |||
755 | static int vx_iec958_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
756 | { | ||
757 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
758 | |||
759 | down(&chip->mixer_mutex); | ||
760 | ucontrol->value.iec958.status[0] = (chip->uer_bits >> 0) & 0xff; | ||
761 | ucontrol->value.iec958.status[1] = (chip->uer_bits >> 8) & 0xff; | ||
762 | ucontrol->value.iec958.status[2] = (chip->uer_bits >> 16) & 0xff; | ||
763 | ucontrol->value.iec958.status[3] = (chip->uer_bits >> 24) & 0xff; | ||
764 | up(&chip->mixer_mutex); | ||
765 | return 0; | ||
766 | } | ||
767 | |||
768 | static int vx_iec958_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
769 | { | ||
770 | ucontrol->value.iec958.status[0] = 0xff; | ||
771 | ucontrol->value.iec958.status[1] = 0xff; | ||
772 | ucontrol->value.iec958.status[2] = 0xff; | ||
773 | ucontrol->value.iec958.status[3] = 0xff; | ||
774 | return 0; | ||
775 | } | ||
776 | |||
777 | static int vx_iec958_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
778 | { | ||
779 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
780 | unsigned int val; | ||
781 | |||
782 | val = (ucontrol->value.iec958.status[0] << 0) | | ||
783 | (ucontrol->value.iec958.status[1] << 8) | | ||
784 | (ucontrol->value.iec958.status[2] << 16) | | ||
785 | (ucontrol->value.iec958.status[3] << 24); | ||
786 | down(&chip->mixer_mutex); | ||
787 | if (chip->uer_bits != val) { | ||
788 | chip->uer_bits = val; | ||
789 | vx_set_iec958_status(chip, val); | ||
790 | up(&chip->mixer_mutex); | ||
791 | return 1; | ||
792 | } | ||
793 | up(&chip->mixer_mutex); | ||
794 | return 0; | ||
795 | } | ||
796 | |||
797 | static snd_kcontrol_new_t vx_control_iec958_mask = { | ||
798 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
799 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
800 | .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), | ||
801 | .info = vx_iec958_info, /* shared */ | ||
802 | .get = vx_iec958_mask_get, | ||
803 | }; | ||
804 | |||
805 | static snd_kcontrol_new_t vx_control_iec958 = { | ||
806 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
807 | .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), | ||
808 | .info = vx_iec958_info, | ||
809 | .get = vx_iec958_get, | ||
810 | .put = vx_iec958_put | ||
811 | }; | ||
812 | |||
813 | |||
814 | /* | ||
815 | * VU meter | ||
816 | */ | ||
817 | |||
818 | #define METER_MAX 0xff | ||
819 | #define METER_SHIFT 16 | ||
820 | |||
821 | static int vx_vu_meter_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
822 | { | ||
823 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
824 | uinfo->count = 2; | ||
825 | uinfo->value.integer.min = 0; | ||
826 | uinfo->value.integer.max = METER_MAX; | ||
827 | return 0; | ||
828 | } | ||
829 | |||
830 | static int vx_vu_meter_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
831 | { | ||
832 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
833 | struct vx_vu_meter meter[2]; | ||
834 | int audio = kcontrol->private_value & 0xff; | ||
835 | int capture = (kcontrol->private_value >> 8) & 1; | ||
836 | |||
837 | vx_get_audio_vu_meter(chip, audio, capture, meter); | ||
838 | ucontrol->value.integer.value[0] = meter[0].vu_level >> METER_SHIFT; | ||
839 | ucontrol->value.integer.value[1] = meter[1].vu_level >> METER_SHIFT; | ||
840 | return 0; | ||
841 | } | ||
842 | |||
843 | static int vx_peak_meter_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
844 | { | ||
845 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
846 | struct vx_vu_meter meter[2]; | ||
847 | int audio = kcontrol->private_value & 0xff; | ||
848 | int capture = (kcontrol->private_value >> 8) & 1; | ||
849 | |||
850 | vx_get_audio_vu_meter(chip, audio, capture, meter); | ||
851 | ucontrol->value.integer.value[0] = meter[0].peak_level >> METER_SHIFT; | ||
852 | ucontrol->value.integer.value[1] = meter[1].peak_level >> METER_SHIFT; | ||
853 | return 0; | ||
854 | } | ||
855 | |||
856 | static int vx_saturation_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
857 | { | ||
858 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
859 | uinfo->count = 2; | ||
860 | uinfo->value.integer.min = 0; | ||
861 | uinfo->value.integer.max = 1; | ||
862 | return 0; | ||
863 | } | ||
864 | |||
865 | static int vx_saturation_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
866 | { | ||
867 | vx_core_t *chip = snd_kcontrol_chip(kcontrol); | ||
868 | struct vx_vu_meter meter[2]; | ||
869 | int audio = kcontrol->private_value & 0xff; | ||
870 | |||
871 | vx_get_audio_vu_meter(chip, audio, 1, meter); /* capture only */ | ||
872 | ucontrol->value.integer.value[0] = meter[0].saturated; | ||
873 | ucontrol->value.integer.value[1] = meter[1].saturated; | ||
874 | return 0; | ||
875 | } | ||
876 | |||
877 | static snd_kcontrol_new_t vx_control_vu_meter = { | ||
878 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
879 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
880 | /* name will be filled later */ | ||
881 | .info = vx_vu_meter_info, | ||
882 | .get = vx_vu_meter_get, | ||
883 | }; | ||
884 | |||
885 | static snd_kcontrol_new_t vx_control_peak_meter = { | ||
886 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
887 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
888 | /* name will be filled later */ | ||
889 | .info = vx_vu_meter_info, /* shared */ | ||
890 | .get = vx_peak_meter_get, | ||
891 | }; | ||
892 | |||
893 | static snd_kcontrol_new_t vx_control_saturation = { | ||
894 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
895 | .name = "Input Saturation", | ||
896 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
897 | .info = vx_saturation_info, | ||
898 | .get = vx_saturation_get, | ||
899 | }; | ||
900 | |||
901 | |||
902 | |||
903 | /* | ||
904 | * | ||
905 | */ | ||
906 | |||
907 | int snd_vx_mixer_new(vx_core_t *chip) | ||
908 | { | ||
909 | unsigned int i, c; | ||
910 | int err; | ||
911 | snd_kcontrol_new_t temp; | ||
912 | snd_card_t *card = chip->card; | ||
913 | char name[32]; | ||
914 | |||
915 | strcpy(card->mixername, card->driver); | ||
916 | |||
917 | /* output level controls */ | ||
918 | for (i = 0; i < chip->hw->num_outs; i++) { | ||
919 | temp = vx_control_output_level; | ||
920 | temp.index = i; | ||
921 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
922 | return err; | ||
923 | } | ||
924 | |||
925 | /* PCM volumes, switches, monitoring */ | ||
926 | for (i = 0; i < chip->hw->num_outs; i++) { | ||
927 | int val = i * 2; | ||
928 | temp = vx_control_audio_gain; | ||
929 | temp.index = i; | ||
930 | temp.name = "PCM Playback Volume"; | ||
931 | temp.private_value = val; | ||
932 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
933 | return err; | ||
934 | temp = vx_control_output_switch; | ||
935 | temp.index = i; | ||
936 | temp.private_value = val; | ||
937 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
938 | return err; | ||
939 | temp = vx_control_monitor_gain; | ||
940 | temp.index = i; | ||
941 | temp.private_value = val; | ||
942 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
943 | return err; | ||
944 | temp = vx_control_monitor_switch; | ||
945 | temp.index = i; | ||
946 | temp.private_value = val; | ||
947 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
948 | return err; | ||
949 | } | ||
950 | for (i = 0; i < chip->hw->num_outs; i++) { | ||
951 | temp = vx_control_audio_gain; | ||
952 | temp.index = i; | ||
953 | temp.name = "PCM Capture Volume"; | ||
954 | temp.private_value = (i * 2) | (1 << 8); | ||
955 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
956 | return err; | ||
957 | } | ||
958 | |||
959 | /* Audio source */ | ||
960 | if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_audio_src, chip))) < 0) | ||
961 | return err; | ||
962 | /* clock mode */ | ||
963 | if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_clock_mode, chip))) < 0) | ||
964 | return err; | ||
965 | /* IEC958 controls */ | ||
966 | if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958_mask, chip))) < 0) | ||
967 | return err; | ||
968 | if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958, chip))) < 0) | ||
969 | return err; | ||
970 | /* VU, peak, saturation meters */ | ||
971 | for (c = 0; c < 2; c++) { | ||
972 | static char *dir[2] = { "Output", "Input" }; | ||
973 | for (i = 0; i < chip->hw->num_ins; i++) { | ||
974 | int val = (i * 2) | (c << 8); | ||
975 | if (c == 1) { | ||
976 | temp = vx_control_saturation; | ||
977 | temp.index = i; | ||
978 | temp.private_value = val; | ||
979 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
980 | return err; | ||
981 | } | ||
982 | sprintf(name, "%s VU Meter", dir[c]); | ||
983 | temp = vx_control_vu_meter; | ||
984 | temp.index = i; | ||
985 | temp.name = name; | ||
986 | temp.private_value = val; | ||
987 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
988 | return err; | ||
989 | sprintf(name, "%s Peak Meter", dir[c]); | ||
990 | temp = vx_control_peak_meter; | ||
991 | temp.index = i; | ||
992 | temp.name = name; | ||
993 | temp.private_value = val; | ||
994 | if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0) | ||
995 | return err; | ||
996 | } | ||
997 | } | ||
998 | vx_reset_audio_levels(chip); | ||
999 | return 0; | ||
1000 | } | ||