All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Subhransu S. Prusty" <subhransu.s.prusty@intel.com>
To: alsa-devel@alsa-project.org
Cc: tiwai@suse.de, lgirdwood@gmail.com, patches.audio@intel.com,
	broonie@kernel.org, Vinod Koul <vinod.koul@intel.com>,
	"Subhransu S. Prusty" <subhransu.s.prusty@intel.com>
Subject: [RFC 05/11] ASoC: hdac: Create DAPM model for HDA widgets
Date: Mon, 27 Jun 2016 09:17:58 +0530	[thread overview]
Message-ID: <1466999284-14782-6-git-send-email-subhransu.s.prusty@intel.com> (raw)
In-Reply-To: <1466999284-14782-1-git-send-email-subhransu.s.prusty@intel.com>

HDA device widgets are mapped to dapm widgets to take advantage
of DAPM. Each HDA widget can be mapped to one or multiple dapm
widgets based on interface and how it is connected with other
widgets.

For example, a PIN widget 2 or 3 dapm widgets are created
depending on the capability. A dapm input/out widget is created
to represent the input/output capability, a pga widget is created
so that pin with retasking capability can be properly represented
in a dapm graph and a mux widget for output pin is created, if
the connection list has more than one input.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 sound/soc/codecs/hdac_generic.c | 484 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 483 insertions(+), 1 deletion(-)

diff --git a/sound/soc/codecs/hdac_generic.c b/sound/soc/codecs/hdac_generic.c
index 79db501..d2b6bec 100644
--- a/sound/soc/codecs/hdac_generic.c
+++ b/sound/soc/codecs/hdac_generic.c
@@ -43,6 +43,483 @@ struct hdac_generic_priv {
 	unsigned int num_dapm_widgets;
 };
 
