ALSA: hda - Allow multiple callbacks for jack

So far, hda_jack infrastructure allows only one callback per jack, and
this makes things slightly complicated when a driver wants to assign
multiple tasks to a jack, e.g. the standard auto-mute with a power
up/down sequence.  This can be simplified if the hda_jack accepts
multiple callbacks.

This patch is such an extension: the callback-specific part (the
function and private_data) is split to another struct from
hda_jack_tbl, and multiple such objects can be assigned to a single
hda_jack_tbl entry.

The new struct hda_jack_callback is passed to each callback function
now, thus the patch became bigger than expected.  But these changes
are mostly trivial.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
index a5fe1b4..f56765a 100644
--- a/sound/pci/hda/hda_jack.c
+++ b/sound/pci/hda/hda_jack.c
@@ -111,17 +111,21 @@
 
 void snd_hda_jack_tbl_clear(struct hda_codec *codec)
 {
+	struct hda_jack_tbl *jack = codec->jacktbl.list;
+	int i;
+
+	for (i = 0; i < codec->jacktbl.used; i++, jack++) {
+		struct hda_jack_callback *cb, *next;
 #ifdef CONFIG_SND_HDA_INPUT_JACK
-	/* free jack instances manually when clearing/reconfiguring */
-	if (!codec->bus->shutdown && codec->jacktbl.list) {
-		struct hda_jack_tbl *jack = codec->jacktbl.list;
-		int i;
-		for (i = 0; i < codec->jacktbl.used; i++, jack++) {
-			if (jack->jack)
-				snd_device_free(codec->bus->card, jack->jack);
+		/* free jack instances manually when clearing/reconfiguring */
+		if (!codec->bus->shutdown && jack->jack)
+			snd_device_free(codec->bus->card, jack->jack);
+#endif
+		for (cb = jack->callback; cb; cb = next) {
+			next = cb->next;
+			kfree(cb);
 		}
 	}
-#endif
 	snd_array_free(&codec->jacktbl);
 }
 
@@ -219,28 +223,38 @@
  * errno.  Check and handle the return value appropriately with standard
  * macros such as @IS_ERR() and @PTR_ERR().
  */
-struct hda_jack_tbl *
+struct hda_jack_callback *
 snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
-				    hda_jack_callback cb)
+				    hda_jack_callback_fn func)
 {
-	struct hda_jack_tbl *jack = snd_hda_jack_tbl_new(codec, nid);
+	struct hda_jack_tbl *jack;
+	struct hda_jack_callback *callback = NULL;
 	int err;
 
+	jack = snd_hda_jack_tbl_new(codec, nid);
 	if (!jack)
 		return ERR_PTR(-ENOMEM);
+	if (func) {
+		callback = kzalloc(sizeof(*callback), GFP_KERNEL);
+		if (!callback)
+			return ERR_PTR(-ENOMEM);
+		callback->func = func;
+		callback->tbl = jack;
+		callback->next = jack->callback;
+		jack->callback = callback;
+	}
+
 	if (jack->jack_detect)
-		return jack; /* already registered */
+		return callback; /* already registered */
 	jack->jack_detect = 1;
-	if (cb)
-		jack->callback = cb;
 	if (codec->jackpoll_interval > 0)
-		return jack; /* No unsol if we're polling instead */
+		return callback; /* No unsol if we're polling instead */
 	err = snd_hda_codec_write_cache(codec, nid, 0,
 					 AC_VERB_SET_UNSOLICITED_ENABLE,
 					 AC_USRSP_EN | jack->tag);
 	if (err < 0)
 		return ERR_PTR(err);
-	return jack;
+	return callback;
 }
 EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable_callback);
 
@@ -503,13 +517,17 @@
 static void call_jack_callback(struct hda_codec *codec,
 			       struct hda_jack_tbl *jack)
 {
-	if (jack->callback)
-		jack->callback(codec, jack);
+	struct hda_jack_callback *cb;
+
+	for (cb = jack->callback; cb; cb = cb->next)
+		cb->func(codec, cb);
 	if (jack->gated_jack) {
 		struct hda_jack_tbl *gated =
 			snd_hda_jack_tbl_get(codec, jack->gated_jack);
-		if (gated && gated->callback)
-			gated->callback(codec, gated);
+		if (gated) {
+			for (cb = gated->callback; cb; cb = cb->next)
+				cb->func(codec, cb);
+		}
 	}
 }