From patchwork Wed Dec 9 16:16:16 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Subhransu S. Prusty" X-Patchwork-Id: 7810261 Return-Path: X-Original-To: patchwork-alsa-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id DCCABBEEE1 for ; Wed, 9 Dec 2015 16:17:34 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id A1421204D8 for ; Wed, 9 Dec 2015 16:17:33 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id 2087F203F4 for ; Wed, 9 Dec 2015 16:17:32 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id 1E849261A8C; Wed, 9 Dec 2015 17:17:31 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_LOW, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id BE773261B20; Wed, 9 Dec 2015 17:15:20 +0100 (CET) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 8488C261B20; Wed, 9 Dec 2015 17:15:20 +0100 (CET) Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by alsa0.perex.cz (Postfix) with ESMTP id AA083261552 for ; Wed, 9 Dec 2015 17:14:18 +0100 (CET) Received: from orsmga001.jf.intel.com ([10.7.209.18]) by fmsmga102.fm.intel.com with ESMTP; 09 Dec 2015 08:14:18 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.20,404,1444719600"; d="scan'208";a="837735510" Received: from subhransu-desktop.iind.intel.com ([10.223.96.57]) by orsmga001.jf.intel.com with ESMTP; 09 Dec 2015 08:14:14 -0800 From: "Subhransu S. Prusty" To: alsa-devel@alsa-project.org Date: Wed, 9 Dec 2015 21:46:16 +0530 Message-Id: <1449677781-7997-9-git-send-email-subhransu.s.prusty@intel.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1449677781-7997-1-git-send-email-subhransu.s.prusty@intel.com> References: <1449677405-7907-1-git-send-email-subhransu.s.prusty@intel.com> <1449677781-7997-1-git-send-email-subhransu.s.prusty@intel.com> Cc: tiwai@suse.de, lgirdwood@gmail.com, patches.audio@intel.com, broonie@kernel.org, Vinod Koul , "Subhransu S. Prusty" Subject: [alsa-devel] [PATCH v4 09/14] ASoC: hdac_hdmi: Assign pin for stream based on dapm connection X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP Now that we have all the widgets enumerated we can route the stream to any pin widget based on Mux connection. So map the pin to stream accordingly. Also seems the connection list to the pin widgets are not static and is updated runtime based on type of display attached to the port. This looks to be a hardware behavior. So querying of the connection list is removed from pin initialization and used in selecting the pin for a dai map. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul --- sound/soc/codecs/hdac_hdmi.c | 212 +++++++++++++++++++++++++++++++------------ 1 file changed, 153 insertions(+), 59 deletions(-) diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 507148c..0aac3cb 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -38,6 +38,8 @@ #define HDA_MAX_CONNECTIONS 32 +#define HDA_MAX_CVTS 3 + #define ELD_MAX_SIZE 256 #define ELD_FIXED_BYTES 20 @@ -80,7 +82,7 @@ struct hdac_hdmi_dai_pin_map { }; struct hdac_hdmi_priv { - struct hdac_hdmi_dai_pin_map dai_map[3]; + struct hdac_hdmi_dai_pin_map dai_map[HDA_MAX_CVTS]; struct list_head pin_list; struct list_head cvt_list; int num_pin; @@ -369,12 +371,125 @@ static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream, return 0; } +static int hdac_hdmi_enable_pin(struct hdac_ext_device *hdac, + struct hdac_hdmi_dai_pin_map *dai_map) +{ + int mux_idx; + struct hdac_hdmi_pin *pin = dai_map->pin; + + for (mux_idx = 0; mux_idx < pin->num_mux_nids; mux_idx++) { + if (pin->mux_nids[mux_idx] == dai_map->cvt->nid) { + snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, + AC_VERB_SET_CONNECT_SEL, mux_idx); + break; + } + } + + if (mux_idx == pin->num_mux_nids) + return -EIO; + + /* Enable out path for this pin widget */ + snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + + hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0); + + snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + + return 0; +} + +static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac, + struct hdac_hdmi_pin *pin) +{ + if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) { + dev_warn(&hdac->hdac.dev, + "HDMI: pin %d wcaps %#x does not support connection list\n", + pin->nid, get_wcaps(&hdac->hdac, pin->nid)); + return -EINVAL; + } + + pin->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid, + pin->mux_nids, HDA_MAX_CONNECTIONS); + if (pin->num_mux_nids == 0) + dev_warn(&hdac->hdac.dev, "No connections found for pin: %d\n", pin->nid); + + dev_dbg(&hdac->hdac.dev, "num_mux_nids %d for pin: %d\n", + pin->num_mux_nids, pin->nid); + + return pin->num_mux_nids; +} + +static inline struct hdac_hdmi_pin *hdac_hdmi_get_pin( + struct hdac_ext_device *edev, + struct snd_soc_dapm_path *p, + struct hdac_hdmi_cvt *cvt) +{ + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pin *pin; + hda_nid_t *nid; + int ret, i; + + nid = (hda_nid_t *)p->sink->priv; + list_for_each_entry(pin, &hdmi->pin_list, head) { + if (pin->nid == *nid) { + ret = hdac_hdmi_query_pin_connlist(edev, pin); + if (ret < 0) + continue; + + for (i = 0; i < pin->num_mux_nids; i++) { + if (pin->mux_nids[i] == cvt->nid) + return pin; + } + } + } + + return NULL; +} + +/* + * This queries mux widgets in each sink path of the dai widget and returns + * a matching pin widget to which the stream may be routed. + * + * The converter may be input to multiple pin muxes. So each + * pin mux (basically each pin widget) is queried to identify if + * the converter as one of the input, then the first pin match + * is selected for rendering. + * + * Same stream rendering to multiple pins simultaneously can be done + * possibly, but not supported for now. + * + * So return the first pin connected + */ +static struct hdac_hdmi_pin *hdac_hdmi_get_pin_from_daistream( + struct hdac_ext_device *edev, + struct snd_soc_dapm_widget *strm_w, + struct hdac_hdmi_cvt *cvt) +{ + struct snd_soc_dapm_path *p; + + snd_soc_dapm_widget_for_each_sink_path(strm_w, p) { + if (!p->connect) + continue; + + if (strstr(p->sink->name, "Mux")) + return hdac_hdmi_get_pin(edev, p, cvt); + else + return hdac_hdmi_get_pin_from_daistream(edev, p->sink, cvt); + } + + return NULL; +} + static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); struct hdac_hdmi_priv *hdmi = hdac->private_data; struct hdac_hdmi_dai_pin_map *dai_map; + struct hdac_hdmi_cvt *cvt; + struct hdac_hdmi_pin *pin; int ret; if (dai->id > 0) { @@ -384,26 +499,32 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, dai_map = &hdmi->dai_map[dai->id]; - if ((!dai_map->pin->eld.monitor_present) || - (!dai_map->pin->eld.eld_valid)) { - dev_err(&hdac->hdac.dev, "Failed: montior present? %d eld valid?: %d\n", - dai_map->pin->eld.monitor_present, - dai_map->pin->eld.eld_valid); + cvt = dai_map->cvt; + pin = hdac_hdmi_get_pin_from_daistream(hdac, dai->playback_widget, cvt); + if (!pin) + return -EIO; + + if ((!pin->eld.monitor_present) || + (!pin->eld.eld_valid)) { + dev_err(&hdac->hdac.dev, + "failed: montior present? %d eld valid?: %d for pin: %d\n", + pin->eld.monitor_present, pin->eld.eld_valid, pin->nid); return -ENODEV; } - hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0); + dai_map->pin = pin; - snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + ret = hdac_hdmi_enable_pin(hdac, dai_map); + if (ret < 0) + return ret; ret = hdac_hdmi_eld_limit_formats(substream->runtime, - dai_map->pin->eld.eld_buffer); + pin->eld.eld_buffer); if (ret < 0) return ret; return snd_pcm_hw_constraint_eld(substream->runtime, - dai_map->pin->eld.eld_buffer); + pin->eld.eld_buffer); } static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, @@ -419,6 +540,8 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + dai_map->pin = NULL; } static int @@ -441,28 +564,6 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt) return err; } -static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac, - struct hdac_hdmi_pin *pin) -{ - if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) { - dev_warn(&hdac->hdac.dev, - "HDMI: pin %d wcaps %#x does not support connection list\n", - pin->nid, get_wcaps(&hdac->hdac, pin->nid)); - return -EINVAL; - } - - pin->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid, - pin->mux_nids, HDA_MAX_CONNECTIONS); - if (pin->num_mux_nids == 0) - dev_warn(&hdac->hdac.dev, "No connections found for pin: %d\n", - pin->nid); - - dev_dbg(&hdac->hdac.dev, "num_mux_nids %d for pin: %d\n", - pin->num_mux_nids, pin->nid); - - return pin->num_mux_nids; -} - static int hdac_hdmi_fill_widget_info(struct device *dev, struct snd_soc_dapm_widget *w, enum snd_soc_dapm_type id, void *priv, @@ -702,40 +803,33 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev) { struct hdac_hdmi_priv *hdmi = edev->private_data; - struct hdac_hdmi_dai_pin_map *dai_map = &hdmi->dai_map[0]; + struct hdac_hdmi_dai_pin_map *dai_map; struct hdac_hdmi_cvt *cvt; - struct hdac_hdmi_pin *pin; + int dai_id = 0; - if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list)) + if (list_empty(&hdmi->cvt_list)) return -EINVAL; - /* - * Currently on board only 1 pin and 1 converter is enabled for - * simplification, more will be added eventually - * So using fixed map for dai_id:pin:cvt - */ - cvt = list_first_entry(&hdmi->cvt_list, struct hdac_hdmi_cvt, head); - pin = list_first_entry(&hdmi->pin_list, struct hdac_hdmi_pin, head); - - dai_map->dai_id = 0; - dai_map->pin = pin; - - dai_map->cvt = cvt; + list_for_each_entry(cvt, &hdmi->cvt_list, head) { + dai_map = &hdmi->dai_map[dai_id]; + dai_map->dai_id = dai_id; + dai_map->cvt = cvt; - /* Enable out path for this pin widget */ - snd_hdac_codec_write(&edev->hdac, pin->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + /* Enable transmission */ + snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, + AC_VERB_SET_DIGI_CONVERT_1, 1); - /* Enable transmission */ - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, - AC_VERB_SET_DIGI_CONVERT_1, 1); + /* Category Code (CC) to zero */ + snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, + AC_VERB_SET_DIGI_CONVERT_2, 0); - /* Category Code (CC) to zero */ - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, - AC_VERB_SET_DIGI_CONVERT_2, 0); + dai_id++; - snd_hdac_codec_write(&edev->hdac, pin->nid, 0, - AC_VERB_SET_CONNECT_SEL, 0); + if (dai_id == HDA_MAX_CVTS) { + dev_warn(&edev->hdac.dev, "Max dais supported: %d\n", dai_id); + break; + } + } return 0; }