From patchwork Fri Sep 9 11:46:59 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: mengdong.lin@linux.intel.com X-Patchwork-Id: 9323755 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id C556E60752 for ; Fri, 9 Sep 2016 15:14:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B59452870B for ; Fri, 9 Sep 2016 15:14:44 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A9E3029F4A; Fri, 9 Sep 2016 15:14:44 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 85B0F2870B for ; Fri, 9 Sep 2016 15:14:43 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id 0EBB4266CAB; Fri, 9 Sep 2016 17:14:42 +0200 (CEST) Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id D02A7266C99; Fri, 9 Sep 2016 17:12:23 +0200 (CEST) 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 15146266492; Fri, 9 Sep 2016 13:40:54 +0200 (CEST) Received: from mga02.intel.com (mga02.intel.com [134.134.136.20]) by alsa0.perex.cz (Postfix) with ESMTP id AFD2226646C for ; Fri, 9 Sep 2016 13:40:50 +0200 (CEST) Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by orsmga101.jf.intel.com with ESMTP; 09 Sep 2016 04:40:50 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.30,304,1470726000"; d="scan'208";a="758788215" Received: from amanda-haswell-pc.sh.intel.com ([10.239.159.21]) by FMSMGA003.fm.intel.com with ESMTP; 09 Sep 2016 04:40:47 -0700 From: mengdong.lin@linux.intel.com To: alsa-devel@alsa-project.org, broonie@kernel.org Date: Fri, 9 Sep 2016 19:46:59 +0800 Message-Id: X-Mailer: git-send-email 2.5.0 In-Reply-To: References: Cc: Mengdong Lin , tiwai@suse.de, hardik.t.shah@intel.com, guneshwor.o.singh@intel.com, liam.r.girdwood@linux.intel.com, vinod.koul@intel.com, rakesh.a.ughreja@intel.com, mengdong.lin@intel.com Subject: [alsa-devel] [PATCH v2 11/11] ASoC: topology: Able to create BE DAI links 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 From: Mengdong Lin Topology will check with ASoC if a BE (Backend) link already exists by checking its ID. If the BE link doesn't exist, topology will create it and the link_load ops and card's add_dai_link will be called for machine specific init. The reason we need topology to create BE DAI links is same as that for BE DAIs: Due to the SoC pin count limitation, not all the interface pins are taken out of the SoC to connect various peripherals. Sometimes BIOS does have enough info about the physical pin output types and their connections to peripherals while we want to create BE DAI links based on what is actually used on a platform. So we choose topology to give us such info and help driver to create BE links dynamically. Signed-off-by: Mengdong Lin diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 17e0b74..3f71f1d 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -49,9 +49,10 @@ #define SOC_TPLG_PASS_GRAPH 5 #define SOC_TPLG_PASS_PINS 6 #define SOC_TPLG_PASS_BE_DAI 7 +#define SOC_TPLG_PASS_LINK 8 #define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST -#define SOC_TPLG_PASS_END SOC_TPLG_PASS_BE_DAI +#define SOC_TPLG_PASS_END SOC_TPLG_PASS_LINK struct soc_tplg { const struct firmware *fw; @@ -493,6 +494,7 @@ static void remove_link(struct snd_soc_component *comp, list_del(&dobj->list); snd_soc_remove_dai_link(comp->card, link); + kfree(link->codecs); kfree(link); } @@ -1631,6 +1633,29 @@ static void set_link_flags(struct snd_soc_dai_link *link, if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_IGNORE_POWERDOWN_TIME) link->ignore_pmdown_time = flags & SND_SOC_TPLG_LNK_FLGBIT_IGNORE_POWERDOWN_TIME ? 1 : 0; + + if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES) + link->symmetric_rates = + flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES ? 1 : 0; + + if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS) + link->symmetric_channels = + flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS ? + 1 : 0; + + if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS) + link->symmetric_samplebits = + flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS ? + 1 : 0; + + if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_DPCM_PLAYBACK) + link->dpcm_playback = + flags & SND_SOC_TPLG_LNK_FLGBIT_DPCM_PLAYBACK ? 1 : 0; + + if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_DPCM_CAPTURE) + link->dpcm_capture = + flags & SND_SOC_TPLG_LNK_FLGBIT_DPCM_CAPTURE ? 1 : 0; + } /* create the FE DAI link */ @@ -1862,6 +1887,189 @@ static int soc_tplg_be_dai_elems_load(struct soc_tplg *tplg, return 0; } +/* * + * set_be_link_hw_format - Set the HW audio format of the BE DAI link. + * @tplg: topology context + * @cfg: topology BE DAI link configs. + * + * Topology context contains a list of supported HW formats (configs) and + * a default format ID for the BE link. This function will use this default ID + * to choose the HW format to set the link's DAI format for init. + */ +static void set_be_link_hw_format(struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg) +{ + struct snd_soc_tplg_hw_config *hw_config; + unsigned char bclk_master, fsync_master; + unsigned char invert_bclk, invert_fsync; + int i; + + for (i = 0; i < cfg->num_hw_configs; i++) { + hw_config = &cfg->hw_config[i]; + if (hw_config->id != cfg->default_hw_config_id) + continue; + + link->dai_fmt = hw_config->fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + /* clock signal polarity */ + invert_bclk = hw_config->invert_bclk; + invert_fsync = hw_config->invert_fsync; + if (!invert_bclk && !invert_fsync) + link->dai_fmt |= SND_SOC_DAIFMT_NB_NF; + else if (!invert_bclk && invert_fsync) + link->dai_fmt |= SND_SOC_DAIFMT_NB_IF; + else if (invert_bclk && !invert_fsync) + link->dai_fmt |= SND_SOC_DAIFMT_IB_NF; + else + link->dai_fmt |= SND_SOC_DAIFMT_IB_IF; + + /* clock masters */ + bclk_master = hw_config->bclk_master; + fsync_master = hw_config->fsync_master; + if (!bclk_master && !fsync_master) + link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + else if (bclk_master && !fsync_master) + link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; + else if (!bclk_master && fsync_master) + link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; + else + link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + + return; + } +} + +/* create a BE DAI link */ +static int soc_tplg_be_link_create(struct soc_tplg *tplg, + struct snd_soc_tplg_link_config *cfg) +{ + struct snd_soc_dai_link *link; + int i, ret = 0; + + dev_dbg(tplg->dev, + "Adding BE link %s, stream name %s\n", + cfg->name, cfg->stream_name); + + link = kzalloc(sizeof(struct snd_soc_dai_link), GFP_KERNEL); + if (link == NULL) + return -ENOMEM; + + /* id and names */ + if (strlen(cfg->name)) + link->name = cfg->name; + if (strlen(cfg->stream_name)) + link->stream_name = cfg->stream_name; + link->id = cfg->id; + + /* components */ + if (strlen(cfg->cpu.name)) + link->cpu_name = cfg->cpu.name; + link->cpu_dai_name = cfg->cpu.dai_name; + + if (!cfg->num_codecs || !cfg->codecs) { + ret = -EINVAL; + goto err; + } + + link->num_codecs = cfg->num_codecs; + link->codecs = kcalloc(link->num_codecs, + sizeof(struct snd_soc_dai_link_component), + GFP_KERNEL); + if (!link->codecs) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < link->num_codecs ; i++) { + if (strlen(cfg->codecs[i].name)) + link->codecs[i].name = cfg->codecs[i].name; + link->codecs[i].dai_name = cfg->codecs[i].dai_name; + } + + /* hw format */ + if (cfg->num_hw_configs) + set_be_link_hw_format(link, cfg); + + /* flags */ + link->no_pcm = 1; + if (cfg->flag_mask) + set_link_flags(link, cfg->flag_mask, cfg->flags); + + /* pass control to component driver for optional further init */ + ret = soc_tplg_dai_link_load(tplg, link); + if (ret < 0) { + dev_err(tplg->dev, "ASoC: BE link loading failed\n"); + goto err; + } + + link->dobj.index = tplg->index; + link->dobj.ops = tplg->ops; + link->dobj.type = SND_SOC_DOBJ_DAI_LINK; + list_add(&link->dobj.list, &tplg->comp->dobj_list); + + snd_soc_add_dai_link(tplg->comp->card, link); + return 0; + +err: + if (link) { + kfree(link->codecs); + kfree(link->params); + kfree(link); + } + + return ret; +} + +/* Config an existing or create a new BE DAI link */ +static int soc_tplg_be_link_config(struct soc_tplg *tplg, + struct snd_soc_tplg_link_config *cfg) +{ + struct snd_soc_dai_link *link; + + link = snd_soc_find_dai_link(tplg->comp->card, cfg->id); + if (!link) + return soc_tplg_be_link_create(tplg, cfg); + + /* TODO: configure existing BE links. No user request atm. */ + return 0; +} + + +/* Load BE link elements from the topology context */ +static int soc_tplg_be_link_elems_load(struct soc_tplg *tplg, + struct snd_soc_tplg_hdr *hdr) +{ + struct snd_soc_tplg_link_config *link; + int count = hdr->count; + int i; + + if (tplg->pass != SOC_TPLG_PASS_LINK) { + tplg->pos += hdr->size + hdr->payload_size; + return 0; + }; + + if (soc_tplg_check_elem_count(tplg, + sizeof(struct snd_soc_tplg_link_config), count, + hdr->payload_size, "BE DAI link")) { + dev_err(tplg->dev, "ASoC: invalid count %d for BE DAI link elems\n", + count); + return -EINVAL; + } + + /* config or create the BE DAI links */ + for (i = 0; i < count; i++) { + link = (struct snd_soc_tplg_link_config *)tplg->pos; + if (link->size != sizeof(*link)) { + dev_err(tplg->dev, "ASoC: invalid link config size\n"); + return -EINVAL; + } + + soc_tplg_be_link_config(tplg, link); + tplg->pos += (sizeof(*link) + link->priv.size); + } + + return 0; +} static int soc_tplg_manifest_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) @@ -1968,6 +2176,8 @@ static int soc_tplg_load_header(struct soc_tplg *tplg, return soc_tplg_pcm_elems_load(tplg, hdr); case SND_SOC_TPLG_TYPE_BE_DAI: return soc_tplg_be_dai_elems_load(tplg, hdr); + case SND_SOC_TPLG_TYPE_BACKEND_LINK: + return soc_tplg_be_link_elems_load(tplg, hdr); case SND_SOC_TPLG_TYPE_MANIFEST: return soc_tplg_manifest_load(tplg, hdr); default: