[alsa-devel] [v4 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 v4: Removed print messages for kzalloc failure as per review comments from Takashi. Subhransu S. Prusty (2): ASoC: Intel: mrfld: set private data for cpu-dai ASoC: Export dapm_kcontrol_get_value
Vinod Koul (10): ASoC: Intel: mfld-pcm: add FE and BE ops ASoC: intel: mfld-pcm: don't call trigger ops to DSP for internal streams ASoC: Intel: add mrfld DSP defines 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 the fe & be dai ops ASoC: Intel: mrfld: add the DSP mixers
include/sound/soc-dapm.h | 1 + sound/soc/intel/Makefile | 3 +- sound/soc/intel/sst-atom-controls.c | 1256 +++++++++++++++++++++++++++++++ sound/soc/intel/sst-atom-controls.h | 844 ++++++++++++++++++++- sound/soc/intel/sst-mfld-platform-pcm.c | 192 ++++- sound/soc/intel/sst-mfld-platform.h | 8 + sound/soc/soc-dapm.c | 3 +- 7 files changed, 2269 insertions(+), 38 deletions(-) create mode 100644 sound/soc/intel/sst-atom-controls.c
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 | 113 ++++++++++++++++++++++++-------- 1 file changed, 84 insertions(+), 29 deletions(-)
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index a89ff7e18e1a..364034024cee 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -101,35 +101,6 @@ 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[] = { -{ - .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, - }, -}, -};
/* helper functions */ void sst_set_stream_status(struct sst_runtime_stream *stream, @@ -441,6 +412,90 @@ static struct snd_soc_dai_ops sst_media_dai_ops = { .hw_free = sst_media_hw_free, };
+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, + .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", + .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", + .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", + .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) { struct snd_pcm_runtime *runtime;
On Mon, Aug 04, 2014 at 03:15:52PM +0530, Subhransu S. Prusty wrote:
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.
This would be a lot easier to apply if the changelog were clearer, I can't tell how we get from having code to manage the pipelines to needing or wanting this code. Is the current code broken and this a bug fix or is this preparing for future changes? The changelog says that this is adding ops but the diff is adding DAIs none of which have any ops defined.
I'm also a bit surprised to see this without anything to add routes between the front and back ends or updates to the machine drivers for new DAI names - won't this cause problems for the machine drivers?
We set the driver private data for media dai so that we can use in media operations
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 | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index 364034024cee..821c9970548c 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -223,7 +223,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; @@ -231,7 +231,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); @@ -347,10 +347,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; @@ -376,7 +376,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), @@ -412,8 +412,16 @@ static struct snd_soc_dai_ops sst_media_dai_ops = { .hw_free = sst_media_hw_free, };
+static int sst_media_dai_probe(struct snd_soc_dai *cpu_dai) +{ + struct sst_data *sst = dev_get_drvdata(cpu_dai->dev); + + snd_soc_dai_set_drvdata(cpu_dai, sst); + return 0; +} static struct snd_soc_dai_driver sst_platform_dai[] = { { + .probe = sst_media_dai_probe, .name = "media-cpu-dai", .ops = &sst_media_dai_ops, .playback = {
On Mon, Aug 04, 2014 at 03:15:53PM +0530, Subhransu S. Prusty wrote:
We set the driver private data for media dai so that we can use in media operations
This is another one where making the changelog clearer - as far as I can tell what this is actually doing is moving the existing private data from being per platform to being per DAI. That actually seems OK and I would've applied this but it looks like it depends on patch 1.
This lack of clarity is a frequent issue with both of these DSP serieses, it's really slowing down review since they're quite big and hard to read.
+static int sst_media_dai_probe(struct snd_soc_dai *cpu_dai) +{
- struct sst_data *sst = dev_get_drvdata(cpu_dai->dev);
- snd_soc_dai_set_drvdata(cpu_dai, sst);
- return 0;
+} static struct snd_soc_dai_driver sst_platform_dai[] = {
Missing blank line here - this is a frequent issue with this code.
On Wed, Aug 13, 2014 at 08:45:31PM +0100, Mark Brown wrote:
On Mon, Aug 04, 2014 at 03:15:53PM +0530, Subhransu S. Prusty wrote:
We set the driver private data for media dai so that we can use in media operations
This is another one where making the changelog clearer - as far as I can tell what this is actually doing is moving the existing private data from being per platform to being per DAI. That actually seems OK and I would've applied this but it looks like it depends on patch 1.
Will take care.
This lack of clarity is a frequent issue with both of these DSP serieses, it's really slowing down review since they're quite big and hard to read.
+static int sst_media_dai_probe(struct snd_soc_dai *cpu_dai) +{
- struct sst_data *sst = dev_get_drvdata(cpu_dai->dev);
- snd_soc_dai_set_drvdata(cpu_dai, sst);
- return 0;
+} static struct snd_soc_dai_driver sst_platform_dai[] = {
Missing blank line here - this is a frequent issue with this code.
--
[...]
+static int sst_media_dai_probe(struct snd_soc_dai *cpu_dai) +{
- struct sst_data *sst = dev_get_drvdata(cpu_dai->dev);
- snd_soc_dai_set_drvdata(cpu_dai, sst);
snd_soc_dai_set_drvdata does dev_set_drvdata(dai->dev, data) internally, so this function does:
dev_set_drvdata(cpu_dai->dev, dev_get_drvdata(cpu_dai->dev));
That's pretty much a noop.
- return 0;
+} static struct snd_soc_dai_driver sst_platform_dai[] = { {
- .probe = sst_media_dai_probe, .name = "media-cpu-dai", .ops = &sst_media_dai_ops, .playback = {
On Wed, Aug 13, 2014 at 09:50:43PM +0200, Lars-Peter Clausen wrote:
[...]
+static int sst_media_dai_probe(struct snd_soc_dai *cpu_dai) +{
- struct sst_data *sst = dev_get_drvdata(cpu_dai->dev);
- snd_soc_dai_set_drvdata(cpu_dai, sst);
snd_soc_dai_set_drvdata does dev_set_drvdata(dai->dev, data) internally, so this function does:
dev_set_drvdata(cpu_dai->dev, dev_get_drvdata(cpu_dai->dev));
That's pretty much a noop.
Agree. Intention was to use snd_soc_dai_* API. Anyway will remove and use dev_get_drvdata(dai->dev) directly. --
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 821c9970548c..a36c5bf67eff 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -524,6 +524,8 @@ static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, int status;
pr_debug("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) {
From: Vinod Koul vinod.koul@intel.com
We define the DSP commands,structures here which will be used to send the IPCs
Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com --- sound/soc/intel/Makefile | 3 +- sound/soc/intel/sst-atom-controls.c | 39 +++++ sound/soc/intel/sst-atom-controls.h | 286 +++++++++++++++++++++++++++++++- sound/soc/intel/sst-mfld-platform-pcm.c | 8 +- sound/soc/intel/sst-mfld-platform.h | 3 + 5 files changed, 335 insertions(+), 4 deletions(-) create mode 100644 sound/soc/intel/sst-atom-controls.c
diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index 7acbfc43a0c6..f841786dad15 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -2,7 +2,8 @@ snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o snd-soc-sst-acpi-objs := sst-acpi.o
-snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o sst-mfld-platform-compress.o +snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \ + sst-mfld-platform-compress.o sst-atom-controls.o snd-soc-mfld-machine-objs := mfld_machine.o
obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c new file mode 100644 index 000000000000..ace3c4a59b14 --- /dev/null +++ b/sound/soc/intel/sst-atom-controls.c @@ -0,0 +1,39 @@ +/* + * sst-atom-controls.c - Intel MID Platform driver DPCM ALSA controls for Mrfld + * + * Copyright (C) 2013-14 Intel Corp + * Author: Omair Mohammed Abdullah omair.m.abdullah@intel.com + * Vinod Koul vinod.koul@intel.com + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/slab.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include "sst-mfld-platform.h" +#include "sst-atom-controls.h" + +int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) +{ + int ret = 0; + struct sst_data *drv = snd_soc_platform_get_drvdata(platform); + + drv->byte_stream = devm_kzalloc(platform->dev, + SST_MAX_BIN_BYTES, GFP_KERNEL); + if (!drv->byte_stream) + return -ENOMEM; + + return ret; +} diff --git a/sound/soc/intel/sst-atom-controls.h b/sound/soc/intel/sst-atom-controls.h index 14063ab8c7c5..8554889c0694 100644 --- a/sound/soc/intel/sst-atom-controls.h +++ b/sound/soc/intel/sst-atom-controls.h @@ -1,4 +1,6 @@ /* + * sst-atom-controls.h - Intel MID Platform driver header file + * * Copyright (C) 2013-14 Intel Corp * Author: Ramesh Babu ramesh.babu.koul@intel.com * Omair M Abdullah omair.m.abdullah@intel.com @@ -18,13 +20,293 @@ * */
-#ifndef __SST_CONTROLS_V2_H__ -#define __SST_CONTROLS_V2_H__ +#ifndef __SST_ATOM_CONTROLS_H__ +#define __SST_ATOM_CONTROLS_H__
enum { MERR_DPCM_AUDIO = 0, MERR_DPCM_COMPR, };
+/* define a bit for each mixer input */ +#define SST_MIX_IP(x) (x) + +#define SST_IP_CODEC0 SST_MIX_IP(2) +#define SST_IP_CODEC1 SST_MIX_IP(3) +#define SST_IP_LOOP0 SST_MIX_IP(4) +#define SST_IP_LOOP1 SST_MIX_IP(5) +#define SST_IP_LOOP2 SST_MIX_IP(6) +#define SST_IP_PROBE SST_MIX_IP(7) +#define SST_IP_VOIP SST_MIX_IP(12) +#define SST_IP_PCM0 SST_MIX_IP(13) +#define SST_IP_PCM1 SST_MIX_IP(14) +#define SST_IP_MEDIA0 SST_MIX_IP(17) +#define SST_IP_MEDIA1 SST_MIX_IP(18) +#define SST_IP_MEDIA2 SST_MIX_IP(19) +#define SST_IP_MEDIA3 SST_MIX_IP(20) + +#define SST_IP_LAST SST_IP_MEDIA3 + +#define SST_SWM_INPUT_COUNT (SST_IP_LAST + 1) +#define SST_CMD_SWM_MAX_INPUTS 6 + +#define SST_PATH_ID_SHIFT 8 +#define SST_DEFAULT_LOCATION_ID 0xFFFF +#define SST_DEFAULT_CELL_NBR 0xFF +#define SST_DEFAULT_MODULE_ID 0xFFFF + +/* + * Audio DSP Path Ids. Specified by the audio DSP FW + */ +enum sst_path_index { + SST_PATH_INDEX_CODEC_OUT0 = (0x02 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_CODEC_OUT1 = (0x03 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_SPROT_LOOP_OUT = (0x04 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA_LOOP1_OUT = (0x05 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA_LOOP2_OUT = (0x06 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_VOIP_OUT = (0x0C << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PCM0_OUT = (0x0D << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PCM1_OUT = (0x0E << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PCM2_OUT = (0x0F << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_MEDIA0_OUT = (0x12 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA1_OUT = (0x13 << SST_PATH_ID_SHIFT), + + + /* Start of input paths */ + SST_PATH_INDEX_CODEC_IN0 = (0x82 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_CODEC_IN1 = (0x83 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_SPROT_LOOP_IN = (0x84 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA_LOOP1_IN = (0x85 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA_LOOP2_IN = (0x86 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_VOIP_IN = (0x8C << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_PCM0_IN = (0x8D << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PCM1_IN = (0x8E << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_MEDIA0_IN = (0x8F << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA1_IN = (0x90 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA2_IN = (0x91 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_MEDIA3_IN = (0x9C << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_RESERVED = (0xFF << SST_PATH_ID_SHIFT), +}; + +/* + * path IDs + */ +enum sst_swm_inputs { + SST_SWM_IN_CODEC0 = (SST_PATH_INDEX_CODEC_IN0 | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_CODEC1 = (SST_PATH_INDEX_CODEC_IN1 | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_VOIP = (SST_PATH_INDEX_VOIP_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_PCM0 = (SST_PATH_INDEX_PCM0_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_PCM1 = (SST_PATH_INDEX_PCM1_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_MEDIA0 = (SST_PATH_INDEX_MEDIA0_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_IN_MEDIA1 = (SST_PATH_INDEX_MEDIA1_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_IN_MEDIA2 = (SST_PATH_INDEX_MEDIA2_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_IN_MEDIA3 = (SST_PATH_INDEX_MEDIA3_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_IN_END = (SST_PATH_INDEX_RESERVED | SST_DEFAULT_CELL_NBR) +}; + +/* + * path IDs + */ +enum sst_swm_outputs { + SST_SWM_OUT_CODEC0 = (SST_PATH_INDEX_CODEC_OUT0 | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_CODEC1 = (SST_PATH_INDEX_CODEC_OUT1 | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_VOIP = (SST_PATH_INDEX_VOIP_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_PCM0 = (SST_PATH_INDEX_PCM0_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_PCM1 = (SST_PATH_INDEX_PCM1_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_PCM2 = (SST_PATH_INDEX_PCM2_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_MEDIA0 = (SST_PATH_INDEX_MEDIA0_OUT | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_OUT_MEDIA1 = (SST_PATH_INDEX_MEDIA1_OUT | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_OUT_END = (SST_PATH_INDEX_RESERVED | SST_DEFAULT_CELL_NBR), +}; + +enum sst_ipc_msg { + SST_IPC_IA_CMD = 1, + SST_IPC_IA_SET_PARAMS, + SST_IPC_IA_GET_PARAMS, +}; + +enum sst_cmd_type { + SST_CMD_BYTES_SET = 1, + SST_CMD_BYTES_GET = 2, +}; + +enum sst_task { + SST_TASK_SBA = 1, + SST_TASK_MMX, +}; + +enum sst_type { + SST_TYPE_CMD = 1, + SST_TYPE_PARAMS, +}; + +enum sst_flag { + SST_FLAG_BLOCKED = 1, + SST_FLAG_NONBLOCK, +}; + +/* + * Enumeration for indexing the gain cells in VB_SET_GAIN DSP command + */ +enum sst_gain_index { + /* GAIN IDs for SB task start here */ + SST_GAIN_INDEX_CODEC_OUT0, + SST_GAIN_INDEX_CODEC_OUT1, + SST_GAIN_INDEX_CODEC_IN0, + SST_GAIN_INDEX_CODEC_IN1, + + SST_GAIN_INDEX_SPROT_LOOP_OUT, + SST_GAIN_INDEX_MEDIA_LOOP1_OUT, + SST_GAIN_INDEX_MEDIA_LOOP2_OUT, + + SST_GAIN_INDEX_PCM0_IN_LEFT, + SST_GAIN_INDEX_PCM0_IN_RIGHT, + + SST_GAIN_INDEX_PCM1_OUT_LEFT, + SST_GAIN_INDEX_PCM1_OUT_RIGHT, + SST_GAIN_INDEX_PCM1_IN_LEFT, + SST_GAIN_INDEX_PCM1_IN_RIGHT, + SST_GAIN_INDEX_PCM2_OUT_LEFT, + + SST_GAIN_INDEX_PCM2_OUT_RIGHT, + SST_GAIN_INDEX_VOIP_OUT, + SST_GAIN_INDEX_VOIP_IN, + + /* Gain IDs for MMX task start here */ + SST_GAIN_INDEX_MEDIA0_IN_LEFT, + SST_GAIN_INDEX_MEDIA0_IN_RIGHT, + SST_GAIN_INDEX_MEDIA1_IN_LEFT, + SST_GAIN_INDEX_MEDIA1_IN_RIGHT, + + SST_GAIN_INDEX_MEDIA2_IN_LEFT, + SST_GAIN_INDEX_MEDIA2_IN_RIGHT, + + SST_GAIN_INDEX_GAIN_END +}; + +/* + * Audio DSP module IDs specified by FW spec + * TODO: Update with all modules + */ +enum sst_module_id { + SST_MODULE_ID_PCM = 0x0001, + SST_MODULE_ID_MP3 = 0x0002, + SST_MODULE_ID_MP24 = 0x0003, + SST_MODULE_ID_AAC = 0x0004, + SST_MODULE_ID_AACP = 0x0005, + SST_MODULE_ID_EAACP = 0x0006, + SST_MODULE_ID_WMA9 = 0x0007, + SST_MODULE_ID_WMA10 = 0x0008, + SST_MODULE_ID_WMA10P = 0x0009, + SST_MODULE_ID_RA = 0x000A, + SST_MODULE_ID_DDAC3 = 0x000B, + SST_MODULE_ID_TRUE_HD = 0x000C, + SST_MODULE_ID_HD_PLUS = 0x000D, + + SST_MODULE_ID_SRC = 0x0064, + SST_MODULE_ID_DOWNMIX = 0x0066, + SST_MODULE_ID_GAIN_CELL = 0x0067, + SST_MODULE_ID_SPROT = 0x006D, + SST_MODULE_ID_BASS_BOOST = 0x006E, + SST_MODULE_ID_STEREO_WDNG = 0x006F, + SST_MODULE_ID_AV_REMOVAL = 0x0070, + SST_MODULE_ID_MIC_EQ = 0x0071, + SST_MODULE_ID_SPL = 0x0072, + SST_MODULE_ID_ALGO_VTSV = 0x0073, + SST_MODULE_ID_NR = 0x0076, + SST_MODULE_ID_BWX = 0x0077, + SST_MODULE_ID_DRP = 0x0078, + SST_MODULE_ID_MDRP = 0x0079, + + SST_MODULE_ID_ANA = 0x007A, + SST_MODULE_ID_AEC = 0x007B, + SST_MODULE_ID_NR_SNS = 0x007C, + SST_MODULE_ID_SER = 0x007D, + SST_MODULE_ID_AGC = 0x007E, + + SST_MODULE_ID_CNI = 0x007F, + SST_MODULE_ID_CONTEXT_ALGO_AWARE = 0x0080, + SST_MODULE_ID_FIR_24 = 0x0081, + SST_MODULE_ID_IIR_24 = 0x0082, + + SST_MODULE_ID_ASRC = 0x0083, + SST_MODULE_ID_TONE_GEN = 0x0084, + SST_MODULE_ID_BMF = 0x0086, + SST_MODULE_ID_EDL = 0x0087, + SST_MODULE_ID_GLC = 0x0088, + + SST_MODULE_ID_FIR_16 = 0x0089, + SST_MODULE_ID_IIR_16 = 0x008A, + SST_MODULE_ID_DNR = 0x008B, + + SST_MODULE_ID_VIRTUALIZER = 0x008C, + SST_MODULE_ID_VISUALIZATION = 0x008D, + SST_MODULE_ID_LOUDNESS_OPTIMIZER = 0x008E, + SST_MODULE_ID_REVERBERATION = 0x008F, + + SST_MODULE_ID_CNI_TX = 0x0090, + SST_MODULE_ID_REF_LINE = 0x0091, + SST_MODULE_ID_VOLUME = 0x0092, + SST_MODULE_ID_FILT_DCR = 0x0094, + SST_MODULE_ID_SLV = 0x009A, + SST_MODULE_ID_NLF = 0x009B, + SST_MODULE_ID_TNR = 0x009C, + SST_MODULE_ID_WNR = 0x009D, + + SST_MODULE_ID_LOG = 0xFF00, + + SST_MODULE_ID_TASK = 0xFFFF, +}; + +enum sst_cmd { + SBA_IDLE = 14, + SBA_VB_SET_SPEECH_PATH = 26, + MMX_SET_GAIN = 33, + SBA_VB_SET_GAIN = 33, + FBA_VB_RX_CNI = 35, + MMX_SET_GAIN_TIMECONST = 36, + SBA_VB_SET_TIMECONST = 36, + SBA_VB_START = 85, + SBA_SET_SWM = 114, + SBA_SET_MDRP = 116, + SBA_HW_SET_SSP = 117, + SBA_SET_MEDIA_LOOP_MAP = 118, + SBA_SET_MEDIA_PATH = 119, + MMX_SET_MEDIA_PATH = 119, + SBA_VB_LPRO = 126, + SBA_VB_SET_FIR = 128, + SBA_VB_SET_IIR = 129, + SBA_SET_SSP_SLOT_MAP = 130, +}; + +enum sst_dsp_switch { + SST_SWITCH_OFF = 0, + SST_SWITCH_ON = 3, +}; + +enum sst_path_switch { + SST_PATH_OFF = 0, + SST_PATH_ON = 1, +}; + +enum sst_swm_state { + SST_SWM_OFF = 0, + SST_SWM_ON = 3, +};
#endif diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index a36c5bf67eff..c71893ce3d93 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -615,7 +615,13 @@ static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd) return retval; }
-static struct snd_soc_platform_driver sst_soc_platform_drv = { +static int sst_soc_probe(struct snd_soc_platform *platform) +{ + return sst_dsp_init_v2_dpcm(platform); +} + +static struct snd_soc_platform_driver sst_soc_platform_drv = { + .probe = sst_soc_probe, .ops = &sst_platform_ops, .compr_ops = &sst_platform_compr_ops, .pcm_new = sst_pcm_new, diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h index d4c28b8fb471..faaba10c1dff 100644 --- a/sound/soc/intel/sst-mfld-platform.h +++ b/sound/soc/intel/sst-mfld-platform.h @@ -143,6 +143,8 @@ struct sst_device { };
struct sst_data; + +int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform); 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); @@ -157,6 +159,7 @@ struct sst_algo_int_control_v2 { struct sst_data { struct platform_device *pdev; struct sst_platform_data *pdata; + char *byte_stream; struct mutex lock; }; int sst_register_dsp(struct sst_device *sst);
On Mon, Aug 04, 2014 at 03:15:55PM +0530, Subhransu S. Prusty wrote:
From: Vinod Koul vinod.koul@intel.com
We define the DSP commands,structures here which will be used to send the IPCs
Applied, 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 | 172 ++++++++++++++++++++++++++++++++++++ sound/soc/intel/sst-atom-controls.h | 130 +++++++++++++++++++++++++++ 2 files changed, 302 insertions(+)
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index ace3c4a59b14..9f41741ddf8a 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -25,6 +25,176 @@ #include "sst-mfld-platform.h" #include "sst-atom-controls.h"
+static inline void sst_fill_byte_control(char *param, + u8 ipc_msg, u8 block, + u8 task_id, u8 pipe_id, + u16 len, void *cmd_data) +{ + + struct snd_sst_bytes_v2 *byte_data = (struct snd_sst_bytes_v2 *)param; + + 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)) { + WARN(1, "%s: command length too big (%u)", __func__, len); /* this happens only if code is wrong */ + len = SST_MAX_BIN_BYTES - sizeof(*byte_data); + } + 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)); +} + +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) +{ + sst_fill_byte_control(drv->byte_stream, ipc_msg, block, task_id, pipe_id, + len, cmd_data); + return sst->ops->send_byte_stream(sst->dev, + (struct snd_sst_bytes_v2 *)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 void sst_send_algo_cmd(struct sst_data *drv, + struct sst_algo_control *bc) +{ + int len; + struct sst_cmd_set_params *cmd; + + if (bc->params == NULL) + return; + + /* 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; + + 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); + + sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS, + SST_FLAG_BLOCKED, bc->task_id, 0, cmd, len); + kfree(cmd); +} + +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; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = bc->max; + + /* 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; + + switch (bc->type) { + case SST_ALGO_PARAMS: + if (bc->params) + memcpy(ucontrol->value.bytes.data, bc->params, bc->max); + break; + case SST_ALGO_BYPASS: + ucontrol->value.integer.value[0] = bc->bypass ? 1 : 0; + pr_debug("%s: bypass %d\n", __func__, bc->bypass); + break; + default: + pr_err("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) +{ + 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; + + pr_debug("in %s control_name=%s\n", __func__, kcontrol->id.name); + mutex_lock(&drv->lock); + switch (bc->type) { + case SST_ALGO_PARAMS: + if (bc->params) + memcpy(bc->params, ucontrol->value.bytes.data, bc->max); + break; + case SST_ALGO_BYPASS: + bc->bypass = !!ucontrol->value.integer.value[0]; + break; + default: + mutex_unlock(&drv->lock); + pr_err("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) + sst_send_algo_cmd(drv, bc); + mutex_unlock(&drv->lock); + + return 0; +} + +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 +205,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 8554889c0694..a73e894b175c 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, xget, xput) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = xinfo, .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_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_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
On Mon, Aug 04, 2014 at 03:15:56PM +0530, Subhransu S. Prusty wrote:
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.
Indeed they are actually exposed by this code.
+static inline void sst_fill_byte_control(char *param,
u8 ipc_msg, u8 block,
u8 task_id, u8 pipe_id,
u16 len, void *cmd_data)
Let the compiler figure out if this should be inline, this doesn't seem something that should obviously be inline and isn't in a header.
- if (len > SST_MAX_BIN_BYTES - sizeof(*byte_data)) {
WARN(1, "%s: command length too big (%u)", __func__, len); /* this happens only if code is wrong */
len = SST_MAX_BIN_BYTES - sizeof(*byte_data);
- }
Coding style, 80 columns. Since the only caller can return an error code you could do that here too.
+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)
+{
- sst_fill_byte_control(drv->byte_stream, ipc_msg, block, task_id, pipe_id,
len, cmd_data);
- return sst->ops->send_byte_stream(sst->dev,
(struct snd_sst_bytes_v2 *)drv->byte_stream);
+}
There's a lot of casting going on in this code.
+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;
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
- uinfo->count = bc->max;
- /* 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;
+}
I wouldn't expect an info call to be allocating anything - why is it doing that? It's not looking at the alocated data except to see if the allocation succeeded. What happens if someone manages to do a get or set without having first done an info and why aren't we doing any allocation on initialisation?
- case SST_ALGO_BYPASS:
ucontrol->value.integer.value[0] = bc->bypass ? 1 : 0;
pr_debug("%s: bypass %d\n", __func__, bc->bypass);
break;
Is bypass not a boolean value already, and shouldn't these just end up with controls called something "Switch" - they look to be just directly usable switches?
- default:
pr_err("Invalid Input- algo type:%d\n", bc->type);
dev_err().
- switch (bc->type) {
- case SST_ALGO_PARAMS:
if (bc->params)
memcpy(bc->params, ucontrol->value.bytes.data, bc->max);
break;
So if bc->params didn't get allocated somehow we just silently drop the set?
- /*if pipe is enabled, need to send the algo params from here */
- if (bc->w && bc->w->power)
sst_send_algo_cmd(drv, bc);
sst_send_algo_cmd() should be returning an eror code (it doesn't but the functions it calls can).
I'm not seeing any code to restore these controls on power up here.
On Wed, Aug 13, 2014 at 09:00:12PM +0100, Mark Brown wrote:
On Mon, Aug 04, 2014 at 03:15:56PM +0530, Subhransu S. Prusty wrote:
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.
Indeed they are actually exposed by this code.
+static inline void sst_fill_byte_control(char *param,
u8 ipc_msg, u8 block,
u8 task_id, u8 pipe_id,
u16 len, void *cmd_data)
Let the compiler figure out if this should be inline, this doesn't seem something that should obviously be inline and isn't in a header.
Ok
- if (len > SST_MAX_BIN_BYTES - sizeof(*byte_data)) {
WARN(1, "%s: command length too big (%u)", __func__, len); /* this happens only if code is wrong */
len = SST_MAX_BIN_BYTES - sizeof(*byte_data);
- }
Coding style, 80 columns. Since the only caller can return an error code you could do that here too.
Yes.
+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)
+{
- sst_fill_byte_control(drv->byte_stream, ipc_msg, block, task_id, pipe_id,
len, cmd_data);
- return sst->ops->send_byte_stream(sst->dev,
(struct snd_sst_bytes_v2 *)drv->byte_stream);
+}
There's a lot of casting going on in this code.
Not required. Should have been removed.
+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;
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
- uinfo->count = bc->max;
- /* 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;
+}
I wouldn't expect an info call to be allocating anything - why is it doing that? It's not looking at the alocated data except to see if the allocation succeeded. What happens if someone manages to do a get or set without having first done an info and why aren't we doing any allocation on initialisation?
Will move after control initialization.
- case SST_ALGO_BYPASS:
ucontrol->value.integer.value[0] = bc->bypass ? 1 : 0;
pr_debug("%s: bypass %d\n", __func__, bc->bypass);
break;
Is bypass not a boolean value already, and shouldn't these just end up with controls called something "Switch" - they look to be just directly usable switches?
Actually BYPASS is not required. Will remove.
- default:
pr_err("Invalid Input- algo type:%d\n", bc->type);
dev_err().
We are using pr_err() in all the error conditions for our driver. If it is really required to change, we can submit a single patch to change to dev_err() once the patches are merged.
- switch (bc->type) {
- case SST_ALGO_PARAMS:
if (bc->params)
memcpy(bc->params, ucontrol->value.bytes.data, bc->max);
break;
So if bc->params didn't get allocated somehow we just silently drop the set?
With above params initialization changes this check will not be required any more.
- /*if pipe is enabled, need to send the algo params from here */
- if (bc->w && bc->w->power)
sst_send_algo_cmd(drv, bc);
sst_send_algo_cmd() should be returning an eror code (it doesn't but the functions it calls can).
I'm not seeing any code to restore these controls on power up here.
Will take care of the return code.
sst_find_and_send_pipe_algo takes care of restoring the controls on power up.
--
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 | 168 +++++++++++++++++++++++++++++++++++- sound/soc/intel/sst-atom-controls.h | 119 +++++++++++++++++++++++++ 2 files changed, 286 insertions(+), 1 deletion(-)
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index 9f41741ddf8a..afb59131ec09 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -173,6 +173,161 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol, return 0; }
+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; + + pr_debug("%s\n", __func__); + + 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 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: + pr_err("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]; + pr_debug("%s: %s: Volume %d, %d\n", __func__, mc->pname, gv->l_gain, gv->r_gain); + break; + case SST_GAIN_MUTE: + gv->mute = !!ucontrol->value.integer.value[0]; + pr_debug("%s: %s: Mute %d\n", __func__, mc->pname, gv->mute); + break; + case SST_GAIN_RAMP_DURATION: + gv->ramp_duration = ucontrol->value.integer.value[0]; + pr_debug("%s: %s: RampDuration %d\n", __func__, mc->pname, gv->ramp_duration); + break; + default: + mutex_unlock(&drv->lock); + pr_err("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), @@ -197,7 +352,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, @@ -205,6 +360,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 a73e894b175c..6e2062fda36f 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,
On Mon, Aug 04, 2014 at 03:15:57PM +0530, Subhransu S. Prusty wrote:
+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]),
Media0 Capture Volume - all the controls here look like they should have standard ALSA style names.
On Wed, Aug 13, 2014 at 09:03:39PM +0100, Mark Brown wrote:
On Mon, Aug 04, 2014 at 03:15:57PM +0530, Subhransu S. Prusty wrote:
+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]),
Media0 Capture Volume - all the controls here look like they should have standard ALSA style names.
Ok
--
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 | 14 ++++++++++++++ sound/soc/intel/sst-mfld-platform.h | 1 + 2 files changed, 15 insertions(+)
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index c71893ce3d93..60ee6ad4b626 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -292,6 +292,16 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream)
}
+static inline int power_up_sst(struct sst_runtime_stream *stream) +{ + return stream->ops->power(sst->dev, true); +} + +static inline int power_down_sst(struct sst_runtime_stream *stream) +{ + return stream->ops->power(sst->dev, false); +} + static int sst_media_open(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -321,6 +331,8 @@ static int sst_media_open(struct snd_pcm_substream *substream, /* allocate memory for SST API set */ runtime->private_data = stream;
+ power_up_sst(stream); + /* Make sure, that the period size is always even */ snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS, 2); @@ -340,6 +352,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 faaba10c1dff..1f42b9ce5554 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 {
On Mon, Aug 04, 2014 at 03:15:58PM +0530, Subhransu S. Prusty wrote:
+static inline int power_up_sst(struct sst_runtime_stream *stream) +{
- return stream->ops->power(sst->dev, true);
+}
+static inline int power_down_sst(struct sst_runtime_stream *stream) +{
- return stream->ops->power(sst->dev, false);
+}
These return values are ignored. Shouldn't we be syncing out control values on power changes?
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 | 635 +++++++++++++++++++++++++++++++++++- sound/soc/intel/sst-atom-controls.h | 309 ++++++++++++++++++ 2 files changed, 943 insertions(+), 1 deletion(-)
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index afb59131ec09..5a181b32c247 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -78,6 +78,153 @@ 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; + + pr_debug("Enter: %s\n", __func__); + + 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; + 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; + pr_debug("%s: %s - %s map = %#x\n", __func__, 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; + + pr_debug("%s: %s %s map = %#x\n", __func__, 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 void sst_send_algo_cmd(struct sst_data *drv, struct sst_algo_control *bc) { @@ -103,7 +250,28 @@ static void sst_send_algo_cmd(struct sst_data *drv, kfree(cmd); }
-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; + + pr_debug("Enter: %s, widget=%s\n", __func__, pipe); + + list_for_each_entry(algo, &ids->algo_list, node) { + bc = (void *)algo->kctl->private_value; + + pr_debug("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; @@ -287,8 +455,270 @@ 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; + + pr_debug("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; + pr_debug("%s: enable=%u, usage=%d\n", __func__, 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; + + pr_err("Enter:%s, enable=%d port_name=%s\n", __func__, 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; + pr_debug("%s: ssp_id: %u\n", __func__, 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); + + pr_debug("Enter: %s, widget=%s\n", __func__, 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; + + pr_debug("%s: widget=%s\n", __func__, w->name); + pr_debug("%s: task=%u, location=%#x\n", __func__, + 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; + + pr_debug("Enter:%s, widget=%s\n", __func__, 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, \ @@ -350,6 +780,204 @@ static const struct snd_kcontrol_new sst_algo_controls[] = {
};
+static inline 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; + + pr_debug("%s: enter, dai-name=%s dir=%d\n", __func__, dai->name, stream); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + pr_debug("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; + + pr_debug("send gains for widget=%s\n", p->sink->name); + sst_set_pipe_gain(ids, drv, mute); + } + } + } else { + pr_debug("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; + + pr_debug("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; + + pr_debug("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; @@ -373,5 +1001,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 6e2062fda36f..cfbfb1ced27d 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 aac04ff84eea..6ae0a1952ad7 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 8348352dc2c6..08c79f09034f 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 | 220 ++++++++++++++++++++++++++++++++++++ sound/soc/intel/sst-mfld-platform.h | 4 + 2 files changed, 224 insertions(+)
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index 5a181b32c247..029e6960ca73 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -492,6 +492,39 @@ 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 swm_input_ids *swm_input, unsigned int reg) +{ + uint i, is_set, nb_inputs = 0; + u16 input_loc_id; + + pr_debug("%s: reg: %#x\n", __func__, 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++; + pr_debug("input id: %#x, nb_inputs: %d\n", input_loc_id, nb_inputs); + + if (nb_inputs == SST_CMD_SWM_MAX_INPUTS) { + pr_warn("%s: SET_SWM cmd max inputs reached", __func__); + 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; @@ -509,6 +542,109 @@ 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; + + pr_debug("%s: widget = %s\n", __func__, w->name); + 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; + } + } + pr_debug("%s: val = %#x\n", __func__, 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(&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; @@ -688,6 +824,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", @@ -981,6 +1194,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, @@ -988,6 +1203,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 1f42b9ce5554..09ecf44f33fb 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);
On Mon, Aug 04, 2014 at 03:16:01PM +0530, Subhransu S. Prusty wrote:
- pr_debug("%s: widget = %s\n", __func__, w->name);
- 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;
}
- }
All my concerns about this still stand - using something called _get_value() to do something other than read the control value (which this does - it uses it as a boolean then does something else to read the value) which doesn't seem good.
On Wed, Aug 13, 2014 at 09:14:54PM +0100, Mark Brown wrote:
On Mon, Aug 04, 2014 at 03:16:01PM +0530, Subhransu S. Prusty wrote:
- pr_debug("%s: widget = %s\n", __func__, w->name);
- 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;
}
- }
All my concerns about this still stand - using something called _get_value() to do something other than read the control value (which this does - it uses it as a boolean then does something else to read the value) which doesn't seem good.
I think this can be optimized. This need a bit of rework in the code, but need to check. But this would need an assumption that the controls are created in order.
for (i = 0; i < w->num_kcontrols; i++) { if (dapm_kcontrol_get_value(w->kcontrols[i])) { val |= 1 << i; }
Is this ok?
--
From: Vinod Koul vinod.koul@intel.com
Now that the DSP topology is added, add the FE and BE dai ops for controls DSP as well mute/unmute of FE through ASoC
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 | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+)
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index 60ee6ad4b626..4e5d2f379a26 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -101,6 +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}, };
+static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + + return sst_send_pipe_gains(dai, stream, mute); +}
/* helper functions */ void sst_set_stream_status(struct sst_runtime_stream *stream, @@ -418,12 +423,41 @@ 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 int sst_media_dai_probe(struct snd_soc_dai *cpu_dai) @@ -433,6 +467,7 @@ static int sst_media_dai_probe(struct snd_soc_dai *cpu_dai) snd_soc_dai_set_drvdata(cpu_dai, sst); return 0; } + static struct snd_soc_dai_driver sst_platform_dai[] = { { .probe = sst_media_dai_probe, @@ -456,6 +491,7 @@ static struct snd_soc_dai_driver sst_platform_dai[] = { { .name = "compress-cpu-dai", .compress_dai = 1, + .ops = &sst_compr_dai_ops, .playback = { .stream_name = "Compress Playback", .channels_min = SST_STEREO, @@ -467,6 +503,7 @@ static struct snd_soc_dai_driver sst_platform_dai[] = { /*BE CPU Dais */ { .name = "ssp0-port", + .ops = &sst_be_dai_ops, .playback = { .stream_name = "ssp0 Tx", .channels_min = SST_STEREO, @@ -484,6 +521,7 @@ static struct snd_soc_dai_driver sst_platform_dai[] = { }, { .name = "ssp1-port", + .ops = &sst_be_dai_ops, .playback = { .stream_name = "ssp1 Tx", .channels_min = SST_STEREO, @@ -501,6 +539,7 @@ static struct snd_soc_dai_driver sst_platform_dai[] = { }, { .name = "ssp2-port", + .ops = &sst_be_dai_ops, .playback = { .stream_name = "ssp2 Tx", .channels_min = SST_STEREO,
On Mon, Aug 04, 2014 at 03:16:02PM +0530, Subhransu S. Prusty wrote:
From: Vinod Koul vinod.koul@intel.com
Now that the DSP topology is added, add the FE and BE dai ops for controls DSP as well mute/unmute of FE through ASoC
It really seems like patch 1 should be part of this change, it'd be much more obvious what's going on then (though the concerns about breaking machine drivers still stand).
Please also try to keep your changelogs comfortably below 80 columns (this is over 80 columns).
On Wed, Aug 13, 2014 at 09:16:57PM +0100, Mark Brown wrote:
On Mon, Aug 04, 2014 at 03:16:02PM +0530, Subhransu S. Prusty wrote:
From: Vinod Koul vinod.koul@intel.com
Now that the DSP topology is added, add the FE and BE dai ops for controls DSP as well mute/unmute of FE through ASoC
It really seems like patch 1 should be part of this change, it'd be much more obvious what's going on then (though the concerns about breaking machine drivers still stand).
Please also try to keep your changelogs comfortably below 80 columns (this is over 80 columns).
Will squash the patches.
--
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 029e6960ca73..a681abe1ff06 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -855,6 +855,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 (3)
-
Lars-Peter Clausen
-
Mark Brown
-
Subhransu S. Prusty