[ALSA] hda-codec - rewrite amp cache more generic
authorTakashi Iwai <tiwai@suse.de>
Fri, 10 Aug 2007 14:59:39 +0000 (16:59 +0200)
committerJaroslav Kysela <perex@perex.cz>
Tue, 16 Oct 2007 13:58:41 +0000 (15:58 +0200)
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>
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h

index fc934baaae653df351a561b3e4afece6585ef855..46f8ab1df874803d4b931d28fc1bf4b795aadb24 100644 (file)
@@ -494,6 +494,10 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node)
 }
 
 
+static void init_hda_cache(struct hda_cache_rec *cache,
+                          unsigned int record_size);
+static inline void free_hda_cache(struct hda_cache_rec *cache);
+
 /*
  * codec destructor
  */
@@ -505,13 +509,11 @@ static void snd_hda_codec_free(struct hda_codec *codec)
        codec->bus->caddr_tbl[codec->addr] = NULL;
        if (codec->patch_ops.free)
                codec->patch_ops.free(codec);
-       kfree(codec->amp_info);
+       free_hda_cache(&codec->amp_cache);
        kfree(codec->wcaps);
        kfree(codec);
 }
 
-static void init_amp_hash(struct hda_codec *codec);
-
 /**
  * snd_hda_codec_new - create a HDA codec
  * @bus: the bus to assign
@@ -545,7 +547,7 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
        codec->bus = bus;
        codec->addr = codec_addr;
        mutex_init(&codec->spdif_mutex);
-       init_amp_hash(codec);
+       init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
 
        list_add_tail(&codec->list, &bus->codec_list);
        bus->caddr_tbl[codec_addr] = codec;
@@ -664,59 +666,72 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
 #define INFO_AMP_VOL(ch)       (1 << (1 + (ch)))
 
 /* initialize the hash table */
-static void __devinit init_amp_hash(struct hda_codec *codec)
+static void __devinit init_hda_cache(struct hda_cache_rec *cache,
+                                    unsigned int record_size)
+{
+       memset(cache, 0, sizeof(*cache));
+       memset(cache->hash, 0xff, sizeof(cache->hash));
+       cache->record_size = record_size;
+}
+
+static inline void free_hda_cache(struct hda_cache_rec *cache)
 {
-       memset(codec->amp_hash, 0xff, sizeof(codec->amp_hash));
-       codec->num_amp_entries = 0;
-       codec->amp_info_size = 0;
-       codec->amp_info = NULL;
+       kfree(cache->buffer);
 }
 
 /* query the hash.  allocate an entry if not found. */
-static struct hda_amp_info *get_alloc_amp_hash(struct hda_codec *codec, u32 key)
+static struct hda_cache_head  *get_alloc_hash(struct hda_cache_rec *cache,
+                                             u32 key)
 {
-       u16 idx = key % (u16)ARRAY_SIZE(codec->amp_hash);
-       u16 cur = codec->amp_hash[idx];
-       struct hda_amp_info *info;
+       u16 idx = key % (u16)ARRAY_SIZE(cache->hash);
+       u16 cur = cache->hash[idx];
+       struct hda_cache_head *info;
 
        while (cur != 0xffff) {
-               info = &codec->amp_info[cur];
+               info = (struct hda_cache_head *)(cache->buffer +
+                                                cur * cache->record_size);
                if (info->key == key)
                        return info;
                cur = info->next;
        }
 
        /* add a new hash entry */
-       if (codec->num_amp_entries >= codec->amp_info_size) {
+       if (cache->num_entries >= cache->size) {
                /* reallocate the array */
-               int new_size = codec->amp_info_size + 64;
-               struct hda_amp_info *new_info;
-               new_info = kcalloc(new_size, sizeof(struct hda_amp_info),
-                                  GFP_KERNEL);
-               if (!new_info) {
+               unsigned int new_size = cache->size + 64;
+               void *new_buffer;
+               new_buffer = kcalloc(new_size, cache->record_size, GFP_KERNEL);
+               if (!new_buffer) {
                        snd_printk(KERN_ERR "hda_codec: "
                                   "can't malloc amp_info\n");
                        return NULL;
                }
-               if (codec->amp_info) {
-                       memcpy(new_info, codec->amp_info,
-                              codec->amp_info_size *
-                              sizeof(struct hda_amp_info));
-                       kfree(codec->amp_info);
+               if (cache->buffer) {
+                       memcpy(new_buffer, cache->buffer,
+                              cache->size * cache->record_size);
+                       kfree(cache->buffer);
                }
-               codec->amp_info_size = new_size;
-               codec->amp_info = new_info;
+               cache->size = new_size;
+               cache->buffer = new_buffer;
        }
-       cur = codec->num_amp_entries++;
-       info = &codec->amp_info[cur];
+       cur = cache->num_entries++;
+       info = (struct hda_cache_head *)(cache->buffer +
+                                        cur * cache->record_size);
        info->key = key;
-       info->status = 0; /* not initialized yet */
-       info->next = codec->amp_hash[idx];
-       codec->amp_hash[idx] = cur;
+       info->val = 0;
+       info->next = cache->hash[idx];
+       cache->hash[idx] = cur;
 
        return info;
 }
 
+/* query and allocate an amp hash entry */
+static inline struct hda_amp_info *
+get_alloc_amp_hash(struct hda_codec *codec, u32 key)
+{
+       return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key);
+}
+
 /*
  * query AMP capabilities for the given widget and direction
  */
@@ -727,7 +742,7 @@ static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
        info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0));
        if (!info)
                return 0;
