diff options
author | Clemens Ladisch <clemens@ladisch.de> | 2009-09-28 05:20:47 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-09-28 05:54:52 -0400 |
commit | 973dca93a3d46cca7e4743300f8a510b779906af (patch) | |
tree | 35ecad5f3d2f5453aff442471d5b1b88e1b0aa98 /sound | |
parent | 76ffe1e3fb2f65e98d7ed001c5a2b6f334655364 (diff) |
sound: virtuoso: add PCM1796 oversampling control
Add a control to increase the oversampling factor to 128x on cards with
PCM1796 or PCM1792A DACs.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/oxygen/xonar_pcm179x.c | 182 |
1 files changed, 157 insertions, 25 deletions
diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index 35b3fb4071fb..7f153fb1848d 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c | |||
@@ -173,8 +173,11 @@ struct xonar_pcm179x { | |||
173 | struct xonar_generic generic; | 173 | struct xonar_generic generic; |
174 | unsigned int dacs; | 174 | unsigned int dacs; |
175 | u8 pcm1796_regs[4][5]; | 175 | u8 pcm1796_regs[4][5]; |
176 | unsigned int current_rate; | ||
177 | bool os_128; | ||
176 | bool hp_active; | 178 | bool hp_active; |
177 | s8 hp_gain_offset; | 179 | s8 hp_gain_offset; |
180 | bool has_cs2000; | ||
178 | u8 cs2000_fun_cfg_1; | 181 | u8 cs2000_fun_cfg_1; |
179 | }; | 182 | }; |
180 | 183 | ||
@@ -277,6 +280,7 @@ static void pcm1796_init(struct oxygen *chip) | |||
277 | PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD; | 280 | PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD; |
278 | data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = PCM1796_OS_64; | 281 | data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = PCM1796_OS_64; |
279 | pcm1796_registers_init(chip); | 282 | pcm1796_registers_init(chip); |
283 | data->current_rate = 48000; | ||
280 | } | 284 | } |
281 | 285 | ||
282 | static void xonar_d2_init(struct oxygen *chip) | 286 | static void xonar_d2_init(struct oxygen *chip) |
@@ -401,6 +405,7 @@ static void xonar_st_init(struct oxygen *chip) | |||
401 | { | 405 | { |
402 | struct xonar_pcm179x *data = chip->model_data; | 406 | struct xonar_pcm179x *data = chip->model_data; |
403 | 407 | ||
408 | data->has_cs2000 = 1; | ||
404 | data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1; | 409 | data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1; |
405 | 410 | ||
406 | oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, | 411 | oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, |
@@ -486,18 +491,57 @@ static void xonar_st_resume(struct oxygen *chip) | |||
486 | xonar_stx_resume(chip); | 491 | xonar_stx_resume(chip); |
487 | } | 492 | } |
488 | 493 | ||
489 | static void set_pcm1796_params(struct oxygen *chip, | 494 | static unsigned int mclk_from_rate(struct oxygen *chip, unsigned int rate) |
490 | struct snd_pcm_hw_params *params) | 495 | { |
496 | struct xonar_pcm179x *data = chip->model_data; | ||
497 | |||
498 | if (rate <= 32000) | ||
499 | return OXYGEN_I2S_MCLK_512; | ||
500 | else if (rate <= 48000 && data->os_128) | ||
501 | return OXYGEN_I2S_MCLK_512; | ||
502 | else if (rate <= 96000) | ||
503 | return OXYGEN_I2S_MCLK_256; | ||
504 | else | ||
505 | return OXYGEN_I2S_MCLK_128; | ||
506 | } | ||
507 | |||
508 | static unsigned int get_pcm1796_i2s_mclk(struct oxygen *chip, | ||
509 | unsigned int channel, | ||
510 | struct snd_pcm_hw_params *params) | ||
511 | { | ||
512 | if (channel == PCM_MULTICH) | ||
513 | return mclk_from_rate(chip, params_rate(params)); | ||
514 | else | ||
515 | return oxygen_default_i2s_mclk(chip, channel, params); | ||
516 | } | ||
517 | |||
518 | static void update_pcm1796_oversampling(struct oxygen *chip) | ||
491 | { | 519 | { |
492 | struct xonar_pcm179x *data = chip->model_data; | 520 | struct xonar_pcm179x *data = chip->model_data; |
493 | unsigned int i; | 521 | unsigned int i; |
494 | u8 reg; | 522 | u8 reg; |
495 | 523 | ||
496 | reg = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64; | 524 | if (data->current_rate <= 32000) |
525 | reg = PCM1796_OS_128; | ||
526 | else if (data->current_rate <= 48000 && data->os_128) | ||
527 | reg = PCM1796_OS_128; | ||
528 | else if (data->current_rate <= 96000 || data->os_128) | ||
529 | reg = PCM1796_OS_64; | ||
530 | else | ||
531 | reg = PCM1796_OS_32; | ||
497 | for (i = 0; i < data->dacs; ++i) | 532 | for (i = 0; i < data->dacs; ++i) |
498 | pcm1796_write_cached(chip, i, 20, reg); | 533 | pcm1796_write_cached(chip, i, 20, reg); |
499 | } | 534 | } |
500 | 535 | ||
536 | static void set_pcm1796_params(struct oxygen *chip, | ||
537 | struct snd_pcm_hw_params *params) | ||
538 | { | ||
539 | struct xonar_pcm179x *data = chip->model_data; | ||
540 | |||
541 | data->current_rate = params_rate(params); | ||
542 | update_pcm1796_oversampling(chip); | ||
543 | } | ||
544 | |||
501 | static void update_pcm1796_volume(struct oxygen *chip) | 545 | static void update_pcm1796_volume(struct oxygen *chip) |
502 | { | 546 | { |
503 | struct xonar_pcm179x *data = chip->model_data; | 547 | struct xonar_pcm179x *data = chip->model_data; |
@@ -526,26 +570,44 @@ static void update_pcm1796_mute(struct oxygen *chip) | |||
526 | pcm1796_write_cached(chip, i, 18, value); | 570 | pcm1796_write_cached(chip, i, 18, value); |
527 | } | 571 | } |
528 | 572 | ||
529 | static void set_cs2000_params(struct oxygen *chip, | 573 | static void update_cs2000_rate(struct oxygen *chip, unsigned int rate) |
530 | struct snd_pcm_hw_params *params) | ||
531 | { | 574 | { |
532 | /* XXX Why is the I2S A MCLK half the actual I2S multich MCLK? */ | 575 | struct xonar_pcm179x *data = chip->model_data; |
533 | static const u8 rate_mclks[] = { | ||
534 | [OXYGEN_RATE_32000] = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_128, | ||
535 | [OXYGEN_RATE_44100] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_128, | ||
536 | [OXYGEN_RATE_48000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_128, | ||
537 | [OXYGEN_RATE_64000] = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256, | ||
538 | [OXYGEN_RATE_88200] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256, | ||
539 | [OXYGEN_RATE_96000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256, | ||
540 | [OXYGEN_RATE_176400] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256, | ||
541 | [OXYGEN_RATE_192000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256, | ||
542 | }; | ||
543 | unsigned int rate_index; | ||
544 | u8 rate_mclk, reg; | 576 | u8 rate_mclk, reg; |
545 | 577 | ||
546 | rate_index = oxygen_read16(chip, OXYGEN_I2S_MULTICH_FORMAT) | 578 | switch (rate) { |
547 | & OXYGEN_I2S_RATE_MASK; | 579 | /* XXX Why is the I2S A MCLK half the actual I2S MCLK? */ |
548 | rate_mclk = rate_mclks[rate_index]; | 580 | case 32000: |
581 | rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256; | ||
582 | break; | ||
583 | case 44100: | ||
584 | if (data->os_128) | ||
585 | rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256; | ||
586 | else | ||
587 | rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_128; | ||
588 | break; | ||
589 | default: /* 48000 */ | ||
590 | if (data->os_128) | ||
591 | rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256; | ||
592 | else | ||
593 | rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_128; | ||
594 | break; | ||
595 | case 64000: | ||
596 | rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256; | ||
597 | break; | ||
598 | case 88200: | ||
599 | rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256; | ||
600 | break; | ||
601 | case 96000: | ||
602 | rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256; | ||
603 | break; | ||
604 | case 176400: | ||
605 | rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256; | ||
606 | break; | ||
607 | case 192000: | ||
608 | rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256; | ||
609 | break; | ||
610 | } | ||
549 | oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk, | 611 | oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk, |
550 | OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK); | 612 | OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK); |
551 | if ((rate_mclk & OXYGEN_I2S_MCLK_MASK) <= OXYGEN_I2S_MCLK_128) | 613 | if ((rate_mclk & OXYGEN_I2S_MCLK_MASK) <= OXYGEN_I2S_MCLK_128) |
@@ -558,7 +620,7 @@ static void set_cs2000_params(struct oxygen *chip, | |||
558 | static void set_st_params(struct oxygen *chip, | 620 | static void set_st_params(struct oxygen *chip, |
559 | struct snd_pcm_hw_params *params) | 621 | struct snd_pcm_hw_params *params) |
560 | { | 622 | { |
561 | set_cs2000_params(chip, params); | 623 | update_cs2000_rate(chip, params_rate(params)); |
562 | set_pcm1796_params(chip, params); | 624 | set_pcm1796_params(chip, params); |
563 | } | 625 | } |
564 | 626 | ||
@@ -580,6 +642,59 @@ static const struct snd_kcontrol_new alt_switch = { | |||
580 | .private_value = GPIO_D2_ALT, | 642 | .private_value = GPIO_D2_ALT, |
581 | }; | 643 | }; |
582 | 644 | ||
645 | static int os_128_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) | ||
646 | { | ||
647 | static const char *const names[2] = { "64x", "128x" }; | ||
648 | |||
649 | info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
650 | info->count = 1; | ||
651 | info->value.enumerated.items = 2; | ||
652 | if (info->value.enumerated.item >= 2) | ||
653 | info->value.enumerated.item = 1; | ||
654 | strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); | ||
655 | return 0; | ||
656 | } | ||
657 | |||
658 | static int os_128_get(struct snd_kcontrol *ctl, | ||
659 | struct snd_ctl_elem_value *value) | ||
660 | { | ||
661 | struct oxygen *chip = ctl->private_data; | ||
662 | struct xonar_pcm179x *data = chip->model_data; | ||
663 | |||
664 | value->value.enumerated.item[0] = data->os_128; | ||
665 | return 0; | ||
666 | } | ||
667 | |||
668 | static int os_128_put(struct snd_kcontrol *ctl, | ||
669 | struct snd_ctl_elem_value *value) | ||
670 | { | ||
671 | struct oxygen *chip = ctl->private_data; | ||
672 | struct xonar_pcm179x *data = chip->model_data; | ||
673 | int changed; | ||
674 | |||
675 | mutex_lock(&chip->mutex); | ||
676 | changed = value->value.enumerated.item[0] != data->os_128; | ||
677 | if (changed) { | ||
678 | data->os_128 = value->value.enumerated.item[0]; | ||
679 | if (data->has_cs2000) | ||
680 | update_cs2000_rate(chip, data->current_rate); | ||
681 | oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT, | ||
682 | mclk_from_rate(chip, data->current_rate), | ||
683 | OXYGEN_I2S_MCLK_MASK); | ||
684 | update_pcm1796_oversampling(chip); | ||
685 | } | ||
686 | mutex_unlock(&chip->mutex); | ||
687 | return changed; | ||
688 | } | ||
689 | |||
690 | static const struct snd_kcontrol_new os_128_control = { | ||
691 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
692 | .name = "DAC Oversampling Playback Enum", | ||
693 | .info = os_128_info, | ||
694 | .get = os_128_get, | ||
695 | .put = os_128_put, | ||
696 | }; | ||
697 | |||
583 | static int st_output_switch_info(struct snd_kcontrol *ctl, | 698 | static int st_output_switch_info(struct snd_kcontrol *ctl, |
584 | struct snd_ctl_elem_info *info) | 699 | struct snd_ctl_elem_info *info) |
585 | { | 700 | { |
@@ -745,7 +860,20 @@ static int xonar_st_control_filter(struct snd_kcontrol_new *template) | |||
745 | 860 | ||
746 | static int xonar_d2_mixer_init(struct oxygen *chip) | 861 | static int xonar_d2_mixer_init(struct oxygen *chip) |
747 | { | 862 | { |
748 | return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); | 863 | int err; |
864 | |||
865 | err = snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); | ||
866 | if (err < 0) | ||
867 | return err; | ||
868 | err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); | ||
869 | if (err < 0) | ||
870 | return err; | ||
871 | return 0; | ||
872 | } | ||
873 | |||
874 | static int xonar_hdav_mixer_init(struct oxygen *chip) | ||
875 | { | ||
876 | return snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); | ||
749 | } | 877 | } |
750 | 878 | ||
751 | static int xonar_st_mixer_init(struct oxygen *chip) | 879 | static int xonar_st_mixer_init(struct oxygen *chip) |
@@ -759,6 +887,9 @@ static int xonar_st_mixer_init(struct oxygen *chip) | |||
759 | if (err < 0) | 887 | if (err < 0) |
760 | return err; | 888 | return err; |
761 | } | 889 | } |
890 | err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); | ||
891 | if (err < 0) | ||
892 | return err; | ||
762 | return 0; | 893 | return 0; |
763 | } | 894 | } |
764 | 895 | ||
@@ -771,7 +902,7 @@ static const struct oxygen_model model_xonar_d2 = { | |||
771 | .cleanup = xonar_d2_cleanup, | 902 | .cleanup = xonar_d2_cleanup, |
772 | .suspend = xonar_d2_suspend, | 903 | .suspend = xonar_d2_suspend, |
773 | .resume = xonar_d2_resume, | 904 | .resume = xonar_d2_resume, |
774 | .get_i2s_mclk = oxygen_default_i2s_mclk, | 905 | .get_i2s_mclk = get_pcm1796_i2s_mclk, |
775 | .set_dac_params = set_pcm1796_params, | 906 | .set_dac_params = set_pcm1796_params, |
776 | .set_adc_params = xonar_set_cs53x1_params, | 907 | .set_adc_params = xonar_set_cs53x1_params, |
777 | .update_dac_volume = update_pcm1796_volume, | 908 | .update_dac_volume = update_pcm1796_volume, |
@@ -798,11 +929,12 @@ static const struct oxygen_model model_xonar_hdav = { | |||
798 | .longname = "Asus Virtuoso 200", | 929 | .longname = "Asus Virtuoso 200", |
799 | .chip = "AV200", | 930 | .chip = "AV200", |
800 | .init = xonar_hdav_init, | 931 | .init = xonar_hdav_init, |
932 | .mixer_init = xonar_hdav_mixer_init, | ||
801 | .cleanup = xonar_hdav_cleanup, | 933 | .cleanup = xonar_hdav_cleanup, |
802 | .suspend = xonar_hdav_suspend, | 934 | .suspend = xonar_hdav_suspend, |
803 | .resume = xonar_hdav_resume, | 935 | .resume = xonar_hdav_resume, |
804 | .pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter, | 936 | .pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter, |
805 | .get_i2s_mclk = oxygen_default_i2s_mclk, | 937 | .get_i2s_mclk = get_pcm1796_i2s_mclk, |
806 | .set_dac_params = set_hdav_params, | 938 | .set_dac_params = set_hdav_params, |
807 | .set_adc_params = xonar_set_cs53x1_params, | 939 | .set_adc_params = xonar_set_cs53x1_params, |
808 | .update_dac_volume = update_pcm1796_volume, | 940 | .update_dac_volume = update_pcm1796_volume, |
@@ -833,7 +965,7 @@ static const struct oxygen_model model_xonar_st = { | |||
833 | .cleanup = xonar_st_cleanup, | 965 | .cleanup = xonar_st_cleanup, |
834 | .suspend = xonar_st_suspend, | 966 | .suspend = xonar_st_suspend, |
835 | .resume = xonar_st_resume, | 967 | .resume = xonar_st_resume, |
836 | .get_i2s_mclk = oxygen_default_i2s_mclk, | 968 | .get_i2s_mclk = get_pcm1796_i2s_mclk, |
837 | .set_dac_params = set_st_params, | 969 | .set_dac_params = set_st_params, |
838 | .set_adc_params = xonar_set_cs53x1_params, | 970 | .set_adc_params = xonar_set_cs53x1_params, |
839 | .update_dac_volume = update_pcm1796_volume, | 971 | .update_dac_volume = update_pcm1796_volume, |