[alsa-devel] [PATCH v5 00/12] Add mrfld DSP topology and widgets
This patch series adds DPCM and DAPM widgets to handle DSP topology in the SST platform driver for merrifield.
Changes in v5: * Added init callback for controls to initialize some private data * Replaced pr_ with dev_ for logging * Handle return values * Addressed review comments
Subhransu S. Prusty (4): ASoC: mfld: pcm: Replace pr_ with dev_ ALSA: control: Add init callback for kcontrol ASoC: Export dapm_kcontrol_get_value ASoC: Intel: mrfld: Use snd_soc_dai_get_drvdata to derive drv data
Vinod Koul (8): ASoC: Intel: mfld-pcm: don't call trigger ops to DSP for internal streams ASoC: Intel: mrfld: add bytes control for modules ASoC: Intel: mrfld: add the gain controls ASoC: Intel: mfld-pcm: add control for powering up/down dsp ASoC: Intel: mrfld: add DSP core controls ASoC: Intel: mrfld: add the DSP DAPM widgets ASoC: Intel: mfld-pcm: add FE and BE ops ASoC: Intel: mrfld: add the DSP mixers
include/sound/control.h | 3 + include/sound/soc-dapm.h | 1 + sound/core/control.c | 7 + sound/soc/intel/sst-atom-controls.c | 1242 ++++++++++++++++++++++++++++++- sound/soc/intel/sst-atom-controls.h | 558 ++++++++++++++ sound/soc/intel/sst-mfld-platform-pcm.c | 221 ++++-- sound/soc/intel/sst-mfld-platform.h | 7 +- sound/soc/soc-dapm.c | 3 +- 8 files changed, 1985 insertions(+), 57 deletions(-)
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/sst-mfld-platform-pcm.c | 44 +++++++++++++++++---------------- 1 file changed, 23 insertions(+), 21 deletions(-)
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index 8e1e9bc..85deecd 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -43,12 +43,12 @@ int sst_register_dsp(struct sst_device *dev) return -ENODEV; mutex_lock(&sst_lock); if (sst) { - pr_err("we already have a device %s\n", sst->name); + dev_err(dev->dev, "we already have a device %s\n", sst->name); module_put(dev->dev->driver->owner); mutex_unlock(&sst_lock); return -EEXIST; } - pr_debug("registering device %s\n", dev->name); + dev_dbg(dev->dev, "registering device %s\n", dev->name); sst = dev; mutex_unlock(&sst_lock); return 0; @@ -70,7 +70,7 @@ int sst_unregister_dsp(struct sst_device *dev) }
module_put(sst->dev->driver->owner); - pr_debug("unreg %s\n", sst->name); + dev_dbg(dev->dev, "unreg %s\n", sst->name); sst = NULL; mutex_unlock(&sst_lock); return 0; @@ -306,9 +306,10 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream) { struct sst_runtime_stream *stream = substream->runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; int ret_val;
- pr_debug("setting buffer ptr param\n"); + dev_dbg(rtd->dev, "setting buffer ptr param\n"); sst_set_stream_status(stream, SST_PLATFORM_INIT); stream->stream_info.period_elapsed = sst_period_elapsed; stream->stream_info.arg = substream; @@ -316,7 +317,7 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream) stream->stream_info.sfreq = substream->runtime->rate; ret_val = stream->ops->stream_init(sst->dev, &stream->stream_info); if (ret_val) - pr_err("control_set ret error %d\n", ret_val); + dev_err(rtd->dev, "control_set ret error %d\n", ret_val); return ret_val;
} @@ -337,7 +338,7 @@ static int sst_media_open(struct snd_pcm_substream *substream, mutex_lock(&sst_lock); if (!sst || !try_module_get(sst->dev->driver->owner)) { - pr_err("no device available to run\n"); + dev_err(dai->dev, "no device available to run\n"); ret_val = -ENODEV; goto out_ops; } @@ -385,10 +386,11 @@ static inline unsigned int get_current_pipe_id(struct snd_soc_platform *platform substream->runtime->private_data; u32 str_id = stream->stream_info.str_id; unsigned int pipe_id; + pipe_id = map[str_id].device_id;
- pr_debug("%s: got pipe_id = %#x for str_id = %d\n", - __func__, pipe_id, str_id); + dev_dbg(platform->dev, "got pipe_id = %#x for str_id = %d\n", + pipe_id, str_id); return pipe_id; }
@@ -459,29 +461,30 @@ static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, int ret_val = 0, str_id; struct sst_runtime_stream *stream; int status; + struct snd_soc_pcm_runtime *rtd = substream->private_data;
- pr_debug("sst_platform_pcm_trigger called\n"); + dev_dbg(rtd->dev, "sst_platform_pcm_trigger called\n"); stream = substream->runtime->private_data; str_id = stream->stream_info.str_id; switch (cmd) { case SNDRV_PCM_TRIGGER_START: - pr_debug("sst: Trigger Start\n"); + dev_dbg(rtd->dev, "sst: Trigger Start\n"); status = SST_PLATFORM_RUNNING; stream->stream_info.arg = substream; ret_val = stream->ops->stream_start(sst->dev, str_id); break; case SNDRV_PCM_TRIGGER_STOP: - pr_debug("sst: in stop\n"); + dev_dbg(rtd->dev, "sst: in stop\n"); status = SST_PLATFORM_DROPPED; ret_val = stream->ops->stream_drop(sst->dev, str_id); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - pr_debug("sst: in pause\n"); + dev_dbg(rtd->dev, "sst: in pause\n"); status = SST_PLATFORM_PAUSED; ret_val = stream->ops->stream_pause(sst->dev, str_id); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - pr_debug("sst: in pause release\n"); + dev_dbg(rtd->dev, "sst: in pause release\n"); status = SST_PLATFORM_RUNNING; ret_val = stream->ops->stream_pause_release(sst->dev, str_id); break; @@ -502,6 +505,7 @@ static snd_pcm_uframes_t sst_platform_pcm_pointer struct sst_runtime_stream *stream; int ret_val, status; struct pcm_stream_info *str_info; + struct snd_soc_pcm_runtime *rtd = substream->private_data;
stream = substream->runtime->private_data; status = sst_get_stream_status(stream); @@ -510,7 +514,7 @@ static snd_pcm_uframes_t sst_platform_pcm_pointer str_info = &stream->stream_info; ret_val = stream->ops->stream_read_tstamp(sst->dev, str_info); if (ret_val) { - pr_err("sst: error code = %d\n", ret_val); + dev_err(rtd->dev, "sst: error code = %d\n", ret_val); return ret_val; } substream->runtime->delay = str_info->pcm_delay; @@ -526,7 +530,7 @@ static struct snd_pcm_ops sst_platform_ops = {
static void sst_pcm_free(struct snd_pcm *pcm) { - pr_debug("sst_pcm_free called\n"); + dev_dbg(pcm->dev, "sst_pcm_free called\n"); snd_pcm_lib_preallocate_free_for_all(pcm); }
@@ -543,7 +547,7 @@ static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd) snd_dma_continuous_data(GFP_DMA), SST_MIN_BUFFER, SST_MAX_BUFFER); if (retval) { - pr_err("dma buffer allocationf fail\n"); + dev_err(rtd->dev, "dma buffer allocationf fail\n"); return retval; } } @@ -576,13 +580,11 @@ static int sst_platform_probe(struct platform_device *pdev)
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); if (drv == NULL) { - pr_err("kzalloc failed\n"); return -ENOMEM; }
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (pdata == NULL) { - pr_err("kzalloc failed for pdata\n"); return -ENOMEM; }
@@ -594,14 +596,14 @@ static int sst_platform_probe(struct platform_device *pdev)
ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv); if (ret) { - pr_err("registering soc platform failed\n"); + dev_err(&pdev->dev, "registering soc platform failed\n"); return ret; }
ret = snd_soc_register_component(&pdev->dev, &sst_component, sst_platform_dai, ARRAY_SIZE(sst_platform_dai)); if (ret) { - pr_err("registering cpu dais failed\n"); + dev_err(&pdev->dev, "registering cpu dais failed\n"); snd_soc_unregister_platform(&pdev->dev); } return ret; @@ -612,7 +614,7 @@ static int sst_platform_remove(struct platform_device *pdev)
snd_soc_unregister_component(&pdev->dev); snd_soc_unregister_platform(&pdev->dev); - pr_debug("sst_platform_remove success\n"); + dev_dbg(&pdev->dev, "sst_platform_remove success\n"); return 0; }
On Tue, Sep 02, 2014 at 06:05:56PM +0530, Subhransu S. Prusty wrote:
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
Applied, thanks.
From: Vinod Koul vinod.koul@intel.com
For internal stream i.e. BE we have don't need trigger ops as that would be handled by DAPM for us in subsequent patches
Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com --- sound/soc/intel/sst-mfld-platform-pcm.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index 85deecd..9906b7c 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -464,6 +464,8 @@ static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data;
dev_dbg(rtd->dev, "sst_platform_pcm_trigger called\n"); + if (substream->pcm->internal) + return 0; stream = substream->runtime->private_data; str_id = stream->stream_info.str_id; switch (cmd) {
Some controls need to initialize stuffs like pvt data, so they need a callback if the control creation is successful.
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/control.h | 3 +++ sound/core/control.c | 7 +++++++ 2 files changed, 10 insertions(+)
diff --git a/include/sound/control.h b/include/sound/control.h index 0426139..1389f69 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -30,6 +30,7 @@ struct snd_kcontrol; typedef int (snd_kcontrol_info_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_info * uinfo); typedef int (snd_kcontrol_get_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); typedef int (snd_kcontrol_put_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); +typedef int (snd_kcontrol_init_t) (struct snd_kcontrol * kcontrol); typedef int (snd_kcontrol_tlv_rw_t)(struct snd_kcontrol *kcontrol, int op_flag, /* SNDRV_CTL_TLV_OP_XXX */ unsigned int size, @@ -52,6 +53,7 @@ struct snd_kcontrol_new { snd_kcontrol_info_t *info; snd_kcontrol_get_t *get; snd_kcontrol_put_t *put; + snd_kcontrol_init_t *init; union { snd_kcontrol_tlv_rw_t *c; const unsigned int *p; @@ -71,6 +73,7 @@ struct snd_kcontrol { snd_kcontrol_info_t *info; snd_kcontrol_get_t *get; snd_kcontrol_put_t *put; + snd_kcontrol_init_t *init; union { snd_kcontrol_tlv_rw_t *c; const unsigned int *p; diff --git a/sound/core/control.c b/sound/core/control.c index b961134..9d30663 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -256,6 +256,7 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, kctl.info = ncontrol->info; kctl.get = ncontrol->get; kctl.put = ncontrol->put; + kctl.init = ncontrol->init; kctl.tlv.p = ncontrol->tlv.p; kctl.private_value = ncontrol->private_value; kctl.private_data = private_data; @@ -362,6 +363,12 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) err = -ENOMEM; goto error; } + if (kcontrol->init) { + err = kcontrol->init(kcontrol); + if (err < 0) + goto error; + } + list_add_tail(&kcontrol->list, &card->controls); card->controls_count += kcontrol->count; kcontrol->id.numid = card->last_numid + 1;
On Tue, Sep 02, 2014 at 06:05:58PM +0530, Subhransu S. Prusty wrote:
Some controls need to initialize stuffs like pvt data, so they need a callback if the control creation is successful.
Adding Takashi - this is ALSA core code so he needs to review it.
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
include/sound/control.h | 3 +++ sound/core/control.c | 7 +++++++ 2 files changed, 10 insertions(+)
diff --git a/include/sound/control.h b/include/sound/control.h index 0426139..1389f69 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -30,6 +30,7 @@ struct snd_kcontrol; typedef int (snd_kcontrol_info_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_info * uinfo); typedef int (snd_kcontrol_get_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); typedef int (snd_kcontrol_put_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); +typedef int (snd_kcontrol_init_t) (struct snd_kcontrol * kcontrol); typedef int (snd_kcontrol_tlv_rw_t)(struct snd_kcontrol *kcontrol, int op_flag, /* SNDRV_CTL_TLV_OP_XXX */ unsigned int size, @@ -52,6 +53,7 @@ struct snd_kcontrol_new { snd_kcontrol_info_t *info; snd_kcontrol_get_t *get; snd_kcontrol_put_t *put;
- snd_kcontrol_init_t *init; union { snd_kcontrol_tlv_rw_t *c; const unsigned int *p;
@@ -71,6 +73,7 @@ struct snd_kcontrol { snd_kcontrol_info_t *info; snd_kcontrol_get_t *get; snd_kcontrol_put_t *put;
- snd_kcontrol_init_t *init; union { snd_kcontrol_tlv_rw_t *c; const unsigned int *p;
diff --git a/sound/core/control.c b/sound/core/control.c index b961134..9d30663 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -256,6 +256,7 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, kctl.info = ncontrol->info; kctl.get = ncontrol->get; kctl.put = ncontrol->put;
- kctl.init = ncontrol->init; kctl.tlv.p = ncontrol->tlv.p; kctl.private_value = ncontrol->private_value; kctl.private_data = private_data;
@@ -362,6 +363,12 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) err = -ENOMEM; goto error; }
- if (kcontrol->init) {
err = kcontrol->init(kcontrol);
if (err < 0)
goto error;
- }
- list_add_tail(&kcontrol->list, &card->controls); card->controls_count += kcontrol->count; kcontrol->id.numid = card->last_numid + 1;
-- 1.9.0
At Sat, 6 Sep 2014 15:21:24 +0100, Mark Brown wrote:
On Tue, Sep 02, 2014 at 06:05:58PM +0530, Subhransu S. Prusty wrote:
Some controls need to initialize stuffs like pvt data, so they need a callback if the control creation is successful.
Adding Takashi - this is ALSA core code so he needs to review it.
This would bloat effectively the data size of all sound drivers, so I can't ack it without more proper reasoning. That is, please convince me why this change must be taken for the cost of size bloat of all sound drivers. Can't you do it in the caller side of snd_ctl_add() like many other drivers already do?
thanks,
Takashi
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
include/sound/control.h | 3 +++ sound/core/control.c | 7 +++++++ 2 files changed, 10 insertions(+)
diff --git a/include/sound/control.h b/include/sound/control.h index 0426139..1389f69 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -30,6 +30,7 @@ struct snd_kcontrol; typedef int (snd_kcontrol_info_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_info * uinfo); typedef int (snd_kcontrol_get_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); typedef int (snd_kcontrol_put_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); +typedef int (snd_kcontrol_init_t) (struct snd_kcontrol * kcontrol); typedef int (snd_kcontrol_tlv_rw_t)(struct snd_kcontrol *kcontrol, int op_flag, /* SNDRV_CTL_TLV_OP_XXX */ unsigned int size, @@ -52,6 +53,7 @@ struct snd_kcontrol_new { snd_kcontrol_info_t *info; snd_kcontrol_get_t *get; snd_kcontrol_put_t *put;
- snd_kcontrol_init_t *init; union { snd_kcontrol_tlv_rw_t *c; const unsigned int *p;
@@ -71,6 +73,7 @@ struct snd_kcontrol { snd_kcontrol_info_t *info; snd_kcontrol_get_t *get; snd_kcontrol_put_t *put;
- snd_kcontrol_init_t *init; union { snd_kcontrol_tlv_rw_t *c; const unsigned int *p;
diff --git a/sound/core/control.c b/sound/core/control.c index b961134..9d30663 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -256,6 +256,7 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, kctl.info = ncontrol->info; kctl.get = ncontrol->get; kctl.put = ncontrol->put;
- kctl.init = ncontrol->init; kctl.tlv.p = ncontrol->tlv.p; kctl.private_value = ncontrol->private_value; kctl.private_data = private_data;
@@ -362,6 +363,12 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) err = -ENOMEM; goto error; }
- if (kcontrol->init) {
err = kcontrol->init(kcontrol);
if (err < 0)
goto error;
- }
- list_add_tail(&kcontrol->list, &card->controls); card->controls_count += kcontrol->count; kcontrol->id.numid = card->last_numid + 1;
-- 1.9.0
[2 Digital signature <application/pgp-signature (7bit)>]
On Sat, Sep 06, 2014 at 05:56:02PM +0200, Takashi Iwai wrote:
At Sat, 6 Sep 2014 15:21:24 +0100, Mark Brown wrote:
On Tue, Sep 02, 2014 at 06:05:58PM +0530, Subhransu S. Prusty wrote:
Some controls need to initialize stuffs like pvt data, so they need a callback if the control creation is successful.
Adding Takashi - this is ALSA core code so he needs to review it.
This would bloat effectively the data size of all sound drivers, so I can't ack it without more proper reasoning. That is, please convince me why this change must be taken for the cost of size bloat of all sound drivers. Can't you do it in the caller side of snd_ctl_add() like many other drivers already do?
Okay lets step back and see why we need this :)
In our case after the control creation we need to allocate memory which will hold the data for the byte controls. This can be done only after the controls are created (by asoc).
For that we need a callback into driver so that we can allocate the memory. Thats why we added .init() method. If you have any other way to do this, we are all ears :)
At Mon, 8 Sep 2014 09:44:21 +0530, Vinod Koul wrote:
On Sat, Sep 06, 2014 at 05:56:02PM +0200, Takashi Iwai wrote:
At Sat, 6 Sep 2014 15:21:24 +0100, Mark Brown wrote:
On Tue, Sep 02, 2014 at 06:05:58PM +0530, Subhransu S. Prusty wrote:
Some controls need to initialize stuffs like pvt data, so they need a callback if the control creation is successful.
Adding Takashi - this is ALSA core code so he needs to review it.
This would bloat effectively the data size of all sound drivers, so I can't ack it without more proper reasoning. That is, please convince me why this change must be taken for the cost of size bloat of all sound drivers. Can't you do it in the caller side of snd_ctl_add() like many other drivers already do?
Okay lets step back and see why we need this :)
In our case after the control creation we need to allocate memory which will hold the data for the byte controls. This can be done only after the controls are created (by asoc).
Why? Because you don't need how many bytes to allocate?
For that we need a callback into driver so that we can allocate the memory. Thats why we added .init() method. If you have any other way to do this, we are all ears :)
For example, you can embed an init flag into your record and call the initializer in get/put callback if not called yet.
Takashi
-- ~Vinod
thanks,
Takashi
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
include/sound/control.h | 3 +++ sound/core/control.c | 7 +++++++ 2 files changed, 10 insertions(+)
diff --git a/include/sound/control.h b/include/sound/control.h index 0426139..1389f69 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -30,6 +30,7 @@ struct snd_kcontrol; typedef int (snd_kcontrol_info_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_info * uinfo); typedef int (snd_kcontrol_get_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); typedef int (snd_kcontrol_put_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); +typedef int (snd_kcontrol_init_t) (struct snd_kcontrol * kcontrol); typedef int (snd_kcontrol_tlv_rw_t)(struct snd_kcontrol *kcontrol, int op_flag, /* SNDRV_CTL_TLV_OP_XXX */ unsigned int size, @@ -52,6 +53,7 @@ struct snd_kcontrol_new { snd_kcontrol_info_t *info; snd_kcontrol_get_t *get; snd_kcontrol_put_t *put;
- snd_kcontrol_init_t *init; union { snd_kcontrol_tlv_rw_t *c; const unsigned int *p;
@@ -71,6 +73,7 @@ struct snd_kcontrol { snd_kcontrol_info_t *info; snd_kcontrol_get_t *get; snd_kcontrol_put_t *put;
- snd_kcontrol_init_t *init; union { snd_kcontrol_tlv_rw_t *c; const unsigned int *p;
diff --git a/sound/core/control.c b/sound/core/control.c index b961134..9d30663 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -256,6 +256,7 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, kctl.info = ncontrol->info; kctl.get = ncontrol->get; kctl.put = ncontrol->put;
- kctl.init = ncontrol->init; kctl.tlv.p = ncontrol->tlv.p; kctl.private_value = ncontrol->private_value; kctl.private_data = private_data;
@@ -362,6 +363,12 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) err = -ENOMEM; goto error; }
- if (kcontrol->init) {
err = kcontrol->init(kcontrol);
if (err < 0)
goto error;
- }
- list_add_tail(&kcontrol->list, &card->controls); card->controls_count += kcontrol->count; kcontrol->id.numid = card->last_numid + 1;
-- 1.9.0
[2 Digital signature <application/pgp-signature (7bit)>]
--
On Mon, Sep 08, 2014 at 10:04:42AM +0200, Takashi Iwai wrote:
At Mon, 8 Sep 2014 09:44:21 +0530, Vinod Koul wrote:
On Sat, Sep 06, 2014 at 05:56:02PM +0200, Takashi Iwai wrote:
At Sat, 6 Sep 2014 15:21:24 +0100, Mark Brown wrote:
On Tue, Sep 02, 2014 at 06:05:58PM +0530, Subhransu S. Prusty wrote:
Some controls need to initialize stuffs like pvt data, so they need a callback if the control creation is successful.
Adding Takashi - this is ALSA core code so he needs to review it.
This would bloat effectively the data size of all sound drivers, so I can't ack it without more proper reasoning. That is, please convince me why this change must be taken for the cost of size bloat of all sound drivers. Can't you do it in the caller side of snd_ctl_add() like many other drivers already do?
Okay lets step back and see why we need this :)
In our case after the control creation we need to allocate memory which will hold the data for the byte controls. This can be done only after the controls are created (by asoc).
Why? Because you don't need how many bytes to allocate?
For that we need a callback into driver so that we can allocate the memory. Thats why we added .init() method. If you have any other way to do this, we are all ears :)
For example, you can embed an init flag into your record and call the initializer in get/put callback if not called yet.
Yes but that would involve open coding in all control hanlers for the driver :
if (!initialized) allocate_control_mem();
This approach was done earlier (checking in info) and flaged off by Mark. It make better to have these handled in the framework.
Either ways we are okay with whatever you two decide :)
At Mon, 8 Sep 2014 13:38:07 +0530, Vinod Koul wrote:
On Mon, Sep 08, 2014 at 10:04:42AM +0200, Takashi Iwai wrote:
At Mon, 8 Sep 2014 09:44:21 +0530, Vinod Koul wrote:
On Sat, Sep 06, 2014 at 05:56:02PM +0200, Takashi Iwai wrote:
At Sat, 6 Sep 2014 15:21:24 +0100, Mark Brown wrote:
On Tue, Sep 02, 2014 at 06:05:58PM +0530, Subhransu S. Prusty wrote:
Some controls need to initialize stuffs like pvt data, so they need a callback if the control creation is successful.
Adding Takashi - this is ALSA core code so he needs to review it.
This would bloat effectively the data size of all sound drivers, so I can't ack it without more proper reasoning. That is, please convince me why this change must be taken for the cost of size bloat of all sound drivers. Can't you do it in the caller side of snd_ctl_add() like many other drivers already do?
Okay lets step back and see why we need this :)
In our case after the control creation we need to allocate memory which will hold the data for the byte controls. This can be done only after the controls are created (by asoc).
Why? Because you don't need how many bytes to allocate?
For that we need a callback into driver so that we can allocate the memory. Thats why we added .init() method. If you have any other way to do this, we are all ears :)
For example, you can embed an init flag into your record and call the initializer in get/put callback if not called yet.
Yes but that would involve open coding in all control hanlers for the driver :
if (!initialized) allocate_control_mem();
This approach was done earlier (checking in info) and flaged off by Mark. It make better to have these handled in the framework.
For the cost of bloating all sound drivers with every control element? No. You can extend the framework, but not in this way.
For example, it's nonsense to keep this permanently in each control element, and both in kcontrol and kcontrol_new. Remember that this is a oneshot callback that is called only after creation...
Takashi
On Mon, Sep 08, 2014 at 10:36:57AM +0200, Takashi Iwai wrote:
At Mon, 8 Sep 2014 13:38:07 +0530, Vinod Koul wrote:
On Mon, Sep 08, 2014 at 10:04:42AM +0200, Takashi Iwai wrote:
At Mon, 8 Sep 2014 09:44:21 +0530, Vinod Koul wrote:
On Sat, Sep 06, 2014 at 05:56:02PM +0200, Takashi Iwai wrote:
At Sat, 6 Sep 2014 15:21:24 +0100, Mark Brown wrote:
On Tue, Sep 02, 2014 at 06:05:58PM +0530, Subhransu S. Prusty wrote: > Some controls need to initialize stuffs like pvt data, so they need a > callback if the control creation is successful.
Adding Takashi - this is ALSA core code so he needs to review it.
This would bloat effectively the data size of all sound drivers, so I can't ack it without more proper reasoning. That is, please convince me why this change must be taken for the cost of size bloat of all sound drivers. Can't you do it in the caller side of snd_ctl_add() like many other drivers already do?
Okay lets step back and see why we need this :)
In our case after the control creation we need to allocate memory which will hold the data for the byte controls. This can be done only after the controls are created (by asoc).
Why? Because you don't need how many bytes to allocate?
For that we need a callback into driver so that we can allocate the memory. Thats why we added .init() method. If you have any other way to do this, we are all ears :)
For example, you can embed an init flag into your record and call the initializer in get/put callback if not called yet.
Yes but that would involve open coding in all control hanlers for the driver :
if (!initialized) allocate_control_mem();
This approach was done earlier (checking in info) and flaged off by Mark. It make better to have these handled in the framework.
For the cost of bloating all sound drivers with every control element? No. You can extend the framework, but not in this way.
Yes makes sense.
For example, it's nonsense to keep this permanently in each control element, and both in kcontrol and kcontrol_new. Remember that this is a oneshot callback that is called only after creation...
Yes indeed, so we are going to rely on the falg in driver records and initialize at first invocation.
Updated series spinning now
On Mon, Sep 08, 2014 at 01:38:07PM +0530, Vinod Koul wrote:
Please (as I'm sure has been pointed out before) fix your mailer to word wrap within paragraphs.
On Mon, Sep 08, 2014 at 10:04:42AM +0200, Takashi Iwai wrote:
For example, you can embed an init flag into your record and call the initializer in get/put callback if not called yet.
Yes but that would involve open coding in all control hanlers for the driver :
if (!initialized) allocate_control_mem();
This approach was done earlier (checking in info) and flaged off by Mark. It make better to have these handled in the framework.
No, that's not the problem I identified. The bug you had was that the driver was initialising the data in only the info callback and would just fail if someone decided to call another callback first.
On Tue, Sep 09, 2014 at 12:44:25PM +0100, Mark Brown wrote:
On Mon, Sep 08, 2014 at 01:38:07PM +0530, Vinod Koul wrote:
Please (as I'm sure has been pointed out before) fix your mailer to word wrap within paragraphs.
Oops, i though I had set mutt to fix it after you pointed out last time, will check again.
On Mon, Sep 08, 2014 at 10:04:42AM +0200, Takashi Iwai wrote:
For example, you can embed an init flag into your record and call the initializer in get/put callback if not called yet.
Yes but that would involve open coding in all control hanlers for the driver :
if (!initialized) allocate_control_mem();
This approach was done earlier (checking in info) and flaged off by Mark. It make better to have these handled in the framework.
No, that's not the problem I identified. The bug you had was that the driver was initialising the data in only the info callback and would just fail if someone decided to call another callback first.
Yes and during the discussed we wanted to handle it in framework leading to init callback.
Nevertheless we are moving it back to driver while fixing the bug you pointed.
Thanks
From: Vinod Koul vinod.koul@intel.com
This patch add support for various modules like eq etc for mrfld DSP. All these modules will be exposed to usermode as bytes controls.
Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com --- sound/soc/intel/sst-atom-controls.c | 174 ++++++++++++++++++++++++++++++++++++ sound/soc/intel/sst-atom-controls.h | 130 +++++++++++++++++++++++++++ sound/soc/intel/sst-mfld-platform.h | 2 +- 3 files changed, 305 insertions(+), 1 deletion(-)
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index ace3c4a..c11712d 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -25,6 +25,178 @@ #include "sst-mfld-platform.h" #include "sst-atom-controls.h"
+static int sst_fill_byte_control(struct sst_data *drv, + u8 ipc_msg, u8 block, + u8 task_id, u8 pipe_id, + u16 len, void *cmd_data) +{ + struct snd_sst_bytes_v2 *byte_data = drv->byte_stream; + + byte_data->type = SST_CMD_BYTES_SET; + byte_data->ipc_msg = ipc_msg; + byte_data->block = block; + byte_data->task_id = task_id; + byte_data->pipe_id = pipe_id; + + if (len > SST_MAX_BIN_BYTES - sizeof(*byte_data)) { + dev_err(&drv->pdev->dev, "command length too big (%u)", len); + return -EINVAL; + } + byte_data->len = len; + memcpy(byte_data->bytes, cmd_data, len); + print_hex_dump_bytes("writing to lpe: ", DUMP_PREFIX_OFFSET, + byte_data, len + sizeof(*byte_data)); + return 0; +} + +static int sst_fill_and_send_cmd_unlocked(struct sst_data *drv, + u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, + void *cmd_data, u16 len) +{ + int ret = 0; + ret = sst_fill_byte_control(drv, ipc_msg, + block, task_id, pipe_id, len, cmd_data); + if (ret < 0) + return ret; + return sst->ops->send_byte_stream(sst->dev, drv->byte_stream); +} + +/** + * sst_fill_and_send_cmd - generate the IPC message and send it to the FW + * @ipc_msg: type of IPC (CMD, SET_PARAMS, GET_PARAMS) + * @cmd_data: the IPC payload + */ +static int sst_fill_and_send_cmd(struct sst_data *drv, + u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, + void *cmd_data, u16 len) +{ + int ret; + + mutex_lock(&drv->lock); + ret = sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block, task_id, pipe_id, + cmd_data, len); + mutex_unlock(&drv->lock); + + return ret; +} + +static int sst_send_algo_cmd(struct sst_data *drv, + struct sst_algo_control *bc) +{ + int len, ret = 0; + struct sst_cmd_set_params *cmd; + + /*bc->max includes sizeof algos + length field*/ + len = sizeof(cmd->dst) + sizeof(cmd->command_id) + bc->max; + + cmd = kzalloc(len, GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + SST_FILL_DESTINATION(2, cmd->dst, bc->pipe_id, bc->module_id); + cmd->command_id = bc->cmd_id; + memcpy(cmd->params, bc->params, bc->max); + + ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS, + SST_FLAG_BLOCKED, bc->task_id, 0, cmd, len); + kfree(cmd); + return ret; +} + +static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct sst_algo_control *bc = (void *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = bc->max; + + return 0; +} + +static int sst_algo_control_init(struct snd_kcontrol *kcontrol) +{ + struct sst_algo_control *bc = (void *)kcontrol->private_value; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + + /*allocate space to cache the algo parameters in the driver*/ + if (bc->params == NULL) { + bc->params = devm_kzalloc(component->dev, bc->max, GFP_KERNEL); + if (bc->params == NULL) + return -ENOMEM; + } + return 0; +} + +static int sst_algo_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sst_algo_control *bc = (void *)kcontrol->private_value; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + + switch (bc->type) { + case SST_ALGO_PARAMS: + memcpy(ucontrol->value.bytes.data, bc->params, bc->max); + break; + default: + dev_err(component->dev, "Invalid Input- algo type:%d\n", + bc->type); + return -EINVAL; + + } + return 0; +} + +static int sst_algo_control_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct sst_data *drv = snd_soc_platform_get_drvdata(platform); + struct sst_algo_control *bc = (void *)kcontrol->private_value; + + dev_dbg(platform->dev, "control_name=%s\n", kcontrol->id.name); + mutex_lock(&drv->lock); + switch (bc->type) { + case SST_ALGO_PARAMS: + memcpy(bc->params, ucontrol->value.bytes.data, bc->max); + break; + default: + mutex_unlock(&drv->lock); + dev_err(platform->dev, "Invalid Input- algo type:%d\n", + bc->type); + return -EINVAL; + } + /*if pipe is enabled, need to send the algo params from here*/ + if (bc->w && bc->w->power) + ret = sst_send_algo_cmd(drv, bc); + mutex_unlock(&drv->lock); + + return ret; +} + +static const struct snd_kcontrol_new sst_algo_controls[] = { + SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 272, SST_MODULE_ID_FIR_24, + SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR), + SST_ALGO_KCONTROL_BYTES("media_loop1_out", "iir", 300, SST_MODULE_ID_IIR_24, + SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR), + SST_ALGO_KCONTROL_BYTES("media_loop1_out", "mdrp", 286, SST_MODULE_ID_MDRP, + SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP), + SST_ALGO_KCONTROL_BYTES("media_loop2_out", "fir", 272, SST_MODULE_ID_FIR_24, + SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR), + SST_ALGO_KCONTROL_BYTES("media_loop2_out", "iir", 300, SST_MODULE_ID_IIR_24, + SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR), + SST_ALGO_KCONTROL_BYTES("media_loop2_out", "mdrp", 286, SST_MODULE_ID_MDRP, + SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP), + SST_ALGO_KCONTROL_BYTES("sprot_loop_out", "lpro", 192, SST_MODULE_ID_SPROT, + SST_PATH_INDEX_SPROT_LOOP_OUT, 0, SST_TASK_SBA, SBA_VB_LPRO), + SST_ALGO_KCONTROL_BYTES("codec_in0", "dcr", 52, SST_MODULE_ID_FILT_DCR, + SST_PATH_INDEX_CODEC_IN0, 0, SST_TASK_SBA, SBA_VB_SET_IIR), + SST_ALGO_KCONTROL_BYTES("codec_in1", "dcr", 52, SST_MODULE_ID_FILT_DCR, + SST_PATH_INDEX_CODEC_IN1, 0, SST_TASK_SBA, SBA_VB_SET_IIR), + +}; + int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) { int ret = 0; @@ -35,5 +207,7 @@ int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) if (!drv->byte_stream) return -ENOMEM;
+ snd_soc_add_platform_controls(platform, sst_algo_controls, + ARRAY_SIZE(sst_algo_controls)); return ret; } diff --git a/sound/soc/intel/sst-atom-controls.h b/sound/soc/intel/sst-atom-controls.h index 8554889..7007010 100644 --- a/sound/soc/intel/sst-atom-controls.h +++ b/sound/soc/intel/sst-atom-controls.h @@ -309,4 +309,134 @@ enum sst_swm_state { SST_SWM_ON = 3, };
+#define SST_FILL_LOCATION_IDS(dst, cell_idx, pipe_id) do { \ + dst.location_id.p.cell_nbr_idx = (cell_idx); \ + dst.location_id.p.path_id = (pipe_id); \ + } while (0) +#define SST_FILL_LOCATION_ID(dst, loc_id) (\ + dst.location_id.f = (loc_id)) +#define SST_FILL_MODULE_ID(dst, mod_id) (\ + dst.module_id = (mod_id)) + +#define SST_FILL_DESTINATION1(dst, id) do { \ + SST_FILL_LOCATION_ID(dst, (id) & 0xFFFF); \ + SST_FILL_MODULE_ID(dst, ((id) & 0xFFFF0000) >> 16); \ + } while (0) +#define SST_FILL_DESTINATION2(dst, loc_id, mod_id) do { \ + SST_FILL_LOCATION_ID(dst, loc_id); \ + SST_FILL_MODULE_ID(dst, mod_id); \ + } while (0) +#define SST_FILL_DESTINATION3(dst, cell_idx, path_id, mod_id) do { \ + SST_FILL_LOCATION_IDS(dst, cell_idx, path_id); \ + SST_FILL_MODULE_ID(dst, mod_id); \ + } while (0) + +#define SST_FILL_DESTINATION(level, dst, ...) \ + SST_FILL_DESTINATION##level(dst, __VA_ARGS__) +#define SST_FILL_DEFAULT_DESTINATION(dst) \ + SST_FILL_DESTINATION(2, dst, SST_DEFAULT_LOCATION_ID, SST_DEFAULT_MODULE_ID) + +struct sst_destination_id { + union sst_location_id { + struct { + u8 cell_nbr_idx; /* module index */ + u8 path_id; /* pipe_id */ + } __packed p; /* part */ + u16 f; /* full */ + } __packed location_id; + u16 module_id; +} __packed; +struct sst_dsp_header { + struct sst_destination_id dst; + u16 command_id; + u16 length; +} __packed; + +/* + * + * Common Commands + * + */ +struct sst_cmd_generic { + struct sst_dsp_header header; +} __packed; +struct sst_cmd_set_params { + struct sst_destination_id dst; + u16 command_id; + char params[0]; +} __packed; +#define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \ + xpname " " xmname " " #xinstance " " xtype + +#define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \ + xpname " " xmname " " #xinstance " " xtype " " xsubmodule +enum sst_algo_kcontrol_type { + SST_ALGO_PARAMS, + SST_ALGO_BYPASS, +}; + +struct sst_algo_control { + enum sst_algo_kcontrol_type type; + int max; + u16 module_id; + u16 pipe_id; + u16 task_id; + u16 cmd_id; + bool bypass; + unsigned char *params; + struct snd_soc_dapm_widget *w; +}; + +/* size of the control = size of params + size of length field */ +#define SST_ALGO_CTL_VALUE(xcount, xtype, xpipe, xmod, xtask, xcmd) \ + (struct sst_algo_control){ \ + .max = xcount + sizeof(u16), .type = xtype, .module_id = xmod, \ + .pipe_id = xpipe, .task_id = xtask, .cmd_id = xcmd, \ + } + +#define SST_ALGO_KCONTROL(xname, xcount, xmod, xpipe, \ + xtask, xcmd, xtype, xinfo, xinit, xget, xput) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = xinfo, .init = xinit, .get = xget, .put = xput, \ + .private_value = (unsigned long)& \ + SST_ALGO_CTL_VALUE(xcount, xtype, xpipe, \ + xmod, xtask, xcmd), \ +} + +#define SST_ALGO_KCONTROL_BYTES(xpname, xmname, xcount, xmod, \ + xpipe, xinstance, xtask, xcmd) \ + SST_ALGO_KCONTROL(SST_CONTROL_NAME(xpname, xmname, xinstance, "params"), \ + xcount, xmod, xpipe, xtask, xcmd, SST_ALGO_PARAMS, \ + sst_algo_bytes_ctl_info, sst_algo_control_init, \ + sst_algo_control_get, sst_algo_control_set) + +#define SST_ALGO_KCONTROL_BOOL(xpname, xmname, xmod, xpipe, xinstance, xtask) \ + SST_ALGO_KCONTROL(SST_CONTROL_NAME(xpname, xmname, xinstance, "bypass"), \ + 0, xmod, xpipe, xtask, 0, SST_ALGO_BYPASS, \ + snd_soc_info_bool_ext, \ + sst_algo_control_get, sst_algo_control_set) + +#define SST_ALGO_BYPASS_PARAMS(xpname, xmname, xcount, xmod, xpipe, \ + xinstance, xtask, xcmd) \ + SST_ALGO_KCONTROL_BOOL(xpname, xmname, xmod, xpipe, xinstance, xtask), \ + SST_ALGO_KCONTROL_BYTES(xpname, xmname, xcount, xmod, xpipe, xinstance, xtask, xcmd) + +#define SST_COMBO_ALGO_KCONTROL_BYTES(xpname, xmname, xsubmod, xcount, xmod, \ + xpipe, xinstance, xtask, xcmd) \ + SST_ALGO_KCONTROL(SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, "params", \ + xsubmod), \ + xcount, xmod, xpipe, xtask, xcmd, SST_ALGO_PARAMS, \ + sst_algo_bytes_ctl_info, sst_algo_control_init, \ + sst_algo_control_get, sst_algo_control_set) + + +struct sst_enum { + bool tx; + unsigned short reg; + unsigned int max; + const char * const *texts; + struct snd_soc_dapm_widget *w; +}; + #endif diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h index faaba10..4b617e5 100644 --- a/sound/soc/intel/sst-mfld-platform.h +++ b/sound/soc/intel/sst-mfld-platform.h @@ -159,7 +159,7 @@ struct sst_algo_int_control_v2 { struct sst_data { struct platform_device *pdev; struct sst_platform_data *pdata; - char *byte_stream; + struct snd_sst_bytes_v2 *byte_stream; struct mutex lock; }; int sst_register_dsp(struct sst_device *sst);
From: Vinod Koul vinod.koul@intel.com
The DSP has various gain modules in the path, add these as ALSA gain controls
Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com --- sound/soc/intel/sst-atom-controls.c | 173 +++++++++++++++++++++++++++++++++++- sound/soc/intel/sst-atom-controls.h | 119 +++++++++++++++++++++++++ 2 files changed, 291 insertions(+), 1 deletion(-)
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index c11712d..5d7120f 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -175,6 +175,166 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol, return ret; }
+static int sst_gain_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = mc->stereo ? 2 : 1; + uinfo->value.integer.min = mc->min; + uinfo->value.integer.max = mc->max; + return 0; +} + +/** + * sst_send_gain_cmd - send the gain algorithm IPC to the FW + * @gv: the stored value of gain (also contains rampduration) + * @mute: flag that indicates whether this was called from the + * digital_mute callback or directly. If called from the + * digital_mute callback, module will be muted/unmuted based on this + * flag. The flag is always 0 if called directly. + * + * The user-set gain value is sent only if the user-controllable 'mute' control + * is OFF (indicated by gv->mute). Otherwise, the mute value (MIN value) is + * sent. + */ +static void sst_send_gain_cmd(struct sst_data *drv, struct sst_gain_value *gv, + u16 task_id, u16 loc_id, u16 module_id, int mute) +{ + struct sst_cmd_set_gain_dual cmd; + + dev_dbg(&drv->pdev->dev, "Enter\n"); + + cmd.header.command_id = MMX_SET_GAIN; + SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); + cmd.gain_cell_num = 1; + + if (mute || gv->mute) { + cmd.cell_gains[0].cell_gain_left = SST_GAIN_MIN_VALUE; + cmd.cell_gains[0].cell_gain_right = SST_GAIN_MIN_VALUE; + } else { + cmd.cell_gains[0].cell_gain_left = gv->l_gain; + cmd.cell_gains[0].cell_gain_right = gv->r_gain; + } + SST_FILL_DESTINATION(2, cmd.cell_gains[0].dest, + loc_id, module_id); + cmd.cell_gains[0].gain_time_constant = gv->ramp_duration; + + cmd.header.length = sizeof(struct sst_cmd_set_gain_dual) + - sizeof(struct sst_dsp_header); + + sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS, + SST_FLAG_BLOCKED, task_id, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); +} + +static int sst_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; + struct sst_gain_value *gv = mc->gain_val; + + switch (mc->type) { + case SST_GAIN_TLV: + ucontrol->value.integer.value[0] = gv->l_gain; + ucontrol->value.integer.value[1] = gv->r_gain; + break; + case SST_GAIN_MUTE: + ucontrol->value.integer.value[0] = gv->mute ? 1 : 0; + break; + case SST_GAIN_RAMP_DURATION: + ucontrol->value.integer.value[0] = gv->ramp_duration; + break; + default: + dev_err(component->dev, "Invalid Input- gain type:%d\n", + mc->type); + return -EINVAL; + }; + return 0; +} + +static int sst_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct sst_data *drv = snd_soc_platform_get_drvdata(platform); + struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; + struct sst_gain_value *gv = mc->gain_val; + + mutex_lock(&drv->lock); + switch (mc->type) { + case SST_GAIN_TLV: + gv->l_gain = ucontrol->value.integer.value[0]; + gv->r_gain = ucontrol->value.integer.value[1]; + dev_dbg(platform->dev, "%s: Volume %d, %d\n", + mc->pname, gv->l_gain, gv->r_gain); + break; + case SST_GAIN_MUTE: + gv->mute = !!ucontrol->value.integer.value[0]; + dev_dbg(platform->dev, "%s: Mute %d\n", mc->pname, gv->mute); + break; + case SST_GAIN_RAMP_DURATION: + gv->ramp_duration = ucontrol->value.integer.value[0]; + dev_dbg(platform->dev, "%s: RampDuration %d\n", + mc->pname, gv->ramp_duration); + break; + default: + mutex_unlock(&drv->lock); + dev_err(platform->dev, "Invalid Input- gain type:%d\n", + mc->type); + return -EINVAL; + }; + + if (mc->w && mc->w->power) + sst_send_gain_cmd(drv, gv, mc->task_id, + mc->pipe_id | mc->instance_id, mc->module_id, 0); + mutex_unlock(&drv->lock); + return 0; +} + +static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0); + +/*Gain helper with min/max set*/ +#define SST_GAIN(name, path_id, task_id, instance, gain_var) \ + SST_GAIN_KCONTROLS(name, "gain", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \ + SST_GAIN_TC_MIN, SST_GAIN_TC_MAX, \ + sst_gain_get, sst_gain_put, \ + SST_MODULE_ID_GAIN_CELL, path_id, instance, task_id, \ + sst_gain_tlv_common, gain_var) + +#define SST_VOLUME(name, path_id, task_id, instance, gain_var) \ + SST_GAIN_KCONTROLS(name, "volume", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \ + SST_GAIN_TC_MIN, SST_GAIN_TC_MAX, \ + sst_gain_get, sst_gain_put, \ + SST_MODULE_ID_VOLUME, path_id, instance, task_id, \ + sst_gain_tlv_common, gain_var) + +#define SST_NUM_GAINS 36 +static struct sst_gain_value sst_gains[SST_NUM_GAINS]; + +static const struct snd_kcontrol_new sst_gain_controls[] = { + SST_GAIN("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[0]), + SST_GAIN("media1_in", SST_PATH_INDEX_MEDIA1_IN, SST_TASK_MMX, 0, &sst_gains[1]), + SST_GAIN("media2_in", SST_PATH_INDEX_MEDIA2_IN, SST_TASK_MMX, 0, &sst_gains[2]), + SST_GAIN("media3_in", SST_PATH_INDEX_MEDIA3_IN, SST_TASK_MMX, 0, &sst_gains[3]), + + SST_GAIN("pcm0_in", SST_PATH_INDEX_PCM0_IN, SST_TASK_SBA, 0, &sst_gains[4]), + SST_GAIN("pcm1_in", SST_PATH_INDEX_PCM1_IN, SST_TASK_SBA, 0, &sst_gains[5]), + SST_GAIN("pcm1_out", SST_PATH_INDEX_PCM1_OUT, SST_TASK_SBA, 0, &sst_gains[7]), + SST_GAIN("pcm2_out", SST_PATH_INDEX_PCM2_OUT, SST_TASK_SBA, 0, &sst_gains[8]), + + SST_GAIN("codec_in0", SST_PATH_INDEX_CODEC_IN0, SST_TASK_SBA, 0, &sst_gains[20]), + SST_GAIN("codec_in1", SST_PATH_INDEX_CODEC_IN1, SST_TASK_SBA, 0, &sst_gains[21]), + SST_GAIN("codec_out0", SST_PATH_INDEX_CODEC_OUT0, SST_TASK_SBA, 0, &sst_gains[22]), + SST_GAIN("codec_out1", SST_PATH_INDEX_CODEC_OUT1, SST_TASK_SBA, 0, &sst_gains[23]), + SST_GAIN("media_loop1_out", SST_PATH_INDEX_MEDIA_LOOP1_OUT, SST_TASK_SBA, 0, &sst_gains[30]), + SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[31]), + SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[32]), + SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[33]), +}; + static const struct snd_kcontrol_new sst_algo_controls[] = { SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 272, SST_MODULE_ID_FIR_24, SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR), @@ -199,7 +359,7 @@ static const struct snd_kcontrol_new sst_algo_controls[] = {
int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) { - int ret = 0; + int i, ret = 0; struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
drv->byte_stream = devm_kzalloc(platform->dev, @@ -207,6 +367,17 @@ int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) if (!drv->byte_stream) return -ENOMEM;
+ + for (i = 0; i < SST_NUM_GAINS; i++) { + sst_gains[i].mute = SST_GAIN_MUTE_DEFAULT; + sst_gains[i].l_gain = SST_GAIN_VOLUME_DEFAULT; + sst_gains[i].r_gain = SST_GAIN_VOLUME_DEFAULT; + sst_gains[i].ramp_duration = SST_GAIN_RAMP_DURATION_DEFAULT; + } + + snd_soc_add_platform_controls(platform, sst_gain_controls, + ARRAY_SIZE(sst_gain_controls)); + snd_soc_add_platform_controls(platform, sst_algo_controls, ARRAY_SIZE(sst_algo_controls)); return ret; diff --git a/sound/soc/intel/sst-atom-controls.h b/sound/soc/intel/sst-atom-controls.h index 7007010..6431c1a 100644 --- a/sound/soc/intel/sst-atom-controls.h +++ b/sound/soc/intel/sst-atom-controls.h @@ -360,16 +360,135 @@ struct sst_dsp_header { struct sst_cmd_generic { struct sst_dsp_header header; } __packed; + +struct gain_cell { + struct sst_destination_id dest; + s16 cell_gain_left; + s16 cell_gain_right; + u16 gain_time_constant; +} __packed; + +#define NUM_GAIN_CELLS 1 +struct sst_cmd_set_gain_dual { + struct sst_dsp_header header; + u16 gain_cell_num; + struct gain_cell cell_gains[NUM_GAIN_CELLS]; +} __packed; struct sst_cmd_set_params { struct sst_destination_id dst; u16 command_id; char params[0]; } __packed; +/**** widget defines *****/ + +#include <sound/soc.h> +#include <sound/tlv.h> +struct sst_ids { + u16 location_id; + u16 module_id; + u8 task_id; + u8 format; + u8 reg; + const char *parent_wname; + struct snd_soc_dapm_widget *parent_w; + struct list_head algo_list; + struct list_head gain_list; + const struct sst_pcm_format *pcm_fmt; +}; +enum sst_gain_kcontrol_type { + SST_GAIN_TLV, + SST_GAIN_MUTE, + SST_GAIN_RAMP_DURATION, +}; + +struct sst_gain_mixer_control { + bool stereo; + enum sst_gain_kcontrol_type type; + struct sst_gain_value *gain_val; + int max; + int min; + u16 instance_id; + u16 module_id; + u16 pipe_id; + u16 task_id; + char pname[44]; + struct snd_soc_dapm_widget *w; +}; + +struct sst_gain_value { + u16 ramp_duration; + s16 l_gain; + s16 r_gain; + bool mute; +}; +#define SST_GAIN_VOLUME_DEFAULT (-1440) +#define SST_GAIN_RAMP_DURATION_DEFAULT 5 /* timeconstant */ +#define SST_GAIN_MUTE_DEFAULT true + +#define SST_GAIN_KCONTROL_TLV(xname, xhandler_get, xhandler_put, \ + xmod, xpipe, xinstance, xtask, tlv_array, xgain_val, \ + xmin, xmax, xpname) \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = sst_gain_ctl_info,\ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct sst_gain_mixer_control) \ + { .stereo = true, .max = xmax, .min = xmin, .type = SST_GAIN_TLV, \ + .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\ + .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname} + +#define SST_GAIN_KCONTROL_INT(xname, xhandler_get, xhandler_put, \ + xmod, xpipe, xinstance, xtask, xtype, xgain_val, \ + xmin, xmax, xpname) \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = sst_gain_ctl_info, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct sst_gain_mixer_control) \ + { .stereo = false, .max = xmax, .min = xmin, .type = xtype, \ + .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\ + .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname} + +#define SST_GAIN_KCONTROL_BOOL(xname, xhandler_get, xhandler_put,\ + xmod, xpipe, xinstance, xtask, xgain_val, xpname) \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_bool_ext, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct sst_gain_mixer_control) \ + { .stereo = false, .type = SST_GAIN_MUTE, \ + .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\ + .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname} #define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \ xpname " " xmname " " #xinstance " " xtype
#define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \ xpname " " xmname " " #xinstance " " xtype " " xsubmodule + +/* + * 3 Controls for each Gain module + * e.g. - pcm0_in gain 0 volume + * - pcm0_in gain 0 rampduration + * - pcm0_in gain 0 mute + */ +#define SST_GAIN_KCONTROLS(xpname, xmname, xmin_gain, xmax_gain, xmin_tc, xmax_tc, \ + xhandler_get, xhandler_put, \ + xmod, xpipe, xinstance, xtask, tlv_array, xgain_val) \ + { SST_GAIN_KCONTROL_INT(SST_CONTROL_NAME(xpname, xmname, xinstance, "rampduration"), \ + xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, SST_GAIN_RAMP_DURATION, \ + xgain_val, xmin_tc, xmax_tc, xpname) }, \ + { SST_GAIN_KCONTROL_BOOL(SST_CONTROL_NAME(xpname, xmname, xinstance, "mute"), \ + xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, \ + xgain_val, xpname) } ,\ + { SST_GAIN_KCONTROL_TLV(SST_CONTROL_NAME(xpname, xmname, xinstance, "volume"), \ + xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, tlv_array, \ + xgain_val, xmin_gain, xmax_gain, xpname) } + +#define SST_GAIN_TC_MIN 5 +#define SST_GAIN_TC_MAX 5000 +#define SST_GAIN_MIN_VALUE -1440 /* in 0.1 DB units */ +#define SST_GAIN_MAX_VALUE 360 + enum sst_algo_kcontrol_type { SST_ALGO_PARAMS, SST_ALGO_BYPASS,
From: Vinod Koul vinod.koul@intel.com
When we have PCM (FE/BE) opened or DAPM widgets triggered we need power up/down DSP accordingly. The DSP will do ref count of these requests i.e. link these runtime_get/put calls of DSP
Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com --- sound/soc/intel/sst-mfld-platform-pcm.c | 16 ++++++++++++++++ sound/soc/intel/sst-mfld-platform.h | 1 + 2 files changed, 17 insertions(+)
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index 9906b7c..6f5edd6 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -322,6 +322,16 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream)
}
+static int power_up_sst(struct sst_runtime_stream *stream) +{ + return stream->ops->power(sst->dev, true); +} + +static void power_down_sst(struct sst_runtime_stream *stream) +{ + stream->ops->power(sst->dev, false); +} + static int sst_media_open(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -351,6 +361,10 @@ static int sst_media_open(struct snd_pcm_substream *substream, /* allocate memory for SST API set */ runtime->private_data = stream;
+ ret_val = power_up_sst(stream); + if (ret_val < 0) + return ret_val; + /* Make sure, that the period size is always even */ snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS, 2); @@ -370,6 +384,8 @@ static void sst_media_close(struct snd_pcm_substream *substream, int ret_val = 0, str_id;
stream = substream->runtime->private_data; + power_down_sst(stream); + str_id = stream->stream_info.str_id; if (str_id) ret_val = stream->ops->close(sst->dev, str_id); diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h index 4b617e5..e23b98f 100644 --- a/sound/soc/intel/sst-mfld-platform.h +++ b/sound/soc/intel/sst-mfld-platform.h @@ -122,6 +122,7 @@ struct sst_ops { int (*stream_read_tstamp) (struct device *dev, struct pcm_stream_info *str_info); int (*send_byte_stream)(struct device *dev, struct snd_sst_bytes_v2 *bytes); int (*close) (struct device *dev, unsigned int str_id); + int (*power)(struct device *dev, bool state); };
struct sst_runtime_stream {
From: Vinod Koul vinod.koul@intel.com
This patch adds core controls like interleavers, SSP BEs, and also logic of sending pipeline and module commands to the DSP.
Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com --- sound/soc/intel/sst-atom-controls.c | 645 +++++++++++++++++++++++++++++++++++- sound/soc/intel/sst-atom-controls.h | 309 +++++++++++++++++ 2 files changed, 953 insertions(+), 1 deletion(-)
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index 5d7120f..267947d 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -80,6 +80,156 @@ static int sst_fill_and_send_cmd(struct sst_data *drv, return ret; }
+/* + * slot map value is a bitfield where each bit represents a FW channel + * + * 3 2 1 0 # 0 = codec0, 1 = codec1 + * RLRLRLRL # 3, 4 = reserved + * + * e.g. slot 0 rx map = 00001100b -> data from slot 0 goes into codec_in1 L,R + */ +static u8 sst_ssp_slot_map[SST_MAX_TDM_SLOTS] = { + 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /*default rx map*/ +}; + +/* + * channel map value is a bitfield where each bit represents a slot + * + * 76543210 # 0 = slot 0, 1 = slot 1 + * + * e.g. codec1_0 tx map = 00000101b -> data from codec_out1_0 goes into slot 0, 2 + */ +static u8 sst_ssp_channel_map[SST_MAX_TDM_SLOTS] = { + 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /*default tx map*/ +}; + +static void sst_send_slot_map(struct sst_data *drv) +{ + struct sst_param_sba_ssp_slot_map cmd; + + dev_dbg(&drv->pdev->dev, "Enter\n"); + + SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); + cmd.header.command_id = SBA_SET_SSP_SLOT_MAP; + cmd.header.length = sizeof(struct sst_param_sba_ssp_slot_map) + - sizeof(struct sst_dsp_header); + + cmd.param_id = SBA_SET_SSP_SLOT_MAP; + cmd.param_len = sizeof(cmd.rx_slot_map) + sizeof(cmd.tx_slot_map) + sizeof(cmd.ssp_index); + cmd.ssp_index = SSP_CODEC; + + memcpy(cmd.rx_slot_map, &sst_ssp_slot_map[0], sizeof(cmd.rx_slot_map)); + memcpy(cmd.tx_slot_map, &sst_ssp_channel_map[0], sizeof(cmd.tx_slot_map)); + + sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS, + SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); +} + +int sst_slot_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct sst_enum *e = (struct sst_enum *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = e->max; + + if (uinfo->value.enumerated.item > e->max - 1) + uinfo->value.enumerated.item = e->max - 1; + strcpy(uinfo->value.enumerated.name, + e->texts[uinfo->value.enumerated.item]); + return 0; +} + +/** + * sst_slot_get - get the status of the interleaver/deinterleaver control + * + * Searches the map where the control status is stored, and gets the + * channel/slot which is currently set for this enumerated control. Since it is + * an enumerated control, there is only one possible value. + */ +static int sst_slot_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sst_enum *e = (void *)kcontrol->private_value; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + unsigned int ctl_no = e->reg; + unsigned int is_tx = e->tx; + unsigned int val, mux; + u8 *map = is_tx ? sst_ssp_channel_map : sst_ssp_slot_map; + + val = 1 << ctl_no; + /*search which slot/channel has this bit set - there should be only one*/ + for (mux = e->max; mux > 0; mux--) + if (map[mux - 1] & val) + break; + + ucontrol->value.enumerated.item[0] = mux; + dev_dbg(component->dev, "%s - %s map = %#x\n", + is_tx ? "tx channel" : "rx slot", + e->texts[mux], mux ? map[mux - 1] : -1); + return 0; +} + +/** + * sst_slot_put - set the status of interleaver/deinterleaver control + * + * (de)interleaver controls are defined in opposite sense to be user-friendly + * + * Instead of the enum value being the value written to the register, it is the + * register address; and the kcontrol number (register num) is the value written + * to the register. This is so that there can be only one value for each + * slot/channel since there is only one control for each slot/channel. + * + * This means that whenever an enum is set, we need to clear the bit + * for that kcontrol_no for all the interleaver OR deinterleaver registers + */ +static int sst_slot_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct sst_data *drv = snd_soc_platform_get_drvdata(platform); + struct sst_enum *e = (void *)kcontrol->private_value; + int i; + unsigned int ctl_no = e->reg; + unsigned int is_tx = e->tx; + unsigned int slot_channel_no; + unsigned int val, mux; + u8 *map; + + mutex_lock(&drv->lock); + map = is_tx ? sst_ssp_channel_map : sst_ssp_slot_map; + + val = 1 << ctl_no; + mux = ucontrol->value.enumerated.item[0]; + if (mux > e->max - 1) { + mutex_unlock(&drv->lock); + return -EINVAL; + } + + + /*first clear all registers of this bit*/ + for (i = 0; i < e->max; i++) + map[i] &= ~val; + + if (mux == 0) /*kctl set to 'none'*/ + return 0; + + /*offset by one to take "None" into account*/ + slot_channel_no = mux - 1; + map[slot_channel_no] |= val; + + dev_dbg(platform->dev, "%s %s map = %#x\n", + is_tx ? "tx channel" : "rx slot", + e->texts[mux], map[slot_channel_no]); + + if (e->w && e->w->power) + sst_send_slot_map(drv); + mutex_unlock(&drv->lock); + return 0; +} + static int sst_send_algo_cmd(struct sst_data *drv, struct sst_algo_control *bc) { @@ -103,7 +253,29 @@ static int sst_send_algo_cmd(struct sst_data *drv, return ret; }
-static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol, +/** + * sst_find_and_send_pipe_algo - send all the algo parameters for a pipe + * + * The algos which are in each pipeline are sent to the firmware one by one + */ +static void sst_find_and_send_pipe_algo(struct sst_data *drv, + const char *pipe, struct sst_ids *ids) +{ + struct sst_algo_control *bc; + struct sst_module *algo = NULL; + + dev_dbg(&drv->pdev->dev, "Enter: widget=%s\n", pipe); + + list_for_each_entry(algo, &ids->algo_list, node) { + bc = (void *)algo->kctl->private_value; + + dev_dbg(&drv->pdev->dev, "Found algo control name=%s pipe=%s\n", + algo->kctl->id.name, pipe); + sst_send_algo_cmd(drv, bc); + } +} + +int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct sst_algo_control *bc = (void *)kcontrol->private_value; @@ -294,8 +466,271 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, return 0; }
+static void sst_set_pipe_gain(struct sst_ids *ids, struct sst_data *drv, int mute); + +static void sst_send_pipe_module_params(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol) +{ + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct sst_data *drv = snd_soc_platform_get_drvdata(platform); + struct sst_ids *ids = w->priv; + + sst_find_and_send_pipe_algo(drv, w->name, ids); + sst_set_pipe_gain(ids, drv, 0); +} + +static int sst_generic_modules_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) + sst_send_pipe_module_params(w, k); + return 0; +} + static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0);
+/*Look up table to convert MIXER SW bit regs to SWM inputs*/ +static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = { + [SST_IP_CODEC0] = SST_SWM_IN_CODEC0, + [SST_IP_CODEC1] = SST_SWM_IN_CODEC1, + [SST_IP_LOOP0] = SST_SWM_IN_SPROT_LOOP, + [SST_IP_LOOP1] = SST_SWM_IN_MEDIA_LOOP1, + [SST_IP_LOOP2] = SST_SWM_IN_MEDIA_LOOP2, + [SST_IP_PCM0] = SST_SWM_IN_PCM0, + [SST_IP_PCM1] = SST_SWM_IN_PCM1, + [SST_IP_MEDIA0] = SST_SWM_IN_MEDIA0, + [SST_IP_MEDIA1] = SST_SWM_IN_MEDIA1, + [SST_IP_MEDIA2] = SST_SWM_IN_MEDIA2, + [SST_IP_MEDIA3] = SST_SWM_IN_MEDIA3, +}; +static void sst_set_pipe_gain(struct sst_ids *ids, struct sst_data *drv, int mute) +{ + struct sst_gain_mixer_control *mc; + struct sst_gain_value *gv; + struct sst_module *gain = NULL; + + list_for_each_entry(gain, &ids->gain_list, node) { + struct snd_kcontrol *kctl = gain->kctl; + + dev_dbg(&drv->pdev->dev, "control name=%s\n", kctl->id.name); + mc = (void *)kctl->private_value; + gv = mc->gain_val; + + sst_send_gain_cmd(drv, gv, mc->task_id, + mc->pipe_id | mc->instance_id, mc->module_id, mute); + } +} +void sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable) +{ + struct sst_cmd_generic cmd; + struct sst_data *drv = snd_soc_dai_get_drvdata(dai); + static int timer_usage; + + if (enable) + cmd.header.command_id = SBA_VB_START; + else + cmd.header.command_id = SBA_IDLE; + dev_dbg(dai->dev, "enable=%u, usage=%d\n", enable, timer_usage); + + SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); + cmd.header.length = 0; + + if (enable) + sst->ops->power(sst->dev, true); + + mutex_lock(&drv->lock); + if (enable) + timer_usage++; + else + timer_usage--; + + /* + * Send the command only if this call is the first enable or last + * disable + */ + if ((enable && (timer_usage == 1)) || + (!enable && (timer_usage == 0))) + sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, + SST_TASK_SBA, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); + mutex_unlock(&drv->lock); + + if (!enable) + sst->ops->power(sst->dev, false); +} + +/** + * sst_ssp_config - contains SSP configuration for media UC + */ +static const struct sst_ssp_config sst_ssp_configs = { + .ssp_id = SSP_CODEC, + .bits_per_slot = 24, + .slots = 4, + .ssp_mode = SSP_MODE_MASTER, + .pcm_mode = SSP_PCM_MODE_NETWORK, + .duplex = SSP_DUPLEX, + .ssp_protocol = SSP_MODE_PCM, + .fs_width = 1, + .fs_frequency = SSP_FS_48_KHZ, + .active_slot_map = 0xF, + .start_delay = 0, +}; + +void send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable) +{ + struct sst_cmd_sba_hw_set_ssp cmd; + struct sst_data *drv = snd_soc_dai_get_drvdata(dai); + const struct sst_ssp_config *config; + + dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id); + + SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); + cmd.header.command_id = SBA_HW_SET_SSP; + cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp) + - sizeof(struct sst_dsp_header); + + config = &sst_ssp_configs; + dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id); + + if (enable) + cmd.switch_state = SST_SWITCH_ON; + else + cmd.switch_state = SST_SWITCH_OFF; + + cmd.selection = config->ssp_id; + cmd.nb_bits_per_slots = config->bits_per_slot; + cmd.nb_slots = config->slots; + cmd.mode = config->ssp_mode | (config->pcm_mode << 1); + cmd.duplex = config->duplex; + cmd.active_tx_slot_map = config->active_slot_map; + cmd.active_rx_slot_map = config->active_slot_map; + cmd.frame_sync_frequency = config->fs_frequency; + cmd.frame_sync_polarity = SSP_FS_ACTIVE_HIGH; + cmd.data_polarity = 1; + cmd.frame_sync_width = config->fs_width; + cmd.ssp_protocol = config->ssp_protocol; + cmd.start_delay = config->start_delay; + cmd.reserved1 = cmd.reserved2 = 0xFF; + + sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, + SST_TASK_SBA, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); +} + +static int sst_set_be_modules(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(k); + struct sst_data *drv = snd_soc_platform_get_drvdata(platform); + + dev_dbg(platform->dev, "Enter: widget=%s\n", w->name); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + sst_send_slot_map(drv); + sst_send_pipe_module_params(w, k); + } + return 0; +} + +static int sst_set_media_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct sst_cmd_set_media_path cmd; + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(k); + struct sst_data *drv = snd_soc_platform_get_drvdata(platform); + struct sst_ids *ids = w->priv; + + dev_dbg(platform->dev, "widget=%s\n", w->name); + dev_dbg(platform->dev, "task=%u, location=%#x\n", + ids->task_id, ids->location_id); + + if (SND_SOC_DAPM_EVENT_ON(event)) + cmd.switch_state = SST_PATH_ON; + else + cmd.switch_state = SST_PATH_OFF; + + SST_FILL_DESTINATION(2, cmd.header.dst, + ids->location_id, SST_DEFAULT_MODULE_ID); + + /*MMX_SET_MEDIA_PATH == SBA_SET_MEDIA_PATH*/ + cmd.header.command_id = MMX_SET_MEDIA_PATH; + cmd.header.length = sizeof(struct sst_cmd_set_media_path) + - sizeof(struct sst_dsp_header); + + sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, + ids->task_id, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); + + if (SND_SOC_DAPM_EVENT_ON(event)) + sst_send_pipe_module_params(w, k); + return 0; +} + +static int sst_set_media_loop(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct sst_cmd_sba_set_media_loop_map cmd; + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(k); + struct sst_data *drv = snd_soc_platform_get_drvdata(platform); + struct sst_ids *ids = w->priv; + + dev_dbg(platform->dev, "Enter:widget=%s\n", w->name); + if (SND_SOC_DAPM_EVENT_ON(event)) + cmd.switch_state = SST_SWITCH_ON; + else + cmd.switch_state = SST_SWITCH_OFF; + + SST_FILL_DESTINATION(2, cmd.header.dst, + ids->location_id, SST_DEFAULT_MODULE_ID); + + cmd.header.command_id = SBA_SET_MEDIA_LOOP_MAP; + cmd.header.length = sizeof(struct sst_cmd_sba_set_media_loop_map) + - sizeof(struct sst_dsp_header); + cmd.param.part.cfg.rate = 2; /*48khz*/ + + cmd.param.part.cfg.format = ids->format; /*stereo/Mono*/ + cmd.param.part.cfg.s_length = 1; /*24bit left justified*/ + cmd.map = 0; /*Algo sequence: Gain - DRP - FIR - IIR*/ + + sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, + SST_TASK_SBA, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); + if (SND_SOC_DAPM_EVENT_ON(event)) + sst_send_pipe_module_params(w, k); + return 0; +} + +static const char * const slot_names[] = { + "none", + "slot 0", "slot 1", "slot 2", "slot 3", + "slot 4", "slot 5", "slot 6", "slot 7", /*not supported by FW*/ +}; + +static const char * const channel_names[] = { + "none", + "codec_out0_0", "codec_out0_1", "codec_out1_0", "codec_out1_1", + "codec_out2_0", "codec_out2_1", "codec_out3_0", "codec_out3_1", /*not supported by FW*/ +}; + +#define SST_INTERLEAVER(xpname, slot_name, slotno) \ + SST_SSP_SLOT_CTL(xpname, "interleaver", slot_name, slotno, true, \ + channel_names, sst_slot_get, sst_slot_put) + +#define SST_DEINTERLEAVER(xpname, channel_name, channel_no) \ + SST_SSP_SLOT_CTL(xpname, "deinterleaver", channel_name, channel_no, false, \ + slot_names, sst_slot_get, sst_slot_put) + +static const struct snd_kcontrol_new sst_slot_controls[] = { + SST_INTERLEAVER("codec_out", "slot 0", 0), + SST_INTERLEAVER("codec_out", "slot 1", 1), + SST_INTERLEAVER("codec_out", "slot 2", 2), + SST_INTERLEAVER("codec_out", "slot 3", 3), + SST_DEINTERLEAVER("codec_in", "codec_in0_0", 0), + SST_DEINTERLEAVER("codec_in", "codec_in0_1", 1), + SST_DEINTERLEAVER("codec_in", "codec_in1_0", 2), + SST_DEINTERLEAVER("codec_in", "codec_in1_1", 3), +}; + /*Gain helper with min/max set*/ #define SST_GAIN(name, path_id, task_id, instance, gain_var) \ SST_GAIN_KCONTROLS(name, "gain", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \ @@ -357,6 +792,209 @@ static const struct snd_kcontrol_new sst_algo_controls[] = {
};
+static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w) +{ + if ((w->id == snd_soc_dapm_pga) || + (w->id == snd_soc_dapm_aif_in) || + (w->id == snd_soc_dapm_aif_out) || + (w->id == snd_soc_dapm_input) || + (w->id == snd_soc_dapm_output) || + (w->id == snd_soc_dapm_mixer)) + return true; + else + return false; +} + +/** + * sst_send_pipe_gains - send gains for the front-end DAIs + * + * The gains in the pipes connected to the front-ends are muted/unmuted + * automatically via the digital_mute() DAPM callback. This function sends the + * gains for the front-end pipes. + */ +int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) +{ + struct sst_data *drv = snd_soc_dai_get_drvdata(dai); + struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_path *p = NULL; + + dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + dev_dbg(dai->dev, "Stream name=%s\n", + dai->playback_widget->name); + w = dai->playback_widget; + list_for_each_entry(p, &w->sinks, list_source) { + if (p->connected && !p->connected(w, p->sink)) + continue; + + if (p->connect && p->sink->power && is_sst_dapm_widget(p->sink)) { + struct sst_ids *ids = p->sink->priv; + + dev_dbg(dai->dev, "send gains for widget=%s\n", + p->sink->name); + sst_set_pipe_gain(ids, drv, mute); + } + } + } else { + dev_dbg(dai->dev, "Stream name=%s\n", + dai->capture_widget->name); + w = dai->capture_widget; + list_for_each_entry(p, &w->sources, list_sink) { + if (p->connected && !p->connected(w, p->sink)) + continue; + + if (p->connect && p->source->power && is_sst_dapm_widget(p->source)) { + struct sst_ids *ids = p->source->priv; + + dev_dbg(dai->dev, "send gain for widget=%s\n", + p->source->name); + sst_set_pipe_gain(ids, drv, mute); + } + } + } + return 0; +} + +/** + * sst_fill_module_list - populate the list of modules/gains for a pipe + * + * + * Fills the widget pointer in the kcontrol private data, and also fills the + * kcontrol pointer in the widget private data. + * + * Widget pointer is used to send the algo/gain in the .put() handler if the + * widget is powerd on. + * + * Kcontrol pointer is used to send the algo/gain in the widget power ON/OFF + * event handler. Each widget (pipe) has multiple algos stored in the algo_list. + */ +static int sst_fill_module_list(struct snd_kcontrol *kctl, + struct snd_soc_dapm_widget *w, int type) +{ + struct sst_module *module = NULL; + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kctl); + struct sst_ids *ids = w->priv; + + module = devm_kzalloc(platform->dev, sizeof(*module), GFP_KERNEL); + if (!module) + return -ENOMEM; + + if (type == SST_MODULE_GAIN) { + struct sst_gain_mixer_control *mc = (void *)kctl->private_value; + + mc->w = w; + module->kctl = kctl; + list_add_tail(&module->node, &ids->gain_list); + } else if (type == SST_MODULE_ALGO) { + struct sst_algo_control *bc = (void *)kctl->private_value; + + bc->w = w; + module->kctl = kctl; + list_add_tail(&module->node, &ids->algo_list); + } + + return 0; +} + +/** + * sst_fill_widget_module_info - fill list of gains/algos for the pipe + * @widget: pipe modelled as a DAPM widget + * + * Fill the list of gains/algos for the widget by looking at all the card + * controls and comparing the name of the widget with the first part of control + * name. First part of control name contains the pipe name (widget name). + */ +static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w, + struct snd_soc_platform *platform) +{ + struct snd_kcontrol *kctl; + int index, ret = 0; + struct snd_card *card = platform->component.card->snd_card; + char *idx; + + down_read(&card->controls_rwsem); + + list_for_each_entry(kctl, &card->controls, list) { + idx = strstr(kctl->id.name, " "); + if (idx == NULL) + continue; + index = strlen(kctl->id.name) - strlen(idx); + if (strstr(kctl->id.name, "volume") && + !strncmp(kctl->id.name, w->name, index)) + ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN); + else if (strstr(kctl->id.name, "params") && + !strncmp(kctl->id.name, w->name, index)) + ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO); + else if (strstr(kctl->id.name, "mute") && + !strncmp(kctl->id.name, w->name, index)) { + struct sst_gain_mixer_control *mc = (void *)kctl->private_value; + + mc->w = w; + } else if (strstr(kctl->id.name, "interleaver") && + !strncmp(kctl->id.name, w->name, index)) { + struct sst_enum *e = (void *)kctl->private_value; + + e->w = w; + } else if (strstr(kctl->id.name, "deinterleaver") && + !strncmp(kctl->id.name, w->name, index)) { + + struct sst_enum *e = (void *)kctl->private_value; + + e->w = w; + } + if (ret < 0) { + up_read(&card->controls_rwsem); + return ret; + } + } + up_read(&card->controls_rwsem); + return 0; +} + +/** + * sst_fill_linked_widgets - fill the parent pointer for the linked widget + */ +static void sst_fill_linked_widgets(struct snd_soc_platform *platform, + struct sst_ids *ids) +{ + struct snd_soc_dapm_widget *w; + unsigned int len = strlen(ids->parent_wname); + + list_for_each_entry(w, &platform->component.card->widgets, list) { + if (!strncmp(ids->parent_wname, w->name, len)) { + ids->parent_w = w; + break; + } + } +} + +/** + * sst_map_modules_to_pipe - fill algo/gains list for all pipes + */ +static int sst_map_modules_to_pipe(struct snd_soc_platform *platform) +{ + struct snd_soc_dapm_widget *w; + int ret = 0; + + list_for_each_entry(w, &platform->component.card->widgets, list) { + if (platform && is_sst_dapm_widget(w) && (w->priv)) { + struct sst_ids *ids = w->priv; + + dev_dbg(platform->dev, "widget type=%d name=%s\n", + w->id, w->name); + INIT_LIST_HEAD(&ids->algo_list); + INIT_LIST_HEAD(&ids->gain_list); + ret = sst_fill_widget_module_info(w, platform); + if (ret < 0) + return ret; + /*fill linked widgets*/ + if (ids->parent_wname != NULL) + sst_fill_linked_widgets(platform, ids); + } + } + return 0; +} int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) { int i, ret = 0; @@ -380,5 +1018,10 @@ int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
snd_soc_add_platform_controls(platform, sst_algo_controls, ARRAY_SIZE(sst_algo_controls)); + snd_soc_add_platform_controls(platform, sst_slot_controls, + ARRAY_SIZE(sst_slot_controls)); + + ret = sst_map_modules_to_pipe(platform); + return ret; } diff --git a/sound/soc/intel/sst-atom-controls.h b/sound/soc/intel/sst-atom-controls.h index 6431c1a..da484d3 100644 --- a/sound/soc/intel/sst-atom-controls.h +++ b/sound/soc/intel/sst-atom-controls.h @@ -361,6 +361,38 @@ struct sst_cmd_generic { struct sst_dsp_header header; } __packed;
+struct swm_input_ids { + struct sst_destination_id input_id; +} __packed; + +struct sst_cmd_set_swm { + struct sst_dsp_header header; + struct sst_destination_id output_id; + u16 switch_state; + u16 nb_inputs; + struct swm_input_ids input[SST_CMD_SWM_MAX_INPUTS]; +} __packed; + +struct sst_cmd_set_media_path { + struct sst_dsp_header header; + u16 switch_state; +} __packed; + +struct pcm_cfg { + u8 s_length:2; + u8 rate:3; + u8 format:3; +} __packed; + +struct sst_cmd_set_speech_path { + struct sst_dsp_header header; + u16 switch_state; + struct { + u16 rsvd:8; + struct pcm_cfg cfg; + } config; +} __packed; + struct gain_cell { struct sst_destination_id dest; s16 cell_gain_left; @@ -379,10 +411,166 @@ struct sst_cmd_set_params { u16 command_id; char params[0]; } __packed; + + +struct sst_cmd_sba_vb_start { + struct sst_dsp_header header; +} __packed; + +union sba_media_loop_params { + struct { + u16 rsvd:8; + struct pcm_cfg cfg; + } part; + u16 full; +} __packed; + +struct sst_cmd_sba_set_media_loop_map { + struct sst_dsp_header header; + u16 switch_state; + union sba_media_loop_params param; + u16 map; +} __packed; + +struct sst_cmd_tone_stop { + struct sst_dsp_header header; + u16 switch_state; +} __packed; + +enum sst_ssp_mode { + SSP_MODE_MASTER = 0, + SSP_MODE_SLAVE = 1, +}; + +enum sst_ssp_pcm_mode { + SSP_PCM_MODE_NORMAL = 0, + SSP_PCM_MODE_NETWORK = 1, +}; + +enum sst_ssp_duplex { + SSP_DUPLEX = 0, + SSP_RX = 1, + SSP_TX = 2, +}; + +enum sst_ssp_fs_frequency { + SSP_FS_8_KHZ = 0, + SSP_FS_16_KHZ = 1, + SSP_FS_44_1_KHZ = 2, + SSP_FS_48_KHZ = 3, +}; + +enum sst_ssp_fs_polarity { + SSP_FS_ACTIVE_LOW = 0, + SSP_FS_ACTIVE_HIGH = 1, +}; + +enum sst_ssp_protocol { + SSP_MODE_PCM = 0, + SSP_MODE_I2S = 1, +}; + +enum sst_ssp_port_id { + SSP_MODEM = 0, + SSP_BT = 1, + SSP_FM = 2, + SSP_CODEC = 3, +}; + +struct sst_cmd_sba_hw_set_ssp { + struct sst_dsp_header header; + u16 selection; /* 0:SSP0(def), 1:SSP1, 2:SSP2 */ + + u16 switch_state; + + u16 nb_bits_per_slots:6; /* 0-32 bits, 24 (def) */ + u16 nb_slots:4; /* 0-8: slots per frame */ + u16 mode:3; /* 0:Master, 1: Slave */ + u16 duplex:3; + + u16 active_tx_slot_map:8; /* Bit map, 0:off, 1:on */ + u16 reserved1:8; + + u16 active_rx_slot_map:8; /* Bit map 0: Off, 1:On */ + u16 reserved2:8; + + u16 frame_sync_frequency; + + u16 frame_sync_polarity:8; + u16 data_polarity:8; + + u16 frame_sync_width; /* 1 to N clocks */ + u16 ssp_protocol:8; + u16 start_delay:8; /* Start delay in terms of clock ticks */ +} __packed; + +#define SST_MAX_TDM_SLOTS 8 + +struct sst_param_sba_ssp_slot_map { + struct sst_dsp_header header; + + u16 param_id; + u16 param_len; + u16 ssp_index; + + u8 rx_slot_map[SST_MAX_TDM_SLOTS]; + u8 tx_slot_map[SST_MAX_TDM_SLOTS]; +} __packed; + +enum { + SST_PROBE_EXTRACTOR = 0, + SST_PROBE_INJECTOR = 1, +}; + /**** widget defines *****/
#include <sound/soc.h> #include <sound/tlv.h> + +#define SST_MODULE_GAIN 1 +#define SST_MODULE_ALGO 2 + +#define SST_FMT_MONO 0 +#define SST_FMT_STEREO 3 + +/* physical SSP numbers */ +enum { + SST_SSP0 = 0, + SST_SSP1, + SST_SSP2, + SST_SSP_LAST = SST_SSP2, +}; + +#define SST_NUM_SSPS (SST_SSP_LAST + 1) /* physical SSPs */ +#define SST_MAX_SSP_MUX 2 /* single SSP muxed between pipes */ +#define SST_MAX_SSP_DOMAINS 2 /* domains present in each pipe */ + +struct sst_module { + struct snd_kcontrol *kctl; + struct list_head node; +}; + +struct sst_ssp_config { + u8 ssp_id; + u8 bits_per_slot; + u8 slots; + u8 ssp_mode; + u8 pcm_mode; + u8 duplex; + u8 ssp_protocol; + u8 fs_frequency; + u8 active_slot_map; + u8 start_delay; + u16 fs_width; +}; + +struct sst_ssp_cfg { + const u8 ssp_number; + const int *mux_shift; + const int (*domain_shift)[SST_MAX_SSP_MUX]; + const struct sst_ssp_config (*ssp_config)[SST_MAX_SSP_MUX][SST_MAX_SSP_DOMAINS]; +}; + struct sst_ids { u16 location_id; u16 module_id; @@ -395,6 +583,102 @@ struct sst_ids { struct list_head gain_list; const struct sst_pcm_format *pcm_fmt; }; + + +#define SST_AIF_IN(wname, wevent) \ +{ .id = snd_soc_dapm_aif_in, .name = wname, .sname = NULL, \ + .reg = SND_SOC_NOPM, .shift = 0, \ + .on_val = 1, .off_val = 0, \ + .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ + .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \ +} + +#define SST_AIF_OUT(wname, wevent) \ +{ .id = snd_soc_dapm_aif_out, .name = wname, .sname = NULL, \ + .reg = SND_SOC_NOPM, .shift = 0, \ + .on_val = 1, .off_val = 0, \ + .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ + .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \ +} + +#define SST_INPUT(wname, wevent) \ +{ .id = snd_soc_dapm_input, .name = wname, .sname = NULL, \ + .reg = SND_SOC_NOPM, .shift = 0, \ + .on_val = 1, .off_val = 0, \ + .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ + .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \ +} + +#define SST_OUTPUT(wname, wevent) \ +{ .id = snd_soc_dapm_output, .name = wname, .sname = NULL, \ + .reg = SND_SOC_NOPM, .shift = 0, \ + .on_val = 1, .off_val = 0, \ + .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ + .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \ +} + +#define SST_DAPM_OUTPUT(wname, wloc_id, wtask_id, wformat, wevent) \ +{ .id = snd_soc_dapm_output, .name = wname, .sname = NULL, \ + .reg = SND_SOC_NOPM, .shift = 0, \ + .on_val = 1, .off_val = 0, \ + .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ + .priv = (void *)&(struct sst_ids) { .location_id = wloc_id, .task_id = wtask_id,\ + .pcm_fmt = wformat, } \ +} + +#define SST_PATH(wname, wtask, wloc_id, wevent, wflags) \ +{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \ + .kcontrol_news = NULL, .num_kcontrols = 0, \ + .on_val = 1, .off_val = 0, \ + .event = wevent, .event_flags = wflags, \ + .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, } \ +} + +#define SST_LINKED_PATH(wname, wtask, wloc_id, linked_wname, wevent, wflags) \ +{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \ + .kcontrol_news = NULL, .num_kcontrols = 0, \ + .on_val = 1, .off_val = 0, \ + .event = wevent, .event_flags = wflags, \ + .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \ + .parent_wname = linked_wname} \ +} + +#define SST_PATH_MEDIA_LOOP(wname, wtask, wloc_id, wformat, wevent, wflags) \ +{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \ + .kcontrol_news = NULL, .num_kcontrols = 0, \ + .event = wevent, .event_flags = wflags, \ + .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \ + .format = wformat,} \ +} + +/* output is triggered before input */ +#define SST_PATH_INPUT(name, task_id, loc_id, event) \ + SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) + +#define SST_PATH_LINKED_INPUT(name, task_id, loc_id, linked_wname, event) \ + SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event, \ + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) + +#define SST_PATH_OUTPUT(name, task_id, loc_id, event) \ + SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD) + +#define SST_PATH_LINKED_OUTPUT(name, task_id, loc_id, linked_wname, event) \ + SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event, \ + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD) + +#define SST_PATH_MEDIA_LOOP_OUTPUT(name, task_id, loc_id, format, event) \ + SST_PATH_MEDIA_LOOP(name, task_id, loc_id, format, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD) + + +#define SST_SWM_MIXER(wname, wreg, wtask, wloc_id, wcontrols, wevent) \ +{ .id = snd_soc_dapm_mixer, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols),\ + .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD | \ + SND_SOC_DAPM_POST_REG, \ + .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \ + .reg = wreg } \ +} + enum sst_gain_kcontrol_type { SST_GAIN_TLV, SST_GAIN_MUTE, @@ -558,4 +842,29 @@ struct sst_enum { struct snd_soc_dapm_widget *w; };
+/* only 4 slots/channels supported atm */ +#define SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts) \ + (struct sst_enum){ .reg = s_ch_no, .tx = is_tx, .max = 4+1, .texts = xtexts, } + +#define SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name) \ + xpname " " xmname " " s_ch_name + +#define SST_SSP_SLOT_CTL(xpname, xmname, s_ch_name, s_ch_no, is_tx, xtexts, xget, xput) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name), \ + .info = sst_slot_enum_info, \ + .get = xget, .put = xput, \ + .private_value = (unsigned long)&SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts), \ +} + +#define SST_MUX_CTL_NAME(xpname, xinstance) \ + xpname " " #xinstance + +#define SST_SSP_MUX_ENUM(xreg, xshift, xtexts) \ + (struct soc_enum) SOC_ENUM_DOUBLE(xreg, xshift, xshift, ARRAY_SIZE(xtexts), xtexts) + +#define SST_SSP_MUX_CTL(xpname, xinstance, xreg, xshift, xtexts) \ + SOC_DAPM_ENUM(SST_MUX_CTL_NAME(xpname, xinstance), \ + SST_SSP_MUX_ENUM(xreg, xshift, xtexts)) + #endif
The DSP driver needs to know widget control value in its event handler for widgets like mixers. This is required in the subsequent patches
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/soc-dapm.h | 1 + sound/soc/soc-dapm.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index aac04ff..6ae0a19 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -432,6 +432,7 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin); void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card); +unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol);
/* Mostly internal - should not normally be used */ void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 177bd86..c5b9862 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -326,12 +326,13 @@ static struct list_head *dapm_kcontrol_get_path_list( list_for_each_entry(path, dapm_kcontrol_get_path_list(kcontrol), \ list_kcontrol)
-static unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol) +unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol) { struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
return data->value; } +EXPORT_SYMBOL_GPL(dapm_kcontrol_get_value);
static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, unsigned int value)
From: Vinod Koul vinod.koul@intel.com
This patch adds all DAPM widgets and the event handlers for DSP expect the mixers. Since we are still discussing mixer update and is dependent upon component series
Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com --- sound/soc/intel/sst-atom-controls.c | 226 ++++++++++++++++++++++++++++++++++++ sound/soc/intel/sst-mfld-platform.h | 4 + 2 files changed, 230 insertions(+)
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index 267947d..b0a56f6 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -503,6 +503,41 @@ static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = { [SST_IP_MEDIA2] = SST_SWM_IN_MEDIA2, [SST_IP_MEDIA3] = SST_SWM_IN_MEDIA3, }; + +/** + * fill_swm_input - fill in the SWM input ids given the register + * + * The register value is a bit-field inicated which mixer inputs are ON. Use the + * lookup table to get the input-id and fill it in the structure. + */ +static int fill_swm_input(struct snd_soc_platform *platform, + struct swm_input_ids *swm_input, unsigned int reg) +{ + uint i, is_set, nb_inputs = 0; + u16 input_loc_id; + + dev_dbg(platform->dev, "reg: %#x\n", reg); + for (i = 0; i < SST_SWM_INPUT_COUNT; i++) { + is_set = reg & BIT(i); + if (!is_set) + continue; + + input_loc_id = swm_mixer_input_ids[i]; + SST_FILL_DESTINATION(2, swm_input->input_id, + input_loc_id, SST_DEFAULT_MODULE_ID); + nb_inputs++; + swm_input++; + dev_dbg(platform->dev, "input id: %#x, nb_inputs: %d\n", + input_loc_id, nb_inputs); + + if (nb_inputs == SST_CMD_SWM_MAX_INPUTS) { + dev_warn(platform->dev, "SET_SWM cmd max inputs reached"); + break; + } + } + return nb_inputs; +} + static void sst_set_pipe_gain(struct sst_ids *ids, struct sst_data *drv, int mute) { struct sst_gain_mixer_control *mc; @@ -520,6 +555,113 @@ static void sst_set_pipe_gain(struct sst_ids *ids, struct sst_data *drv, int mut mc->pipe_id | mc->instance_id, mc->module_id, mute); } } + +static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct sst_cmd_set_swm cmd; + struct snd_soc_platform *platform = snd_soc_dapm_to_platform(w->dapm); + struct sst_data *drv = snd_soc_platform_get_drvdata(platform); + struct sst_ids *ids = w->priv; + bool set_mixer = false; + struct soc_mixer_control *mc; + int val = 0; + int i = 0; + + dev_dbg(platform->dev, "widget = %s\n", w->name); + /* + * Identify which mixer input is on and send the bitmap of the + * inputs as an IPC to the DSP. + */ + for (i = 0; i < w->num_kcontrols; i++) { + if (dapm_kcontrol_get_value(w->kcontrols[i])) { + mc = (struct soc_mixer_control *)(w->kcontrols[i])->private_value; + val |= 1 << mc->shift; + } + } + dev_dbg(platform->dev, "val = %#x\n", val); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_POST_PMD: + set_mixer = true; + break; + case SND_SOC_DAPM_POST_REG: + if (w->power) + set_mixer = true; + break; + default: + set_mixer = false; + } + + if (set_mixer == false) + return 0; + + if (SND_SOC_DAPM_EVENT_ON(event) || + event == SND_SOC_DAPM_POST_REG) + cmd.switch_state = SST_SWM_ON; + else + cmd.switch_state = SST_SWM_OFF; + + SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); + /*MMX_SET_SWM == SBA_SET_SWM*/ + cmd.header.command_id = SBA_SET_SWM; + + SST_FILL_DESTINATION(2, cmd.output_id, + ids->location_id, SST_DEFAULT_MODULE_ID); + cmd.nb_inputs = fill_swm_input(platform, &cmd.input[0], val); + cmd.header.length = offsetof(struct sst_cmd_set_swm, input) - sizeof(struct sst_dsp_header) + + (cmd.nb_inputs * sizeof(cmd.input[0])); + + sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, + ids->task_id, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); + return 0; +} + +/*SBA mixers - 16 inputs*/ +#define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name) \ + static const struct snd_kcontrol_new kctl_name[] = { \ + SOC_DAPM_SINGLE("codec_in0", SND_SOC_NOPM, SST_IP_CODEC0, 1, 0), \ + SOC_DAPM_SINGLE("codec_in1", SND_SOC_NOPM, SST_IP_CODEC1, 1, 0), \ + SOC_DAPM_SINGLE("sprot_loop_in", SND_SOC_NOPM, SST_IP_LOOP0, 1, 0), \ + SOC_DAPM_SINGLE("media_loop1_in", SND_SOC_NOPM, SST_IP_LOOP1, 1, 0), \ + SOC_DAPM_SINGLE("media_loop2_in", SND_SOC_NOPM, SST_IP_LOOP2, 1, 0), \ + SOC_DAPM_SINGLE("pcm0_in", SND_SOC_NOPM, SST_IP_PCM0, 1, 0), \ + SOC_DAPM_SINGLE("pcm1_in", SND_SOC_NOPM, SST_IP_PCM1, 1, 0), \ + } + +#define SST_SBA_MIXER_GRAPH_MAP(mix_name) \ + { mix_name, "codec_in0", "codec_in0" }, \ + { mix_name, "codec_in1", "codec_in1" }, \ + { mix_name, "sprot_loop_in", "sprot_loop_in" }, \ + { mix_name, "media_loop1_in", "media_loop1_in" }, \ + { mix_name, "media_loop2_in", "media_loop2_in" }, \ + { mix_name, "pcm0_in", "pcm0_in" }, \ + { mix_name, "pcm1_in", "pcm1_in" } + +#define SST_MMX_DECLARE_MIX_CONTROLS(kctl_name) \ + static const struct snd_kcontrol_new kctl_name[] = { \ + SOC_DAPM_SINGLE("media0_in", SND_SOC_NOPM, SST_IP_MEDIA0, 1, 0), \ + SOC_DAPM_SINGLE("media1_in", SND_SOC_NOPM, SST_IP_MEDIA1, 1, 0), \ + SOC_DAPM_SINGLE("media2_in", SND_SOC_NOPM, SST_IP_MEDIA2, 1, 0), \ + SOC_DAPM_SINGLE("media3_in", SND_SOC_NOPM, SST_IP_MEDIA3, 1, 0), \ + } + +SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media0_controls); +SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media1_controls); + +/*18 SBA mixers*/ +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm0_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm1_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm2_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_sprot_l0_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l1_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_voip_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls); + void sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable) { struct sst_cmd_generic cmd; @@ -700,6 +842,83 @@ static int sst_set_media_loop(struct snd_soc_dapm_widget *w, return 0; }
+static const struct snd_soc_dapm_widget sst_dapm_widgets[] = { + SST_AIF_IN("codec_in0", sst_set_be_modules), + SST_AIF_IN("codec_in1", sst_set_be_modules), + SST_AIF_OUT("codec_out0", sst_set_be_modules), + SST_AIF_OUT("codec_out1", sst_set_be_modules), + + /*Media Paths*/ + /*MediaX IN paths are set via ALLOC, so no SET_MEDIA_PATH command*/ + SST_PATH_INPUT("media0_in", SST_TASK_MMX, SST_SWM_IN_MEDIA0, sst_generic_modules_event), + SST_PATH_INPUT("media1_in", SST_TASK_MMX, SST_SWM_IN_MEDIA1, NULL), + SST_PATH_INPUT("media2_in", SST_TASK_MMX, SST_SWM_IN_MEDIA2, sst_set_media_path), + SST_PATH_INPUT("media3_in", SST_TASK_MMX, SST_SWM_IN_MEDIA3, NULL), + SST_PATH_OUTPUT("media0_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA0, sst_set_media_path), + SST_PATH_OUTPUT("media1_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA1, sst_set_media_path), + + /*SBA PCM Paths*/ + SST_PATH_INPUT("pcm0_in", SST_TASK_SBA, SST_SWM_IN_PCM0, sst_set_media_path), + SST_PATH_INPUT("pcm1_in", SST_TASK_SBA, SST_SWM_IN_PCM1, sst_set_media_path), + SST_PATH_OUTPUT("pcm0_out", SST_TASK_SBA, SST_SWM_OUT_PCM0, sst_set_media_path), + SST_PATH_OUTPUT("pcm1_out", SST_TASK_SBA, SST_SWM_OUT_PCM1, sst_set_media_path), + SST_PATH_OUTPUT("pcm2_out", SST_TASK_SBA, SST_SWM_OUT_PCM2, sst_set_media_path), + + /*SBA Loops*/ + SST_PATH_INPUT("sprot_loop_in", SST_TASK_SBA, SST_SWM_IN_SPROT_LOOP, NULL), + SST_PATH_INPUT("media_loop1_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP1, NULL), + SST_PATH_INPUT("media_loop2_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP2, NULL), + SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_MONO, sst_set_media_loop), + SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_MONO, sst_set_media_loop), + SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop), + + /*Media Mixers*/ +}; + +static const struct snd_soc_dapm_route intercon[] = { + {"media0_in", NULL, "Compress Playback"}, + {"media1_in", NULL, "Headset Playback"}, + {"media2_in", NULL, "pcm0_out"}, + + {"media0_out mix 0", "media0_in", "media0_in"}, + {"media0_out mix 0", "media1_in", "media1_in"}, + {"media0_out mix 0", "media2_in", "media2_in"}, + {"media0_out mix 0", "media3_in", "media3_in"}, + {"media1_out mix 0", "media0_in", "media0_in"}, + {"media1_out mix 0", "media1_in", "media1_in"}, + {"media1_out mix 0", "media2_in", "media2_in"}, + {"media1_out mix 0", "media3_in", "media3_in"}, + + {"media0_out", NULL, "media0_out mix 0"}, + {"media1_out", NULL, "media1_out mix 0"}, + {"pcm0_in", NULL, "media0_out"}, + {"pcm1_in", NULL, "media1_out"}, + + {"Headset Capture", NULL, "pcm1_out"}, + {"Headset Capture", NULL, "pcm2_out"}, + {"pcm0_out", NULL, "pcm0_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("pcm0_out mix 0"), + {"pcm1_out", NULL, "pcm1_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("pcm1_out mix 0"), + {"pcm2_out", NULL, "pcm2_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("pcm2_out mix 0"), + + {"media_loop1_in", NULL, "media_loop1_out"}, + {"media_loop1_out", NULL, "media_loop1_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("media_loop1_out mix 0"), + {"media_loop2_in", NULL, "media_loop2_out"}, + {"media_loop2_out", NULL, "media_loop2_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("media_loop2_out mix 0"), + {"sprot_loop_in", NULL, "sprot_loop_out"}, + {"sprot_loop_out", NULL, "sprot_loop_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("sprot_loop_out mix 0"), + + {"codec_out0", NULL, "codec_out0 mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("codec_out0 mix 0"), + {"codec_out1", NULL, "codec_out1 mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("codec_out1 mix 0"), + +}; static const char * const slot_names[] = { "none", "slot 0", "slot 1", "slot 2", "slot 3", @@ -998,6 +1217,8 @@ static int sst_map_modules_to_pipe(struct snd_soc_platform *platform) int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) { int i, ret = 0; + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(&platform->component); struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
drv->byte_stream = devm_kzalloc(platform->dev, @@ -1005,6 +1226,11 @@ int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) if (!drv->byte_stream) return -ENOMEM;
+ snd_soc_dapm_new_controls(dapm, sst_dapm_widgets, + ARRAY_SIZE(sst_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon, + ARRAY_SIZE(intercon)); + snd_soc_dapm_new_widgets(dapm->card);
for (i = 0; i < SST_NUM_GAINS; i++) { sst_gains[i].mute = SST_GAIN_MUTE_DEFAULT; diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h index e23b98f..f6491f2 100644 --- a/sound/soc/intel/sst-mfld-platform.h +++ b/sound/soc/intel/sst-mfld-platform.h @@ -146,6 +146,10 @@ struct sst_device { struct sst_data;
int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform); +int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute); +void send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable); +void sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable); + void sst_set_stream_status(struct sst_runtime_stream *stream, int state); int sst_fill_stream_params(void *substream, const struct sst_data *ctx, struct snd_sst_params *str_params, bool is_compress);
From: Vinod Koul vinod.koul@intel.com
Now that we have added code for managing DSP pipelines we need to add the code for DSPs FrontEnd and Backend dai.
Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com --- sound/soc/intel/sst-mfld-platform-pcm.c | 149 ++++++++++++++++++++++++++------ 1 file changed, 121 insertions(+), 28 deletions(-)
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index 6f5edd6..5a9e84c 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -101,35 +101,11 @@ static struct sst_dev_stream_map dpcm_strm_map[] = { {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, 0}, };
-/* MFLD - MSIC */ -static struct snd_soc_dai_driver sst_platform_dai[] = { +static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream) { - .name = "Headset-cpu-dai", - .id = 0, - .playback = { - .channels_min = SST_STEREO, - .channels_max = SST_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S24_LE, - }, - .capture = { - .channels_min = 1, - .channels_max = 5, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S24_LE, - }, -}, -{ - .name = "Compress-cpu-dai", - .compress_dai = 1, - .playback = { - .channels_min = SST_STEREO, - .channels_max = SST_STEREO, - .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, -}, -}; + + return sst_send_pipe_gains(dai, stream, mute); +}
/* helper functions */ void sst_set_stream_status(struct sst_runtime_stream *stream, @@ -451,12 +427,129 @@ static int sst_media_hw_free(struct snd_pcm_substream *substream, return snd_pcm_lib_free_pages(substream); }
+static int sst_enable_ssp(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (!dai->active) { + sst_handle_vb_timer(dai, true); + send_ssp_cmd(dai, dai->name, 1); + } + return 0; +} + +static void sst_disable_ssp(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (!dai->active) { + send_ssp_cmd(dai, dai->name, 0); + sst_handle_vb_timer(dai, false); + } +} + static struct snd_soc_dai_ops sst_media_dai_ops = { .startup = sst_media_open, .shutdown = sst_media_close, .prepare = sst_media_prepare, .hw_params = sst_media_hw_params, .hw_free = sst_media_hw_free, + .mute_stream = sst_media_digital_mute, +}; + +static struct snd_soc_dai_ops sst_compr_dai_ops = { + .mute_stream = sst_media_digital_mute, +}; + +static struct snd_soc_dai_ops sst_be_dai_ops = { + .startup = sst_enable_ssp, + .shutdown = sst_disable_ssp, +}; + +static struct snd_soc_dai_driver sst_platform_dai[] = { +{ + .name = "media-cpu-dai", + .ops = &sst_media_dai_ops, + .playback = { + .stream_name = "Headset Playback", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Headset Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = "compress-cpu-dai", + .compress_dai = 1, + .ops = &sst_compr_dai_ops, + .playback = { + .stream_name = "Compress Playback", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +/*BE CPU Dais*/ +{ + .name = "ssp0-port", + .ops = &sst_be_dai_ops, + .playback = { + .stream_name = "ssp0 Tx", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp0 Rx", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = "ssp1-port", + .ops = &sst_be_dai_ops, + .playback = { + .stream_name = "ssp1 Tx", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp1 Rx", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = "ssp2-port", + .ops = &sst_be_dai_ops, + .playback = { + .stream_name = "ssp2 Tx", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp2 Rx", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, };
static int sst_platform_open(struct snd_pcm_substream *substream)
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/sst-mfld-platform-pcm.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index 5a9e84c..4e44e36 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -228,7 +228,7 @@ int sst_fill_stream_params(void *substream, }
static int sst_platform_alloc_stream(struct snd_pcm_substream *substream, - struct snd_soc_platform *platform) + struct snd_soc_dai *dai) { struct sst_runtime_stream *stream = substream->runtime->private_data; @@ -236,7 +236,7 @@ static int sst_platform_alloc_stream(struct snd_pcm_substream *substream, struct snd_sst_params str_params = {0}; struct snd_sst_alloc_params_ext alloc_params = {0}; int ret_val = 0; - struct sst_data *ctx = snd_soc_platform_get_drvdata(platform); + struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);
/* set codec params and inform SST driver the same */ sst_fill_pcm_params(substream, ¶m); @@ -369,10 +369,10 @@ static void sst_media_close(struct snd_pcm_substream *substream, kfree(stream); }
-static inline unsigned int get_current_pipe_id(struct snd_soc_platform *platform, +static inline unsigned int get_current_pipe_id(struct snd_soc_dai *dai, struct snd_pcm_substream *substream) { - struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + struct sst_data *sst = snd_soc_dai_get_drvdata(dai); struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map; struct sst_runtime_stream *stream = substream->runtime->private_data; @@ -399,7 +399,7 @@ static int sst_media_prepare(struct snd_pcm_substream *substream, return ret_val; }
- ret_val = sst_platform_alloc_stream(substream, dai->platform); + ret_val = sst_platform_alloc_stream(substream, dai); if (ret_val <= 0) return ret_val; snprintf(substream->pcm->id, sizeof(substream->pcm->id),
From: Vinod Koul vinod.koul@intel.com
Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com --- sound/soc/intel/sst-atom-controls.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index b0a56f6..9f25ab5 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -873,6 +873,32 @@ static const struct snd_soc_dapm_widget sst_dapm_widgets[] = { SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop),
/*Media Mixers*/ + SST_SWM_MIXER("media0_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA0, + sst_mix_media0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("media1_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA1, + sst_mix_media1_controls, sst_swm_mixer_event), + + /*SBA PCM mixers*/ + SST_SWM_MIXER("pcm0_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM0, + sst_mix_pcm0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("pcm1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM1, + sst_mix_pcm1_controls, sst_swm_mixer_event), + SST_SWM_MIXER("pcm2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM2, + sst_mix_pcm2_controls, sst_swm_mixer_event), + + /*SBA Loop mixers*/ + SST_SWM_MIXER("sprot_loop_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, + sst_mix_sprot_l0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("media_loop1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, + sst_mix_media_l1_controls, sst_swm_mixer_event), + SST_SWM_MIXER("media_loop2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, + sst_mix_media_l2_controls, sst_swm_mixer_event), + + /*SBA Backend mixers*/ + SST_SWM_MIXER("codec_out0 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC0, + sst_mix_codec0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("codec_out1 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC1, + sst_mix_codec1_controls, sst_swm_mixer_event), };
static const struct snd_soc_dapm_route intercon[] = {
participants (4)
-
Mark Brown
-
Subhransu S. Prusty
-
Takashi Iwai
-
Vinod Koul