-       if (!(info->status & INFO_AMP_CAPS)) {
+       if (!(info->head.val & INFO_AMP_CAPS)) {
                if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
                        nid = codec->afg;
                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)
                                                    AC_PAR_AMP_OUT_CAP :
                                                    AC_PAR_AMP_IN_CAP);
                if (info->amp_caps)
-                       info->status |= INFO_AMP_CAPS;
+                       info->head.val |= INFO_AMP_CAPS;
        }
        return info->amp_caps;
 }
@@ -749,7 +764,7 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
        if (!info)
                return -EINVAL;
        info->amp_caps = caps;
-       info->status |= INFO_AMP_CAPS;
+       info->head.val |= INFO_AMP_CAPS;
        return 0;
 }
 
@@ -763,7 +778,7 @@ static unsigned int get_vol_mute(struct hda_codec *codec,
 {
        u32 val, parm;
 
-       if (info->status & INFO_AMP_VOL(ch))
+       if (info->head.val & INFO_AMP_VOL(ch))
                return info->vol[ch];
 
        parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
@@ -772,7 +787,7 @@ static unsigned int get_vol_mute(struct hda_codec *codec,
        val = snd_hda_codec_read(codec, nid, 0,
                                 AC_VERB_GET_AMP_GAIN_MUTE, parm);
        info->vol[ch] = val & 0xff;
-       info->status |= INFO_AMP_VOL(ch);
+       info->head.val |= INFO_AMP_VOL(ch);
        return info->vol[ch];
 }
 
index 1370e346bf34572f3985a0ddb640fb12560266d5..1a69743160ce10a7125f4a431155cbb68b107481 100644 (file)
@@ -480,12 +480,24 @@ struct hda_codec_ops {
 };
 
 /* record for amp information cache */
-struct hda_amp_info {
+struct hda_cache_head {
        u32 key;                /* hash key */
+       u16 val;                /* assigned value */
+       u16 next;               /* next link; -1 = terminal */
+};
+
+struct hda_amp_info {
+       struct hda_cache_head head;
        u32 amp_caps;           /* amp capabilities */
        u16 vol[2];             /* current volume & mute */
-       u16 status;             /* update flag */
-       u16 next;               /* next link */
+};
+
+struct hda_cache_rec {
+       u16 hash[64];                   /* hash table for index */
+       unsigned int num_entries;       /* number of assigned entries */
+       unsigned int size;              /* allocated size */
+       unsigned int record_size;       /* record size (including header) */
+       void *buffer;                   /* hash table entries */
 };
 
 /* PCM callbacks */
@@ -557,11 +569,7 @@ struct hda_codec {
        hda_nid_t start_nid;
        u32 *wcaps;
 
-       /* hash for amp access */
-       u16 amp_hash[32];
-       int num_amp_entries;
-       int amp_info_size;
-       struct hda_amp_info *amp_info;
+       struct hda_cache_rec amp_cache; /* cache for amp access */
 
        struct mutex spdif_mutex;
        unsigned int spdif_status;      /* IEC958 status bits */