diff options
-rw-r--r-- | sound/pci/hda/hda_codec.c | 89 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 24 |
2 files changed, 68 insertions, 45 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index fc934baaae65..46f8ab1df874 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c | |||
@@ -494,6 +494,10 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node) | |||
494 | } | 494 | } |
495 | 495 | ||
496 | 496 | ||
497 | static void init_hda_cache(struct hda_cache_rec *cache, | ||
498 | unsigned int record_size); | ||
499 | static inline void free_hda_cache(struct hda_cache_rec *cache); | ||
500 | |||
497 | /* | 501 | /* |
498 | * codec destructor | 502 | * codec destructor |
499 | */ | 503 | */ |
@@ -505,13 +509,11 @@ static void snd_hda_codec_free(struct hda_codec *codec) | |||
505 | codec->bus->caddr_tbl[codec->addr] = NULL; | 509 | codec->bus->caddr_tbl[codec->addr] = NULL; |
506 | if (codec->patch_ops.free) | 510 | if (codec->patch_ops.free) |
507 | codec->patch_ops.free(codec); | 511 | codec->patch_ops.free(codec); |
508 | kfree(codec->amp_info); | 512 | free_hda_cache(&codec->amp_cache); |
509 | kfree(codec->wcaps); | 513 | kfree(codec->wcaps); |
510 | kfree(codec); | 514 | kfree(codec); |
511 | } | 515 | } |
512 | 516 | ||
513 | static void init_amp_hash(struct hda_codec *codec); | ||
514 | |||
515 | /** | 517 | /** |
516 | * snd_hda_codec_new - create a HDA codec | 518 | * snd_hda_codec_new - create a HDA codec |
517 | * @bus: the bus to assign | 519 | * @bus: the bus to assign |
@@ -545,7 +547,7 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, | |||
545 | codec->bus = bus; | 547 | codec->bus = bus; |
546 | codec->addr = codec_addr; | 548 | codec->addr = codec_addr; |
547 | mutex_init(&codec->spdif_mutex); | 549 | mutex_init(&codec->spdif_mutex); |
548 | init_amp_hash(codec); | 550 | init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); |
549 | 551 | ||
550 | list_add_tail(&codec->list, &bus->codec_list); | 552 | list_add_tail(&codec->list, &bus->codec_list); |
551 | bus->caddr_tbl[codec_addr] = codec; | 553 | bus->caddr_tbl[codec_addr] = codec; |
@@ -664,59 +666,72 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, | |||
664 | #define INFO_AMP_VOL(ch) (1 << (1 + (ch))) | 666 | #define INFO_AMP_VOL(ch) (1 << (1 + (ch))) |
665 | 667 | ||
666 | /* initialize the hash table */ | 668 | /* initialize the hash table */ |
667 | static void __devinit init_amp_hash(struct hda_codec *codec) | 669 | static void __devinit init_hda_cache(struct hda_cache_rec *cache, |
670 | unsigned int record_size) | ||
671 | { | ||
672 | memset(cache, 0, sizeof(*cache)); | ||
673 | memset(cache->hash, 0xff, sizeof(cache->hash)); | ||
674 | cache->record_size = record_size; | ||
675 | } | ||
676 | |||
677 | static inline void free_hda_cache(struct hda_cache_rec *cache) | ||
668 | { | 678 | { |
669 | memset(codec->amp_hash, 0xff, sizeof(codec->amp_hash)); | 679 | kfree(cache->buffer); |
670 | codec->num_amp_entries = 0; | ||
671 | codec->amp_info_size = 0; | ||
672 | codec->amp_info = NULL; | ||
673 | } | 680 | } |
674 | 681 | ||
675 | /* query the hash. allocate an entry if not found. */ | 682 | /* query the hash. allocate an entry if not found. */ |
676 | static struct hda_amp_info *get_alloc_amp_hash(struct hda_codec *codec, u32 key) | 683 | static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache, |
684 | u32 key) | ||
677 | { | 685 | { |
678 | u16 idx = key % (u16)ARRAY_SIZE(codec->amp_hash); | 686 | u16 idx = key % (u16)ARRAY_SIZE(cache->hash); |
679 | u16 cur = codec->amp_hash[idx]; | 687 | u16 cur = cache->hash[idx]; |
680 | struct hda_amp_info *info; | 688 | struct hda_cache_head *info; |
681 | 689 | ||
682 | while (cur != 0xffff) { | 690 | while (cur != 0xffff) { |
683 | info = &codec->amp_info[cur]; | 691 | info = (struct hda_cache_head *)(cache->buffer + |
692 | cur * cache->record_size); | ||
684 | if (info->key == key) | 693 | if (info->key == key) |
685 | return info; | 694 | return info; |
686 | cur = info->next; | 695 | cur = info->next; |
687 | } | 696 | } |
688 | 697 | ||
689 | /* add a new hash entry */ | 698 | /* add a new hash entry */ |
690 | if (codec->num_amp_entries >= codec->amp_info_size) { | 699 | if (cache->num_entries >= cache->size) { |
691 | /* reallocate the array */ | 700 | /* reallocate the array */ |
692 | int new_size = codec->amp_info_size + 64; | 701 | unsigned int new_size = cache->size + 64; |
693 | struct hda_amp_info *new_info; | 702 | void *new_buffer; |
694 | new_info = kcalloc(new_size, sizeof(struct hda_amp_info), | 703 | new_buffer = kcalloc(new_size, cache->record_size, GFP_KERNEL); |
695 | GFP_KERNEL); | 704 | if (!new_buffer) { |
696 | if (!new_info) { | ||
697 | snd_printk(KERN_ERR "hda_codec: " | 705 | snd_printk(KERN_ERR "hda_codec: " |
698 | "can't malloc amp_info\n"); | 706 | "can't malloc amp_info\n"); |
699 | return NULL; | 707 | return NULL; |
700 | } | 708 | } |
701 | if (codec->amp_info) { | 709 | if (cache->buffer) { |
702 | memcpy(new_info, codec->amp_info, | 710 | memcpy(new_buffer, cache->buffer, |
703 | codec->amp_info_size * | 711 | cache->size * cache->record_size); |
704 | sizeof(struct hda_amp_info)); | 712 | kfree(cache->buffer); |
705 | kfree(codec->amp_info); | ||
706 | } | 713 | } |
707 | codec->amp_info_size = new_size; | 714 | cache->size = new_size; |
708 | codec->amp_info = new_info; | 715 | cache->buffer = new_buffer; |
709 | } | 716 | } |
710 | cur = codec->num_amp_entries++; | 717 | cur = cache->num_entries++; |
711 | info = &codec->amp_info[cur]; | 718 | info = (struct hda_cache_head *)(cache->buffer + |
719 | cur * cache->record_size); | ||
712 | info->key = key; | 720 | info->key = key; |
713 | info->status = 0; /* not initialized yet */ | 721 | info->val = 0; |
714 | info->next = codec->amp_hash[idx]; | 722 | info->next = cache->hash[idx]; |
715 | codec->amp_hash[idx] = cur; | 723 | cache->hash[idx] = cur; |
716 | 724 | ||
717 | return info; | 725 | return info; |
718 | } | 726 | } |
719 | 727 | ||
728 | /* query and allocate an amp hash entry */ | ||
729 | static inline struct hda_amp_info * | ||
730 | get_alloc_amp_hash(struct hda_codec *codec, u32 key) | ||
731 | { | ||
732 | return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key); | ||
733 | } | ||
734 | |||
720 | /* | 735 | /* |
721 | * query AMP capabilities for the given widget and direction | 736 | * query AMP capabilities for the given widget and direction |
722 | */ | 737 | */ |
@@ -727,7 +742,7 @@ static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) | |||
727 | info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0)); | 742 | info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0)); |
728 | if (!info) | 743 | if (!info) |
729 | return 0; | 744 | return 0; |
730 | if (!(info->status & INFO_AMP_CAPS)) { | 745 | if (!(info->head.val & INFO_AMP_CAPS)) { |
731 | if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) | 746 | if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) |
732 | nid = codec->afg; | 747 | nid = codec->afg; |
733 | info->amp_caps = snd_hda_param_read(codec, nid, | 748 | info->amp_caps = snd_hda_param_read(codec, nid, |
@@ -735,7 +750,7 @@ static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) | |||
735 | AC_PAR_AMP_OUT_CAP : | 750 | AC_PAR_AMP_OUT_CAP : |
736 | AC_PAR_AMP_IN_CAP); | 751 | AC_PAR_AMP_IN_CAP); |
737 | if (info->amp_caps) | 752 | if (info->amp_caps) |
738 | info->status |= INFO_AMP_CAPS; | 753 | info->head.val |= INFO_AMP_CAPS; |
739 | } | 754 | } |
740 | return info->amp_caps; | 755 | return info->amp_caps; |
741 | } | 756 | } |
@@ -749,7 +764,7 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, | |||
749 | if (!info) | 764 | if (!info) |
750 | return -EINVAL; | 765 | return -EINVAL; |
751 | info->amp_caps = caps; | 766 | info->amp_caps = caps; |
752 | info->status |= INFO_AMP_CAPS; | 767 | info->head.val |= INFO_AMP_CAPS; |
753 | return 0; | 768 | return 0; |
754 | } | 769 | } |
755 | 770 | ||
@@ -763,7 +778,7 @@ static unsigned int get_vol_mute(struct hda_codec *codec, | |||
763 | { | 778 | { |
764 | u32 val, parm; | 779 | u32 val, parm; |
765 | 780 | ||
766 | if (info->status & INFO_AMP_VOL(ch)) | 781 | if (info->head.val & INFO_AMP_VOL(ch)) |
767 | return info->vol[ch]; | 782 | return info->vol[ch]; |
768 | 783 | ||
769 | parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT; | 784 | parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT; |
@@ -772,7 +787,7 @@ static unsigned int get_vol_mute(struct hda_codec *codec, | |||
772 | val = snd_hda_codec_read(codec, nid, 0, | 787 | val = snd_hda_codec_read(codec, nid, 0, |
773 | AC_VERB_GET_AMP_GAIN_MUTE, parm); | 788 | AC_VERB_GET_AMP_GAIN_MUTE, parm); |
774 | info->vol[ch] = val & 0xff; | 789 | info->vol[ch] = val & 0xff; |
775 | info->status |= INFO_AMP_VOL(ch); | 790 | info->head.val |= INFO_AMP_VOL(ch); |
776 | return info->vol[ch]; | 791 | return info->vol[ch]; |
777 | } | 792 | } |
778 | 793 | ||
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 1370e346bf34..1a69743160ce 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h | |||
@@ -480,12 +480,24 @@ struct hda_codec_ops { | |||
480 | }; | 480 | }; |
481 | 481 | ||
482 | /* record for amp information cache */ | 482 | /* record for amp information cache */ |
483 | struct hda_amp_info { | 483 | struct hda_cache_head { |
484 | u32 key; /* hash key */ | 484 | u32 key; /* hash key */ |
485 | u16 val; /* assigned value */ | ||
486 | u16 next; /* next link; -1 = terminal */ | ||
487 | }; | ||
488 | |||
489 | struct hda_amp_info { | ||
490 | struct hda_cache_head head; | ||
485 | u32 amp_caps; /* amp capabilities */ | 491 | u32 amp_caps; /* amp capabilities */ |
486 | u16 vol[2]; /* current volume & mute */ | 492 | u16 vol[2]; /* current volume & mute */ |
487 | u16 status; /* update flag */ | 493 | }; |
488 | u16 next; /* next link */ | 494 | |
495 | struct hda_cache_rec { | ||
496 | u16 hash[64]; /* hash table for index */ | ||
497 | unsigned int num_entries; /* number of assigned entries */ | ||
498 | unsigned int size; /* allocated size */ | ||
499 | unsigned int record_size; /* record size (including header) */ | ||
500 | void *buffer; /* hash table entries */ | ||
489 | }; | 501 | }; |
490 | 502 | ||
491 | /* PCM callbacks */ | 503 | /* PCM callbacks */ |
@@ -557,11 +569,7 @@ struct hda_codec { | |||
557 | hda_nid_t start_nid; | 569 | hda_nid_t start_nid; |
558 | u32 *wcaps; | 570 | u32 *wcaps; |
559 | 571 | ||
560 | /* hash for amp access */ | 572 | struct hda_cache_rec amp_cache; /* cache for amp access */ |
561 | u16 amp_hash[32]; | ||
562 | int num_amp_entries; | ||
563 | int amp_info_size; | ||
564 | struct hda_amp_info *amp_info; | ||
565 | 573 | ||
566 | struct mutex spdif_mutex; | 574 | struct mutex spdif_mutex; |
567 | unsigned int spdif_status; /* IEC958 status bits */ | 575 | unsigned int spdif_status; /* IEC958 status bits */ |