From 43ac946922b337507c4131c45bf339ddcd7e7402 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Fri, 18 Sep 2015 17:18:48 +0800 Subject: [PATCH 01/28] ASoC: imx-spdif: add snd_soc_pm_ops for spdif machine driver Add snd_soc_pm_ops in machine driver to make the trigger suspend/resume be called in suspend/resume. Signed-off-by: Zidan Wang Signed-off-by: Mark Brown --- sound/soc/fsl/imx-spdif.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c index 33da26a12457..a407e833c612 100644 --- a/sound/soc/fsl/imx-spdif.c +++ b/sound/soc/fsl/imx-spdif.c @@ -89,6 +89,7 @@ MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids); static struct platform_driver imx_spdif_driver = { .driver = { .name = "imx-spdif", + .pm = &snd_soc_pm_ops, .of_match_table = imx_spdif_dt_ids, }, .probe = imx_spdif_audio_probe, From b5e5a4549c8b88a880fa3866fa3803ea9396ba03 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 5 Oct 2015 14:53:50 +0300 Subject: [PATCH 02/28] ASoC: Intel: use dw_dmac autoconfiguration Instead of hardconding a platform data for dw_dmac let's use it's own autoconfiguration feature. Thus, remove hardcoded values. Acked-by: Liam Girdwood Cc: Mark Brown Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-firmware.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c index ebcca6dc48d1..1636a1eeb002 100644 --- a/sound/soc/intel/common/sst-firmware.c +++ b/sound/soc/intel/common/sst-firmware.c @@ -26,7 +26,6 @@ #include /* supported DMA engine drivers */ -#include #include #include @@ -169,12 +168,6 @@ err: return ret; } -static struct dw_dma_platform_data dw_pdata = { - .is_private = 1, - .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, - .chan_priority = CHAN_PRIORITY_ASCENDING, -}; - static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem, int irq) { @@ -195,7 +188,8 @@ static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem, return ERR_PTR(err); chip->dev = dev; - err = dw_dma_probe(chip, &dw_pdata); + + err = dw_dma_probe(chip, NULL); if (err) return ERR_PTR(err); From e4e2d2f45284f620eeb9eea25570838b2a42c968 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 7 Oct 2015 11:31:52 +0100 Subject: [PATCH 03/28] ASoC: Intel: Skylake: Add pipe and modules handlers SKL driver needs to instantiate pipelines and modules in the DSP. The topology in the DSP is modelled as DAPM graph with a PGA representing a module instance and mixer representing a pipeline for a group of modules along with the mixer itself. Here we start adding building block for handling these. We add resource checks (memory/compute) for pipelines, find the modules in a pipeline, init modules in a pipe and lastly bind/unbind modules in a pipe These will be used by pipe event handlers in subsequent patches Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/Makefile | 3 +- sound/soc/intel/skylake/skl-topology.c | 187 +++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 10 ++ sound/soc/intel/skylake/skl.h | 11 ++ 4 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/skylake/skl-topology.c diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index 27db22178204..914b6dab9bea 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -1,4 +1,5 @@ -snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o +snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o \ +skl-topology.o obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c new file mode 100644 index 000000000000..b0ee1fe05274 --- /dev/null +++ b/sound/soc/intel/skylake/skl-topology.c @@ -0,0 +1,187 @@ +/* + * skl-topology.c - Implements Platform component ALSA controls/widget + * handlers. + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Jeeja KP + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include "skl-sst-dsp.h" +#include "skl-sst-ipc.h" +#include "skl-topology.h" +#include "skl.h" +#include "skl-tplg-interface.h" + +/* + * SKL DSP driver modelling uses only few DAPM widgets so for rest we will + * ignore. This helpers checks if the SKL driver handles this widget type + */ +static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w) +{ + switch (w->id) { + case snd_soc_dapm_dai_link: + case snd_soc_dapm_dai_in: + case snd_soc_dapm_aif_in: + case snd_soc_dapm_aif_out: + case snd_soc_dapm_dai_out: + case snd_soc_dapm_switch: + return false; + default: + return true; + } +} + +/* + * Each pipelines needs memory to be allocated. Check if we have free memory + * from available pool. Then only add this to pool + * This is freed when pipe is deleted + * Note: DSP does actual memory management we only keep track for complete + * pool + */ +static bool skl_tplg_alloc_pipe_mem(struct skl *skl, + struct skl_module_cfg *mconfig) +{ + struct skl_sst *ctx = skl->skl_sst; + + if (skl->resource.mem + mconfig->pipe->memory_pages > + skl->resource.max_mem) { + dev_err(ctx->dev, + "%s: module_id %d instance %d\n", __func__, + mconfig->id.module_id, + mconfig->id.instance_id); + dev_err(ctx->dev, + "exceeds ppl memory available %d mem %d\n", + skl->resource.max_mem, skl->resource.mem); + return false; + } + + skl->resource.mem += mconfig->pipe->memory_pages; + return true; +} + +/* + * Pipeline needs needs DSP CPU resources for computation, this is + * quantified in MCPS (Million Clocks Per Second) required for module/pipe + * + * Each pipelines needs mcps to be allocated. Check if we have mcps for this + * pipe. This adds the mcps to driver counter + * This is removed on pipeline delete + */ +static bool skl_tplg_alloc_pipe_mcps(struct skl *skl, + struct skl_module_cfg *mconfig) +{ + struct skl_sst *ctx = skl->skl_sst; + + if (skl->resource.mcps + mconfig->mcps > skl->resource.max_mcps) { + dev_err(ctx->dev, + "%s: module_id %d instance %d\n", __func__, + mconfig->id.module_id, mconfig->id.instance_id); + dev_err(ctx->dev, + "exceeds ppl memory available %d > mem %d\n", + skl->resource.max_mcps, skl->resource.mcps); + return false; + } + + skl->resource.mcps += mconfig->mcps; + return true; +} + +/* + * Free the mcps when tearing down + */ +static void +skl_tplg_free_pipe_mcps(struct skl *skl, struct skl_module_cfg *mconfig) +{ + skl->resource.mcps -= mconfig->mcps; +} + +/* + * Free the memory when tearing down + */ +static void +skl_tplg_free_pipe_mem(struct skl *skl, struct skl_module_cfg *mconfig) +{ + skl->resource.mem -= mconfig->pipe->memory_pages; +} + +/* + * A pipe can have multiple modules, each of them will be a DAPM widget as + * well. While managing a pipeline we need to get the list of all the + * widgets in a pipelines, so this helper - skl_tplg_get_pipe_widget() helps + * to get the SKL type widgets in that pipeline + */ +static int skl_tplg_alloc_pipe_widget(struct device *dev, + struct snd_soc_dapm_widget *w, struct skl_pipe *pipe) +{ + struct skl_module_cfg *src_module = NULL; + struct snd_soc_dapm_path *p = NULL; + struct skl_pipe_module *p_module = NULL; + + p_module = devm_kzalloc(dev, sizeof(*p_module), GFP_KERNEL); + if (!p_module) + return -ENOMEM; + + p_module->w = w; + list_add_tail(&p_module->node, &pipe->w_list); + + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if ((p->sink->priv == NULL) + && (!is_skl_dsp_widget_type(w))) + continue; + + if ((p->sink->priv != NULL) && p->connect + && is_skl_dsp_widget_type(p->sink)) { + + src_module = p->sink->priv; + if (pipe->ppl_id == src_module->pipe->ppl_id) + skl_tplg_alloc_pipe_widget(dev, + p->sink, pipe); + } + } + return 0; +} + +/* + * Inside a pipe instance, we can have various modules. These modules need + * to instantiated in DSP by invoking INIT_MODULE IPC, which is achieved by + * skl_init_module() routine, so invoke that for all modules in a pipeline + */ +static int +skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) +{ + struct skl_pipe_module *w_module; + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mconfig; + struct skl_sst *ctx = skl->skl_sst; + int ret = 0; + + list_for_each_entry(w_module, &pipe->w_list, node) { + w = w_module->w; + mconfig = w->priv; + + /* check resource available */ + if (!skl_tplg_alloc_pipe_mcps(skl, mconfig)) + return -ENOMEM; + + ret = skl_init_module(ctx, mconfig, NULL); + if (ret < 0) + return ret; + } + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 8c7767baa94f..73d7916ee33e 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -263,6 +263,16 @@ struct skl_module_cfg { struct skl_specific_cfg formats_config; }; +struct skl_pipeline { + struct skl_pipe *pipe; + struct list_head node; +}; + +struct skl_dapm_path_list { + struct snd_soc_dapm_path *dapm_path; + struct list_head node; +}; + int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index f7fdbb02947f..e980d7897642 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -48,6 +48,13 @@ #define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094 #define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20 +struct skl_dsp_resource { + u32 max_mcps; + u32 max_mem; + u32 mcps; + u32 mem; +}; + struct skl { struct hdac_ext_bus ebus; struct pci_dev *pci; @@ -57,6 +64,10 @@ struct skl { void __iomem *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ + + struct skl_dsp_resource resource; + struct list_head ppl_list; + struct list_head dapm_path_list; }; #define skl_to_ebus(s) (&(s)->ebus) From f7590d4f1565b0d609d25113c37b9bea8a13e9f7 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 7 Oct 2015 11:31:53 +0100 Subject: [PATCH 04/28] ASoC: Intel: Skylake: Add module configuration helpers To configure a module, driver needs to send input and output PCM params for a module in DSP. The FE PCM params come from hw_params ie from user, for a BE they also come from hw_params but from BE-link fixups. So based on PCM params required driver has to find a converter module (src/updown/format) and then do the conversion and calculate PCM params in these pipelines In this patch we add the helper modules which allow driver to do these calculations. Signed-off-by: Hardik T Shah Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 140 +++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index b0ee1fe05274..56baf9d88f28 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -27,6 +27,10 @@ #include "skl.h" #include "skl-tplg-interface.h" +#define SKL_CH_FIXUP_MASK (1 << 0) +#define SKL_RATE_FIXUP_MASK (1 << 1) +#define SKL_FMT_FIXUP_MASK (1 << 2) + /* * SKL DSP driver modelling uses only few DAPM widgets so for rest we will * ignore. This helpers checks if the SKL driver handles this widget type @@ -119,6 +123,137 @@ skl_tplg_free_pipe_mem(struct skl *skl, struct skl_module_cfg *mconfig) skl->resource.mem -= mconfig->pipe->memory_pages; } + +static void skl_dump_mconfig(struct skl_sst *ctx, + struct skl_module_cfg *mcfg) +{ + dev_dbg(ctx->dev, "Dumping config\n"); + dev_dbg(ctx->dev, "Input Format:\n"); + dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt.channels); + dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt.s_freq); + dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt.ch_cfg); + dev_dbg(ctx->dev, "valid bit depth = %d\n", + mcfg->in_fmt.valid_bit_depth); + dev_dbg(ctx->dev, "Output Format:\n"); + dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt.channels); + dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt.s_freq); + dev_dbg(ctx->dev, "valid bit depth = %d\n", + mcfg->out_fmt.valid_bit_depth); + dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt.ch_cfg); +} + +static void skl_tplg_update_params(struct skl_module_fmt *fmt, + struct skl_pipe_params *params, int fixup) +{ + if (fixup & SKL_RATE_FIXUP_MASK) + fmt->s_freq = params->s_freq; + if (fixup & SKL_CH_FIXUP_MASK) + fmt->channels = params->ch; + if (fixup & SKL_FMT_FIXUP_MASK) + fmt->valid_bit_depth = params->s_fmt; +} + +/* + * A pipeline may have modules which impact the pcm parameters, like SRC, + * channel converter, format converter. + * We need to calculate the output params by applying the 'fixup' + * Topology will tell driver which type of fixup is to be applied by + * supplying the fixup mask, so based on that we calculate the output + * + * Now In FE the pcm hw_params is source/target format. Same is applicable + * for BE with its hw_params invoked. + * here based on FE, BE pipeline and direction we calculate the input and + * outfix and then apply that for a module + */ +static void skl_tplg_update_params_fixup(struct skl_module_cfg *m_cfg, + struct skl_pipe_params *params, bool is_fe) +{ + int in_fixup, out_fixup; + struct skl_module_fmt *in_fmt, *out_fmt; + + in_fmt = &m_cfg->in_fmt; + out_fmt = &m_cfg->out_fmt; + + if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (is_fe) { + in_fixup = m_cfg->params_fixup; + out_fixup = (~m_cfg->converter) & + m_cfg->params_fixup; + } else { + out_fixup = m_cfg->params_fixup; + in_fixup = (~m_cfg->converter) & + m_cfg->params_fixup; + } + } else { + if (is_fe) { + out_fixup = m_cfg->params_fixup; + in_fixup = (~m_cfg->converter) & + m_cfg->params_fixup; + } else { + in_fixup = m_cfg->params_fixup; + out_fixup = (~m_cfg->converter) & + m_cfg->params_fixup; + } + } + + skl_tplg_update_params(in_fmt, params, in_fixup); + skl_tplg_update_params(out_fmt, params, out_fixup); +} + +/* + * A module needs input and output buffers, which are dependent upon pcm + * params, so once we have calculate params, we need buffer calculation as + * well. + */ +static void skl_tplg_update_buffer_size(struct skl_sst *ctx, + struct skl_module_cfg *mcfg) +{ + int multiplier = 1; + + if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT) + multiplier = 5; + + mcfg->ibs = (mcfg->in_fmt.s_freq / 1000) * + (mcfg->in_fmt.channels) * + (mcfg->in_fmt.bit_depth >> 3) * + multiplier; + + mcfg->obs = (mcfg->out_fmt.s_freq / 1000) * + (mcfg->out_fmt.channels) * + (mcfg->out_fmt.bit_depth >> 3) * + multiplier; +} + +static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx) +{ + struct skl_module_cfg *m_cfg = w->priv; + struct skl_pipe_params *params = m_cfg->pipe->p_params; + int p_conn_type = m_cfg->pipe->conn_type; + bool is_fe; + + if (!m_cfg->params_fixup) + return; + + dev_dbg(ctx->dev, "Mconfig for widget=%s BEFORE updation\n", + w->name); + + skl_dump_mconfig(ctx, m_cfg); + + if (p_conn_type == SKL_PIPE_CONN_TYPE_FE) + is_fe = true; + else + is_fe = false; + + skl_tplg_update_params_fixup(m_cfg, params, is_fe); + skl_tplg_update_buffer_size(ctx, m_cfg); + + dev_dbg(ctx->dev, "Mconfig for widget=%s AFTER updation\n", + w->name); + + skl_dump_mconfig(ctx, m_cfg); +} + /* * A pipe can have multiple modules, each of them will be a DAPM widget as * well. While managing a pipeline we need to get the list of all the @@ -178,6 +313,11 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) if (!skl_tplg_alloc_pipe_mcps(skl, mconfig)) return -ENOMEM; + /* + * apply fix/conversion to module params based on + * FE/BE params + */ + skl_tplg_update_module_params(w, ctx); ret = skl_init_module(ctx, mconfig, NULL); if (ret < 0) return ret; From d93f8e550f44ed1f54285f77f40f7962a3f25267 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 7 Oct 2015 11:31:54 +0100 Subject: [PATCH 05/28] ASoC: Intel: Skylake: add DSP platform widget event handlers The Skylake driver topology model tries to model the firmware rule for pipeline and module creation. The creation rule is: - Create Pipe - Add modules to Pipe - Connect the modules (bind) - Start the pipes Similarly destroy rule is: - Stop the pipe - Disconnect it (unbind) - Delete the pipe In driver we use Mixer, as there will always be ONE mixer in a pipeline to model a pipe. The modules in pipe are modelled as PGA widgets. The DAPM sequencing rules (mixer and then PGA) are used to create the sequence DSP expects as depicted above, and then widget handlers for PMU and PMD events help in that. This patch adds widget event handlers for PRE/POST PMU and PRE/POST PMD event for mixer and pga modules. These event handlers invoke pipeline creation, destroy, module creation, module bind, unbind and pipeline bind unbind Event handler sequencing is implement to target the DSP FW sequence expectations to enable path from source to sink pipe for Playback/Capture. Signed-off-by: Jeeja KP Signed-off-by: Hardik T Shah Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 448 +++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 7 + 2 files changed, 455 insertions(+) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 56baf9d88f28..6d1d29aa933a 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -325,3 +325,451 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) return 0; } + +/* + * Mixer module represents a pipeline. So in the Pre-PMU event of mixer we + * need create the pipeline. So we do following: + * - check the resources + * - Create the pipeline + * - Initialize the modules in pipeline + * - finally bind all modules together + */ +static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + int ret; + struct skl_module_cfg *mconfig = w->priv; + struct skl_pipe_module *w_module; + struct skl_pipe *s_pipe = mconfig->pipe; + struct skl_module_cfg *src_module = NULL, *dst_module; + struct skl_sst *ctx = skl->skl_sst; + + /* check resource available */ + if (!skl_tplg_alloc_pipe_mcps(skl, mconfig)) + return -EBUSY; + + if (!skl_tplg_alloc_pipe_mem(skl, mconfig)) + return -ENOMEM; + + /* + * Create a list of modules for pipe. + * This list contains modules from source to sink + */ + ret = skl_create_pipeline(ctx, mconfig->pipe); + if (ret < 0) + return ret; + + /* + * we create a w_list of all widgets in that pipe. This list is not + * freed on PMD event as widgets within a pipe are static. This + * saves us cycles to get widgets in pipe every time. + * + * So if we have already initialized all the widgets of a pipeline + * we skip, so check for list_empty and create the list if empty + */ + if (list_empty(&s_pipe->w_list)) { + ret = skl_tplg_alloc_pipe_widget(ctx->dev, w, s_pipe); + if (ret < 0) + return ret; + } + + /* Init all pipe modules from source to sink */ + ret = skl_tplg_init_pipe_modules(skl, s_pipe); + if (ret < 0) + return ret; + + /* Bind modules from source to sink */ + list_for_each_entry(w_module, &s_pipe->w_list, node) { + dst_module = w_module->w->priv; + + if (src_module == NULL) { + src_module = dst_module; + continue; + } + + ret = skl_bind_modules(ctx, src_module, dst_module); + if (ret < 0) + return ret; + + src_module = dst_module; + } + + return 0; +} + +/* + * A PGA represents a module in a pipeline. So in the Pre-PMU event of PGA + * we need to do following: + * - Bind to sink pipeline + * Since the sink pipes can be running and we don't get mixer event on + * connect for already running mixer, we need to find the sink pipes + * here and bind to them. This way dynamic connect works. + * - Start sink pipeline, if not running + * - Then run current pipe + */ +static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct snd_soc_dapm_path *p; + struct skl_dapm_path_list *path_list; + struct snd_soc_dapm_widget *source, *sink; + struct skl_module_cfg *src_mconfig, *sink_mconfig; + struct skl_sst *ctx = skl->skl_sst; + int ret = 0; + + source = w; + src_mconfig = source->priv; + + /* + * find which sink it is connected to, bind with the sink, + * if sink is not started, start sink pipe first, then start + * this pipe + */ + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (!p->connect) + continue; + + dev_dbg(ctx->dev, "%s: src widget=%s\n", __func__, w->name); + dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name); + + /* + * here we will check widgets in sink pipelines, so that + * can be any widgets type and we are only interested if + * they are ones used for SKL so check that first + */ + if ((p->sink->priv != NULL) && + is_skl_dsp_widget_type(p->sink)) { + + sink = p->sink; + src_mconfig = source->priv; + sink_mconfig = sink->priv; + + /* Bind source to sink, mixin is always source */ + ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig); + if (ret) + return ret; + + /* Start sinks pipe first */ + if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) { + ret = skl_run_pipe(ctx, sink_mconfig->pipe); + if (ret) + return ret; + } + + path_list = kzalloc( + sizeof(struct skl_dapm_path_list), + GFP_KERNEL); + if (path_list == NULL) + return -ENOMEM; + + /* Add connected path to one global list */ + path_list->dapm_path = p; + list_add_tail(&path_list->node, &skl->dapm_path_list); + break; + } + } + + /* Start source pipe last after starting all sinks */ + ret = skl_run_pipe(ctx, src_mconfig->pipe); + if (ret) + return ret; + + return 0; +} + +/* + * in the Post-PMU event of mixer we need to do following: + * - Check if this pipe is running + * - if not, then + * - bind this pipeline to its source pipeline + * if source pipe is already running, this means it is a dynamic + * connection and we need to bind only to that pipe + * - start this pipeline + */ +static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + int ret = 0; + struct snd_soc_dapm_path *p; + struct snd_soc_dapm_widget *source, *sink; + struct skl_module_cfg *src_mconfig, *sink_mconfig; + struct skl_sst *ctx = skl->skl_sst; + int src_pipe_started = 0; + + sink = w; + sink_mconfig = sink->priv; + + /* + * If source pipe is already started, that means source is driving + * one more sink before this sink got connected, Since source is + * started, bind this sink to source and start this pipe. + */ + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (!p->connect) + continue; + + dev_dbg(ctx->dev, "sink widget=%s\n", w->name); + dev_dbg(ctx->dev, "src widget=%s\n", p->source->name); + + /* + * here we will check widgets in sink pipelines, so that + * can be any widgets type and we are only interested if + * they are ones used for SKL so check that first + */ + if ((p->source->priv != NULL) && + is_skl_dsp_widget_type(p->source)) { + source = p->source; + src_mconfig = source->priv; + sink_mconfig = sink->priv; + src_pipe_started = 1; + + /* + * check pipe state, then no need to bind or start + * the pipe + */ + if (src_mconfig->pipe->state != SKL_PIPE_STARTED) + src_pipe_started = 0; + } + } + + if (src_pipe_started) { + ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig); + if (ret) + return ret; + + ret = skl_run_pipe(ctx, sink_mconfig->pipe); + } + + return ret; +} + +/* + * in the Pre-PMD event of mixer we need to do following: + * - Stop the pipe + * - find the source connections and remove that from dapm_path_list + * - unbind with source pipelines if still connected + */ +static int skl_tplg_mixer_dapm_pre_pmd_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct snd_soc_dapm_widget *source, *sink; + struct skl_module_cfg *src_mconfig, *sink_mconfig; + int ret = 0, path_found = 0; + struct skl_dapm_path_list *path_list, *tmp_list; + struct skl_sst *ctx = skl->skl_sst; + + sink = w; + sink_mconfig = sink->priv; + + /* Stop the pipe */ + ret = skl_stop_pipe(ctx, sink_mconfig->pipe); + if (ret) + return ret; + + /* + * This list, dapm_path_list handling here does not need any locks + * as we are under dapm lock while handling widget events. + * List can be manipulated safely only under dapm widgets handler + * routines + */ + list_for_each_entry_safe(path_list, tmp_list, + &skl->dapm_path_list, node) { + if (path_list->dapm_path->sink == sink) { + dev_dbg(ctx->dev, "Path found = %s\n", + path_list->dapm_path->name); + source = path_list->dapm_path->source; + src_mconfig = source->priv; + path_found = 1; + + list_del(&path_list->node); + kfree(path_list); + break; + } + } + + /* + * If path_found == 1, that means pmd for source pipe has + * not occurred, source is connected to some other sink. + * so its responsibility of sink to unbind itself from source. + */ + if (path_found) { + ret = skl_stop_pipe(ctx, src_mconfig->pipe); + if (ret < 0) + return ret; + + ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig); + } + + return ret; +} + +/* + * in the Post-PMD event of mixer we need to do following: + * - Free the mcps used + * - Free the mem used + * - Unbind the modules within the pipeline + * - Delete the pipeline (modules are not required to be explicitly + * deleted, pipeline delete is enough here + */ +static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct skl_module_cfg *mconfig = w->priv; + struct skl_pipe_module *w_module; + struct skl_module_cfg *src_module = NULL, *dst_module; + struct skl_sst *ctx = skl->skl_sst; + struct skl_pipe *s_pipe = mconfig->pipe; + int ret = 0; + + skl_tplg_free_pipe_mcps(skl, mconfig); + + list_for_each_entry(w_module, &s_pipe->w_list, node) { + dst_module = w_module->w->priv; + + if (src_module == NULL) { + src_module = dst_module; + continue; + } + + ret = skl_unbind_modules(ctx, src_module, dst_module); + if (ret < 0) + return ret; + + src_module = dst_module; + } + + ret = skl_delete_pipe(ctx, mconfig->pipe); + skl_tplg_free_pipe_mem(skl, mconfig); + + return ret; +} + +/* + * in the Post-PMD event of PGA we need to do following: + * - Free the mcps used + * - Stop the pipeline + * - In source pipe is connected, unbind with source pipelines + */ +static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct snd_soc_dapm_widget *source, *sink; + struct skl_module_cfg *src_mconfig, *sink_mconfig; + int ret = 0, path_found = 0; + struct skl_dapm_path_list *path_list, *tmp_path_list; + struct skl_sst *ctx = skl->skl_sst; + + source = w; + src_mconfig = source->priv; + + skl_tplg_free_pipe_mcps(skl, src_mconfig); + /* Stop the pipe since this is a mixin module */ + ret = skl_stop_pipe(ctx, src_mconfig->pipe); + if (ret) + return ret; + + list_for_each_entry_safe(path_list, tmp_path_list, &skl->dapm_path_list, node) { + if (path_list->dapm_path->source == source) { + dev_dbg(ctx->dev, "Path found = %s\n", + path_list->dapm_path->name); + sink = path_list->dapm_path->sink; + sink_mconfig = sink->priv; + path_found = 1; + + list_del(&path_list->node); + kfree(path_list); + break; + } + } + + /* + * This is a connector and if path is found that means + * unbind between source and sink has not happened yet + */ + if (path_found) { + ret = skl_stop_pipe(ctx, src_mconfig->pipe); + if (ret < 0) + return ret; + + ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig); + } + + return ret; +} + +/* + * In modelling, we assume there will be ONLY one mixer in a pipeline. If + * mixer is not required then it is treated as static mixer aka vmixer with + * a hard path to source module + * So we don't need to check if source is started or not as hard path puts + * dependency on each other + */ +static int skl_tplg_vmixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct skl *skl = get_skl_ctx(dapm->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return skl_tplg_mixer_dapm_pre_pmu_event(w, skl); + + case SND_SOC_DAPM_POST_PMD: + return skl_tplg_mixer_dapm_post_pmd_event(w, skl); + } + + return 0; +} + +/* + * In modelling, we assume there will be ONLY one mixer in a pipeline. If a + * second one is required that is created as another pipe entity. + * The mixer is responsible for pipe management and represent a pipeline + * instance + */ +static int skl_tplg_mixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct skl *skl = get_skl_ctx(dapm->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return skl_tplg_mixer_dapm_pre_pmu_event(w, skl); + + case SND_SOC_DAPM_POST_PMU: + return skl_tplg_mixer_dapm_post_pmu_event(w, skl); + + case SND_SOC_DAPM_PRE_PMD: + return skl_tplg_mixer_dapm_pre_pmd_event(w, skl); + + case SND_SOC_DAPM_POST_PMD: + return skl_tplg_mixer_dapm_post_pmd_event(w, skl); + } + + return 0; +} + +/* + * In modelling, we assumed rest of the modules in pipeline are PGA. But we + * are interested in last PGA (leaf PGA) in a pipeline to disconnect with + * the sink when it is running (two FE to one BE or one FE to two BE) + * scenarios + */ +static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) + +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct skl *skl = get_skl_ctx(dapm->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return skl_tplg_pga_dapm_pre_pmu_event(w, skl); + + case SND_SOC_DAPM_POST_PMD: + return skl_tplg_pga_dapm_post_pmd_event(w, skl); + } + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 73d7916ee33e..b3a22a6f8d27 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -273,6 +273,13 @@ struct skl_dapm_path_list { struct list_head node; }; +static inline struct skl *get_skl_ctx(struct device *dev) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + + return ebus_to_skl(ebus); +} + int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); From cfb0a87383c6c2827f108ecee3471ef981876b38 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 7 Oct 2015 11:31:55 +0100 Subject: [PATCH 06/28] ASoC: Intel: Skylake: Add FE and BE hw_params handling For FE and BE, the PCM parameters come from FE and BE hw_params values passed. For a FE we convert the FE params to DSP expected module format and pass to DSP. For a BE we need to find the gateway settings (i2s/PDM) to be applied. These are queried from NHLT table and applied. Further for BE based on direction the settings are applied as either source or destination parameters. These helpers here allow the format to be calculated and queried as per firmware format. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 238 +++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 12 ++ 2 files changed, 250 insertions(+) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 6d1d29aa933a..827f21db0eb2 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -773,3 +773,241 @@ static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w, return 0; } + +/* + * The FE params are passed by hw_params of the DAI. + * On hw_params, the params are stored in Gateway module of the FE and we + * need to calculate the format in DSP module configuration, that + * conversion is done here + */ +int skl_tplg_update_pipe_params(struct device *dev, + struct skl_module_cfg *mconfig, + struct skl_pipe_params *params) +{ + struct skl_pipe *pipe = mconfig->pipe; + struct skl_module_fmt *format = NULL; + + memcpy(pipe->p_params, params, sizeof(*params)); + + if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) + format = &mconfig->in_fmt; + else + format = &mconfig->out_fmt; + + /* set the hw_params */ + format->s_freq = params->s_freq; + format->channels = params->ch; + format->valid_bit_depth = skl_get_bit_depth(params->s_fmt); + + /* + * 16 bit is 16 bit container whereas 24 bit is in 32 bit + * container so update bit depth accordingly + */ + switch (format->valid_bit_depth) { + case SKL_DEPTH_16BIT: + format->bit_depth = format->valid_bit_depth; + break; + + case SKL_DEPTH_24BIT: + format->bit_depth = SKL_DEPTH_32BIT; + break; + + default: + dev_err(dev, "Invalid bit depth %x for pipe\n", + format->valid_bit_depth); + return -EINVAL; + } + + if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) { + mconfig->ibs = (format->s_freq / 1000) * + (format->channels) * + (format->bit_depth >> 3); + } else { + mconfig->obs = (format->s_freq / 1000) * + (format->channels) * + (format->bit_depth >> 3); + } + + return 0; +} + +/* + * Query the module config for the FE DAI + * This is used to find the hw_params set for that DAI and apply to FE + * pipeline + */ +struct skl_module_cfg * +skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) +{ + struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_path *p = NULL; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + w = dai->playback_widget; + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (p->connect && p->sink->power && + is_skl_dsp_widget_type(p->sink)) + continue; + + if (p->sink->priv) { + dev_dbg(dai->dev, "set params for %s\n", + p->sink->name); + return p->sink->priv; + } + } + } else { + w = dai->capture_widget; + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (p->connect && p->source->power && + is_skl_dsp_widget_type(p->source)) + continue; + + if (p->source->priv) { + dev_dbg(dai->dev, "set params for %s\n", + p->source->name); + return p->source->priv; + } + } + } + + return NULL; +} + +static u8 skl_tplg_be_link_type(int dev_type) +{ + int ret; + + switch (dev_type) { + case SKL_DEVICE_BT: + ret = NHLT_LINK_SSP; + break; + + case SKL_DEVICE_DMIC: + ret = NHLT_LINK_DMIC; + break; + + case SKL_DEVICE_I2S: + ret = NHLT_LINK_SSP; + break; + + case SKL_DEVICE_HDALINK: + ret = NHLT_LINK_HDA; + break; + + default: + ret = NHLT_LINK_INVALID; + break; + } + + return ret; +} + +/* + * Fill the BE gateway parameters + * The BE gateway expects a blob of parameters which are kept in the ACPI + * NHLT blob, so query the blob for interface type (i2s/pdm) and instance. + * The port can have multiple settings so pick based on the PCM + * parameters + */ +static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, + struct skl_module_cfg *mconfig, + struct skl_pipe_params *params) +{ + struct skl_pipe *pipe = mconfig->pipe; + struct nhlt_specific_cfg *cfg; + struct skl *skl = get_skl_ctx(dai->dev); + int link_type = skl_tplg_be_link_type(mconfig->dev_type); + + memcpy(pipe->p_params, params, sizeof(*params)); + + /* update the blob based on virtual bus_id*/ + cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type, + params->s_fmt, params->ch, + params->s_freq, params->stream); + if (cfg) { + mconfig->formats_config.caps_size = cfg->size; + memcpy(mconfig->formats_config.caps, &cfg->caps, cfg->size); + } else { + dev_err(dai->dev, "Blob NULL for id %x type %d dirn %d\n", + mconfig->vbus_id, link_type, + params->stream); + dev_err(dai->dev, "PCM: ch %d, freq %d, fmt %d\n", + params->ch, params->s_freq, params->s_fmt); + return -EINVAL; + } + + return 0; +} + +static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai, + struct snd_soc_dapm_widget *w, + struct skl_pipe_params *params) +{ + struct snd_soc_dapm_path *p; + + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (p->connect && is_skl_dsp_widget_type(p->source) && + p->source->priv) { + + if (!p->source->power) + return skl_tplg_be_fill_pipe_params( + dai, p->source->priv, + params); + else + return -EBUSY; + } else { + return skl_tplg_be_set_src_pipe_params( + dai, p->source, params); + } + } + + return -EIO; +} + +static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai, + struct snd_soc_dapm_widget *w, struct skl_pipe_params *params) +{ + struct snd_soc_dapm_path *p = NULL; + + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (p->connect && is_skl_dsp_widget_type(p->sink) && + p->sink->priv) { + + if (!p->sink->power) + return skl_tplg_be_fill_pipe_params( + dai, p->sink->priv, params); + else + return -EBUSY; + + } else { + return skl_tplg_be_set_sink_pipe_params( + dai, p->sink, params); + } + } + + return -EIO; +} + +/* + * BE hw_params can be a source parameters (capture) or sink parameters + * (playback). Based on sink and source we need to either find the source + * list or the sink list and set the pipeline parameters + */ +int skl_tplg_be_update_params(struct snd_soc_dai *dai, + struct skl_pipe_params *params) +{ + struct snd_soc_dapm_widget *w; + + if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) { + w = dai->playback_widget; + + return skl_tplg_be_set_src_pipe_params(dai, w, params); + + } else { + w = dai->capture_widget; + + return skl_tplg_be_set_sink_pipe_params(dai, w, params); + } + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index b3a22a6f8d27..12db94d238f2 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -280,6 +280,18 @@ static inline struct skl *get_skl_ctx(struct device *dev) return ebus_to_skl(ebus); } +int skl_tplg_be_update_params(struct snd_soc_dai *dai, + struct skl_pipe_params *params); +void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai, + struct skl_pipe_params *params, int stream); +int skl_tplg_init(struct snd_soc_platform *platform, + struct hdac_ext_bus *ebus); +struct skl_module_cfg *skl_tplg_fe_get_cpr_module( + struct snd_soc_dai *dai, int stream); +int skl_tplg_update_pipe_params(struct device *dev, + struct skl_module_cfg *mconfig, struct skl_pipe_params *params); + + int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); From 3af36706ff6c4ea8695e92b1ba80e183f1919684 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 7 Oct 2015 11:31:56 +0100 Subject: [PATCH 07/28] ASoC: Intel: Skylake: Add topology core init and handlers The SKL driver does not code DSP topology in driver. It uses the newly added ASoC topology core to parse the topology information (controls, widgets and map) from topology binary. Each topology element passed private data which contains information that driver used to identify the module instance within firmware and send IPCs for that module to DSP firmware along with parameters. This patch adds init routine to invoke topology load and callback for topology creation. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 1 + sound/soc/intel/skylake/skl-topology.c | 218 +++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 1 - sound/soc/intel/skylake/skl-tplg-interface.h | 80 +++++++ 4 files changed, 299 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 05fde5e6e257..664df1f44530 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -138,4 +138,5 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH config SND_SOC_INTEL_SKYLAKE tristate select SND_HDA_EXT_CORE + select SND_SOC_TOPOLOGY select SND_SOC_INTEL_SST diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 827f21db0eb2..648bbbfdd472 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1011,3 +1011,221 @@ int skl_tplg_be_update_params(struct snd_soc_dai *dai, return 0; } + +static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = { + {SKL_MIXER_EVENT, skl_tplg_mixer_event}, + {SKL_VMIXER_EVENT, skl_tplg_vmixer_event}, + {SKL_PGA_EVENT, skl_tplg_pga_event}, +}; + +/* + * The topology binary passes the pin info for a module so initialize the pin + * info passed into module instance + */ +static void skl_fill_module_pin_info(struct device *dev, + struct skl_module_pin *m_pin, + int max_pin) +{ + int i; + + for (i = 0; i < max_pin; i++) { + m_pin[i].id.module_id = 0; + m_pin[i].id.instance_id = 0; + m_pin[i].in_use = false; + m_pin[i].is_dynamic = true; + m_pin[i].pin_index = i; + } +} + +/* + * Add pipeline from topology binary into driver pipeline list + * + * If already added we return that instance + * Otherwise we create a new instance and add into driver list + */ +static struct skl_pipe *skl_tplg_add_pipe(struct device *dev, + struct skl *skl, struct skl_dfw_pipe *dfw_pipe) +{ + struct skl_pipeline *ppl; + struct skl_pipe *pipe; + struct skl_pipe_params *params; + + list_for_each_entry(ppl, &skl->ppl_list, node) { + if (ppl->pipe->ppl_id == dfw_pipe->pipe_id) + return ppl->pipe; + } + + ppl = devm_kzalloc(dev, sizeof(*ppl), GFP_KERNEL); + if (!ppl) + return NULL; + + pipe = devm_kzalloc(dev, sizeof(*pipe), GFP_KERNEL); + if (!pipe) + return NULL; + + params = devm_kzalloc(dev, sizeof(*params), GFP_KERNEL); + if (!params) + return NULL; + + pipe->ppl_id = dfw_pipe->pipe_id; + pipe->memory_pages = dfw_pipe->memory_pages; + pipe->pipe_priority = dfw_pipe->pipe_priority; + pipe->conn_type = dfw_pipe->conn_type; + pipe->state = SKL_PIPE_INVALID; + pipe->p_params = params; + INIT_LIST_HEAD(&pipe->w_list); + + ppl->pipe = pipe; + list_add(&ppl->node, &skl->ppl_list); + + return ppl->pipe; +} + +/* + * Topology core widget load callback + * + * This is used to save the private data for each widget which gives + * information to the driver about module and pipeline parameters which DSP + * FW expects like ids, resource values, formats etc + */ +static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tplg_w) +{ + int ret; + struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); + struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl_module_cfg *mconfig; + struct skl_pipe *pipe; + struct skl_dfw_module *dfw_config = (struct skl_dfw_module *)tplg_w->priv.data; + + if (!tplg_w->priv.size) + goto bind_event; + + mconfig = devm_kzalloc(bus->dev, sizeof(*mconfig), GFP_KERNEL); + + if (!mconfig) + return -ENOMEM; + + w->priv = mconfig; + mconfig->id.module_id = dfw_config->module_id; + mconfig->id.instance_id = dfw_config->instance_id; + mconfig->mcps = dfw_config->max_mcps; + mconfig->ibs = dfw_config->ibs; + mconfig->obs = dfw_config->obs; + mconfig->core_id = dfw_config->core_id; + mconfig->max_in_queue = dfw_config->max_in_queue; + mconfig->max_out_queue = dfw_config->max_out_queue; + mconfig->is_loadable = dfw_config->is_loadable; + mconfig->in_fmt.channels = dfw_config->in_fmt.channels; + mconfig->in_fmt.s_freq = dfw_config->in_fmt.freq; + mconfig->in_fmt.bit_depth = dfw_config->in_fmt.bit_depth; + mconfig->in_fmt.valid_bit_depth = dfw_config->in_fmt.valid_bit_depth; + mconfig->in_fmt.ch_cfg = dfw_config->in_fmt.ch_cfg; + mconfig->out_fmt.channels = dfw_config->out_fmt.channels; + mconfig->out_fmt.s_freq = dfw_config->out_fmt.freq; + mconfig->out_fmt.bit_depth = dfw_config->out_fmt.bit_depth; + mconfig->out_fmt.valid_bit_depth = dfw_config->out_fmt.valid_bit_depth; + mconfig->out_fmt.ch_cfg = dfw_config->out_fmt.ch_cfg; + mconfig->params_fixup = dfw_config->params_fixup; + mconfig->converter = dfw_config->converter; + mconfig->m_type = dfw_config->module_type; + mconfig->vbus_id = dfw_config->vbus_id; + + pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe); + if (pipe) + mconfig->pipe = pipe; + + mconfig->dev_type = dfw_config->dev_type; + mconfig->hw_conn_type = dfw_config->hw_conn_type; + mconfig->time_slot = dfw_config->time_slot; + mconfig->formats_config.caps_size = dfw_config->caps.caps_size; + + mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * + sizeof(*mconfig->m_in_pin), + GFP_KERNEL); + if (!mconfig->m_in_pin) + return -ENOMEM; + + mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * + sizeof(*mconfig->m_out_pin), + GFP_KERNEL); + if (!mconfig->m_out_pin) + return -ENOMEM; + + skl_fill_module_pin_info(bus->dev, mconfig->m_in_pin, + mconfig->max_in_queue); + skl_fill_module_pin_info(bus->dev, mconfig->m_out_pin, + mconfig->max_out_queue); + + if (mconfig->formats_config.caps_size == 0) + goto bind_event; + + mconfig->formats_config.caps = (u32 *)devm_kzalloc(bus->dev, + mconfig->formats_config.caps_size, GFP_KERNEL); + + if (mconfig->formats_config.caps == NULL) + return -ENOMEM; + + memcpy(mconfig->formats_config.caps, dfw_config->caps.caps, + dfw_config->caps.caps_size); + +bind_event: + if (tplg_w->event_type == 0) { + dev_info(bus->dev, "ASoC: No event handler required\n"); + return 0; + } + + ret = snd_soc_tplg_widget_bind_event(w, skl_tplg_widget_ops, + ARRAY_SIZE(skl_tplg_widget_ops), tplg_w->event_type); + + if (ret) { + dev_err(bus->dev, "%s: No matching event handlers found for %d\n", + __func__, tplg_w->event_type); + return -EINVAL; + } + + return 0; +} + +static struct snd_soc_tplg_ops skl_tplg_ops = { + .widget_load = skl_tplg_widget_load, +}; + +/* This will be read from topology manifest, currently defined here */ +#define SKL_MAX_MCPS 30000000 +#define SKL_FW_MAX_MEM 1000000 + +/* + * SKL topology init routine + */ +int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) +{ + int ret; + const struct firmware *fw; + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = ebus_to_skl(ebus); + + ret = request_firmware(&fw, "dfw_sst.bin", bus->dev); + if (ret < 0) { + dev_err(bus->dev, "config firmware %s request failed with %d\n", + "dfw_sst.bin", ret); + return ret; + } + + /* + * The complete tplg for SKL is loaded as index 0, we don't use + * any other index + */ + ret = snd_soc_tplg_component_load(&platform->component, &skl_tplg_ops, fw, 0); + if (ret < 0) { + dev_err(bus->dev, "tplg component load failed%d\n", ret); + return -EINVAL; + } + + skl->resource.max_mcps = SKL_MAX_MCPS; + skl->resource.max_mem = SKL_FW_MAX_MEM; + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 12db94d238f2..0c83dc3ea3e2 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -291,7 +291,6 @@ struct skl_module_cfg *skl_tplg_fe_get_cpr_module( int skl_tplg_update_pipe_params(struct device *dev, struct skl_module_cfg *mconfig, struct skl_pipe_params *params); - int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index a50689825bca..d14f7fe64aa8 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -19,6 +19,29 @@ #ifndef __HDA_TPLG_INTERFACE_H__ #define __HDA_TPLG_INTERFACE_H__ +/* + * Default types range from 0~12. type can range from 0 to 0xff + * SST types start at higher to avoid any overlapping in future + */ +#define SOC_CONTROL_TYPE_HDA_SST_ALGO_PARAMS 0x100 +#define SOC_CONTROL_TYPE_HDA_SST_MUX 0x101 +#define SOC_CONTROL_TYPE_HDA_SST_MIX 0x101 +#define SOC_CONTROL_TYPE_HDA_SST_BYTE 0x103 + +#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ +#define MAX_IN_QUEUE 8 +#define MAX_OUT_QUEUE 8 + +/* Event types goes here */ +/* Reserve event type 0 for no event handlers */ +enum skl_event_types { + SKL_EVENT_NONE = 0, + SKL_MIXER_EVENT, + SKL_MUX_EVENT, + SKL_VMIXER_EVENT, + SKL_PGA_EVENT +}; + /** * enum skl_ch_cfg - channel configuration * @@ -85,4 +108,61 @@ enum skl_dev_type { SKL_DEVICE_HDALINK = 0x4, SKL_DEVICE_NONE }; + +struct skl_dfw_module_pin { + u16 module_id; + u16 instance_id; + u8 pin_id; + bool is_dynamic; +} __packed; + +struct skl_dfw_module_fmt { + u32 channels; + u32 freq; + u32 bit_depth; + u32 valid_bit_depth; + u32 ch_cfg; +} __packed; + +struct skl_dfw_module_caps { + u32 caps_size; + u32 caps[HDA_SST_CFG_MAX]; +}; + +struct skl_dfw_pipe { + u8 pipe_id; + u8 pipe_priority; + u16 conn_type; + u32 memory_pages; +} __packed; + +struct skl_dfw_module { + u16 module_id; + u16 instance_id; + u32 max_mcps; + u8 core_id; + u8 max_in_queue; + u8 max_out_queue; + u8 is_loadable; + u8 conn_type; + u8 dev_type; + u8 hw_conn_type; + u8 time_slot; + u32 obs; + u32 ibs; + u32 params_fixup; + u32 converter; + u32 module_type; + u32 vbus_id; + struct skl_dfw_pipe pipe; + struct skl_dfw_module_fmt in_fmt; + struct skl_dfw_module_fmt out_fmt; + struct skl_dfw_module_caps caps; +} __packed; + +struct skl_dfw_algo_data { + u32 max; + char *params; +} __packed; + #endif From b663a8c5c9c043c47e5e0eea9f9c77ea88fbe67a Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 7 Oct 2015 11:31:57 +0100 Subject: [PATCH 08/28] ASoC: Intel: Skylake: Initialize and load DSP controls Initialize and creates DSP controls if processing pipe capability is supported by HW. Updates the dma_id, hw_params to module param to be used when DSP module has to be configured. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-pcm.c | 141 ++++++++++++++++++++----- sound/soc/intel/skylake/skl-topology.c | 39 ++++--- 2 files changed, 136 insertions(+), 44 deletions(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index bea26730873c..a8f53da11ae3 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -24,6 +24,7 @@ #include #include #include "skl.h" +#include "skl-topology.h" #define HDA_MONO 1 #define HDA_STEREO 2 @@ -214,6 +215,8 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream, struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); struct snd_pcm_runtime *runtime = substream->runtime; + struct skl_pipe_params p_params = {0}; + struct skl_module_cfg *m_cfg; int ret, dma_id; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); @@ -228,6 +231,16 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream, dma_id = hdac_stream(stream)->stream_tag - 1; dev_dbg(dai->dev, "dma_id=%d\n", dma_id); + p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.ch = params_channels(params); + p_params.s_freq = params_rate(params); + p_params.host_dma_id = dma_id; + p_params.stream = substream->stream; + + m_cfg = skl_tplg_fe_get_cpr_module(dai, p_params.stream); + if (m_cfg) + skl_tplg_update_pipe_params(dai->dev, m_cfg, &p_params); + return 0; } @@ -268,6 +281,46 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream, return skl_substream_free_pages(ebus_to_hbus(ebus), substream); } +static int skl_be_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct skl_pipe_params p_params = {0}; + + p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.ch = params_channels(params); + p_params.s_freq = params_rate(params); + p_params.stream = substream->stream; + skl_tplg_be_update_params(dai, &p_params); + + return 0; +} + +static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_sst *ctx = skl->skl_sst; + struct skl_module_cfg *mconfig; + + mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); + if (!mconfig) + return -EIO; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + return skl_run_pipe(ctx, mconfig->pipe); + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + return skl_stop_pipe(ctx, mconfig->pipe); + + default: + return 0; + } +} + static int skl_link_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -277,9 +330,8 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct skl_dma_params *dma_params; struct snd_soc_dai *codec_dai = rtd->codec_dai; - int dma_id; + struct skl_pipe_params p_params = {0}; - pr_debug("%s\n", __func__); link_dev = snd_hdac_ext_stream_assign(ebus, substream, HDAC_EXT_STREAM_TYPE_LINK); if (!link_dev) @@ -293,7 +345,14 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, if (dma_params) dma_params->stream_tag = hdac_stream(link_dev)->stream_tag; snd_soc_dai_set_dma_data(codec_dai, substream, (void *)dma_params); - dma_id = hdac_stream(link_dev)->stream_tag - 1; + + p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.ch = params_channels(params); + p_params.s_freq = params_rate(params); + p_params.stream = substream->stream; + p_params.link_dma_id = hdac_stream(link_dev)->stream_tag - 1; + + skl_tplg_be_update_params(dai, &p_params); return 0; } @@ -308,27 +367,12 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream, unsigned int format_val = 0; struct skl_dma_params *dma_params; struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_pcm_hw_params *params; - struct snd_interval *channels, *rate; struct hdac_ext_link *link; - dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); if (link_dev->link_prepared) { dev_dbg(dai->dev, "already stream is prepared - returning\n"); return 0; } - params = devm_kzalloc(dai->dev, sizeof(*params), GFP_KERNEL); - if (params == NULL) - return -ENOMEM; - - channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - channels->min = channels->max = substream->runtime->channels; - rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - rate->min = rate->max = substream->runtime->rate; - snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - - SNDRV_PCM_HW_PARAM_FIRST_MASK], - substream->runtime->format); - dma_params = (struct skl_dma_params *) snd_soc_dai_get_dma_data(codec_dai, substream); @@ -399,13 +443,13 @@ static int skl_link_hw_free(struct snd_pcm_substream *substream, return 0; } -static int skl_hda_be_startup(struct snd_pcm_substream *substream, +static int skl_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { return pm_runtime_get_sync(dai->dev); } -static void skl_hda_be_shutdown(struct snd_pcm_substream *substream, +static void skl_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { pm_runtime_mark_last_busy(dai->dev); @@ -418,20 +462,28 @@ static struct snd_soc_dai_ops skl_pcm_dai_ops = { .prepare = skl_pcm_prepare, .hw_params = skl_pcm_hw_params, .hw_free = skl_pcm_hw_free, + .trigger = skl_pcm_trigger, }; static struct snd_soc_dai_ops skl_dmic_dai_ops = { - .startup = skl_hda_be_startup, - .shutdown = skl_hda_be_shutdown, + .startup = skl_be_startup, + .hw_params = skl_be_hw_params, + .shutdown = skl_be_shutdown, +}; + +static struct snd_soc_dai_ops skl_be_ssp_dai_ops = { + .startup = skl_be_startup, + .hw_params = skl_be_hw_params, + .shutdown = skl_be_shutdown, }; static struct snd_soc_dai_ops skl_link_dai_ops = { - .startup = skl_hda_be_startup, + .startup = skl_be_startup, .prepare = skl_link_pcm_prepare, .hw_params = skl_link_hw_params, .hw_free = skl_link_hw_free, .trigger = skl_link_pcm_trigger, - .shutdown = skl_hda_be_shutdown, + .shutdown = skl_be_shutdown, }; static struct snd_soc_dai_driver skl_platform_dai[] = { @@ -487,6 +539,24 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }, /* BE CPU Dais */ +{ + .name = "SSP0 Pin", + .ops = &skl_be_ssp_dai_ops, + .playback = { + .stream_name = "ssp0 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp0 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, { .name = "iDisp Pin", .ops = &skl_link_dai_ops, @@ -544,7 +614,7 @@ static int skl_platform_open(struct snd_pcm_substream *substream) return 0; } -static int skl_pcm_trigger(struct snd_pcm_substream *substream, +static int skl_coupled_trigger(struct snd_pcm_substream *substream, int cmd) { struct hdac_ext_bus *ebus = get_bus_ctx(substream); @@ -618,7 +688,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, return 0; } -static int skl_dsp_trigger(struct snd_pcm_substream *substream, +static int skl_decoupled_trigger(struct snd_pcm_substream *substream, int cmd) { struct hdac_ext_bus *ebus = get_bus_ctx(substream); @@ -675,9 +745,9 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, struct hdac_ext_bus *ebus = get_bus_ctx(substream); if (ebus->ppcap) - return skl_dsp_trigger(substream, cmd); + return skl_decoupled_trigger(substream, cmd); else - return skl_pcm_trigger(substream, cmd); + return skl_coupled_trigger(substream, cmd); } /* calculate runtime delay from LPIB */ @@ -844,7 +914,17 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd) return retval; } +static int skl_platform_soc_probe(struct snd_soc_platform *platform) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev); + + if (ebus->ppcap) + return skl_tplg_init(platform, ebus); + + return 0; +} static struct snd_soc_platform_driver skl_platform_drv = { + .probe = skl_platform_soc_probe, .ops = &skl_platform_ops, .pcm_new = skl_pcm_new, .pcm_free = skl_pcm_free, @@ -857,6 +937,11 @@ static const struct snd_soc_component_driver skl_component = { int skl_platform_register(struct device *dev) { int ret; + struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct skl *skl = ebus_to_skl(ebus); + + INIT_LIST_HEAD(&skl->ppl_list); + INIT_LIST_HEAD(&skl->dapm_path_list); ret = snd_soc_register_platform(dev, &skl_platform_drv); if (ret) { diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 648bbbfdd472..b1bffa52cbcd 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1089,8 +1089,8 @@ static struct skl_pipe *skl_tplg_add_pipe(struct device *dev, * FW expects like ids, resource values, formats etc */ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, - struct snd_soc_dapm_widget *w, - struct snd_soc_tplg_dapm_widget *tplg_w) + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tplg_w) { int ret; struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); @@ -1098,7 +1098,8 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, struct hdac_bus *bus = ebus_to_hbus(ebus); struct skl_module_cfg *mconfig; struct skl_pipe *pipe; - struct skl_dfw_module *dfw_config = (struct skl_dfw_module *)tplg_w->priv.data; + struct skl_dfw_module *dfw_config = + (struct skl_dfw_module *)tplg_w->priv.data; if (!tplg_w->priv.size) goto bind_event; @@ -1121,12 +1122,14 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, mconfig->in_fmt.channels = dfw_config->in_fmt.channels; mconfig->in_fmt.s_freq = dfw_config->in_fmt.freq; mconfig->in_fmt.bit_depth = dfw_config->in_fmt.bit_depth; - mconfig->in_fmt.valid_bit_depth = dfw_config->in_fmt.valid_bit_depth; + mconfig->in_fmt.valid_bit_depth = + dfw_config->in_fmt.valid_bit_depth; mconfig->in_fmt.ch_cfg = dfw_config->in_fmt.ch_cfg; mconfig->out_fmt.channels = dfw_config->out_fmt.channels; mconfig->out_fmt.s_freq = dfw_config->out_fmt.freq; mconfig->out_fmt.bit_depth = dfw_config->out_fmt.bit_depth; - mconfig->out_fmt.valid_bit_depth = dfw_config->out_fmt.valid_bit_depth; + mconfig->out_fmt.valid_bit_depth = + dfw_config->out_fmt.valid_bit_depth; mconfig->out_fmt.ch_cfg = dfw_config->out_fmt.ch_cfg; mconfig->params_fixup = dfw_config->params_fixup; mconfig->converter = dfw_config->converter; @@ -1142,15 +1145,17 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, mconfig->time_slot = dfw_config->time_slot; mconfig->formats_config.caps_size = dfw_config->caps.caps_size; - mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * - sizeof(*mconfig->m_in_pin), - GFP_KERNEL); + mconfig->m_in_pin = devm_kzalloc(bus->dev, + (mconfig->max_in_queue) * + sizeof(*mconfig->m_in_pin), + GFP_KERNEL); if (!mconfig->m_in_pin) return -ENOMEM; - mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * - sizeof(*mconfig->m_out_pin), - GFP_KERNEL); + mconfig->m_out_pin = devm_kzalloc(bus->dev, + (mconfig->max_in_queue) * + sizeof(*mconfig->m_out_pin), + GFP_KERNEL); if (!mconfig->m_out_pin) return -ENOMEM; @@ -1163,13 +1168,13 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, goto bind_event; mconfig->formats_config.caps = (u32 *)devm_kzalloc(bus->dev, - mconfig->formats_config.caps_size, GFP_KERNEL); + mconfig->formats_config.caps_size, GFP_KERNEL); if (mconfig->formats_config.caps == NULL) return -ENOMEM; memcpy(mconfig->formats_config.caps, dfw_config->caps.caps, - dfw_config->caps.caps_size); + dfw_config->caps.caps_size); bind_event: if (tplg_w->event_type == 0) { @@ -1178,7 +1183,8 @@ bind_event: } ret = snd_soc_tplg_widget_bind_event(w, skl_tplg_widget_ops, - ARRAY_SIZE(skl_tplg_widget_ops), tplg_w->event_type); + ARRAY_SIZE(skl_tplg_widget_ops), + tplg_w->event_type); if (ret) { dev_err(bus->dev, "%s: No matching event handlers found for %d\n", @@ -1209,7 +1215,7 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) ret = request_firmware(&fw, "dfw_sst.bin", bus->dev); if (ret < 0) { - dev_err(bus->dev, "config firmware %s request failed with %d\n", + dev_err(bus->dev, "tplg fw %s load failed with %d\n", "dfw_sst.bin", ret); return ret; } @@ -1218,7 +1224,8 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) * The complete tplg for SKL is loaded as index 0, we don't use * any other index */ - ret = snd_soc_tplg_component_load(&platform->component, &skl_tplg_ops, fw, 0); + ret = snd_soc_tplg_component_load(&platform->component, + &skl_tplg_ops, fw, 0); if (ret < 0) { dev_err(bus->dev, "tplg component load failed%d\n", ret); return -EINVAL; From 2a29b200c6f17827ee712e719ad29366f79205ee Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 7 Oct 2015 11:31:58 +0100 Subject: [PATCH 09/28] ASoC: Intel: Skylake: Add DSP support and enable it If processing pipe capability is supported, add DSP support. Adds initialization/free/suspend/resume DSP functionality. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 348d094e81d6..2f1890e703c6 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -166,11 +166,16 @@ static int skl_runtime_suspend(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct hdac_ext_bus *ebus = pci_get_drvdata(pci); struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = ebus_to_skl(ebus); + int ret; dev_dbg(bus->dev, "in %s\n", __func__); /* enable controller wake up event */ snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK); + ret = skl_suspend_dsp(skl); + if (ret < 0) + return ret; snd_hdac_bus_stop_chip(bus); snd_hdac_bus_enter_link_reset(bus); @@ -183,7 +188,7 @@ static int skl_runtime_resume(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct hdac_ext_bus *ebus = pci_get_drvdata(pci); struct hdac_bus *bus = ebus_to_hbus(ebus); - struct skl *hda = ebus_to_skl(ebus); + struct skl *skl = ebus_to_skl(ebus); int status; dev_dbg(bus->dev, "in %s\n", __func__); @@ -191,12 +196,12 @@ static int skl_runtime_resume(struct device *dev) /* Read STATESTS before controller reset */ status = snd_hdac_chip_readw(bus, STATESTS); - skl_init_pci(hda); + skl_init_pci(skl); snd_hdac_bus_init_chip(bus, true); /* disable controller Wake Up event */ snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0); - return 0; + return skl_resume_dsp(skl); } #endif /* CONFIG_PM */ @@ -457,17 +462,19 @@ static int skl_probe(struct pci_dev *pci, /* check if dsp is there */ if (ebus->ppcap) { - /* TODO register with dsp IPC */ - dev_dbg(bus->dev, "Register dsp\n"); + err = skl_init_dsp(skl); + if (err < 0) { + dev_dbg(bus->dev, "error failed to register dsp\n"); + goto out_free; + } } - if (ebus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(ebus); /* create device for soc dmic */ err = skl_dmic_device_register(skl); if (err < 0) - goto out_free; + goto out_dsp_free; /* register platform dai and controls */ err = skl_platform_register(bus->dev); @@ -491,6 +498,8 @@ out_unregister: skl_platform_unregister(bus->dev); out_dmic_free: skl_dmic_device_unregister(skl); +out_dsp_free: + skl_free_dsp(skl); out_free: skl->init_failed = 1; skl_free(ebus); @@ -507,6 +516,7 @@ static void skl_remove(struct pci_dev *pci) pm_runtime_get_noresume(&pci->dev); pci_dev_put(pci); skl_platform_unregister(&pci->dev); + skl_free_dsp(skl); skl_dmic_device_unregister(skl); skl_free(ebus); dev_set_drvdata(&pci->dev, NULL); From 87b2bdf02278e5623cc66e5189792fde01f829a3 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 7 Oct 2015 11:31:59 +0100 Subject: [PATCH 10/28] ASoC: Intel: Skylake: Initialize NHLT table Load and Initialize Non HDA Link Table in Skylake driver to get platform configuration. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 2f1890e703c6..ca135b8ab5c0 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -458,6 +458,11 @@ static int skl_probe(struct pci_dev *pci, if (err < 0) goto out_free; + skl->nhlt = skl_nhlt_init(bus->dev); + + if (skl->nhlt == NULL) + goto out_free; + pci_set_drvdata(skl->pci, ebus); /* check if dsp is there */ From 3373f716830f36c0042f5845201caf1d9290f95b Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 7 Oct 2015 16:39:38 +0100 Subject: [PATCH 11/28] ASoC: Intel: Skylake: Modify the log level dev_info is too noisy for tplg wiget loading, so move it to debug level Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index b1bffa52cbcd..a5d8420ce69a 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1178,7 +1178,7 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, bind_event: if (tplg_w->event_type == 0) { - dev_info(bus->dev, "ASoC: No event handler required\n"); + dev_dbg(bus->dev, "ASoC: No event handler required\n"); return 0; } From 6ea8ba33e65d3d284de7e7373939352e2c728f10 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 9 Oct 2015 09:01:48 +0100 Subject: [PATCH 12/28] ASoC: Intel: Skylake: Check CPA bit in DSP core power down Skylake driver will set the SPA bit to 0 to turn off the DSP core. Driver will poll the Current Power Active (CPA) bit to match the Set Power Active (SPA) bit value. When CPA bit matches the value of SPA bit, the achieved power state has reached. In case of DSP power down, register that was polled is SPA instead of CPA. This patch corrects the register to be polled in case of DSP power down. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-sst-dsp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 94875b008b0b..194bd0036454 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -175,7 +175,7 @@ static int skl_dsp_core_power_down(struct sst_dsp *ctx) /* poll with timeout to check if operation successful */ return sst_dsp_register_poll(ctx, SKL_ADSP_REG_ADSPCS, - SKL_ADSPCS_SPA_MASK, + SKL_ADSPCS_CPA_MASK, 0, SKL_DSP_PD_TO, "Power down"); From def656fe22abb4fbf174a982dcef1d40274ddb11 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 9 Oct 2015 09:01:49 +0100 Subject: [PATCH 13/28] ASoC: Intel: Skylake: Verify the status bit before handling interrupt Like we have in legacy mode HDA driver, we need to check the status bit and handle interrupt only when it is not zero or all bits set. We typically see the status as all 1's when controller resumes from suspend, So add the check here as well and don't handle for these cases. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-sst-dsp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 194bd0036454..1bfb7f63b572 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -262,6 +262,11 @@ irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id) val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPIS); ctx->intr_status = val; + if (val == 0xffffffff) { + spin_unlock(&ctx->spinlock); + return IRQ_NONE; + } + if (val & SKL_ADSPIS_IPC) { skl_ipc_int_disable(ctx); result = IRQ_WAKE_THREAD; From 84c9e2836aa7c87b19a24de091c7e7cf16124645 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 9 Oct 2015 09:01:50 +0100 Subject: [PATCH 14/28] ASoC: Intel: Skylake: Correct the runtime handler behaviour On runtime pm resume, we need to download the firmware, also on suspend we need to ensure all the interrupts from controller and DSP are disabled. Also since we download the firmware on resume, we don't need to do so on init, so remove that bit Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-dsp-priv.h | 1 + sound/soc/intel/skylake/skl-sst-ipc.c | 12 ++++++ sound/soc/intel/skylake/skl-sst-ipc.h | 1 + sound/soc/intel/skylake/skl-sst.c | 56 ++++++++++++++------------- 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index cbd568eac033..2151652d37b7 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -314,6 +314,7 @@ struct sst_dsp { int sst_state; struct skl_cl_dev cl_dev; u32 intr_status; + const struct firmware *fw; }; /* Size optimised DRAM/IRAM memcpy */ diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 937a0a3a63a0..3345ea0d4414 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -464,6 +464,18 @@ void skl_ipc_op_int_enable(struct sst_dsp *ctx) SKL_ADSP_REG_HIPCCTL_BUSY, SKL_ADSP_REG_HIPCCTL_BUSY); } +void skl_ipc_op_int_disable(struct sst_dsp *ctx) +{ + /* disable IPC DONE interrupt */ + sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL, + SKL_ADSP_REG_HIPCCTL_DONE, 0); + + /* Disable IPC BUSY interrupt */ + sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL, + SKL_ADSP_REG_HIPCCTL_BUSY, 0); + +} + bool skl_ipc_int_status(struct sst_dsp *ctx) { return sst_dsp_shim_read_unlocked(ctx, diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 9f5f67202858..f1a154e45dc3 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -116,6 +116,7 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, void skl_ipc_int_enable(struct sst_dsp *dsp); void skl_ipc_op_int_enable(struct sst_dsp *ctx); +void skl_ipc_op_int_disable(struct sst_dsp *ctx); void skl_ipc_int_disable(struct sst_dsp *dsp); bool skl_ipc_int_status(struct sst_dsp *dsp); diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index c18ea51b7484..3b83dc99f1d4 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -70,15 +70,31 @@ static int skl_transfer_firmware(struct sst_dsp *ctx, static int skl_load_base_firmware(struct sst_dsp *ctx) { int ret = 0, i; - const struct firmware *fw = NULL; struct skl_sst *skl = ctx->thread_context; u32 reg; - ret = request_firmware(&fw, "dsp_fw_release.bin", ctx->dev); + skl->boot_complete = false; + init_waitqueue_head(&skl->boot_wait); + + if (ctx->fw == NULL) { + ret = request_firmware(&ctx->fw, "dsp_fw_release.bin", ctx->dev); + if (ret < 0) { + dev_err(ctx->dev, "Request firmware failed %d\n", ret); + skl_dsp_disable_core(ctx); + return -EIO; + } + } + + ret = skl_dsp_boot(ctx); if (ret < 0) { - dev_err(ctx->dev, "Request firmware failed %d\n", ret); - skl_dsp_disable_core(ctx); - return -EIO; + dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret); + goto skl_load_base_firmware_failed; + } + + ret = skl_cldma_prepare(ctx); + if (ret < 0) { + dev_err(ctx->dev, "CL dma prepare failed : %d", ret); + goto skl_load_base_firmware_failed; } /* enable Interrupt */ @@ -102,7 +118,7 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) goto skl_load_base_firmware_failed; } - ret = skl_transfer_firmware(ctx, fw->data, fw->size); + ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size); if (ret < 0) { dev_err(ctx->dev, "Transfer firmware failed%d\n", ret); goto skl_load_base_firmware_failed; @@ -118,13 +134,12 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) dev_dbg(ctx->dev, "Download firmware successful%d\n", ret); skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); } - release_firmware(fw); - return 0; skl_load_base_firmware_failed: skl_dsp_disable_core(ctx); - release_firmware(fw); + release_firmware(ctx->fw); + ctx->fw = NULL; return ret; } @@ -172,6 +187,12 @@ static int skl_set_dsp_D3(struct sst_dsp *ctx) } skl_dsp_set_state_locked(ctx, SKL_DSP_RESET); + /* disable Interrupt */ + ctx->cl_dev.ops.cl_cleanup_controller(ctx); + skl_cldma_int_disable(ctx); + skl_ipc_op_int_disable(ctx); + skl_ipc_int_disable(ctx); + return ret; } @@ -235,22 +256,6 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, if (ret) return ret; - skl->boot_complete = false; - init_waitqueue_head(&skl->boot_wait); - - ret = skl_dsp_boot(sst); - if (ret < 0) { - dev_err(skl->dev, "Boot dsp core failed ret: %d", ret); - goto free_ipc; - } - - ret = skl_cldma_prepare(sst); - if (ret < 0) { - dev_err(dev, "CL dma prepare failed : %d", ret); - goto free_ipc; - } - - ret = sst->fw_ops.load_fw(sst); if (ret < 0) { dev_err(dev, "Load base fw failed : %d", ret); @@ -262,7 +267,6 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, return 0; -free_ipc: skl_ipc_free(&skl->ipc); return ret; } From 01bb84b5001e4a6221582b163b34e90b3095f451 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 9 Oct 2015 09:01:51 +0100 Subject: [PATCH 15/28] ASoC: Intel: Skylake: power down all link in suspend This ensures that the link is not requesting any clock and the PLL can turn off. The link is powered when controller is brought out of reset. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index ca135b8ab5c0..5319529aedf7 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -173,6 +173,9 @@ static int skl_runtime_suspend(struct device *dev) /* enable controller wake up event */ snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK); + + snd_hdac_ext_bus_link_power_down_all(ebus); + ret = skl_suspend_dsp(skl); if (ret < 0) return ret; From 624729fd51871bfbddb647764f180126789a29ee Mon Sep 17 00:00:00 2001 From: Omair M Abdullah Date: Wed, 14 Oct 2015 21:11:43 +0530 Subject: [PATCH 16/28] ASoC: Intel: Skylake - Add Skylake RT286 I2S machine driver Add the SKL I2S machine driver using Realtek ALC286S codec in I2S mode. Signed-off-by: Omair M Abdullah Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 13 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/skl_rt286.c | 266 +++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+) create mode 100644 sound/soc/intel/boards/skl_rt286.c diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 664df1f44530..6d67357f2d74 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -140,3 +140,16 @@ config SND_SOC_INTEL_SKYLAKE select SND_HDA_EXT_CORE select SND_SOC_TOPOLOGY select SND_SOC_INTEL_SST + +config SND_SOC_INTEL_SKL_RT286_MACH + tristate "ASoC Audio driver for SKL with RT286 I2S mode" + depends on X86 && ACPI + select SND_SOC_INTEL_SST + select SND_SOC_INTEL_SKYLAKE + select SND_SOC_RT286 + select SND_SOC_DMIC + help + This adds support for ASoC machine driver for Skylake platforms + with RT286 I2S audio codec. + Say Y if you have such a device + If unsure select "N". diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index cb94895c9edb..371c4565cad8 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -6,6 +6,7 @@ snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o +snd-soc-skl_rt286-objs := skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -15,3 +16,4 @@ obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o +obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c new file mode 100644 index 000000000000..488c98ee1ded --- /dev/null +++ b/sound/soc/intel/boards/skl_rt286.c @@ -0,0 +1,266 @@ +/* + * Intel Skylake I2S Machine Driver + * + * Copyright (C) 2014-2015, Intel Corporation. All rights reserved. + * + * Modified from: + * Intel Broadwell Wildcatpoint SST Audio + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/rt286.h" + +static struct snd_soc_jack skylake_headset; +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin skylake_headset_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static const struct snd_kcontrol_new skylake_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; + +static const struct snd_soc_dapm_widget skylake_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("DMIC2", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; + +static const struct snd_soc_dapm_route skylake_rt286_map[] = { + /* speaker */ + {"Speaker", NULL, "SPOR"}, + {"Speaker", NULL, "SPOL"}, + + /* HP jack connectors - unknown if we have jack deteck */ + {"Headphone Jack", NULL, "HPO Pin"}, + + /* other jacks */ + {"MIC1", NULL, "Mic Jack"}, + + /* digital mics */ + {"DMIC1 Pin", NULL, "DMIC2"}, + {"DMIC AIF", NULL, "SoC DMIC"}, + + /* CODEC BE connections */ + { "AIF1 Playback", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "codec0_out"}, + { "ssp0 Tx", NULL, "codec1_out"}, + + { "codec0_in", NULL, "ssp0 Rx" }, + { "codec1_in", NULL, "ssp0 Rx" }, + { "ssp0 Rx", NULL, "AIF1 Capture" }, + + { "dmic01_hifi", NULL, "DMIC01 Rx" }, + { "DMIC01 Rx", NULL, "Capture" }, + + { "hif1", NULL, "iDisp Tx"}, + { "iDisp Tx", NULL, "iDisp_out"}, + +}; + +static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + int ret; + + ret = snd_soc_card_jack_new(rtd->card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &skylake_headset, + skylake_headset_pins, ARRAY_SIZE(skylake_headset_pins)); + + if (ret) + return ret; + + rt286_mic_detect(codec, &skylake_headset); + + return 0; +} + + +static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* The output is 48KHz, stereo, 16bits */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); + + return 0; +} + +static int skylake_rt286_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret); + + return ret; +} + +static struct snd_soc_ops skylake_rt286_ops = { + .hw_params = skylake_rt286_hw_params, +}; + +/* skylake digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link skylake_rt286_dais[] = { + /* Front End DAI links */ + { + .name = "Skl Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + }, + { + .name = "Skl Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_capture = 1, + }, + { + .name = "Skl Audio Reference cap", + .stream_name = "refcap", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .be_id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codec_name = "i2c-INT343A:00", + .codec_dai_name = "rt286-aif1", + .init = skylake_rt286_codec_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = skylake_ssp0_fixup, + .ops = &skylake_rt286_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .be_id = 1, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:00:1f.3", + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, +}; + +/* skylake audio machine driver for SPT + RT286S */ +static struct snd_soc_card skylake_rt286 = { + .name = "skylake-rt286", + .owner = THIS_MODULE, + .dai_link = skylake_rt286_dais, + .num_links = ARRAY_SIZE(skylake_rt286_dais), + .controls = skylake_controls, + .num_controls = ARRAY_SIZE(skylake_controls), + .dapm_widgets = skylake_widgets, + .num_dapm_widgets = ARRAY_SIZE(skylake_widgets), + .dapm_routes = skylake_rt286_map, + .num_dapm_routes = ARRAY_SIZE(skylake_rt286_map), + .fully_routed = true, +}; + +static int skylake_audio_probe(struct platform_device *pdev) +{ + skylake_rt286.dev = &pdev->dev; + + return snd_soc_register_card(&skylake_rt286); +} + +static int skylake_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&skylake_rt286); + return 0; +} + +static struct platform_driver skylake_audio = { + .probe = skylake_audio_probe, + .remove = skylake_audio_remove, + .driver = { + .name = "skl_alc286s_i2s", + }, +}; + +module_platform_driver(skylake_audio) + +/* Module information */ +MODULE_AUTHOR("Omair Mohammed Abdullah "); +MODULE_DESCRIPTION("Intel SST Audio for Skylake"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:skl_alc286s_i2s"); From ef3e199a49c8e605e326ae60c5e156bfb1ca7e3d Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Thu, 22 Oct 2015 13:55:07 +0800 Subject: [PATCH 17/28] ASoC: Intel: sst: only use sst-firmware when DW DMAC is available Currentlly, we use Synopsys DesignWare DMA Controller for baytrail/haswell/broadwell ADSP firmware loading, but for skylake, we don't use it, compiling sst-firmware.c may introduce error when CONFIG_DW_DMAC_CORE is not enabled: sound/built-in.o: In function `sst_dma_new': (.text+0xd7b38): undefined reference to `dw_dma_probe' sound/built-in.o: In function `sst_dma_free': (.text+0xd7c0a): undefined reference to `dw_dma_remove' Here we only compile sst-firmware when CONFIG_DW_DMAC_CORE is selected, to fix the linking error issue. Reported-by: Randy Dunlap Signed-off-by: Jie Yang Signed-off-by: Mark Brown --- sound/soc/intel/common/Makefile | 6 +++++- sound/soc/intel/common/sst-dsp.c | 2 ++ sound/soc/intel/common/sst-dsp.h | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index f24154ca4e98..d9105584c51f 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -1,7 +1,11 @@ -snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o +snd-soc-sst-dsp-objs := sst-dsp.o snd-soc-sst-acpi-objs := sst-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o +ifneq ($(CONFIG_DW_DMAC_CORE),) +snd-soc-sst-dsp-objs += sst-firmware.o +endif + obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o diff --git a/sound/soc/intel/common/sst-dsp.c b/sound/soc/intel/common/sst-dsp.c index a627236dd1f5..c9452e02e0dd 100644 --- a/sound/soc/intel/common/sst-dsp.c +++ b/sound/soc/intel/common/sst-dsp.c @@ -420,6 +420,7 @@ void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes) } EXPORT_SYMBOL_GPL(sst_dsp_inbox_read); +#if IS_ENABLED(CONFIG_DW_DMAC_CORE) struct sst_dsp *sst_dsp_new(struct device *dev, struct sst_dsp_device *sst_dev, struct sst_pdata *pdata) { @@ -484,6 +485,7 @@ void sst_dsp_free(struct sst_dsp *sst) sst_dma_free(sst->dma); } EXPORT_SYMBOL_GPL(sst_dsp_free); +#endif /* Module information */ MODULE_AUTHOR("Liam Girdwood"); diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h index 1f45f18715c0..859f0de00339 100644 --- a/sound/soc/intel/common/sst-dsp.h +++ b/sound/soc/intel/common/sst-dsp.h @@ -216,10 +216,12 @@ struct sst_pdata { void *dsp; }; +#if IS_ENABLED(CONFIG_DW_DMAC_CORE) /* Initialization */ struct sst_dsp *sst_dsp_new(struct device *dev, struct sst_dsp_device *sst_dev, struct sst_pdata *pdata); void sst_dsp_free(struct sst_dsp *sst); +#endif /* SHIM Read / Write */ void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value); From ba40a854ea4f9d81368dc023bd25ac4073058039 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 9 Oct 2015 18:16:36 -0400 Subject: [PATCH 18/28] ASoC: Intel: switch from ioremap_cache to memremap In preparation for deprecating ioremap_cache() convert its usage in skl-nhlt to memremap. Signed-off-by: Dan Williams Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-nhlt.c | 10 +++++----- sound/soc/intel/skylake/skl.h | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 13036b19d7e5..b0c7bd113aac 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -25,7 +25,7 @@ static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45, #define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS" -void __iomem *skl_nhlt_init(struct device *dev) +void *skl_nhlt_init(struct device *dev) { acpi_handle handle; union acpi_object *obj; @@ -40,17 +40,17 @@ void __iomem *skl_nhlt_init(struct device *dev) if (obj && obj->type == ACPI_TYPE_BUFFER) { nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer; - return ioremap_cache(nhlt_ptr->min_addr, nhlt_ptr->length); + return memremap(nhlt_ptr->min_addr, nhlt_ptr->length, + MEMREMAP_WB); } dev_err(dev, "device specific method to extract NHLT blob failed\n"); return NULL; } -void skl_nhlt_free(void __iomem *addr) +void skl_nhlt_free(void *addr) { - iounmap(addr); - addr = NULL; + memunmap(addr); } static struct nhlt_specific_cfg *skl_get_specific_cfg( diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index e980d7897642..dd2e79ae45a8 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -62,7 +62,7 @@ struct skl { unsigned int init_failed:1; /* delayed init failed */ struct platform_device *dmic_dev; - void __iomem *nhlt; /* nhlt ptr */ + void *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ struct skl_dsp_resource resource; @@ -83,8 +83,8 @@ struct skl_dma_params { int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); -void __iomem *skl_nhlt_init(struct device *dev); -void skl_nhlt_free(void __iomem *addr); +void *skl_nhlt_init(struct device *dev); +void skl_nhlt_free(void *addr); struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn); From 4901aa065bced55be5ae55b58fc032bfc727fdda Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 20 Oct 2015 19:13:45 +0800 Subject: [PATCH 19/28] ASoC: Intel: Skylake: Convert to devm_snd_soc_register_card Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/intel/boards/skl_rt286.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index 488c98ee1ded..a73a431bd8b7 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -240,18 +240,11 @@ static int skylake_audio_probe(struct platform_device *pdev) { skylake_rt286.dev = &pdev->dev; - return snd_soc_register_card(&skylake_rt286); -} - -static int skylake_audio_remove(struct platform_device *pdev) -{ - snd_soc_unregister_card(&skylake_rt286); - return 0; + return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286); } static struct platform_driver skylake_audio = { .probe = skylake_audio_probe, - .remove = skylake_audio_remove, .driver = { .name = "skl_alc286s_i2s", }, From 9270b7b92615324ecda2b3fa0be98b4ab6c3e361 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:34 +0530 Subject: [PATCH 20/28] ASoC: Intel: Skylake: Fix return for pm_runtime_get_sync() pm_runtime_get_sync() will return a negative value in case of error and can return postive value for success. The return check for pm_runtime_get_sync() must be less than 0, so fix it Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index a8f53da11ae3..a2f94ce1679d 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -116,7 +116,7 @@ static int skl_pcm_open(struct snd_pcm_substream *substream, dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); ret = pm_runtime_get_sync(dai->dev); - if (ret) + if (ret < 0) return ret; stream = snd_hdac_ext_stream_assign(ebus, substream, From bc03281a5c706f6372dfc2100b04aa4055a15c88 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:35 +0530 Subject: [PATCH 21/28] ASoC: Intel: Skylake: Remove BE copier blob memcpy The BE copier private data allowed endpoint configuration blobs, now these are queried from BIOS, we don't need to copy the blob, but only capability. Removing the blob from private data will not allocate memory for module specific config in which case memcpy will fail. Fix is to assign the ptr queried from the NHLT table for the endpoint configuration. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index a5d8420ce69a..7c920857007e 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -926,7 +926,7 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, params->s_freq, params->stream); if (cfg) { mconfig->formats_config.caps_size = cfg->size; - memcpy(mconfig->formats_config.caps, &cfg->caps, cfg->size); + mconfig->formats_config.caps = (u32 *) &cfg->caps; } else { dev_err(dai->dev, "Blob NULL for id %x type %d dirn %d\n", mconfig->vbus_id, link_type, From f0900eb213e0f6f37e1567531f9604b9bed6771d Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 22 Oct 2015 23:22:36 +0530 Subject: [PATCH 22/28] ASoC: Intel: Skylake: Fix to use correct macros for the path iteration In case of playback, for the BE dai source path should be iterated to find the pipe params. With sink path iterated, this resulted in a loop and kernel panic with page request failure. Similar are the cases for Capture and FE dais. Using correct macros to fix the panic Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 7c920857007e..0fba39dcb2ab 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -844,7 +844,7 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) if (stream == SNDRV_PCM_STREAM_PLAYBACK) { w = dai->playback_widget; - snd_soc_dapm_widget_for_each_source_path(w, p) { + snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->connect && p->sink->power && is_skl_dsp_widget_type(p->sink)) continue; @@ -857,7 +857,7 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) } } else { w = dai->capture_widget; - snd_soc_dapm_widget_for_each_sink_path(w, p) { + snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->connect && p->source->power && is_skl_dsp_widget_type(p->source)) continue; @@ -945,7 +945,7 @@ static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai, { struct snd_soc_dapm_path *p; - snd_soc_dapm_widget_for_each_sink_path(w, p) { + snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->connect && is_skl_dsp_widget_type(p->source) && p->source->priv) { @@ -969,7 +969,7 @@ static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai, { struct snd_soc_dapm_path *p = NULL; - snd_soc_dapm_widget_for_each_source_path(w, p) { + snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->connect && is_skl_dsp_widget_type(p->sink) && p->sink->priv) { From 4d8adccb220ca270cfcdd80752618095fdc9990c Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 22 Oct 2015 23:22:37 +0530 Subject: [PATCH 23/28] ASoC: Intel: Skylake: Fix to fill all sink/source pipe params Currently params only for first copier widget identified in the source/sink path is queried from NHLT. In the dapm route the playback/capture widget may be connected to more than one copier widget. This patch adds return check to return only for any error case. Signed-off-by: Subhransu S. Prusty Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 32 ++++++++++++++++++-------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 0fba39dcb2ab..68e1b00ce7ed 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -944,48 +944,60 @@ static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai, struct skl_pipe_params *params) { struct snd_soc_dapm_path *p; + int ret = -EIO; snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->connect && is_skl_dsp_widget_type(p->source) && p->source->priv) { - if (!p->source->power) - return skl_tplg_be_fill_pipe_params( + if (!p->source->power) { + ret = skl_tplg_be_fill_pipe_params( dai, p->source->priv, params); - else + if (ret < 0) + return ret; + } else { return -EBUSY; + } } else { - return skl_tplg_be_set_src_pipe_params( + ret = skl_tplg_be_set_src_pipe_params( dai, p->source, params); + if (ret < 0) + return ret; } } - return -EIO; + return ret; } static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai, struct snd_soc_dapm_widget *w, struct skl_pipe_params *params) { struct snd_soc_dapm_path *p = NULL; + int ret = -EIO; snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->connect && is_skl_dsp_widget_type(p->sink) && p->sink->priv) { - if (!p->sink->power) - return skl_tplg_be_fill_pipe_params( + if (!p->sink->power) { + ret = skl_tplg_be_fill_pipe_params( dai, p->sink->priv, params); - else + if (ret < 0) + return ret; + } else { return -EBUSY; + } } else { - return skl_tplg_be_set_sink_pipe_params( + ret = skl_tplg_be_set_sink_pipe_params( dai, p->sink, params); + if (ret < 0) + return ret; } } - return -EIO; + return ret; } /* From d7b188131c4823a85d17b992083bf8a8c32a2be3 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:38 +0530 Subject: [PATCH 24/28] ASoC: Intel: Skylake: Update for ssp node index in copier cfg DSP firmware has interface change for SSP node index structure. New FW interface removes the dual_mono field and adds 4 bits for TDM slot group index. This patch updates the ssp dma to align with the DSP firmware structure. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 7 ++++--- sound/soc/intel/skylake/skl-topology.h | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 826d4fd8930a..77c0736d3c9e 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -303,6 +303,7 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, struct skl_cpr_cfg *cpr_mconfig) { union skl_connector_node_id node_id = {0}; + union skl_ssp_dma_node ssp_node = {0}; struct skl_pipe_params *params = mconfig->pipe->p_params; switch (mconfig->dev_type) { @@ -320,9 +321,9 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, (SKL_CONN_SOURCE == mconfig->hw_conn_type) ? SKL_DMA_I2S_LINK_OUTPUT_CLASS : SKL_DMA_I2S_LINK_INPUT_CLASS; - node_id.node.vindex = params->host_dma_id + - (mconfig->time_slot << 1) + - (mconfig->vbus_id << 3); + ssp_node.dma_node.time_slot_index = mconfig->time_slot; + ssp_node.dma_node.i2s_instance = mconfig->vbus_id; + node_id.node.vindex = ssp_node.val; break; case SKL_DEVICE_DMIC: diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 0c83dc3ea3e2..2399535a196a 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -153,8 +153,7 @@ enum skl_dma_type { union skl_ssp_dma_node { u8 val; struct { - u8 dual_mono:1; - u8 time_slot:3; + u8 time_slot_index:4; u8 i2s_instance:4; } dma_node; }; From 4e10996ba810031a493d903539f595e053826103 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:39 +0530 Subject: [PATCH 25/28] ASoC: Intel: Skylake: Add support to disable module notifications Each FW modules can report underrun/overrun notification from all modules. This patch disables underrun/overrun notification after firmware is loaded. This will be supportted for debug mode only thru debugfs Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 28 ++++++++++++++++++++++++-- sound/soc/intel/skylake/skl-topology.h | 5 +++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 77c0736d3c9e..ca7bbeea2cd9 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -54,6 +54,24 @@ static int skl_free_dma_buf(struct device *dev, struct snd_dma_buffer *dmab) return 0; } +#define NOTIFICATION_PARAM_ID 3 +#define NOTIFICATION_MASK 0xf + +/* disable notfication for underruns/overruns from firmware module */ +static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable) +{ + struct notification_mask mask; + struct skl_ipc_large_config_msg msg = {0}; + + mask.notify = NOTIFICATION_MASK; + mask.enable = enable; + + msg.large_param_id = NOTIFICATION_PARAM_ID; + msg.param_data_size = sizeof(mask); + + skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask); +} + int skl_init_dsp(struct skl *skl) { void __iomem *mmio_base; @@ -79,7 +97,7 @@ int skl_init_dsp(struct skl *skl) ret = skl_sst_dsp_init(bus->dev, mmio_base, irq, loader_ops, &skl->skl_sst); - + skl_dsp_enable_notification(skl->skl_sst, false); dev_dbg(bus->dev, "dsp registration status=%d\n", ret); return ret; @@ -122,6 +140,7 @@ int skl_suspend_dsp(struct skl *skl) int skl_resume_dsp(struct skl *skl) { struct skl_sst *ctx = skl->skl_sst; + int ret; /* if ppcap is not supported return 0 */ if (!skl->ebus.ppcap) @@ -131,7 +150,12 @@ int skl_resume_dsp(struct skl *skl) snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true); - return skl_dsp_wake(ctx->dsp); + ret = skl_dsp_wake(ctx->dsp); + if (ret < 0) + return ret; + + skl_dsp_enable_notification(skl->skl_sst, false); + return ret; } enum skl_bitdepth skl_get_bit_depth(int params) diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 2399535a196a..76053a8de41c 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -129,6 +129,11 @@ struct skl_src_module_cfg { enum skl_s_freq src_cfg; } __packed; +struct notification_mask { + u32 notify; + u32 enable; +} __packed; + struct skl_up_down_mixer_cfg { struct skl_base_cfg base_cfg; enum skl_ch_cfg out_ch_cfg; From 2ac454ffe6c2cb923711edc8027bbc0e782be457 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:40 +0530 Subject: [PATCH 26/28] ASoC: Intel: Skylake: Fix to check return value of dsp init If DSP initialization fails, ipc to disable notification will cause NULL ptr exception as ipc is not initialized. This patch returns error if dsp init fails before sending disable notification ipc. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index ca7bbeea2cd9..a1001a686aa9 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -97,6 +97,9 @@ int skl_init_dsp(struct skl *skl) ret = skl_sst_dsp_init(bus->dev, mmio_base, irq, loader_ops, &skl->skl_sst); + if (ret < 0) + return ret; + skl_dsp_enable_notification(skl->skl_sst, false); dev_dbg(bus->dev, "dsp registration status=%d\n", ret); From bfa764accdea017b1c557f43fa5f2c77a0dc3b15 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:41 +0530 Subject: [PATCH 27/28] ASoC: Intel: Skylake: Fix to set Non gateway copier cfg If copier instance is connected to a DMA gateway then driver needs to configure the gateway configuration otherwise set it to invalid. This patch sets the non gateway copier node value to invalid. Signed-off-by: Dharageswari.R Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 8 +++++++- sound/soc/intel/skylake/skl-tplg-interface.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index a1001a686aa9..50a109503a3f 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -321,6 +321,7 @@ static void skl_copy_copier_caps(struct skl_module_cfg *mconfig, (mconfig->formats_config.caps_size) / 4; } +#define SKL_NON_GATEWAY_CPR_NODE_ID 0xFFFFFFFF /* * Calculate the gatewat settings required for copier module, type of * gateway and index of gateway to use @@ -367,13 +368,18 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, node_id.node.vindex = params->link_dma_id; break; - default: + case SKL_DEVICE_HDAHOST: node_id.node.dma_type = (SKL_CONN_SOURCE == mconfig->hw_conn_type) ? SKL_DMA_HDA_HOST_OUTPUT_CLASS : SKL_DMA_HDA_HOST_INPUT_CLASS; node_id.node.vindex = params->host_dma_id; break; + + default: + cpr_mconfig->gtw_cfg.node_id = SKL_NON_GATEWAY_CPR_NODE_ID; + cpr_mconfig->cpr_feature_mask = 0; + return; } cpr_mconfig->gtw_cfg.node_id = node_id.val; diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index d14f7fe64aa8..718d3d5df9a8 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -106,6 +106,7 @@ enum skl_dev_type { SKL_DEVICE_I2S = 0x2, SKL_DEVICE_SLIMBUS = 0x3, SKL_DEVICE_HDALINK = 0x4, + SKL_DEVICE_HDAHOST = 0x5, SKL_DEVICE_NONE }; From 6abca1d71b62090821240ee2a4507c94ddd144e9 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 22 Oct 2015 23:22:42 +0530 Subject: [PATCH 28/28] ASoC: Intel: Skylake: Add support to topology for module static pin Some module pin connection are static and defined by the topology. This patch adds support for static pin definitions in topology widget private data Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 32 +++++++++++--------- sound/soc/intel/skylake/skl-tplg-interface.h | 6 ++-- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 68e1b00ce7ed..a7854c8fc523 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1034,18 +1034,17 @@ static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = { * The topology binary passes the pin info for a module so initialize the pin * info passed into module instance */ -static void skl_fill_module_pin_info(struct device *dev, - struct skl_module_pin *m_pin, - int max_pin) +static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin, + struct skl_module_pin *m_pin, + bool is_dynamic, int max_pin) { int i; for (i = 0; i < max_pin; i++) { - m_pin[i].id.module_id = 0; - m_pin[i].id.instance_id = 0; + m_pin[i].id.module_id = dfw_pin[i].module_id; + m_pin[i].id.instance_id = dfw_pin[i].instance_id; m_pin[i].in_use = false; - m_pin[i].is_dynamic = true; - m_pin[i].pin_index = i; + m_pin[i].is_dynamic = is_dynamic; } } @@ -1164,17 +1163,20 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, if (!mconfig->m_in_pin) return -ENOMEM; - mconfig->m_out_pin = devm_kzalloc(bus->dev, - (mconfig->max_in_queue) * - sizeof(*mconfig->m_out_pin), - GFP_KERNEL); + mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_out_queue) * + sizeof(*mconfig->m_out_pin), + GFP_KERNEL); if (!mconfig->m_out_pin) return -ENOMEM; - skl_fill_module_pin_info(bus->dev, mconfig->m_in_pin, - mconfig->max_in_queue); - skl_fill_module_pin_info(bus->dev, mconfig->m_out_pin, - mconfig->max_out_queue); + skl_fill_module_pin_info(dfw_config->in_pin, mconfig->m_in_pin, + dfw_config->is_dynamic_in_pin, + mconfig->max_in_queue); + + skl_fill_module_pin_info(dfw_config->out_pin, mconfig->m_out_pin, + dfw_config->is_dynamic_out_pin, + mconfig->max_out_queue); + if (mconfig->formats_config.caps_size == 0) goto bind_event; diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index 718d3d5df9a8..2bc396d54cbe 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -113,8 +113,6 @@ enum skl_dev_type { struct skl_dfw_module_pin { u16 module_id; u16 instance_id; - u8 pin_id; - bool is_dynamic; } __packed; struct skl_dfw_module_fmt { @@ -155,9 +153,13 @@ struct skl_dfw_module { u32 converter; u32 module_type; u32 vbus_id; + u8 is_dynamic_in_pin; + u8 is_dynamic_out_pin; struct skl_dfw_pipe pipe; struct skl_dfw_module_fmt in_fmt; struct skl_dfw_module_fmt out_fmt; + struct skl_dfw_module_pin in_pin[MAX_IN_QUEUE]; + struct skl_dfw_module_pin out_pin[MAX_OUT_QUEUE]; struct skl_dfw_module_caps caps; } __packed;