[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>
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index fc934ba..46f8ab1 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -494,6 +494,10 @@
 }
 
 
+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 @@
 	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 @@
 	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 @@
 #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(codec->amp_hash, 0xff, sizeof(codec->amp_hash));
-	codec->num_amp_entries = 0;
-	codec->amp_info_size = 0;
-	codec->amp_info = NULL;
+	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)
+{
+	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 @@
 	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 @@
 						    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 @@
 	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 @@
 {
 	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 @@
 	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];
 }