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