aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/lola/lola_mixer.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2011-05-03 10:14:46 -0400
committerTakashi Iwai <tiwai@suse.de>2011-05-03 10:31:05 -0400
commitd43f3010b8fa7530c3780c087fad9b0a8a437ba1 (patch)
treef54b55676b5b66115ee0bfc1df0994044cd9b697 /sound/pci/lola/lola_mixer.c
parent521cb40b0c44418a4fd36dc633f575813d59a43d (diff)
ALSA: Add the driver for Digigram Lola PCI-e boards
Added a new driver for supporting Digigram Lola PCI-e boards. Lola has a similar h/w design like HD-audio but with extended verbs. Thus the driver is written similarly like HD-audio driver in the bus part. The codec part is rather written in a fixed way specific to the Lola board because of the verb incompatibility. The driver provides basic PCM, supporting multi-streams and mixing. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/lola/lola_mixer.c')
-rw-r--r--sound/pci/lola/lola_mixer.c826
1 files changed, 826 insertions, 0 deletions
diff --git a/sound/pci/lola/lola_mixer.c b/sound/pci/lola/lola_mixer.c
new file mode 100644
index 000000000000..0d09689d7b8d
--- /dev/null
+++ b/sound/pci/lola/lola_mixer.c
@@ -0,0 +1,826 @@
1/*
2 * Support for Digigram Lola PCI-e boards
3 *
4 * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program; if not, write to the Free Software Foundation, Inc., 59
18 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21#include <linux/kernel.h>
22#include <linux/init.h>
23#include <linux/vmalloc.h>
24#include <linux/io.h>
25#include <sound/core.h>
26#include <sound/control.h>
27#include <sound/pcm.h>
28#include <sound/tlv.h>
29#include "lola.h"
30
31static int __devinit lola_init_pin(struct lola *chip, struct lola_pin *pin,
32 int dir, int nid)
33{
34 unsigned int val;
35 int err;
36
37 pin->nid = nid;
38 err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
39 if (err < 0) {
40 printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid);
41 return err;
42 }
43 val &= 0x00f00fff; /* test TYPE and bits 0..11 */
44 if (val == 0x00400200) /* Type = 4, Digital = 1 */
45 pin->is_analog = false;
46 else if (val == 0x0040000a && dir == CAPT) /* Dig=0, InAmp/ovrd */
47 pin->is_analog = true;
48 else if (val == 0x0040000c && dir == PLAY) /* Dig=0, OutAmp/ovrd */
49 pin->is_analog = true;
50 else {
51 printk(KERN_ERR SFX "Invalid wcaps 0x%x for 0x%x\n", val, nid);
52 return -EINVAL;
53 }
54
55 /* analog parameters only following, so continue in case of Digital pin
56 */
57 if (!pin->is_analog)
58 return 0;
59
60 if (dir == PLAY)
61 err = lola_read_param(chip, nid, LOLA_PAR_AMP_OUT_CAP, &val);
62 else
63 err = lola_read_param(chip, nid, LOLA_PAR_AMP_IN_CAP, &val);
64 if (err < 0) {
65 printk(KERN_ERR SFX "Can't read AMP-caps for 0x%x\n", nid);
66 return err;
67 }
68
69 pin->amp_mute = LOLA_AMP_MUTE_CAPABLE(val);
70 pin->amp_step_size = LOLA_AMP_STEP_SIZE(val);
71 pin->amp_num_steps = LOLA_AMP_NUM_STEPS(val);
72 if (pin->amp_num_steps) {
73 /* zero as mute state */
74 pin->amp_num_steps++;
75 pin->amp_step_size++;
76 }
77 pin->amp_offset = LOLA_AMP_OFFSET(val);
78
79 err = lola_codec_read(chip, nid, LOLA_VERB_GET_MAX_LEVEL, 0, 0, &val,
80 NULL);
81 if (err < 0) {
82 printk(KERN_ERR SFX "Can't get MAX_LEVEL 0x%x\n", nid);
83 return err;
84 }
85 pin->max_level = val & 0x3ff; /* 10 bits */
86
87 pin->config_default_reg = 0;
88 pin->fixed_gain_list_len = 0;
89 pin->cur_gain_step = 0;
90
91 return 0;
92}
93
94int __devinit lola_init_pins(struct lola *chip, int dir, int *nidp)
95{
96 int i, err, nid;
97 nid = *nidp;
98 for (i = 0; i < chip->pin[dir].num_pins; i++, nid++) {
99 err = lola_init_pin(chip, &chip->pin[dir].pins[i], dir, nid);
100 if (err < 0)
101 return err;
102 }
103 *nidp = nid;
104 return 0;
105}
106
107void lola_free_mixer(struct lola *chip)
108{
109 if (chip->mixer.array_saved)
110 vfree(chip->mixer.array_saved);
111}
112
113int __devinit lola_init_mixer_widget(struct lola *chip, int nid)
114{
115 unsigned int val;
116 int err;
117
118 err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
119 if (err < 0) {
120 printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid);
121 return err;
122 }
123
124 if ((val & 0xfff00000) != 0x02f00000) { /* test SubType and Type */
125 snd_printdd("No valid mixer widget\n");
126 return 0;
127 }
128
129 chip->mixer.nid = nid;
130 chip->mixer.caps = val;
131 chip->mixer.array = (struct lola_mixer_array __iomem *)
132 (chip->bar[BAR1].remap_addr + LOLA_BAR1_SOURCE_GAIN_ENABLE);
133
134 /* reserve memory to copy mixer data for sleep mode transitions */
135 chip->mixer.array_saved = vmalloc(sizeof(struct lola_mixer_array));
136
137 /* mixer matrix sources are physical input data and play streams */
138 chip->mixer.src_stream_outs = chip->pcm[PLAY].num_streams;
139 chip->mixer.src_phys_ins = chip->pin[CAPT].num_pins;
140
141 /* mixer matrix destinations are record streams and physical output */
142 chip->mixer.dest_stream_ins = chip->pcm[CAPT].num_streams;
143 chip->mixer.dest_phys_outs = chip->pin[PLAY].num_pins;
144
145 /* mixer matrix can have unused areas between PhysIn and
146 * Play or Record and PhysOut zones
147 */
148 chip->mixer.src_stream_out_ofs = chip->mixer.src_phys_ins +
149 LOLA_MIXER_SRC_INPUT_PLAY_SEPARATION(val);
150 chip->mixer.dest_phys_out_ofs = chip->mixer.dest_stream_ins +
151 LOLA_MIXER_DEST_REC_OUTPUT_SEPATATION(val);
152
153 /* example : MixerMatrix of LoLa881
154 * 0-------8------16-------8------16
155 * | | | | |
156 * | INPUT | | INPUT | |
157 * | -> |unused | -> |unused |
158 * | RECORD| | OUTPUT| |
159 * | | | | |
160 * 8--------------------------------
161 * | | | | |
162 * | | | | |
163 * |unused |unused |unused |unused |
164 * | | | | |
165 * | | | | |
166 * 16-------------------------------
167 * | | | | |
168 * | PLAY | | PLAY | |
169 * | -> |unused | -> |unused |
170 * | RECORD| | OUTPUT| |
171 * | | | | |
172 * 8--------------------------------
173 * | | | | |
174 * | | | | |
175 * |unused |unused |unused |unused |
176 * | | | | |
177 * | | | | |
178 * 16-------------------------------
179 */
180 if (chip->mixer.src_stream_out_ofs > MAX_AUDIO_INOUT_COUNT ||
181 chip->mixer.dest_phys_out_ofs > MAX_STREAM_IN_COUNT) {
182 printk(KERN_ERR SFX "Invalid mixer widget size\n");
183 return -EINVAL;
184 }
185
186 chip->mixer.src_mask = ((1U << chip->mixer.src_phys_ins) - 1) |
187 (((1U << chip->mixer.src_stream_outs) - 1)
188 << chip->mixer.src_stream_out_ofs);
189 chip->mixer.dest_mask = ((1U << chip->mixer.dest_stream_ins) - 1) |
190 (((1U << chip->mixer.dest_phys_outs) - 1)
191 << chip->mixer.dest_phys_out_ofs);
192
193 return 0;
194}
195
196static int lola_mixer_set_src_gain(struct lola *chip, unsigned int id,
197 unsigned short gain, bool on)
198{
199 unsigned int oldval, val;
200
201 if (!(chip->mixer.src_mask & (1 << id)))
202 return -EINVAL;
203 writew(gain, &chip->mixer.array->src_gain[id]);
204 oldval = val = readl(&chip->mixer.array->src_gain_enable);
205 if (on)
206 val |= (1 << id);
207 else
208 val &= ~(1 << id);
209 writel(val, &chip->mixer.array->src_gain_enable);
210 lola_codec_flush(chip);
211 /* inform micro-controller about the new source gain */
212 return lola_codec_write(chip, chip->mixer.nid,
213 LOLA_VERB_SET_SOURCE_GAIN, id, 0);
214}
215
216#if 0 /* not used */
217static int lola_mixer_set_src_gains(struct lola *chip, unsigned int mask,
218 unsigned short *gains)
219{
220 int i;
221
222 if ((chip->mixer.src_mask & mask) != mask)
223 return -EINVAL;
224 for (i = 0; i < LOLA_MIXER_DIM; i++) {
225 if (mask & (1 << i)) {
226 writew(*gains, &chip->mixer.array->src_gain[i]);
227 gains++;
228 }
229 }
230 writel(mask, &chip->mixer.array->src_gain_enable);
231 lola_codec_flush(chip);
232 if (chip->mixer.caps & LOLA_PEAK_METER_CAN_AGC_MASK) {
233 /* update for all srcs at once */
234 return lola_codec_write(chip, chip->mixer.nid,
235 LOLA_VERB_SET_SOURCE_GAIN, 0x80, 0);
236 }
237 /* update manually */
238 for (i = 0; i < LOLA_MIXER_DIM; i++) {
239 if (mask & (1 << i)) {
240 lola_codec_write(chip, chip->mixer.nid,
241 LOLA_VERB_SET_SOURCE_GAIN, i, 0);
242 }
243 }
244 return 0;
245}
246#endif /* not used */
247
248static int lola_mixer_set_mapping_gain(struct lola *chip,
249 unsigned int src, unsigned int dest,
250 unsigned short gain, bool on)
251{
252 unsigned int val;
253
254 if (!(chip->mixer.src_mask & (1 << src)) ||
255 !(chip->mixer.dest_mask & (1 << dest)))
256 return -EINVAL;
257 if (on)
258 writew(gain, &chip->mixer.array->dest_mix_gain[dest][src]);
259 val = readl(&chip->mixer.array->dest_mix_gain_enable[dest]);
260 if (on)
261 val |= (1 << src);
262 else
263 val &= ~(1 << src);
264 writel(val, &chip->mixer.array->dest_mix_gain_enable[dest]);
265 lola_codec_flush(chip);
266 return lola_codec_write(chip, chip->mixer.nid, LOLA_VERB_SET_MIX_GAIN,
267 src, dest);
268}
269
270static int lola_mixer_set_dest_gains(struct lola *chip, unsigned int id,
271 unsigned int mask, unsigned short *gains)
272{
273 int i;
274
275 if (!(chip->mixer.dest_mask & (1 << id)) ||
276 (chip->mixer.src_mask & mask) != mask)
277 return -EINVAL;
278 for (i = 0; i < LOLA_MIXER_DIM; i++) {
279 if (mask & (1 << i)) {
280 writew(*gains, &chip->mixer.array->dest_mix_gain[id][i]);
281 gains++;
282 }
283 }
284 writel(mask, &chip->mixer.array->dest_mix_gain_enable[id]);
285 lola_codec_flush(chip);
286 /* update for all dests at once */
287 return lola_codec_write(chip, chip->mixer.nid,
288 LOLA_VERB_SET_DESTINATION_GAIN, id, 0);
289}
290
291/*
292 */
293
294static int set_analog_volume(struct lola *chip, int dir,
295 unsigned int idx, unsigned int val,
296 bool external_call);
297
298int lola_setup_all_analog_gains(struct lola *chip, int dir, bool mute)
299{
300 struct lola_pin *pin;
301 int idx, max_idx;
302
303 pin = chip->pin[dir].pins;
304 max_idx = chip->pin[dir].num_pins;
305 for (idx = 0; idx < max_idx; idx++) {
306 if (pin[idx].is_analog) {
307 unsigned int val = mute ? 0 : pin[idx].cur_gain_step;
308 /* set volume and do not save the value */
309 set_analog_volume(chip, dir, idx, val, false);
310 }
311 }
312 return lola_codec_flush(chip);
313}
314
315void lola_save_mixer(struct lola *chip)
316{
317 /* mute analog output */
318 if (chip->mixer.array_saved) {
319 /* store contents of mixer array */
320 memcpy_fromio(chip->mixer.array_saved, chip->mixer.array,
321 sizeof(*chip->mixer.array));
322 }
323 lola_setup_all_analog_gains(chip, PLAY, true); /* output mute */
324}
325
326void lola_restore_mixer(struct lola *chip)
327{
328 int i;
329
330 /*lola_reset_setups(chip);*/
331 if (chip->mixer.array_saved) {
332 /* restore contents of mixer array */
333 memcpy_toio(chip->mixer.array, chip->mixer.array_saved,
334 sizeof(*chip->mixer.array));
335 /* inform micro-controller about all restored values
336 * and ignore return values
337 */
338 for (i = 0; i < chip->mixer.src_phys_ins; i++)
339 lola_codec_write(chip, chip->mixer.nid,
340 LOLA_VERB_SET_SOURCE_GAIN,
341 i, 0);
342 for (i = 0; i < chip->mixer.src_stream_outs; i++)
343 lola_codec_write(chip, chip->mixer.nid,
344 LOLA_VERB_SET_SOURCE_GAIN,
345 chip->mixer.src_stream_out_ofs + i, 0);
346 for (i = 0; i < chip->mixer.dest_stream_ins; i++)
347 lola_codec_write(chip, chip->mixer.nid,
348 LOLA_VERB_SET_DESTINATION_GAIN,
349 i, 0);
350 for (i = 0; i < chip->mixer.dest_phys_outs; i++)
351 lola_codec_write(chip, chip->mixer.nid,
352 LOLA_VERB_SET_DESTINATION_GAIN,
353 chip->mixer.dest_phys_out_ofs + i, 0);
354 lola_codec_flush(chip);
355 }
356}
357
358/*
359 */
360
361static int set_analog_volume(struct lola *chip, int dir,
362 unsigned int idx, unsigned int val,
363 bool external_call)
364{
365 struct lola_pin *pin;
366 int err;
367
368 if (idx >= chip->pin[dir].num_pins)
369 return -EINVAL;
370 pin = &chip->pin[dir].pins[idx];
371 if (!pin->is_analog || pin->amp_num_steps <= val)
372 return -EINVAL;
373 if (external_call && pin->cur_gain_step == val)
374 return 0;
375 if (external_call)
376 lola_codec_flush(chip);
377 err = lola_codec_write(chip, pin->nid,
378 LOLA_VERB_SET_AMP_GAIN_MUTE, val, 0);
379 if (err < 0)
380 return err;
381 if (external_call)
382 pin->cur_gain_step = val;
383 return 0;
384}
385
386int lola_set_src_config(struct lola *chip, unsigned int src_mask, bool update)
387{
388 int ret = 0;
389 int success = 0;
390 int n, err;
391
392 /* SRC can be activated and the dwInputSRCMask is valid? */
393 if ((chip->input_src_caps_mask & src_mask) != src_mask)
394 return -EINVAL;
395 /* handle all even Inputs - SRC is a stereo setting !!! */
396 for (n = 0; n < chip->pin[CAPT].num_pins; n += 2) {
397 unsigned int mask = 3U << n; /* handle the stereo case */
398 unsigned int new_src, src_state;
399 if (!(chip->input_src_caps_mask & mask))
400 continue;
401 /* if one IO needs SRC, both stereo IO will get SRC */
402 new_src = (src_mask & mask) != 0;
403 if (update) {
404 src_state = (chip->input_src_mask & mask) != 0;
405 if (src_state == new_src)
406 continue; /* nothing to change for this IO */
407 }
408 err = lola_codec_write(chip, chip->pcm[CAPT].streams[n].nid,
409 LOLA_VERB_SET_SRC, new_src, 0);
410 if (!err)
411 success++;
412 else
413 ret = err;
414 }
415 if (success)
416 ret = lola_codec_flush(chip);
417 if (!ret)
418 chip->input_src_mask = src_mask;
419 return ret;
420}
421
422/*
423 */
424static int init_mixer_values(struct lola *chip)
425{
426 int i;
427
428 /* all src on */
429 lola_set_src_config(chip, (1 << chip->pin[CAPT].num_pins) - 1, false);
430
431 /* clear all matrix */
432 memset_io(chip->mixer.array, 0, sizeof(*chip->mixer.array));
433 /* set src gain to 0dB */
434 for (i = 0; i < chip->mixer.src_phys_ins; i++)
435 lola_mixer_set_src_gain(chip, i, 336, true); /* 0dB */
436 for (i = 0; i < chip->mixer.src_stream_outs; i++)
437 lola_mixer_set_src_gain(chip,
438 i + chip->mixer.src_stream_out_ofs,
439 336, true); /* 0dB */
440 /* set 1:1 dest gain */
441 for (i = 0; i < chip->mixer.dest_stream_ins; i++) {
442 int src = i % chip->mixer.src_phys_ins;
443 lola_mixer_set_mapping_gain(chip, src, i, 336, true);
444 }
445 for (i = 0; i < chip->mixer.src_stream_outs; i++) {
446 int src = chip->mixer.src_stream_out_ofs + i;
447 int dst = chip->mixer.dest_phys_out_ofs +
448 i % chip->mixer.dest_phys_outs;
449 lola_mixer_set_mapping_gain(chip, src, dst, 336, true);
450 }
451 return 0;
452}
453
454/*
455 * analog mixer control element
456 */
457static int lola_analog_vol_info(struct snd_kcontrol *kcontrol,
458 struct snd_ctl_elem_info *uinfo)
459{
460 struct lola *chip = snd_kcontrol_chip(kcontrol);
461 int dir = kcontrol->private_value;
462
463 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
464 uinfo->count = chip->pin[dir].num_pins;
465 uinfo->value.integer.min = 0;
466 uinfo->value.integer.max = chip->pin[dir].pins[0].amp_num_steps;
467 return 0;
468}
469
470static int lola_analog_vol_get(struct snd_kcontrol *kcontrol,
471 struct snd_ctl_elem_value *ucontrol)
472{
473 struct lola *chip = snd_kcontrol_chip(kcontrol);
474 int dir = kcontrol->private_value;
475 int i;
476
477 for (i = 0; i < chip->pin[dir].num_pins; i++)
478 ucontrol->value.integer.value[i] =
479 chip->pin[dir].pins[i].cur_gain_step;
480 return 0;
481}
482
483static int lola_analog_vol_put(struct snd_kcontrol *kcontrol,
484 struct snd_ctl_elem_value *ucontrol)
485{
486 struct lola *chip = snd_kcontrol_chip(kcontrol);
487 int dir = kcontrol->private_value;
488 int i, err;
489
490 for (i = 0; i < chip->pin[dir].num_pins; i++) {
491 err = set_analog_volume(chip, dir, i,
492 ucontrol->value.integer.value[i],
493 true);
494 if (err < 0)
495 return err;
496 }
497 return 0;
498}
499
500static int lola_analog_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
501 unsigned int size, unsigned int __user *tlv)
502{
503 struct lola *chip = snd_kcontrol_chip(kcontrol);
504 int dir = kcontrol->private_value;
505 unsigned int val1, val2;
506 struct lola_pin *pin;
507
508 if (size < 4 * sizeof(unsigned int))
509 return -ENOMEM;
510 pin = &chip->pin[dir].pins[0];
511
512 val2 = pin->amp_step_size * 25;
513 val1 = -1 * (int)pin->amp_offset * (int)val2;
514#ifdef TLV_DB_SCALE_MUTE
515 val2 |= TLV_DB_SCALE_MUTE;
516#endif
517 if (put_user(SNDRV_CTL_TLVT_DB_SCALE, tlv))
518 return -EFAULT;
519 if (put_user(2 * sizeof(unsigned int), tlv + 1))
520 return -EFAULT;
521 if (put_user(val1, tlv + 2))
522 return -EFAULT;
523 if (put_user(val2, tlv + 3))
524 return -EFAULT;
525 return 0;
526}
527
528static struct snd_kcontrol_new lola_analog_mixer __devinitdata = {
529 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
530 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
531 SNDRV_CTL_ELEM_ACCESS_TLV_READ |
532 SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK),
533 .info = lola_analog_vol_info,
534 .get = lola_analog_vol_get,
535 .put = lola_analog_vol_put,
536 .tlv.c = lola_analog_vol_tlv,
537};
538
539static int __devinit create_analog_mixer(struct lola *chip, int dir, char *name)
540{
541 if (!chip->pin[dir].num_pins)
542 return 0;
543 lola_analog_mixer.name = name;
544 lola_analog_mixer.private_value = dir;
545 return snd_ctl_add(chip->card,
546 snd_ctl_new1(&lola_analog_mixer, chip));
547}
548
549/*
550 */
551static int lola_input_src_info(struct snd_kcontrol *kcontrol,
552 struct snd_ctl_elem_info *uinfo)
553{
554 struct lola *chip = snd_kcontrol_chip(kcontrol);
555
556 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
557 uinfo->count = chip->pin[CAPT].num_pins;
558 uinfo->value.integer.min = 0;
559 uinfo->value.integer.max = 1;
560 return 0;
561}
562
563static int lola_input_src_get(struct snd_kcontrol *kcontrol,
564 struct snd_ctl_elem_value *ucontrol)
565{
566 struct lola *chip = snd_kcontrol_chip(kcontrol);
567 int i;
568
569 for (i = 0; i < chip->pin[CAPT].num_pins; i++)
570 ucontrol->value.integer.value[i] =
571 !!(chip->input_src_mask & (1 << i));
572 return 0;
573}
574
575static int lola_input_src_put(struct snd_kcontrol *kcontrol,
576 struct snd_ctl_elem_value *ucontrol)
577{
578 struct lola *chip = snd_kcontrol_chip(kcontrol);
579 int i;
580 unsigned int mask;
581
582 mask = 0;
583 for (i = 0; i < chip->pin[CAPT].num_pins; i++)
584 if (ucontrol->value.integer.value[i])
585 mask |= 1 << i;
586 return lola_set_src_config(chip, mask, true);
587}
588
589static struct snd_kcontrol_new lola_input_src_mixer __devinitdata = {
590 .name = "Analog Capture Switch",
591 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
592 .info = lola_input_src_info,
593 .get = lola_input_src_get,
594 .put = lola_input_src_put,
595};
596
597static int __devinit create_input_src_mixer(struct lola *chip)
598{
599 return snd_ctl_add(chip->card,
600 snd_ctl_new1(&lola_input_src_mixer, chip));
601}
602
603/*
604 * src gain mixer
605 */
606static int lola_src_gain_info(struct snd_kcontrol *kcontrol,
607 struct snd_ctl_elem_info *uinfo)
608{
609 unsigned int count = (kcontrol->private_value >> 8) & 0xff;
610
611 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
612 uinfo->count = count;
613 uinfo->value.integer.min = 0;
614 uinfo->value.integer.max = 409;
615 return 0;
616}
617
618static int lola_src_gain_get(struct snd_kcontrol *kcontrol,
619 struct snd_ctl_elem_value *ucontrol)
620{
621 struct lola *chip = snd_kcontrol_chip(kcontrol);
622 unsigned int ofs = kcontrol->private_value & 0xff;
623 unsigned int count = (kcontrol->private_value >> 8) & 0xff;
624 unsigned int mask, i;
625
626 mask = readl(&chip->mixer.array->src_gain_enable);
627 for (i = 0; i < count; i++) {
628 unsigned int idx = ofs + i;
629 unsigned short val;
630 if (!(chip->mixer.src_mask & (1 << idx)))
631 return -EINVAL;
632 if (mask & (1 << idx))
633 val = readw(&chip->mixer.array->src_gain[idx]) + 1;
634 else
635 val = 0;
636 ucontrol->value.integer.value[i] = val;
637 }
638 return 0;
639}
640
641static int lola_src_gain_put(struct snd_kcontrol *kcontrol,
642 struct snd_ctl_elem_value *ucontrol)
643{
644 struct lola *chip = snd_kcontrol_chip(kcontrol);
645 unsigned int ofs = kcontrol->private_value & 0xff;
646 unsigned int count = (kcontrol->private_value >> 8) & 0xff;
647 int i, err;
648
649 for (i = 0; i < count; i++) {
650 unsigned int idx = ofs + i;
651 unsigned short val = ucontrol->value.integer.value[i];
652 if (val)
653 val--;
654 err = lola_mixer_set_src_gain(chip, idx, val, !!val);
655 if (err < 0)
656 return err;
657 }
658 return 0;
659}
660
661/* raw value: 0 = -84dB, 336 = 0dB, 408=18dB, incremented 1 for mute */
662static const DECLARE_TLV_DB_SCALE(lola_src_gain_tlv, -8425, 25, 1);
663
664static struct snd_kcontrol_new lola_src_gain_mixer __devinitdata = {
665 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
666 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
667 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
668 .info = lola_src_gain_info,
669 .get = lola_src_gain_get,
670 .put = lola_src_gain_put,
671 .tlv.p = lola_src_gain_tlv,
672};
673
674static int __devinit create_src_gain_mixer(struct lola *chip,
675 int num, int ofs, char *name)
676{
677 lola_src_gain_mixer.name = name;
678 lola_src_gain_mixer.private_value = ofs + (num << 8);
679 return snd_ctl_add(chip->card,
680 snd_ctl_new1(&lola_src_gain_mixer, chip));
681}
682
683/*
684 * destination gain (matrix-like) mixer
685 */
686static int lola_dest_gain_info(struct snd_kcontrol *kcontrol,
687 struct snd_ctl_elem_info *uinfo)
688{
689 unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
690
691 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
692 uinfo->count = src_num;
693 uinfo->value.integer.min = 0;
694 uinfo->value.integer.max = 433;
695 return 0;
696}
697
698static int lola_dest_gain_get(struct snd_kcontrol *kcontrol,
699 struct snd_ctl_elem_value *ucontrol)
700{
701 struct lola *chip = snd_kcontrol_chip(kcontrol);
702 unsigned int src_ofs = kcontrol->private_value & 0xff;
703 unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
704 unsigned int dst_ofs = (kcontrol->private_value >> 16) & 0xff;
705 unsigned int dst, mask, i;
706
707 dst = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + dst_ofs;
708 mask = readl(&chip->mixer.array->dest_mix_gain_enable[dst]);
709 for (i = 0; i < src_num; i++) {
710 unsigned int src = src_ofs + i;
711 unsigned short val;
712 if (!(chip->mixer.src_mask & (1 << src)))
713 return -EINVAL;
714 if (mask & (1 << dst))
715 val = readw(&chip->mixer.array->dest_mix_gain[dst][src]) + 1;
716 else
717 val = 0;
718 ucontrol->value.integer.value[i] = val;
719 }
720 return 0;
721}
722
723static int lola_dest_gain_put(struct snd_kcontrol *kcontrol,
724 struct snd_ctl_elem_value *ucontrol)
725{
726 struct lola *chip = snd_kcontrol_chip(kcontrol);
727 unsigned int src_ofs = kcontrol->private_value & 0xff;
728 unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
729 unsigned int dst_ofs = (kcontrol->private_value >> 16) & 0xff;
730 unsigned int dst, mask;
731 unsigned short gains[MAX_STREAM_COUNT];
732 int i, num;
733
734 mask = 0;
735 num = 0;
736 for (i = 0; i < src_num; i++) {
737 unsigned short val = ucontrol->value.integer.value[i];
738 if (val) {
739 gains[num++] = val - 1;
740 mask |= 1 << i;
741 }
742 }
743 mask <<= src_ofs;
744 dst = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + dst_ofs;
745 return lola_mixer_set_dest_gains(chip, dst, mask, gains);
746}
747
748static const DECLARE_TLV_DB_SCALE(lola_dest_gain_tlv, -8425, 25, 1);
749
750static struct snd_kcontrol_new lola_dest_gain_mixer __devinitdata = {
751 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
752 .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
753 SNDRV_CTL_ELEM_ACCESS_TLV_READ),
754 .info = lola_dest_gain_info,
755 .get = lola_dest_gain_get,
756 .put = lola_dest_gain_put,
757 .tlv.p = lola_dest_gain_tlv,
758};
759
760static int __devinit create_dest_gain_mixer(struct lola *chip,
761 int src_num, int src_ofs,
762 int num, int ofs, char *name)
763{
764 lola_dest_gain_mixer.count = num;
765 lola_dest_gain_mixer.name = name;
766 lola_dest_gain_mixer.private_value =
767 src_ofs + (src_num << 8) + (ofs << 16) + (num << 24);
768 return snd_ctl_add(chip->card,
769 snd_ctl_new1(&lola_dest_gain_mixer, chip));
770}
771
772/*
773 */
774int __devinit lola_create_mixer(struct lola *chip)
775{
776 int err;
777
778 err = create_analog_mixer(chip, PLAY, "Analog Playback Volume");
779 if (err < 0)
780 return err;
781 err = create_analog_mixer(chip, CAPT, "Analog Capture Volume");
782 if (err < 0)
783 return err;
784 err = create_input_src_mixer(chip);
785 if (err < 0)
786 return err;
787 err = create_src_gain_mixer(chip, chip->mixer.src_phys_ins, 0,
788 "Line Source Gain Volume");
789 if (err < 0)
790 return err;
791 err = create_src_gain_mixer(chip, chip->mixer.src_stream_outs,
792 chip->mixer.src_stream_out_ofs,
793 "Stream Source Gain Volume");
794 if (err < 0)
795 return err;
796 err = create_dest_gain_mixer(chip,
797 chip->mixer.src_phys_ins, 0,
798 chip->mixer.dest_stream_ins, 0,
799 "Line Capture Volume");
800 if (err < 0)
801 return err;
802 err = create_dest_gain_mixer(chip,
803 chip->mixer.src_stream_outs,
804 chip->mixer.src_stream_out_ofs,
805 chip->mixer.dest_stream_ins, 0,
806 "Stream-Loopback Capture Volume");
807 if (err < 0)
808 return err;
809 err = create_dest_gain_mixer(chip,
810 chip->mixer.src_phys_ins, 0,
811 chip->mixer.dest_phys_outs,
812 chip->mixer.dest_phys_out_ofs,
813 "Line-Loopback Playback Volume");
814 if (err < 0)
815 return err;
816 err = create_dest_gain_mixer(chip,
817 chip->mixer.src_stream_outs,
818 chip->mixer.src_stream_out_ofs,
819 chip->mixer.dest_phys_outs,
820 chip->mixer.dest_phys_out_ofs,
821 "Stream Playback Volume");
822 if (err < 0)
823 return err;
824
825 return init_mixer_values(chip);
826}