diff options
author | Takashi Iwai <tiwai@suse.de> | 2007-08-10 10:59:39 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2007-10-16 09:58:41 -0400 |
commit | 01751f54ff23b9d59e07f9c9ef189d4733525463 (patch) | |
tree | cdd37b09978123b87f673fbedf5102d34f68d459 /sound/pci/hda | |
parent | 45cffef1ff4679f5961146101ea1b8235bdd25b5 (diff) |
[ALSA] hda-codec - rewrite amp cache more generic
Rewrite the code to handle amp cache and hash tables to be more
generic. This routine will be used by the register caches in the
next patch.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound/pci/hda')
-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 */ |