diff options
Diffstat (limited to 'sound/pci')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 74 |
1 files changed, 34 insertions, 40 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e6efaed4b464..cb3a76139341 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c | |||
@@ -566,9 +566,10 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stre | |||
566 | * amp access functions | 566 | * amp access functions |
567 | */ | 567 | */ |
568 | 568 | ||
569 | #define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + (idx) * 32 + (dir) * 64) | 569 | /* FIXME: more better hash key? */ |
570 | #define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24)) | ||
570 | #define INFO_AMP_CAPS (1<<0) | 571 | #define INFO_AMP_CAPS (1<<0) |
571 | #define INFO_AMP_VOL (1<<1) | 572 | #define INFO_AMP_VOL(ch) (1 << (1 + (ch))) |
572 | 573 | ||
573 | /* initialize the hash table */ | 574 | /* initialize the hash table */ |
574 | static void init_amp_hash(struct hda_codec *codec) | 575 | static void init_amp_hash(struct hda_codec *codec) |
@@ -627,28 +628,29 @@ static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) | |||
627 | 628 | ||
628 | /* | 629 | /* |
629 | * read the current volume to info | 630 | * read the current volume to info |
630 | * if the cache exists, read from the cache. | 631 | * if the cache exists, read the cache value. |
631 | */ | 632 | */ |
632 | static void get_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, | 633 | static unsigned int get_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, |
633 | hda_nid_t nid, int ch, int direction, int index) | 634 | hda_nid_t nid, int ch, int direction, int index) |
634 | { | 635 | { |
635 | u32 val, parm; | 636 | u32 val, parm; |
636 | 637 | ||
637 | if (info->status & (INFO_AMP_VOL << ch)) | 638 | if (info->status & INFO_AMP_VOL(ch)) |
638 | return; | 639 | return info->vol[ch]; |
639 | 640 | ||
640 | parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT; | 641 | parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT; |
641 | parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; | 642 | parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; |
642 | parm |= index; | 643 | parm |= index; |
643 | val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, parm); | 644 | val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, parm); |
644 | info->vol[ch] = val & 0xff; | 645 | info->vol[ch] = val & 0xff; |
645 | info->status |= INFO_AMP_VOL << ch; | 646 | info->status |= INFO_AMP_VOL(ch); |
647 | return info->vol[ch]; | ||
646 | } | 648 | } |
647 | 649 | ||
648 | /* | 650 | /* |
649 | * write the current volume in info to the h/w | 651 | * write the current volume in info to the h/w and update the cache |
650 | */ | 652 | */ |
651 | static void put_vol_mute(struct hda_codec *codec, | 653 | static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, |
652 | hda_nid_t nid, int ch, int direction, int index, int val) | 654 | hda_nid_t nid, int ch, int direction, int index, int val) |
653 | { | 655 | { |
654 | u32 parm; | 656 | u32 parm; |
@@ -658,30 +660,34 @@ static void put_vol_mute(struct hda_codec *codec, | |||
658 | parm |= index << AC_AMP_SET_INDEX_SHIFT; | 660 | parm |= index << AC_AMP_SET_INDEX_SHIFT; |
659 | parm |= val; | 661 | parm |= val; |
660 | snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm); | 662 | snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm); |
663 | info->vol[ch] = val; | ||
661 | } | 664 | } |
662 | 665 | ||
663 | /* | 666 | /* |
664 | * read/write AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. | 667 | * read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. |
665 | */ | 668 | */ |
666 | static int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index) | 669 | static int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index) |
667 | { | 670 | { |
668 | struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index)); | 671 | struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index)); |
669 | if (! info) | 672 | if (! info) |
670 | return 0; | 673 | return 0; |
671 | get_vol_mute(codec, info, nid, ch, direction, index); | 674 | return get_vol_mute(codec, info, nid, ch, direction, index); |
672 | return info->vol[ch]; | ||
673 | } | 675 | } |
674 | 676 | ||
675 | static int snd_hda_codec_amp_write(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int val) | 677 | /* |
678 | * update the AMP value, mask = bit mask to set, val = the value | ||
679 | */ | ||
680 | static int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val) | ||
676 | { | 681 | { |
677 | struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx)); | 682 | struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx)); |
683 | |||
678 | if (! info) | 684 | if (! info) |
679 | return 0; | 685 | return 0; |
680 | get_vol_mute(codec, info, nid, ch, direction, idx); | 686 | val &= mask; |
687 | val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask; | ||
681 | if (info->vol[ch] == val && ! codec->in_resume) | 688 | if (info->vol[ch] == val && ! codec->in_resume) |
682 | return 0; | 689 | return 0; |
683 | put_vol_mute(codec, nid, ch, direction, idx, val); | 690 | put_vol_mute(codec, info, nid, ch, direction, idx, val); |
684 | info->vol[ch] = val; | ||
685 | return 1; | 691 | return 1; |
686 | } | 692 | } |
687 | 693 | ||
@@ -740,21 +746,15 @@ int snd_hda_mixer_amp_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t | |||
740 | int chs = get_amp_channels(kcontrol); | 746 | int chs = get_amp_channels(kcontrol); |
741 | int dir = get_amp_direction(kcontrol); | 747 | int dir = get_amp_direction(kcontrol); |
742 | int idx = get_amp_index(kcontrol); | 748 | int idx = get_amp_index(kcontrol); |
743 | int val; | ||
744 | long *valp = ucontrol->value.integer.value; | 749 | long *valp = ucontrol->value.integer.value; |
745 | int change = 0; | 750 | int change = 0; |
746 | 751 | ||
747 | if (chs & 1) { | 752 | if (chs & 1) |
748 | val = *valp & 0x7f; | 753 | change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, |
749 | val |= snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x80; | 754 | 0x7f, *valp); |
750 | change = snd_hda_codec_amp_write(codec, nid, 0, dir, idx, val); | 755 | if (chs & 2) |
751 | valp++; | 756 | change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, |
752 | } | 757 | 0x7f, valp[1]); |
753 | if (chs & 2) { | ||
754 | val = *valp & 0x7f; | ||
755 | val |= snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x80; | ||
756 | change |= snd_hda_codec_amp_write(codec, nid, 1, dir, idx, val); | ||
757 | } | ||
758 | return change; | 758 | return change; |
759 | } | 759 | } |
760 | 760 | ||
@@ -793,21 +793,15 @@ int snd_hda_mixer_amp_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t | |||
793 | int chs = get_amp_channels(kcontrol); | 793 | int chs = get_amp_channels(kcontrol); |
794 | int dir = get_amp_direction(kcontrol); | 794 | int dir = get_amp_direction(kcontrol); |
795 | int idx = get_amp_index(kcontrol); | 795 | int idx = get_amp_index(kcontrol); |
796 | int val; | ||
797 | long *valp = ucontrol->value.integer.value; | 796 | long *valp = ucontrol->value.integer.value; |
798 | int change = 0; | 797 | int change = 0; |
799 | 798 | ||
800 | if (chs & 1) { | 799 | if (chs & 1) |
801 | val = snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x7f; | 800 | change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, |
802 | val |= *valp ? 0 : 0x80; | 801 | 0x80, *valp ? 0 : 0x80); |
803 | change = snd_hda_codec_amp_write(codec, nid, 0, dir, idx, val); | 802 | if (chs & 2) |
804 | valp++; | 803 | change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, |
805 | } | 804 | 0x80, valp[1] ? 0 : 0x80); |
806 | if (chs & 2) { | ||
807 | val = snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x7f; | ||
808 | val |= *valp ? 0 : 0x80; | ||
809 | change = snd_hda_codec_amp_write(codec, nid, 1, dir, idx, val); | ||
810 | } | ||
811 | return change; | 805 | return change; |
812 | } | 806 | } |
813 | 807 | ||