aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2007-08-10 10:59:39 -0400
committerJaroslav Kysela <perex@perex.cz>2007-10-16 09:58:41 -0400
commit01751f54ff23b9d59e07f9c9ef189d4733525463 (patch)
treecdd37b09978123b87f673fbedf5102d34f68d459 /sound/pci/hda
parent45cffef1ff4679f5961146101ea1b8235bdd25b5 (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.c89
-rw-r--r--sound/pci/hda/hda_codec.h24
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
497static void init_hda_cache(struct hda_cache_rec *cache,
498 unsigned int record_size);
499static 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
513static 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 */
667static void __devinit init_amp_hash(struct hda_codec *codec) 669static 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
677static 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. */
676static struct hda_amp_info *get_alloc_amp_hash(struct hda_codec *codec, u32 key) 683static 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 */
729static inline struct hda_amp_info *
730get_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 */
483struct hda_amp_info { 483struct 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
489struct 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
495struct 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 */