+static char *wid_names[] = {
+		"dac", "adc", "mixer", "mux", "pin", "power",
+		"volme knob", "beep", NULL, NULL, NULL, NULL,
+		NULL, NULL, NULL, "vendor",
+};
+
+static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
+{
+	struct hdac_device *hdac = dev_to_hdac_dev(dev);
+
+	return to_ehdac_device(hdac);
+}
+
+static int hdac_generic_fill_widget_info(struct device *dev,
+		struct snd_soc_dapm_widget *w, enum snd_soc_dapm_type id,
+		void *priv, const char *wname, const char *stream,
+		struct snd_kcontrol_new *wc, int numkc,
+		int (*event)(struct snd_soc_dapm_widget *, struct snd_kcontrol *, int),
+		unsigned short event_flags)
+{
+	w->id = id;
+	w->name = devm_kstrdup(dev, wname, GFP_KERNEL);
+	if (!w->name)
+		return -ENOMEM;
+
+	w->sname = stream;
+	w->reg = SND_SOC_NOPM;
+	w->shift = 0;
+	w->kcontrol_news = wc;
+	w->num_kcontrols = numkc;
+	w->priv = priv;
+	w->event = event;
+	w->event_flags = event_flags;
+
+	return 0;
+}
+
+static int hdac_generic_alloc_mux_widget(struct snd_soc_dapm_context *dapm,
+		struct snd_soc_dapm_widget *widgets, int index,
+		struct hdac_codec_widget *wid)
+
+{
+	struct snd_kcontrol_new *kc;
+	struct soc_enum *se;
+	char kc_name[HDAC_GENERIC_NAME_SIZE];
+	char mux_items[HDAC_GENERIC_NAME_SIZE];
+	char widget_name[HDAC_GENERIC_NAME_SIZE];
+	const char *name;
+	/* To hold inputs to the Pin mux */
+	char *items[HDA_MAX_CONNECTIONS];
+	int i = 0, ret;
+	int num_items = wid->num_inputs + 1;
+
+	if (wid->type == AC_WID_AUD_SEL)
+		sprintf(widget_name, "Mux %x", wid->nid);
+	else if (wid->type == AC_WID_PIN)
+		sprintf(widget_name, "Pin %x Mux", wid->nid);
+	else
+		return -EINVAL;
+
+	kc = devm_kzalloc(dapm->dev, sizeof(*kc), GFP_KERNEL);
+	if (!kc)
+		return -ENOMEM;
+
+	se = devm_kzalloc(dapm->dev, sizeof(*se), GFP_KERNEL);
+	if (!se)
+		return -ENOMEM;
+
+	sprintf(kc_name, "Mux %d Input", wid->nid);
+	kc->name = devm_kstrdup(dapm->dev, kc_name, GFP_KERNEL);
+	if (!kc->name)
+		return -ENOMEM;
+
+	kc->private_value = (long)se;
+	kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	kc->access = 0;
+	kc->info = snd_soc_info_enum_double;
+	kc->put = snd_soc_dapm_put_enum_double;
+	kc->get = snd_soc_dapm_get_enum_double;
+
+	se->reg = SND_SOC_NOPM;
+
+	se->items = num_items;
+	se->mask = roundup_pow_of_two(se->items) - 1;
+
+	sprintf(mux_items, "NONE");
+	items[i] = devm_kstrdup(dapm->dev, mux_items, GFP_KERNEL);
+	if (!items[i])
+		return -ENOMEM;
+
+	for (i = 0; i < wid->num_inputs; i++)	{
+		name = wid_names[wid->conn_list[i].type];
+		if (!name)
+			return -EINVAL;
+
+		sprintf(mux_items, "%s %x", name, wid->conn_list[i].nid);
+		items[i + 1] = devm_kstrdup(dapm->dev, mux_items, GFP_KERNEL);
+		if (!items[i])
+			return -ENOMEM;
+	}
+
+	se->texts = devm_kmemdup(dapm->dev, items,
+			(num_items  * sizeof(char *)), GFP_KERNEL);
+	if (!se->texts)
+		return -ENOMEM;
+
+	ret = hdac_generic_fill_widget_info(dapm->dev, &widgets[index],
+			snd_soc_dapm_mux, wid, widget_name, NULL, kc, 1,
+			NULL, 0);
+
+	if (ret < 0)
+		return ret;
+
+	wid->priv = &widgets[index];
+
+	return 0;
+}
+
+static const char *get_dai_stream(struct snd_soc_dai_driver *dai_drv,
+			int num_dais, struct hdac_codec_widget *wid)
+{
+	int i;
+	struct hdac_codec_widget *tmp;
+
+	for (i = 0; i < num_dais; i++) {
+		tmp = dai_drv[i].dobj.private;
+		if (tmp->nid == wid->nid) {
+			if (wid->type == AC_WID_AUD_IN)
+				return dai_drv[i].capture.stream_name;
+			else
+				return dai_drv[i].playback.stream_name;
+		}
+	}
+
+	return NULL;
+}
+
+static int hdac_codec_alloc_cvt_widget(struct snd_soc_dapm_context *dapm,
+			struct snd_soc_dapm_widget *widgets, int index,
+			struct hdac_codec_widget *wid)
+{
+	struct snd_soc_dai_driver *dai_drv = dapm->component->dai_drv;
+	char widget_name[HDAC_GENERIC_NAME_SIZE];
+	const char *dai_strm_name;
+	int ret = 0;
+
+	dai_strm_name = get_dai_stream(dai_drv,
+				dapm->component->num_dai, wid);
+	if (!dai_strm_name)
+		return -EINVAL;
+
+	if (wid->type == AC_WID_AUD_IN) {
+		sprintf(widget_name, "ADC %x", wid->nid);
+	} else {
+		sprintf(widget_name, "%s DAC %x",
+			(wid->caps & AC_WCAP_DIGITAL) ? "Digital" : "Analog",
+			wid->nid);
+	}
+
+	ret = hdac_generic_fill_widget_info(dapm->dev, &widgets[index],
+			wid->type == AC_WID_AUD_IN ?
+			snd_soc_dapm_aif_in : snd_soc_dapm_aif_out,
+			wid, widget_name, dai_strm_name, NULL, 0, NULL, 0);
+	if (ret < 0)
+		return ret;
+
+	wid->priv = &widgets[index];
+
+	return 0;
+}
+
+static int hdac_codec_alloc_mixer_widget(struct snd_soc_dapm_context *dapm,
+				struct snd_soc_dapm_widget *w, int index,
+				struct hdac_codec_widget *wid)
+{
+	struct snd_kcontrol_new *kc;
+	struct soc_mixer_control *mc;
+	char kc_name[HDAC_GENERIC_NAME_SIZE];
+	char widget_name[HDAC_GENERIC_NAME_SIZE];
+	const char *name;
+	int i, ret;
+
+	kc = devm_kzalloc(dapm->dev,
+			(sizeof(*kc) * wid->num_inputs),
+			GFP_KERNEL);
+	if (!kc)
+		return -ENOMEM;
+
+	for (i = 0; i < wid->num_inputs; i++) {
+		name = wid_names[wid->conn_list[i].type];
+		if (!name)
+			return -EINVAL;
+
+		sprintf(kc_name, "%s %x in Switch",
+				name, wid->conn_list[i].nid);
+		kc[i].name = devm_kstrdup(dapm->dev, kc_name, GFP_KERNEL);
+		if (!kc[i].name)
+			return -ENOMEM;
+
+		mc = devm_kzalloc(dapm->dev, (sizeof(*mc)), GFP_KERNEL);
+		if (!mc)
+			return -ENOMEM;
+
+		mc->reg = SND_SOC_NOPM;
+		mc->rreg = SND_SOC_NOPM;
+		mc->max = 1;
+
+		kc[i].private_value = (long)mc;
+		kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		kc[i].info = snd_soc_info_volsw;
+		kc[i].put = snd_soc_dapm_get_volsw;
+		kc[i].get = snd_soc_dapm_put_volsw;
+	}
+
+	sprintf(widget_name, "Mixer %x", wid->nid);
+	ret = hdac_generic_fill_widget_info(dapm->dev, &w[index],
+			snd_soc_dapm_mixer, wid, widget_name, NULL,
+			kc, wid->num_inputs, NULL, 0);
+	if (ret < 0)
+		return ret;
+
+	wid->priv = &w[index];
+
+	return 0;
+}
+
+/*
+ * Each Pin widget will be represented with:
+ *	DAPM input/output - Based on out/in capability queried
+ *	DAPM PGA - To program the PIN configuration
+ *	DAPM Mux - Create a virtual Mux widget, if output capable pin can
+ *		   select from multiple inputs.
+ *
+ * Returns number of dapm widgets created on success else returns -ve error
+ * code.
+ */
+static int hdac_codec_alloc_pin_widget(struct snd_soc_dapm_context *dapm,
+			struct snd_soc_dapm_widget *widgets, int index,
+			struct hdac_codec_widget *wid)
+{
+	struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev);
+	char widget_name[HDAC_GENERIC_NAME_SIZE];
+	int i = index;
+	int ret;
+	bool input;
+	/*
+	 * Pin complex are represented with multiple dapm widgets. Cache them
+	 * for easy reference. wid_ref[0]->input/output, wid_ref[1]->pga,
+	 * wid_ref[2]->mux.
+	 */
+	struct snd_soc_dapm_widget **wid_ref;
+
+	input = is_input_pin(&edev->hdac, wid->nid);
+
+	wid_ref = devm_kzalloc(dapm->dev,
+			3 * sizeof(struct snd_soc_dapm_widget),
+			GFP_KERNEL);
+	if (!wid_ref)
+		return -ENOMEM;
+
+	/* Create output/input widget */
+	sprintf(widget_name, "Pin %x %s", wid->nid,
+				input ? "Input" : "Output");
+
+	ret = hdac_generic_fill_widget_info(dapm->dev, &widgets[i],
+			input ? snd_soc_dapm_input : snd_soc_dapm_output,
+			wid, widget_name, NULL, NULL, 0, NULL, 0);
+	if (ret < 0)
+		return ret;
+
+	wid_ref[0] = &widgets[i++];
+
+	/* Create PGA widget */
+	sprintf(widget_name, "Pin %x PGA", wid->nid);
+	ret = hdac_generic_fill_widget_info(dapm->dev, &widgets[i],
+			snd_soc_dapm_pga, wid, widget_name, NULL,
+			NULL, 0, NULL, 0);
+	if (ret < 0)
+		return ret;
+
+	wid_ref[1] = &widgets[i++];
+
+	/* Create Mux if Pin widget can select from multiple inputs */
+	if (!input && wid->num_inputs > 1) {
+		sprintf(widget_name, "Pin %x Mux", wid->nid);
+		ret = hdac_generic_alloc_mux_widget(dapm, widgets, i, wid);
+		if (ret < 0)
+			return ret;
+
+		wid_ref[2] = &widgets[i++];
+	}
+
+	/* override hda widget private with dapm widget group */
+	wid->priv = wid_ref;
+
+	/* Return number of dapm widgets created */
+	return i - index;
+}
+
+static int hdac_codec_alloc_power_widget(struct snd_soc_dapm_context *dapm,
+			struct snd_soc_dapm_widget *widgets, int index,
+			struct hdac_codec_widget *wid)
+{
+	char widget_name[HDAC_GENERIC_NAME_SIZE];
+	int ret = 0;
+
+	sprintf(widget_name, "Power %x", wid->nid);
+	ret = hdac_generic_fill_widget_info(dapm->dev, &widgets[index],
+			snd_soc_dapm_supply, wid, widget_name,
+			NULL, NULL, 0, NULL, 0);
+	if (ret < 0)
+		return ret;
+
+	wid->priv = &widgets[index];
+
+	return 0;
+}
+
+/*
+ * Each Beep hda widget will be represented with two dapm widget a siggen
+ * and a PGA. A virtual switch control will be added to turn on/off DAPM.
+ */
+static int hdac_codec_alloc_beep_widget(struct snd_soc_dapm_context *dapm,
+			struct snd_soc_dapm_widget *widgets, int index,
+			struct hdac_codec_widget *wid)
+{
+	char widget_name[HDAC_GENERIC_NAME_SIZE];
+	int i = index, ret = 0;
+	struct soc_mixer_control *mc;
+	struct snd_kcontrol_new *kc;
+	char kc_name[HDAC_GENERIC_NAME_SIZE];
+	/*
+	 * Beep widgets are represented with multiple dapm widgets. Cache them
+	 * for each reference. wid_ref[0]->siggen, wid_ref[1]->pga.
+	 */
+	struct snd_soc_dapm_widget **wid_ref;
+
+	wid_ref = devm_kzalloc(dapm->dev,
+			2 * sizeof(struct snd_soc_dapm_widget),
+			GFP_KERNEL);
+	if (!wid_ref)
+		return -ENOMEM;
+
+	sprintf(widget_name, "Beep Gen %x", wid->nid);
+	ret = hdac_generic_fill_widget_info(dapm->dev, &widgets[i++],
+			snd_soc_dapm_siggen, wid, widget_name,
+			NULL, NULL, 0, NULL, 0);
+		if (ret < 0)
+			return ret;
+
+	kc = devm_kzalloc(dapm->dev,
+			(sizeof(*kc) * wid->num_inputs),
+			GFP_KERNEL);
+	if (!kc)
+		return -ENOMEM;
+
+	sprintf(kc_name, "%s %x in Switch", wid_names[wid->type], wid->nid);
+	kc[i].name = devm_kstrdup(dapm->dev, kc_name, GFP_KERNEL);
+	if (!kc[i].name)
+		return -ENOMEM;
+	mc = devm_kzalloc(dapm->dev, (sizeof(*mc)), GFP_KERNEL);
+	if (!mc)
+		return -ENOMEM;
+
+	mc->reg = SND_SOC_NOPM;
+	mc->rreg = SND_SOC_NOPM;
+	mc->max = 1;
+
+	kc[i].private_value = (long)mc;
+	kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	kc[i].info = snd_soc_info_volsw;
+	kc[i].put = snd_soc_dapm_get_volsw;
+	kc[i].get = snd_soc_dapm_put_volsw;
+
+	sprintf(widget_name, "Beep Gen %x PGA", wid->nid);
+	ret = hdac_generic_fill_widget_info(dapm->dev, &widgets[i],
+			snd_soc_dapm_pga, wid, widget_name,
+			NULL, kc, 1, NULL, 0);
+	if (ret < 0)
+		return ret;
+
+	wid->priv = wid_ref;
+
+	return 0;
+}
+
+/* Create DAPM widgets to represent each codec widget */
+static int hdac_codec_alloc_widgets(struct snd_soc_dapm_context *dapm,
+		struct snd_soc_dapm_widget *widgets)
+{
+	struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev);
+	struct hdac_codec_widget *wid;
+	int index = 0;
+	int ret = 0;
+
+	list_for_each_entry(wid, &edev->hdac.widget_list, head) {
+		switch (wid->type) {
+		case AC_WID_AUD_IN:
+		case AC_WID_AUD_OUT:
+			ret = hdac_codec_alloc_cvt_widget(dapm, widgets,
+							index, wid);
+			if (ret < 0)
+				return ret;
+			index++;
+			break;
+
+		case AC_WID_PIN:
+			ret = hdac_codec_alloc_pin_widget(dapm, widgets,
+							index, wid);
+			if (ret < 0)
+				return ret;
+			index += ret;
+			break;
+
+		case AC_WID_AUD_MIX:
+			ret = hdac_codec_alloc_mixer_widget(dapm, widgets,
+							index, wid);
+			if (ret < 0)
+				return ret;
+			index++;
+			break;
+
+		case AC_WID_AUD_SEL:
+			ret = hdac_generic_alloc_mux_widget(dapm, widgets,
+							index, wid);
+			if (ret < 0)
+				return ret;
+			index++;
+			break;
+
+		case AC_WID_POWER:
+			ret = hdac_codec_alloc_power_widget(dapm, widgets,
+							index, wid);
+			if (ret < 0)
+				return ret;
+			index++;
+			break;
+
+		case AC_WID_BEEP:
+			ret = hdac_codec_alloc_beep_widget(dapm, widgets,
+							index, wid);
+			if (ret < 0)
+				return ret;
+			index += 2;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int hdac_generic_create_fill_widget_route_map(
+		struct snd_soc_dapm_context *dapm)
+{
+	struct snd_soc_dapm_widget *widgets;
+	struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev);
+	struct hdac_generic_priv *hdac_priv = edev->private_data;
+
+	widgets = devm_kzalloc(dapm->dev,
+			(sizeof(*widgets) * hdac_priv->num_dapm_widgets),
+			GFP_KERNEL);
+	if (!widgets)
+		return -ENOMEM;
+
+	/* Create DAPM widgets */
+	hdac_codec_alloc_widgets(dapm, widgets);
+
+	snd_soc_dapm_new_controls(dapm, widgets, hdac_priv->num_dapm_widgets);
+
+	/* TODO:  Add each path to dapm graph when enumerated */
+
+	return 0;
+}
+
 static void hdac_generic_calc_dapm_widgets(struct hdac_ext_device *edev)
 {
 	struct hdac_generic_priv *hdac_priv = edev->private_data;
@@ -178,10 +655,15 @@ static int hdac_codec_probe(struct snd_soc_codec *codec)
 	struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
 	struct snd_soc_dapm_context *dapm =
 		snd_soc_component_get_dapm(&codec->component);
+	int ret;
 
 	edev->scodec = codec;
 
-	/* TODO: create widget, route and controls */
+	/* create widget, route and controls */
+	ret = hdac_generic_create_fill_widget_route_map(dapm);
+	if (ret < 0)
+		return ret;
+
 	/* TODO: jack sense */
 
 	/* Imp: Store the card pointer in hda_codec */
-- 
1.9.1

  parent reply	other threads:[~2016-06-27  3:51 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-06-27  3:47 [RFC 00/11] ASoC: hdac: Add hdac generic driver Subhransu S. Prusty
2016-06-27  3:47 ` [RFC 01/11] ALSA: hdac: Add codec helper library Subhransu S. Prusty
2016-06-27  3:47 ` [RFC 02/11] ASoC: Add dai_ops to set the stream tag Subhransu S. Prusty
2016-06-27 17:30   ` Mark Brown
2016-06-28  5:21     ` Subhransu S. Prusty
2016-06-28 11:15       ` Mark Brown
2016-06-29  4:08         ` Subhransu S. Prusty
2016-06-29 18:50           ` Mark Brown
2016-06-30  8:59             ` Subhransu S. Prusty
2016-07-05 12:54               ` Mark Brown
2016-07-08  5:27                 ` Subhransu S. Prusty
2016-06-27  3:47 ` [RFC 03/11] ALSA: hda - Add macro to test pin widget's input capability Subhransu S. Prusty
2016-06-27  3:47 ` [RFC 04/11] ASoC: hdac: Add a generic hdac driver framework Subhransu S. Prusty
2016-06-27  3:47 ` Subhransu S. Prusty [this message]
2016-06-27  3:47 ` [RFC 06/11] ASoC: dapm: Create API to add a single route element Subhransu S. Prusty
2016-06-27 18:02   ` Mark Brown
2016-06-28  5:22     ` Subhransu S. Prusty
2016-06-27  3:48 ` [RFC 07/11] ASoC: hdac: Build DAPM graph by querying through widget connection list Subhransu S. Prusty
2016-06-27  3:48 ` [RFC 08/11] ASoC: hdac: Register widget event handlers Subhransu S. Prusty
2016-06-27  3:48 ` [RFC 09/11] ALSA: hda - macro to get default config device of pin widgets Subhransu S. Prusty
2016-06-27  3:48 ` [RFC 10/11] ASoC: dapm: Export snd_soc_dapm_new_control Subhransu S. Prusty
2016-07-05 14:57   ` Applied "ASoC: dapm: Export snd_soc_dapm_new_control" to the asoc tree Mark Brown
2016-06-27  3:48 ` [RFC 11/11] ASoC: hdac: Export API to create machine controls Subhransu S. Prusty
2016-06-27  7:05 ` [RFC 00/11] ASoC: hdac: Add hdac generic driver Takashi Iwai
2016-06-28  5:13   ` Subhransu S. Prusty
2016-07-08  7:33     ` Subhransu S. Prusty
2016-07-08  7:53       ` Takashi Iwai
2016-07-08  8:21         ` Subhransu S. Prusty
2016-07-08  8:37           ` Takashi Iwai
2016-07-08  8:49             ` Subhransu S. Prusty

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1466999284-14782-6-git-send-email-subhransu.s.prusty@intel.com \
    --to=subhransu.s.prusty@intel.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=broonie@kernel.org \
    --cc=lgirdwood@gmail.com \
    --cc=patches.audio@intel.com \
    --cc=tiwai@suse.de \
    --cc=vinod.koul@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.