summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorRoman Volkov <v1ron@mail.ru>2014-01-24 07:18:13 -0500
committerClemens Ladisch <clemens@ladisch.de>2014-01-29 14:45:49 -0500
commit041f26b6257d15449e1634ba8dea426dd06514c7 (patch)
treefd41f937106e450216fa52dc3226b803591fa24d /sound
parent06f70d0da029a0ae9dbb7e383f853db483575f87 (diff)
ALSA: oxygen: Xonar DG(X): move the mixer code into another file
Moving the mixer code away makes things easier. The mixer will control the driver, so the functions of the driver need to be non-static. Signed-off-by: Roman Volkov <v1ron@mail.ru> Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/pci/oxygen/Makefile2
-rw-r--r--sound/pci/oxygen/xonar_dg.c373
-rw-r--r--sound/pci/oxygen/xonar_dg.h19
-rw-r--r--sound/pci/oxygen/xonar_dg_mixer.c381
4 files changed, 411 insertions, 364 deletions
diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile
index 0f8726551fde..8f4c409f7e45 100644
--- a/sound/pci/oxygen/Makefile
+++ b/sound/pci/oxygen/Makefile
@@ -1,5 +1,5 @@
1snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o 1snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
2snd-oxygen-objs := oxygen.o xonar_dg.o 2snd-oxygen-objs := oxygen.o xonar_dg_mixer.o xonar_dg.o
3snd-virtuoso-objs := virtuoso.o xonar_lib.o \ 3snd-virtuoso-objs := virtuoso.o xonar_lib.o \
4 xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o 4 xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o
5 5
diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c
index 4a4266519934..d12d83554abb 100644
--- a/sound/pci/oxygen/xonar_dg.c
+++ b/sound/pci/oxygen/xonar_dg.c
@@ -123,7 +123,7 @@ int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op)
123 return 0; 123 return 0;
124} 124}
125 125
126static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) 126void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value)
127{ 127{
128 struct dg *data = chip->model_data; 128 struct dg *data = chip->model_data;
129 129
@@ -138,7 +138,7 @@ static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value)
138 data->cs4245_shadow[reg] = value; 138 data->cs4245_shadow[reg] = value;
139} 139}
140 140
141static void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value) 141void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value)
142{ 142{
143 struct dg *data = chip->model_data; 143 struct dg *data = chip->model_data;
144 144
@@ -178,7 +178,7 @@ static void cs4245_init(struct oxygen *chip)
178 snd_component_add(chip->card, "CS4245"); 178 snd_component_add(chip->card, "CS4245");
179} 179}
180 180
181static void dg_init(struct oxygen *chip) 181void dg_init(struct oxygen *chip)
182{ 182{
183 struct dg *data = chip->model_data; 183 struct dg *data = chip->model_data;
184 184
@@ -195,24 +195,24 @@ static void dg_init(struct oxygen *chip)
195 GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE); 195 GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE);
196} 196}
197 197
198static void dg_cleanup(struct oxygen *chip) 198void dg_cleanup(struct oxygen *chip)
199{ 199{
200 oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); 200 oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
201} 201}
202 202
203static void dg_suspend(struct oxygen *chip) 203void dg_suspend(struct oxygen *chip)
204{ 204{
205 dg_cleanup(chip); 205 dg_cleanup(chip);
206} 206}
207 207
208static void dg_resume(struct oxygen *chip) 208void dg_resume(struct oxygen *chip)
209{ 209{
210 cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW); 210 cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
211 msleep(2500); 211 msleep(2500);
212 oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); 212 oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
213} 213}
214 214
215static void set_cs4245_dac_params(struct oxygen *chip, 215void set_cs4245_dac_params(struct oxygen *chip,
216 struct snd_pcm_hw_params *params) 216 struct snd_pcm_hw_params *params)
217{ 217{
218 struct dg *data = chip->model_data; 218 struct dg *data = chip->model_data;
@@ -237,7 +237,7 @@ static void set_cs4245_dac_params(struct oxygen *chip,
237 cs4245_write_spi(chip, CS4245_MCLK_FREQ); 237 cs4245_write_spi(chip, CS4245_MCLK_FREQ);
238} 238}
239 239
240static void set_cs4245_adc_params(struct oxygen *chip, 240void set_cs4245_adc_params(struct oxygen *chip,
241 struct snd_pcm_hw_params *params) 241 struct snd_pcm_hw_params *params)
242{ 242{
243 struct dg *data = chip->model_data; 243 struct dg *data = chip->model_data;
@@ -262,7 +262,7 @@ static void set_cs4245_adc_params(struct oxygen *chip,
262 cs4245_write_spi(chip, CS4245_MCLK_FREQ); 262 cs4245_write_spi(chip, CS4245_MCLK_FREQ);
263} 263}
264 264
265static unsigned int adjust_dg_dac_routing(struct oxygen *chip, 265unsigned int adjust_dg_dac_routing(struct oxygen *chip,
266 unsigned int play_routing) 266 unsigned int play_routing)
267{ 267{
268 struct dg *data = chip->model_data; 268 struct dg *data = chip->model_data;
@@ -287,333 +287,7 @@ static unsigned int adjust_dg_dac_routing(struct oxygen *chip,
287 return routing; 287 return routing;
288} 288}
289 289
290static int output_switch_info(struct snd_kcontrol *ctl, 290void dump_cs4245_registers(struct oxygen *chip,
291 struct snd_ctl_elem_info *info)
292{
293 static const char *const names[3] = {
294 "Speakers", "Headphones", "FP Headphones"
295 };
296
297 return snd_ctl_enum_info(info, 1, 3, names);
298}
299
300static int output_switch_get(struct snd_kcontrol *ctl,
301 struct snd_ctl_elem_value *value)
302{
303 struct oxygen *chip = ctl->private_data;
304 struct dg *data = chip->model_data;
305
306 mutex_lock(&chip->mutex);
307 value->value.enumerated.item[0] = data->output_sel;
308 mutex_unlock(&chip->mutex);
309 return 0;
310}
311
312static int output_switch_put(struct snd_kcontrol *ctl,
313 struct snd_ctl_elem_value *value)
314{
315 struct oxygen *chip = ctl->private_data;
316 struct dg *data = chip->model_data;
317 u8 reg;
318 int changed;
319
320 if (value->value.enumerated.item[0] > 2)
321 return -EINVAL;
322
323 mutex_lock(&chip->mutex);
324 changed = value->value.enumerated.item[0] != data->output_sel;
325 if (changed) {
326 data->output_sel = value->value.enumerated.item[0];
327
328 reg = data->cs4245_shadow[CS4245_SIGNAL_SEL] &
329 ~CS4245_A_OUT_SEL_MASK;
330 reg |= data->output_sel == 2 ?
331 CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ;
332 cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg);
333
334 cs4245_write_cached(chip, CS4245_DAC_A_CTRL,
335 data->output_sel ? data->hp_vol_att : 0);
336 cs4245_write_cached(chip, CS4245_DAC_B_CTRL,
337 data->output_sel ? data->hp_vol_att : 0);
338
339 oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
340 data->output_sel == 1 ? GPIO_HP_REAR : 0,
341 GPIO_HP_REAR);
342 }
343 mutex_unlock(&chip->mutex);
344 return changed;
345}
346
347static int hp_volume_offset_info(struct snd_kcontrol *ctl,
348 struct snd_ctl_elem_info *info)
349{
350 static const char *const names[3] = {
351 "< 64 ohms", "64-150 ohms", "150-300 ohms"
352 };
353
354 return snd_ctl_enum_info(info, 1, 3, names);
355}
356
357static int hp_volume_offset_get(struct snd_kcontrol *ctl,
358 struct snd_ctl_elem_value *value)
359{
360 struct oxygen *chip = ctl->private_data;
361 struct dg *data = chip->model_data;
362
363 mutex_lock(&chip->mutex);
364 if (data->hp_vol_att > 2 * 7)
365 value->value.enumerated.item[0] = 0;
366 else if (data->hp_vol_att > 0)
367 value->value.enumerated.item[0] = 1;
368 else
369 value->value.enumerated.item[0] = 2;
370 mutex_unlock(&chip->mutex);
371 return 0;
372}
373
374static int hp_volume_offset_put(struct snd_kcontrol *ctl,
375 struct snd_ctl_elem_value *value)
376{
377 static const s8 atts[3] = { 2 * 16, 2 * 7, 0 };
378 struct oxygen *chip = ctl->private_data;
379 struct dg *data = chip->model_data;
380 s8 att;
381 int changed;
382
383 if (value->value.enumerated.item[0] > 2)
384 return -EINVAL;
385 att = atts[value->value.enumerated.item[0]];
386 mutex_lock(&chip->mutex);
387 changed = att != data->hp_vol_att;
388 if (changed) {
389 data->hp_vol_att = att;
390 if (data->output_sel) {
391 cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att);
392 cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att);
393 }
394 }
395 mutex_unlock(&chip->mutex);
396 return changed;
397}
398
399static int input_vol_info(struct snd_kcontrol *ctl,
400 struct snd_ctl_elem_info *info)
401{
402 info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
403 info->count = 2;
404 info->value.integer.min = 2 * -12;
405 info->value.integer.max = 2 * 12;
406 return 0;
407}
408
409static int input_vol_get(struct snd_kcontrol *ctl,
410 struct snd_ctl_elem_value *value)
411{
412 struct oxygen *chip = ctl->private_data;
413 struct dg *data = chip->model_data;
414 unsigned int idx = ctl->private_value;
415
416 mutex_lock(&chip->mutex);
417 value->value.integer.value[0] = data->input_vol[idx][0];
418 value->value.integer.value[1] = data->input_vol[idx][1];
419 mutex_unlock(&chip->mutex);
420 return 0;
421}
422
423static int input_vol_put(struct snd_kcontrol *ctl,
424 struct snd_ctl_elem_value *value)
425{
426 struct oxygen *chip = ctl->private_data;
427 struct dg *data = chip->model_data;
428 unsigned int idx = ctl->private_value;
429 int changed = 0;
430
431 if (value->value.integer.value[0] < 2 * -12 ||
432 value->value.integer.value[0] > 2 * 12 ||
433 value->value.integer.value[1] < 2 * -12 ||
434 value->value.integer.value[1] > 2 * 12)
435 return -EINVAL;
436 mutex_lock(&chip->mutex);
437 changed = data->input_vol[idx][0] != value->value.integer.value[0] ||
438 data->input_vol[idx][1] != value->value.integer.value[1];
439 if (changed) {
440 data->input_vol[idx][0] = value->value.integer.value[0];
441 data->input_vol[idx][1] = value->value.integer.value[1];
442 if (idx == data->input_sel) {
443 cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
444 data->input_vol[idx][0]);
445 cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
446 data->input_vol[idx][1]);
447 }
448 }
449 mutex_unlock(&chip->mutex);
450 return changed;
451}
452
453static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0);
454
455static int input_sel_info(struct snd_kcontrol *ctl,
456 struct snd_ctl_elem_info *info)
457{
458 static const char *const names[4] = {
459 "Mic", "Aux", "Front Mic", "Line"
460 };
461
462 return snd_ctl_enum_info(info, 1, 4, names);
463}
464
465static int input_sel_get(struct snd_kcontrol *ctl,
466 struct snd_ctl_elem_value *value)
467{
468 struct oxygen *chip = ctl->private_data;
469 struct dg *data = chip->model_data;
470
471 mutex_lock(&chip->mutex);
472 value->value.enumerated.item[0] = data->input_sel;
473 mutex_unlock(&chip->mutex);
474 return 0;
475}
476
477static int input_sel_put(struct snd_kcontrol *ctl,
478 struct snd_ctl_elem_value *value)
479{
480 static const u8 sel_values[4] = {
481 CS4245_SEL_MIC,
482 CS4245_SEL_INPUT_1,
483 CS4245_SEL_INPUT_2,
484 CS4245_SEL_INPUT_4
485 };
486 struct oxygen *chip = ctl->private_data;
487 struct dg *data = chip->model_data;
488 int changed;
489
490 if (value->value.enumerated.item[0] > 3)
491 return -EINVAL;
492
493 mutex_lock(&chip->mutex);
494 changed = value->value.enumerated.item[0] != data->input_sel;
495 if (changed) {
496 data->input_sel = value->value.enumerated.item[0];
497
498 cs4245_write(chip, CS4245_ANALOG_IN,
499 (data->cs4245_shadow[CS4245_ANALOG_IN] &
500 ~CS4245_SEL_MASK) |
501 sel_values[data->input_sel]);
502
503 cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
504 data->input_vol[data->input_sel][0]);
505 cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
506 data->input_vol[data->input_sel][1]);
507
508 oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
509 data->input_sel ? 0 : GPIO_INPUT_ROUTE,
510 GPIO_INPUT_ROUTE);
511 }
512 mutex_unlock(&chip->mutex);
513 return changed;
514}
515
516static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
517{
518 static const char *const names[2] = { "Active", "Frozen" };
519
520 return snd_ctl_enum_info(info, 1, 2, names);
521}
522
523static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
524{
525 struct oxygen *chip = ctl->private_data;
526 struct dg *data = chip->model_data;
527
528 value->value.enumerated.item[0] =
529 !!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE);
530 return 0;
531}
532
533static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
534{
535 struct oxygen *chip = ctl->private_data;
536 struct dg *data = chip->model_data;
537 u8 reg;
538 int changed;
539
540 mutex_lock(&chip->mutex);
541 reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE;
542 if (value->value.enumerated.item[0])
543 reg |= CS4245_HPF_FREEZE;
544 changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL];
545 if (changed)
546 cs4245_write(chip, CS4245_ADC_CTRL, reg);
547 mutex_unlock(&chip->mutex);
548 return changed;
549}
550
551#define INPUT_VOLUME(xname, index) { \
552 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
553 .name = xname, \
554 .info = input_vol_info, \
555 .get = input_vol_get, \
556 .put = input_vol_put, \
557 .tlv = { .p = cs4245_pga_db_scale }, \
558 .private_value = index, \
559}
560static const struct snd_kcontrol_new dg_controls[] = {
561 {
562 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
563 .name = "Analog Output Playback Enum",
564 .info = output_switch_info,
565 .get = output_switch_get,
566 .put = output_switch_put,
567 },
568 {
569 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
570 .name = "Headphones Impedance Playback Enum",
571 .info = hp_volume_offset_info,
572 .get = hp_volume_offset_get,
573 .put = hp_volume_offset_put,
574 },
575 INPUT_VOLUME("Mic Capture Volume", 0),
576 INPUT_VOLUME("Aux Capture Volume", 1),
577 INPUT_VOLUME("Front Mic Capture Volume", 2),
578 INPUT_VOLUME("Line Capture Volume", 3),
579 {
580 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
581 .name = "Capture Source",
582 .info = input_sel_info,
583 .get = input_sel_get,
584 .put = input_sel_put,
585 },
586 {
587 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
588 .name = "ADC High-pass Filter Capture Enum",
589 .info = hpf_info,
590 .get = hpf_get,
591 .put = hpf_put,
592 },
593};
594
595static int dg_control_filter(struct snd_kcontrol_new *template)
596{
597 if (!strncmp(template->name, "Master Playback ", 16))
598 return 1;
599 return 0;
600}
601
602static int dg_mixer_init(struct oxygen *chip)
603{
604 unsigned int i;
605 int err;
606
607 for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) {
608 err = snd_ctl_add(chip->card,
609 snd_ctl_new1(&dg_controls[i], chip));
610 if (err < 0)
611 return err;
612 }
613 return 0;
614}
615
616static void dump_cs4245_registers(struct oxygen *chip,
617 struct snd_info_buffer *buffer) 291 struct snd_info_buffer *buffer)
618{ 292{
619 struct dg *data = chip->model_data; 293 struct dg *data = chip->model_data;
@@ -625,30 +299,3 @@ static void dump_cs4245_registers(struct oxygen *chip,
625 snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]); 299 snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]);
626 snd_iprintf(buffer, "\n"); 300 snd_iprintf(buffer, "\n");
627} 301}
628
629struct oxygen_model model_xonar_dg = {
630 .longname = "C-Media Oxygen HD Audio",
631 .chip = "CMI8786",
632 .init = dg_init,
633 .control_filter = dg_control_filter,
634 .mixer_init = dg_mixer_init,
635 .cleanup = dg_cleanup,
636 .suspend = dg_suspend,
637 .resume = dg_resume,
638 .set_dac_params = set_cs4245_dac_params,
639 .set_adc_params = set_cs4245_adc_params,
640 .adjust_dac_routing = adjust_dg_dac_routing,
641 .dump_registers = dump_cs4245_registers,
642 .model_data_size = sizeof(struct dg),
643 .device_config = PLAYBACK_0_TO_I2S |
644 PLAYBACK_1_TO_SPDIF |
645 CAPTURE_0_FROM_I2S_2 |
646 CAPTURE_1_FROM_SPDIF,
647 .dac_channels_pcm = 6,
648 .dac_channels_mixer = 0,
649 .function_flags = OXYGEN_FUNCTION_SPI,
650 .dac_mclks = OXYGEN_MCLKS(256, 128, 128),
651 .adc_mclks = OXYGEN_MCLKS(256, 128, 128),
652 .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
653 .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
654};
diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h
index a9fba40bc013..0941ca2e0ca2 100644
--- a/sound/pci/oxygen/xonar_dg.h
+++ b/sound/pci/oxygen/xonar_dg.h
@@ -34,6 +34,25 @@ struct dg {
34 u8 hp_vol_att; 34 u8 hp_vol_att;
35}; 35};
36 36
37/* Xonar DG control routines */
38int cs4245_write_spi(struct oxygen *chip, u8 reg);
39int cs4245_read_spi(struct oxygen *chip, u8 reg);
40int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op);
41void dg_init(struct oxygen *chip);
42void set_cs4245_dac_params(struct oxygen *chip,
43 struct snd_pcm_hw_params *params);
44void set_cs4245_adc_params(struct oxygen *chip,
45 struct snd_pcm_hw_params *params);
46unsigned int adjust_dg_dac_routing(struct oxygen *chip,
47 unsigned int play_routing);
48void dump_cs4245_registers(struct oxygen *chip,
49 struct snd_info_buffer *buffer);
50void dg_suspend(struct oxygen *chip);
51void dg_resume(struct oxygen *chip);
52void dg_cleanup(struct oxygen *chip);
53void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value);
54void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value);
55
37extern struct oxygen_model model_xonar_dg; 56extern struct oxygen_model model_xonar_dg;
38 57
39#endif 58#endif
diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c
new file mode 100644
index 000000000000..a2cd0d31ac77
--- /dev/null
+++ b/sound/pci/oxygen/xonar_dg_mixer.c
@@ -0,0 +1,381 @@
1/*
2 * Mixer controls for the Xonar DG/DGX
3 *
4 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
5 * Copyright (c) Roman Volkov <v1ron@mail.ru>
6 *
7 * This driver is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License, version 2.
9 *
10 * This driver is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this driver; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <linux/pci.h>
20#include <linux/delay.h>
21#include <sound/control.h>
22#include <sound/core.h>
23#include <sound/info.h>
24#include <sound/pcm.h>
25#include <sound/tlv.h>
26#include "oxygen.h"
27#include "xonar_dg.h"
28#include "cs4245.h"
29
30static int output_switch_info(struct snd_kcontrol *ctl,
31 struct snd_ctl_elem_info *info)
32{
33 static const char *const names[3] = {
34 "Speakers", "Headphones", "FP Headphones"
35 };
36
37 return snd_ctl_enum_info(info, 1, 3, names);
38}
39
40static int output_switch_get(struct snd_kcontrol *ctl,
41 struct snd_ctl_elem_value *value)
42{
43 struct oxygen *chip = ctl->private_data;
44 struct dg *data = chip->model_data;
45
46 mutex_lock(&chip->mutex);
47 value->value.enumerated.item[0] = data->output_sel;
48 mutex_unlock(&chip->mutex);
49 return 0;
50}
51
52static int output_switch_put(struct snd_kcontrol *ctl,
53 struct snd_ctl_elem_value *value)
54{
55 struct oxygen *chip = ctl->private_data;
56 struct dg *data = chip->model_data;
57 u8 reg;
58 int changed;
59
60 if (value->value.enumerated.item[0] > 2)
61 return -EINVAL;
62
63 mutex_lock(&chip->mutex);
64 changed = value->value.enumerated.item[0] != data->output_sel;
65 if (changed) {
66 data->output_sel = value->value.enumerated.item[0];
67
68 reg = data->cs4245_shadow[CS4245_SIGNAL_SEL] &
69 ~CS4245_A_OUT_SEL_MASK;
70 reg |= data->output_sel == 2 ?
71 CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ;
72 cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg);
73
74 cs4245_write_cached(chip, CS4245_DAC_A_CTRL,
75 data->output_sel ? data->hp_vol_att : 0);
76 cs4245_write_cached(chip, CS4245_DAC_B_CTRL,
77 data->output_sel ? data->hp_vol_att : 0);
78
79 oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
80 data->output_sel == 1 ? GPIO_HP_REAR : 0,
81 GPIO_HP_REAR);
82 }
83 mutex_unlock(&chip->mutex);
84 return changed;
85}
86
87static int hp_volume_offset_info(struct snd_kcontrol *ctl,
88 struct snd_ctl_elem_info *info)
89{
90 static const char *const names[3] = {
91 "< 64 ohms", "64-150 ohms", "150-300 ohms"
92 };
93
94 return snd_ctl_enum_info(info, 1, 3, names);
95}
96
97static int hp_volume_offset_get(struct snd_kcontrol *ctl,
98 struct snd_ctl_elem_value *value)
99{
100 struct oxygen *chip = ctl->private_data;
101 struct dg *data = chip->model_data;
102
103 mutex_lock(&chip->mutex);
104 if (data->hp_vol_att > 2 * 7)
105 value->value.enumerated.item[0] = 0;
106 else if (data->hp_vol_att > 0)
107 value->value.enumerated.item[0] = 1;
108 else
109 value->value.enumerated.item[0] = 2;
110 mutex_unlock(&chip->mutex);
111 return 0;
112}
113
114static int hp_volume_offset_put(struct snd_kcontrol *ctl,
115 struct snd_ctl_elem_value *value)
116{
117 static const s8 atts[3] = { 2 * 16, 2 * 7, 0 };
118 struct oxygen *chip = ctl->private_data;
119 struct dg *data = chip->model_data;
120 s8 att;
121 int changed;
122
123 if (value->value.enumerated.item[0] > 2)
124 return -EINVAL;
125 att = atts[value->value.enumerated.item[0]];
126 mutex_lock(&chip->mutex);
127 changed = att != data->hp_vol_att;
128 if (changed) {
129 data->hp_vol_att = att;
130 if (data->output_sel) {
131 cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att);
132 cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att);
133 }
134 }
135 mutex_unlock(&chip->mutex);
136 return changed;
137}
138
139static int input_vol_info(struct snd_kcontrol *ctl,
140 struct snd_ctl_elem_info *info)
141{
142 info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
143 info->count = 2;
144 info->value.integer.min = 2 * -12;
145 info->value.integer.max = 2 * 12;
146 return 0;
147}
148
149static int input_vol_get(struct snd_kcontrol *ctl,
150 struct snd_ctl_elem_value *value)
151{
152 struct oxygen *chip = ctl->private_data;
153 struct dg *data = chip->model_data;
154 unsigned int idx = ctl->private_value;
155
156 mutex_lock(&chip->mutex);
157 value->value.integer.value[0] = data->input_vol[idx][0];
158 value->value.integer.value[1] = data->input_vol[idx][1];
159 mutex_unlock(&chip->mutex);
160 return 0;
161}
162
163static int input_vol_put(struct snd_kcontrol *ctl,
164 struct snd_ctl_elem_value *value)
165{
166 struct oxygen *chip = ctl->private_data;
167 struct dg *data = chip->model_data;
168 unsigned int idx = ctl->private_value;
169 int changed = 0;
170
171 if (value->value.integer.value[0] < 2 * -12 ||
172 value->value.integer.value[0] > 2 * 12 ||
173 value->value.integer.value[1] < 2 * -12 ||
174 value->value.integer.value[1] > 2 * 12)
175 return -EINVAL;
176 mutex_lock(&chip->mutex);
177 changed = data->input_vol[idx][0] != value->value.integer.value[0] ||
178 data->input_vol[idx][1] != value->value.integer.value[1];
179 if (changed) {
180 data->input_vol[idx][0] = value->value.integer.value[0];
181 data->input_vol[idx][1] = value->value.integer.value[1];
182 if (idx == data->input_sel) {
183 cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
184 data->input_vol[idx][0]);
185 cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
186 data->input_vol[idx][1]);
187 }
188 }
189 mutex_unlock(&chip->mutex);
190 return changed;
191}
192
193static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0);
194
195static int input_sel_info(struct snd_kcontrol *ctl,
196 struct snd_ctl_elem_info *info)
197{
198 static const char *const names[4] = {
199 "Mic", "Aux", "Front Mic", "Line"
200 };
201
202 return snd_ctl_enum_info(info, 1, 4, names);
203}
204
205static int input_sel_get(struct snd_kcontrol *ctl,
206 struct snd_ctl_elem_value *value)
207{
208 struct oxygen *chip = ctl->private_data;
209 struct dg *data = chip->model_data;
210
211 mutex_lock(&chip->mutex);
212 value->value.enumerated.item[0] = data->input_sel;
213 mutex_unlock(&chip->mutex);
214 return 0;
215}
216
217static int input_sel_put(struct snd_kcontrol *ctl,
218 struct snd_ctl_elem_value *value)
219{
220 static const u8 sel_values[4] = {
221 CS4245_SEL_MIC,
222 CS4245_SEL_INPUT_1,
223 CS4245_SEL_INPUT_2,
224 CS4245_SEL_INPUT_4
225 };
226 struct oxygen *chip = ctl->private_data;
227 struct dg *data = chip->model_data;
228 int changed;
229
230 if (value->value.enumerated.item[0] > 3)
231 return -EINVAL;
232
233 mutex_lock(&chip->mutex);
234 changed = value->value.enumerated.item[0] != data->input_sel;
235 if (changed) {
236 data->input_sel = value->value.enumerated.item[0];
237
238 cs4245_write(chip, CS4245_ANALOG_IN,
239 (data->cs4245_shadow[CS4245_ANALOG_IN] &
240 ~CS4245_SEL_MASK) |
241 sel_values[data->input_sel]);
242
243 cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
244 data->input_vol[data->input_sel][0]);
245 cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
246 data->input_vol[data->input_sel][1]);
247
248 oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
249 data->input_sel ? 0 : GPIO_INPUT_ROUTE,
250 GPIO_INPUT_ROUTE);
251 }
252 mutex_unlock(&chip->mutex);
253 return changed;
254}
255
256static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
257{
258 static const char *const names[2] = { "Active", "Frozen" };
259
260 return snd_ctl_enum_info(info, 1, 2, names);
261}
262
263static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
264{
265 struct oxygen *chip = ctl->private_data;
266 struct dg *data = chip->model_data;
267
268 value->value.enumerated.item[0] =
269 !!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE);
270 return 0;
271}
272
273static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
274{
275 struct oxygen *chip = ctl->private_data;
276 struct dg *data = chip->model_data;
277 u8 reg;
278 int changed;
279
280 mutex_lock(&chip->mutex);
281 reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE;
282 if (value->value.enumerated.item[0])
283 reg |= CS4245_HPF_FREEZE;
284 changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL];
285 if (changed)
286 cs4245_write(chip, CS4245_ADC_CTRL, reg);
287 mutex_unlock(&chip->mutex);
288 return changed;
289}
290
291#define INPUT_VOLUME(xname, index) { \
292 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
293 .name = xname, \
294 .info = input_vol_info, \
295 .get = input_vol_get, \
296 .put = input_vol_put, \
297 .tlv = { .p = cs4245_pga_db_scale }, \
298 .private_value = index, \
299}
300static const struct snd_kcontrol_new dg_controls[] = {
301 {
302 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
303 .name = "Analog Output Playback Enum",
304 .info = output_switch_info,
305 .get = output_switch_get,
306 .put = output_switch_put,
307 },
308 {
309 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
310 .name = "Headphones Impedance Playback Enum",
311 .info = hp_volume_offset_info,
312 .get = hp_volume_offset_get,
313 .put = hp_volume_offset_put,
314 },
315 INPUT_VOLUME("Mic Capture Volume", 0),
316 INPUT_VOLUME("Aux Capture Volume", 1),
317 INPUT_VOLUME("Front Mic Capture Volume", 2),
318 INPUT_VOLUME("Line Capture Volume", 3),
319 {
320 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
321 .name = "Capture Source",
322 .info = input_sel_info,
323 .get = input_sel_get,
324 .put = input_sel_put,
325 },
326 {
327 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
328 .name = "ADC High-pass Filter Capture Enum",
329 .info = hpf_info,
330 .get = hpf_get,
331 .put = hpf_put,
332 },
333};
334
335static int dg_control_filter(struct snd_kcontrol_new *template)
336{
337 if (!strncmp(template->name, "Master Playback ", 16))
338 return 1;
339 return 0;
340}
341
342static int dg_mixer_init(struct oxygen *chip)
343{
344 unsigned int i;
345 int err;
346
347 for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) {
348 err = snd_ctl_add(chip->card,
349 snd_ctl_new1(&dg_controls[i], chip));
350 if (err < 0)
351 return err;
352 }
353 return 0;
354}
355
356struct oxygen_model model_xonar_dg = {
357 .longname = "C-Media Oxygen HD Audio",
358 .chip = "CMI8786",
359 .init = dg_init,
360 .control_filter = dg_control_filter,
361 .mixer_init = dg_mixer_init,
362 .cleanup = dg_cleanup,
363 .suspend = dg_suspend,
364 .resume = dg_resume,
365 .set_dac_params = set_cs4245_dac_params,
366 .set_adc_params = set_cs4245_adc_params,
367 .adjust_dac_routing = adjust_dg_dac_routing,
368 .dump_registers = dump_cs4245_registers,
369 .model_data_size = sizeof(struct dg),
370 .device_config = PLAYBACK_0_TO_I2S |
371 PLAYBACK_1_TO_SPDIF |
372 CAPTURE_0_FROM_I2S_2 |
373 CAPTURE_1_FROM_SPDIF,
374 .dac_channels_pcm = 6,
375 .dac_channels_mixer = 0,
376 .function_flags = OXYGEN_FUNCTION_SPI,
377 .dac_mclks = OXYGEN_MCLKS(256, 128, 128),
378 .adc_mclks = OXYGEN_MCLKS(256, 128, 128),
379 .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
380 .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
381};