[alsa-devel] [PATCH 00/19]: add mrfld DSP support
Here is the split patch series for adding DSP support for Intel's merrfield platform.
The last patch si mxer update patch which we have been discussing over the list. This is only for discussion and sake of complteness. For that patch we need the dapm_kcontrol_get/set series to do merged OR patch to be reworked after compenent series
Vinod Koul (19): ASoC: Intel: add COMPILE_TEST to mfld machine ASoC: Intel: mfld_pcm: move stream handling to dai_ops ASoC: Intel: mfld-pcm rename period callback arg ASoc: Intel: mfld-pcm: report pcm delay ASoC: Intel: add the mrfld fw IPC definations ASoC: Intel: mfld-pcm: modularize stream allocation code ASoC: Intel: add mrfld pipelines ASoC: Intel: use common stream allocation method for compressed stream ASoC: Intel: mfld-pcm: add FE and BE ops ASoC: Intel: add mrfld DSP registers ASoC: intel: mfld-pcm: don't call trigger ops to DSP for internal streams ASoC: Intel: add generic parameter set interface 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
arch/x86/include/asm/platform_sst_audio.h | 78 ++ sound/soc/intel/Kconfig | 2 +- sound/soc/intel/Makefile | 3 +- sound/soc/intel/sst-atom-controls.c | 1386 ++++++++++++++++++++++++++ sound/soc/intel/sst-atom-controls.h | 906 +++++++++++++++++ sound/soc/intel/sst-mfld-dsp.h | 414 ++++++++- sound/soc/intel/sst-mfld-platform-compress.c | 11 +- sound/soc/intel/sst-mfld-platform-pcm.c | 492 +++++++--- sound/soc/intel/sst-mfld-platform.h | 47 +- 9 files changed, 3199 insertions(+), 140 deletions(-) create mode 100644 arch/x86/include/asm/platform_sst_audio.h create mode 100644 sound/soc/intel/sst-atom-controls.c create mode 100644 sound/soc/intel/sst-atom-controls.h
Allows driver to be built on wider configs
Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/Kconfig | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index c30fedb..c6fe1e4 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -1,6 +1,6 @@ config SND_MFLD_MACHINE tristate "SOC Machine Audio driver for Intel Medfield MID platform" - depends on INTEL_SCU_IPC + depends on (INTEL_SCU_IPC || COMPILE_TEST) select SND_SOC_SN95031 select SND_SST_MFLD_PLATFORM help
On Sat, Jun 21, 2014 at 11:42:41AM +0100, Mark Brown wrote:
On Fri, Jun 13, 2014 at 06:03:50PM +0530, Vinod Koul wrote:
Allows driver to be built on wider configs
Applied, thanks.
...and dropped since it causes build failures all over the place especially around the SCU. I'd guess you need a dependency on at least the SCU as well as COMPILE_TEST.
On Sat, Jun 21, 2014 at 04:34:53PM +0100, Mark Brown wrote:
On Sat, Jun 21, 2014 at 11:42:41AM +0100, Mark Brown wrote:
On Fri, Jun 13, 2014 at 06:03:50PM +0530, Vinod Koul wrote:
Allows driver to be built on wider configs
Applied, thanks.
...and dropped since it causes build failures all over the place especially around the SCU. I'd guess you need a dependency on at least the SCU as well as COMPILE_TEST.
Sure makes sense, I will fix the SCU bits and resend this patch
Btw, there are few patches in the series which can go thru like: - path 5, its IPC defines for DSP - patch 6, stream modularization - patch 8, common stream for pc/compress - path 9, FE, BE ops - patch 11, fix trigger for BE - patch 12, generic DSP set params - patch 15, power control for DSP If there are no comments on these. Since the kcontrol patches are in seprate patches, I think they should apply, otherwise I cna split up in next set
Thanks
This helps us to handle pcm and compress ops seperately and per dai
Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/sst-mfld-platform-pcm.c | 112 +++++++++++++++++------------- 1 files changed, 63 insertions(+), 49 deletions(-)
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index 7c790f5..0d46005 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -230,19 +230,12 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream) } /* end -- helper functions */
-static int sst_platform_open(struct snd_pcm_substream *substream) +static int sst_media_open(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) { + int ret_val = 0; struct snd_pcm_runtime *runtime = substream->runtime; struct sst_runtime_stream *stream; - int ret_val; - - pr_debug("sst_platform_open called\n"); - - snd_soc_set_runtime_hwparams(substream, &sst_platform_pcm_hw); - ret_val = snd_pcm_hw_constraint_integer(runtime, - SNDRV_PCM_HW_PARAM_PERIODS); - if (ret_val < 0) - return ret_val;
stream = kzalloc(sizeof(*stream), GFP_KERNEL); if (!stream) @@ -251,50 +244,54 @@ static int sst_platform_open(struct snd_pcm_substream *substream)
/* get the sst ops */ mutex_lock(&sst_lock); - if (!sst) { + if (!sst || + !try_module_get(sst->dev->driver->owner)) { pr_err("no device available to run\n"); - mutex_unlock(&sst_lock); - kfree(stream); - return -ENODEV; - } - if (!try_module_get(sst->dev->driver->owner)) { - mutex_unlock(&sst_lock); - kfree(stream); - return -ENODEV; + ret_val = -ENODEV; + goto out_ops; } stream->ops = sst->ops; mutex_unlock(&sst_lock);
stream->stream_info.str_id = 0; - sst_set_stream_status(stream, SST_PLATFORM_INIT); + stream->stream_info.mad_substream = substream; /* allocate memory for SST API set */ runtime->private_data = stream;
- return 0; + /* Make sure, that the period size is always even */ + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIODS, 2); + + return snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); +out_ops: + kfree(stream); + mutex_unlock(&sst_lock); + return ret_val; }
-static int sst_platform_close(struct snd_pcm_substream *substream) +static void sst_media_close(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) { struct sst_runtime_stream *stream; int ret_val = 0, str_id;
- pr_debug("sst_platform_close called\n"); stream = substream->runtime->private_data; str_id = stream->stream_info.str_id; if (str_id) ret_val = stream->ops->close(str_id); module_put(sst->dev->driver->owner); kfree(stream); - return ret_val; + return; }
-static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream) +static int sst_media_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) { struct sst_runtime_stream *stream; int ret_val = 0, str_id;
- pr_debug("sst_platform_pcm_prepare called\n"); stream = substream->runtime->private_data; str_id = stream->stream_info.str_id; if (stream->stream_info.str_id) { @@ -316,6 +313,41 @@ static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream) return ret_val; }
+static int sst_media_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); + return 0; +} + +static int sst_media_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return snd_pcm_lib_free_pages(substream); +} + +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, +}; + +static int sst_platform_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime; + + if (substream->pcm->internal) + return 0; + + runtime = substream->runtime; + runtime->hw = sst_platform_pcm_hw; + return 0; +} + static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { @@ -377,32 +409,14 @@ static snd_pcm_uframes_t sst_platform_pcm_pointer pr_err("sst: error code = %d\n", ret_val); return ret_val; } - return stream->stream_info.buffer_ptr; -} - -static int sst_platform_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); - memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); - - return 0; -} - -static int sst_platform_pcm_hw_free(struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_pages(substream); + return str_info->buffer_ptr; }
static struct snd_pcm_ops sst_platform_ops = { .open = sst_platform_open, - .close = sst_platform_close, .ioctl = snd_pcm_lib_ioctl, - .prepare = sst_platform_pcm_prepare, .trigger = sst_platform_pcm_trigger, .pointer = sst_platform_pcm_pointer, - .hw_params = sst_platform_pcm_hw_params, - .hw_free = sst_platform_pcm_hw_free, };
static void sst_pcm_free(struct snd_pcm *pcm) @@ -413,15 +427,15 @@ static void sst_pcm_free(struct snd_pcm *pcm)
static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_soc_dai *dai = rtd->cpu_dai; struct snd_pcm *pcm = rtd->pcm; int retval = 0;
- pr_debug("sst_pcm_new called\n"); - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream || - pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + if (dai->driver->playback.channels_min || + dai->driver->capture.channels_min) { retval = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), + snd_dma_continuous_data(GFP_DMA), SST_MIN_BUFFER, SST_MAX_BUFFER); if (retval) { pr_err("dma buffer allocationf fail\n");
The argument was called mad_substream which is no longer apt as older driver is not used anymore so rename as arg
Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/sst-mfld-platform-pcm.c | 10 +++++----- sound/soc/intel/sst-mfld-platform.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index 0d46005..4528946 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -192,9 +192,9 @@ static int sst_platform_alloc_stream(struct snd_pcm_substream *substream) return ret_val; }
-static void sst_period_elapsed(void *mad_substream) +static void sst_period_elapsed(void *arg) { - struct snd_pcm_substream *substream = mad_substream; + struct snd_pcm_substream *substream = arg; struct sst_runtime_stream *stream; int status;
@@ -218,7 +218,7 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream) pr_debug("setting buffer ptr param\n"); sst_set_stream_status(stream, SST_PLATFORM_INIT); stream->stream_info.period_elapsed = sst_period_elapsed; - stream->stream_info.mad_substream = substream; + stream->stream_info.arg = substream; stream->stream_info.buffer_ptr = 0; stream->stream_info.sfreq = substream->runtime->rate; ret_val = stream->ops->device_control( @@ -255,7 +255,7 @@ static int sst_media_open(struct snd_pcm_substream *substream,
stream->stream_info.str_id = 0;
- stream->stream_info.mad_substream = substream; + stream->stream_info.arg = substream; /* allocate memory for SST API set */ runtime->private_data = stream;
@@ -363,7 +363,7 @@ static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, pr_debug("sst: Trigger Start\n"); str_cmd = SST_SND_START; status = SST_PLATFORM_RUNNING; - stream->stream_info.mad_substream = substream; + stream->stream_info.arg = substream; break; case SNDRV_PCM_TRIGGER_STOP: pr_debug("sst: in stop\n"); diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h index 6c5e7dc..6d929c7 100644 --- a/sound/soc/intel/sst-mfld-platform.h +++ b/sound/soc/intel/sst-mfld-platform.h @@ -39,8 +39,8 @@ extern struct sst_device *sst;
struct pcm_stream_info { int str_id; - void *mad_substream; - void (*period_elapsed) (void *mad_substream); + void *arg; + void (*period_elapsed) (void *arg); unsigned long long buffer_ptr; int sfreq; };
On Fri, Jun 13, 2014 at 06:03:52PM +0530, Vinod Koul wrote:
The argument was called mad_substream which is no longer apt as older driver is not used anymore so rename as arg
Applied, thanks.
Now the DSP is capable of reporting the delay, report it to upper layers
Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/sst-mfld-platform-pcm.c | 1 + sound/soc/intel/sst-mfld-platform.h | 1 + 2 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index 4528946..80879e5 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -409,6 +409,7 @@ static snd_pcm_uframes_t sst_platform_pcm_pointer pr_err("sst: error code = %d\n", ret_val); return ret_val; } + substream->runtime->delay = str_info->pcm_delay; return str_info->buffer_ptr; }
diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h index 6d929c7..33a0a27 100644 --- a/sound/soc/intel/sst-mfld-platform.h +++ b/sound/soc/intel/sst-mfld-platform.h @@ -42,6 +42,7 @@ struct pcm_stream_info { void *arg; void (*period_elapsed) (void *arg); unsigned long long buffer_ptr; + unsigned long long pcm_delay; int sfreq; };
This will be used to update current driver as well as in support for the mrfld patches
Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/sst-mfld-dsp.h | 414 ++++++++++++++++++++++++++++++++++++++- 1 files changed, 403 insertions(+), 11 deletions(-)
diff --git a/sound/soc/intel/sst-mfld-dsp.h b/sound/soc/intel/sst-mfld-dsp.h index 8d482d7..2c88785 100644 --- a/sound/soc/intel/sst-mfld-dsp.h +++ b/sound/soc/intel/sst-mfld-dsp.h @@ -3,7 +3,7 @@ /* * sst_mfld_dsp.h - Intel SST Driver for audio engine * - * Copyright (C) 2008-12 Intel Corporation + * Copyright (C) 2008-14 Intel Corporation * Authors: Vinod Koul vinod.koul@linux.intel.com * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -19,6 +19,142 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+#define SST_MAX_BIN_BYTES 1024 + +#define MAX_DBG_RW_BYTES 80 +#define MAX_NUM_SCATTER_BUFFERS 8 +#define MAX_LOOP_BACK_DWORDS 8 +/* IPC base address and mailbox, timestamp offsets */ +#define SST_MAILBOX_SIZE 0x0400 +#define SST_MAILBOX_SEND 0x0000 +#define SST_TIME_STAMP 0x1800 +#define SST_TIME_STAMP_MRFLD 0x800 +#define SST_RESERVED_OFFSET 0x1A00 +#define SST_SCU_LPE_MAILBOX 0x1000 +#define SST_LPE_SCU_MAILBOX 0x1400 +#define SST_SCU_LPE_LOG_BUF (SST_SCU_LPE_MAILBOX+16) +#define PROCESS_MSG 0x80 + +/* Message ID's for IPC messages */ +/* Bits B7: SST or IA/SC ; B6-B4: Msg Category; B3-B0: Msg Type */ + +/* I2L Firmware/Codec Download msgs */ +#define IPC_IA_PREP_LIB_DNLD 0x01 +#define IPC_IA_LIB_DNLD_CMPLT 0x02 +#define IPC_IA_GET_FW_VERSION 0x04 +#define IPC_IA_GET_FW_BUILD_INF 0x05 +#define IPC_IA_GET_FW_INFO 0x06 +#define IPC_IA_GET_FW_CTXT 0x07 +#define IPC_IA_SET_FW_CTXT 0x08 +#define IPC_IA_PREPARE_SHUTDOWN 0x31 +/* I2L Codec Config/control msgs */ +#define IPC_PREP_D3 0x10 +#define IPC_IA_SET_CODEC_PARAMS 0x10 +#define IPC_IA_GET_CODEC_PARAMS 0x11 +#define IPC_IA_SET_PPP_PARAMS 0x12 +#define IPC_IA_GET_PPP_PARAMS 0x13 +#define IPC_SST_PERIOD_ELAPSED_MRFLD 0xA +#define IPC_IA_ALG_PARAMS 0x1A +#define IPC_IA_TUNING_PARAMS 0x1B +#define IPC_IA_SET_RUNTIME_PARAMS 0x1C +#define IPC_IA_SET_PARAMS 0x1 +#define IPC_IA_GET_PARAMS 0x2 + +#define IPC_EFFECTS_CREATE 0xE +#define IPC_EFFECTS_DESTROY 0xF + +/* I2L Stream config/control msgs */ +#define IPC_IA_ALLOC_STREAM_MRFLD 0x2 +#define IPC_IA_ALLOC_STREAM 0x20 /* Allocate a stream ID */ +#define IPC_IA_FREE_STREAM_MRFLD 0x03 +#define IPC_IA_FREE_STREAM 0x21 /* Free the stream ID */ +#define IPC_IA_SET_STREAM_PARAMS 0x22 +#define IPC_IA_SET_STREAM_PARAMS_MRFLD 0x12 +#define IPC_IA_GET_STREAM_PARAMS 0x23 +#define IPC_IA_PAUSE_STREAM 0x24 +#define IPC_IA_PAUSE_STREAM_MRFLD 0x4 +#define IPC_IA_RESUME_STREAM 0x25 +#define IPC_IA_RESUME_STREAM_MRFLD 0x5 +#define IPC_IA_DROP_STREAM 0x26 +#define IPC_IA_DROP_STREAM_MRFLD 0x07 +#define IPC_IA_DRAIN_STREAM 0x27 /* Short msg with str_id */ +#define IPC_IA_DRAIN_STREAM_MRFLD 0x8 +#define IPC_IA_CONTROL_ROUTING 0x29 +#define IPC_IA_VTSV_UPDATE_MODULES 0x20 +#define IPC_IA_VTSV_DETECTED 0x21 + +#define IPC_IA_START_STREAM_MRFLD 0X06 +#define IPC_IA_START_STREAM 0x30 /* Short msg with str_id */ + +#define IPC_IA_SET_GAIN_MRFLD 0x21 +/* Debug msgs */ +#define IPC_IA_DBG_MEM_READ 0x40 +#define IPC_IA_DBG_MEM_WRITE 0x41 +#define IPC_IA_DBG_LOOP_BACK 0x42 +#define IPC_IA_DBG_LOG_ENABLE 0x45 +#define IPC_IA_DBG_SET_PROBE_PARAMS 0x47 + +/* L2I Firmware/Codec Download msgs */ +#define IPC_IA_FW_INIT_CMPLT 0x81 +#define IPC_IA_FW_INIT_CMPLT_MRFLD 0x01 +#define IPC_IA_FW_ASYNC_ERR_MRFLD 0x11 + +/* L2I Codec Config/control msgs */ +#define IPC_SST_FRAGMENT_ELPASED 0x90 /* Request IA more data */ + +#define IPC_SST_BUF_UNDER_RUN 0x92 /* PB Under run and stopped */ +#define IPC_SST_BUF_OVER_RUN 0x93 /* CAP Under run and stopped */ +#define IPC_SST_DRAIN_END 0x94 /* PB Drain complete and stopped */ +#define IPC_SST_CHNGE_SSP_PARAMS 0x95 /* PB SSP parameters changed */ +#define IPC_SST_STREAM_PROCESS_FATAL_ERR 0x96/* error in processing a stream */ +#define IPC_SST_PERIOD_ELAPSED 0x97 /* period elapsed */ + +#define IPC_SST_ERROR_EVENT 0x99 /* Buffer over run occurred */ +/* L2S messages */ +#define IPC_SC_DDR_LINK_UP 0xC0 +#define IPC_SC_DDR_LINK_DOWN 0xC1 +#define IPC_SC_SET_LPECLK_REQ 0xC2 +#define IPC_SC_SSP_BIT_BANG 0xC3 + +/* L2I Error reporting msgs */ +#define IPC_IA_MEM_ALLOC_FAIL 0xE0 +#define IPC_IA_PROC_ERR 0xE1 /* error in processing a + stream can be used by playback and + capture modules */ + +/* L2I Debug msgs */ +#define IPC_IA_PRINT_STRING 0xF0 + +/* Buffer under-run */ +#define IPC_IA_BUF_UNDER_RUN_MRFLD 0x0B + +/* Mrfld specific defines: + * For asynchronous messages(INIT_CMPLT, PERIOD_ELAPSED, ASYNC_ERROR) + * received from FW, the format is: + * - IPC High: pvt_id is set to zero. Always short message. + * - msg_id is in lower 16-bits of IPC low payload. + * - pipe_id is in higher 16-bits of IPC low payload for period_elapsed. + * - error id is in higher 16-bits of IPC low payload for async errors. + */ +#define SST_ASYNC_DRV_ID 0 + +/* Command Response or Acknowledge message to any IPC message will have + * same message ID and stream ID information which is sent. + * There is no specific Ack message ID. The data field is used as response + * meaning. + */ +enum ackData { + IPC_ACK_SUCCESS = 0, + IPC_ACK_FAILURE, +}; + +enum ipc_ia_msg_id { + IPC_CMD = 1, /*!< Task Control message ID */ + IPC_SET_PARAMS = 2,/*!< Task Set param message ID */ + IPC_GET_PARAMS = 3, /*!< Task Get param message ID */ + IPC_INVALID = 0xFF, /*!<Task Get param message ID */ +}; + enum sst_codec_types { /* AUDIO/MUSIC CODEC Type Definitions */ SST_CODEC_TYPE_UNKNOWN = 0, @@ -35,14 +171,142 @@ enum stream_type { SST_STREAM_TYPE_MUSIC = 1, };
+struct ipc_dsp_hdr { + u16 mod_index_id:8; /*!< DSP Command ID specific to tasks */ + u16 pipe_id:8; /*!< instance of the module in the pipeline */ + u16 mod_id; /*!< Pipe_id */ + u16 cmd_id; /*!< Module ID = lpe_algo_types_t */ + u16 length; /*!< Length of the payload only */ +} __packed; + +union ipc_header_high { + struct { + u32 msg_id:8; /* Message ID - Max 256 Message Types */ + u32 task_id:4; /* Task ID associated with this comand */ + u32 drv_id:4; /* Identifier for the driver to track*/ + u32 rsvd1:8; /* Reserved */ + u32 result:4; /* Reserved */ + u32 res_rqd:1; /* Response rqd */ + u32 large:1; /* Large Message if large = 1 */ + u32 done:1; /* bit 30 - Done bit */ + u32 busy:1; /* bit 31 - busy bit*/ + } part; + u32 full; +} __packed; +/* IPC header */ +union ipc_header_mrfld { + struct { + u32 header_low_payload; + union ipc_header_high header_high; + } p; + u64 full; +} __packed; +/* CAUTION NOTE: All IPC message body must be multiple of 32 bits.*/ + +/* IPC Header */ +union ipc_header { + struct { + u32 msg_id:8; /* Message ID - Max 256 Message Types */ + u32 str_id:5; + u32 large:1; /* Large Message if large = 1 */ + u32 reserved:2; /* Reserved for future use */ + u32 data:14; /* Ack/Info for msg, size of msg in Mailbox */ + u32 done:1; /* bit 30 */ + u32 busy:1; /* bit 31 */ + } part; + u32 full; +} __packed; + +/* Firmware build info */ +struct sst_fw_build_info { + unsigned char date[16]; /* Firmware build date */ + unsigned char time[16]; /* Firmware build time */ +} __packed; + +/* Firmware Version info */ +struct snd_sst_fw_version { + u8 build; /* build number*/ + u8 minor; /* minor number*/ + u8 major; /* major number*/ + u8 type; /* build type */ +}; + +struct ipc_header_fw_init { + struct snd_sst_fw_version fw_version;/* Firmware version details */ + struct sst_fw_build_info build_info; + u16 result; /* Fw init result */ + u8 module_id; /* Module ID in case of error */ + u8 debug_info; /* Debug info from Module ID in case of fail */ +} __packed; + +struct snd_sst_tstamp { + u64 ring_buffer_counter; /* PB/CP: Bytes copied from/to DDR. */ + u64 hardware_counter; /* PB/CP: Bytes DMAed to/from SSP. */ + u64 frames_decoded; + u64 bytes_decoded; + u64 bytes_copied; + u32 sampling_frequency; + u32 channel_peak[8]; +} __packed; + +/* Stream type params struture for Alloc stream */ +struct snd_sst_str_type { + u8 codec_type; /* Codec type */ + u8 str_type; /* 1 = voice 2 = music */ + u8 operation; /* Playback or Capture */ + u8 protected_str; /* 0=Non DRM, 1=DRM */ + u8 time_slots; + u8 reserved; /* Reserved */ + u16 result; /* Result used for acknowledgment */ +} __packed; + +/* Library info structure */ +struct module_info { + u32 lib_version; + u32 lib_type;/*TBD- KLOCKWORK u8 lib_type;*/ + u32 media_type; + u8 lib_name[12]; + u32 lib_caps; + unsigned char b_date[16]; /* Lib build date */ + unsigned char b_time[16]; /* Lib build time */ +} __packed; + +/* Library slot info */ +struct lib_slot_info { + u8 slot_num; /* 1 or 2 */ + u8 reserved1; + u16 reserved2; + u32 iram_size; /* slot size in IRAM */ + u32 dram_size; /* slot size in DRAM */ + u32 iram_offset; /* starting offset of slot in IRAM */ + u32 dram_offset; /* starting offset of slot in DRAM */ +} __packed; + +struct snd_ppp_mixer_params { + __u32 type; /*Type of the parameter */ + __u32 size; + __u32 input_stream_bitmap; /*Input stream Bit Map*/ +} __packed; + +struct snd_sst_lib_download { + struct module_info lib_info; /* library info type, capabilities etc */ + struct lib_slot_info slot_info; /* slot info to be downloaded */ + u32 mod_entry_pt; +}; + +struct snd_sst_lib_download_info { + struct snd_sst_lib_download dload_lib; + u16 result; /* Result used for acknowledgment */ + u8 pvt_id; /* Private ID */ + u8 reserved; /* for alignment */ +}; struct snd_pcm_params { u8 num_chan; /* 1=Mono, 2=Stereo */ u8 pcm_wd_sz; /* 16/24 - bit*/ - u32 reserved; /* Bitrate in bits per second */ - u32 sfreq; /* Sampling rate in Hz */ - u8 use_offload_path; + u8 use_offload_path; /* 0-PCM using period elpased & ALSA interfaces + 1-PCM stream via compressed interface */ u8 reserved2; - u16 reserved3; + u32 sfreq; /* Sampling rate in Hz */ u8 channel_map[8]; } __packed;
@@ -76,6 +340,7 @@ struct snd_aac_params { struct snd_wma_params { u8 num_chan; /* 1=Mono, 2=Stereo */ u8 pcm_wd_sz; /* 16/24 - bit*/ + u16 reserved1; u32 brate; /* Use the hard coded value. */ u32 sfreq; /* Sampling freq eg. 8000, 441000, 48000 */ u32 channel_mask; /* Channel Mask */ @@ -101,26 +366,153 @@ struct sst_address_info { };
struct snd_sst_alloc_params_ext { - struct sst_address_info ring_buf_info[8]; - u8 sg_count; - u8 reserved; - u16 reserved2; - u32 frag_size; /*Number of samples after which period elapsed + __u16 sg_count; + __u16 reserved; + __u32 frag_size; /*Number of samples after which period elapsed message is sent valid only if path = 0*/ -} __packed; + struct sst_address_info ring_buf_info[8]; +};
struct snd_sst_stream_params { union snd_sst_codec_params uc; } __packed;
struct snd_sst_params { + u32 result; u32 stream_id; u8 codec; u8 ops; u8 stream_type; u8 device_type; + u8 task; struct snd_sst_stream_params sparams; struct snd_sst_alloc_params_ext aparams; };
+struct snd_sst_alloc_mrfld { + u16 codec_type; + u8 operation; + u8 sg_count; + struct sst_address_info ring_buf_info[8]; + u32 frag_size; + u32 ts; + struct snd_sst_stream_params codec_params; +} __packed; + +/* Alloc stream params structure */ +struct snd_sst_alloc_params { + struct snd_sst_str_type str_type; + struct snd_sst_stream_params stream_params; + struct snd_sst_alloc_params_ext alloc_params; +} __packed; + +/* Alloc stream response message */ +struct snd_sst_alloc_response { + struct snd_sst_str_type str_type; /* Stream type for allocation */ + struct snd_sst_lib_download lib_dnld; /* Valid only for codec dnld */ +}; + +/* Drop response */ +struct snd_sst_drop_response { + u32 result; + u32 bytes; +}; + +struct snd_sst_async_msg { + u32 msg_id; /* Async msg id */ + u32 payload[0]; +}; + +struct snd_sst_async_err_msg { + u32 fw_resp; /* Firmware Result */ + u32 lib_resp; /*Library result */ +} __packed; + +struct snd_sst_vol { + u32 stream_id; + s32 volume; + u32 ramp_duration; + u32 ramp_type; /* Ramp type, default=0 */ +}; + +/* Gain library parameters for mrfld + * based on DSP command spec v0.82 + */ +struct snd_sst_gain_v2 { + u16 gain_cell_num; /* num of gain cells to modify*/ + u8 cell_nbr_idx; /* instance index*/ + u8 cell_path_idx; /* pipe-id */ + u16 module_id; /*module id */ + u16 left_cell_gain; /* left gain value in dB*/ + u16 right_cell_gain; /* right gain value in dB*/ + u16 gain_time_const; /* gain time constant*/ +} __packed; + +struct snd_sst_mute { + u32 stream_id; + u32 mute; +}; + +struct snd_sst_runtime_params { + u8 type; + u8 str_id; + u8 size; + u8 rsvd; + void *addr; +} __packed; + +enum stream_param_type { + SST_SET_TIME_SLOT = 0, + SST_SET_CHANNEL_INFO = 1, + OTHERS = 2, /*reserved for future params*/ +}; + +/* CSV Voice call routing structure */ +struct snd_sst_control_routing { + u8 control; /* 0=start, 1=Stop */ + u8 reserved[3]; /* Reserved- for 32 bit alignment */ +}; + +struct ipc_post { + struct list_head node; + union ipc_header header; /* driver specific */ + bool is_large; + bool is_process_reply; + union ipc_header_mrfld mrfld_header; + char *mailbox_data; +}; + +struct snd_sst_ctxt_params { + u32 address; /* Physical Address in DDR where the context is stored */ + u32 size; /* size of the context */ +}; + +struct snd_sst_lpe_log_params { + u8 dbg_type; + u8 module_id; + u8 log_level; + u8 reserved; +} __packed; + +enum snd_sst_bytes_type { + SND_SST_BYTES_SET = 0x1, + SND_SST_BYTES_GET = 0x2, +}; + +struct snd_sst_bytes_v2 { + u8 type; + u8 ipc_msg; + u8 block; + u8 task_id; + u8 pipe_id; + u8 rsvd; + u16 len; + char bytes[0]; +}; + +#define MAX_VTSV_FILES 2 +struct snd_sst_vtsv_info { + struct sst_address_info vfiles[MAX_VTSV_FILES]; +} __packed; + #endif /* __SST_MFLD_DSP_H__ */
Tis will be used to add table based support for pcm front ends in subsequent patches
Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/sst-mfld-platform-pcm.c | 102 +++++++++++++++++++++---------- sound/soc/intel/sst-mfld-platform.h | 2 +- 2 files changed, 71 insertions(+), 33 deletions(-)
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index 80879e5..6e7bfb1 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -143,52 +143,90 @@ static inline int sst_get_stream_status(struct sst_runtime_stream *stream) return state; }
+static void sst_fill_alloc_params(struct snd_pcm_substream *substream, + struct snd_sst_alloc_params_ext *alloc_param) +{ + unsigned int channels; + snd_pcm_uframes_t period_size; + ssize_t periodbytes; + ssize_t buffer_bytes = snd_pcm_lib_buffer_bytes(substream); + u32 buffer_addr = virt_to_phys(substream->dma_buffer.area); + + channels = substream->runtime->channels; + period_size = substream->runtime->period_size; + periodbytes = samples_to_bytes(substream->runtime, period_size); + alloc_param->ring_buf_info[0].addr = buffer_addr; + alloc_param->ring_buf_info[0].size = buffer_bytes; + alloc_param->sg_count = 1; + alloc_param->reserved = 0; + alloc_param->frag_size = periodbytes * channels; + +} static void sst_fill_pcm_params(struct snd_pcm_substream *substream, - struct sst_pcm_params *param) + struct snd_sst_stream_params *param) { + param->uc.pcm_params.num_chan = (u8) substream->runtime->channels; + param->uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits; + param->uc.pcm_params.sfreq = substream->runtime->rate;
- param->num_chan = (u8) substream->runtime->channels; - param->pcm_wd_sz = substream->runtime->sample_bits; - param->reserved = 0; - param->sfreq = substream->runtime->rate; - param->ring_buffer_size = snd_pcm_lib_buffer_bytes(substream); - param->period_count = substream->runtime->period_size; - param->ring_buffer_addr = virt_to_phys(substream->dma_buffer.area); - pr_debug("period_cnt = %d\n", param->period_count); - pr_debug("sfreq= %d, wd_sz = %d\n", param->sfreq, param->pcm_wd_sz); + /* PCM stream via ALSA interface */ + param->uc.pcm_params.use_offload_path = 0; + param->uc.pcm_params.reserved2 = 0; + memset(param->uc.pcm_params.channel_map, 0, sizeof(u8)); + +} +int sst_fill_stream_params(void *substream, + struct snd_sst_params *str_params, bool is_compress) +{ + struct snd_pcm_substream *pstream = NULL; + struct snd_compr_stream *cstream = NULL; + + if (is_compress == true) + cstream = (struct snd_compr_stream *)substream; + else + pstream = (struct snd_pcm_substream *)substream; + + str_params->stream_type = SST_STREAM_TYPE_MUSIC; + + /* For pcm streams */ + if (pstream) + str_params->ops = (u8)pstream->stream; + if (cstream) + str_params->ops = (u8)cstream->direction; + + return 0; }
-static int sst_platform_alloc_stream(struct snd_pcm_substream *substream) +static int sst_platform_alloc_stream(struct snd_pcm_substream *substream, + struct snd_soc_platform *platform) { struct sst_runtime_stream *stream = substream->runtime->private_data; - struct sst_pcm_params param = {0}; - struct sst_stream_params str_params = {0}; - int ret_val; + struct snd_sst_stream_params param = {{{0,},},}; + struct snd_sst_params str_params = {0}; + struct snd_sst_alloc_params_ext alloc_params = {0}; + int ret_val = 0;
/* set codec params and inform SST driver the same */ sst_fill_pcm_params(substream, ¶m); + sst_fill_alloc_params(substream, &alloc_params); substream->runtime->dma_area = substream->dma_buffer.area; str_params.sparams = param; - str_params.codec = param.codec; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - str_params.ops = STREAM_OPS_PLAYBACK; - str_params.device_type = substream->pcm->device + 1; - pr_debug("Playbck stream,Device %d\n", - substream->pcm->device); - } else { - str_params.ops = STREAM_OPS_CAPTURE; - str_params.device_type = SND_SST_DEVICE_CAPTURE; - pr_debug("Capture stream,Device %d\n", - substream->pcm->device); - } - ret_val = stream->ops->open(&str_params); - pr_debug("SST_SND_PLAY/CAPTURE ret_val = %x\n", ret_val); + str_params.aparams = alloc_params; + str_params.codec = SST_CODEC_TYPE_PCM; + + /* fill the device type and stream id to pass to SST driver */ + ret_val = sst_fill_stream_params(substream, &str_params, false); if (ret_val < 0) return ret_val;
- stream->stream_info.str_id = ret_val; - pr_debug("str id : %d\n", stream->stream_info.str_id); + stream->stream_info.str_id = str_params.stream_id; + + ret_val = stream->ops->open(&str_params); + if (ret_val <= 0) + return ret_val; + + return ret_val; }
@@ -300,8 +338,8 @@ static int sst_media_prepare(struct snd_pcm_substream *substream, return ret_val; }
- ret_val = sst_platform_alloc_stream(substream); - if (ret_val < 0) + ret_val = sst_platform_alloc_stream(substream, dai->platform); + if (ret_val <= 0) return ret_val; snprintf(substream->pcm->id, sizeof(substream->pcm->id), "%d", stream->stream_info.str_id); diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h index 33a0a27..aa5ddbb 100644 --- a/sound/soc/intel/sst-mfld-platform.h +++ b/sound/soc/intel/sst-mfld-platform.h @@ -125,7 +125,7 @@ struct compress_sst_ops { };
struct sst_ops { - int (*open) (struct sst_stream_params *str_param); + int (*open) (struct snd_sst_params *str_param); int (*device_control) (int cmd, void *arg); int (*close) (unsigned int str_id); };
Merrifield DSP used various pipelines to identify the streams and processing modules. Add these defination in the pcm driver and also add a table for device entries to firmware pipeline id conversion
Signed-off-by: Vinod Koul vinod.koul@intel.com --- arch/x86/include/asm/platform_sst_audio.h | 78 ++++++++++++++++++++++ sound/soc/intel/sst-atom-controls.h | 30 +++++++++ sound/soc/intel/sst-mfld-platform-pcm.c | 100 ++++++++++++++++++++++++++--- sound/soc/intel/sst-mfld-platform.h | 18 +++++ 4 files changed, 217 insertions(+), 9 deletions(-) create mode 100644 arch/x86/include/asm/platform_sst_audio.h create mode 100644 sound/soc/intel/sst-atom-controls.h
diff --git a/arch/x86/include/asm/platform_sst_audio.h b/arch/x86/include/asm/platform_sst_audio.h new file mode 100644 index 0000000..0a4e140 --- /dev/null +++ b/arch/x86/include/asm/platform_sst_audio.h @@ -0,0 +1,78 @@ +/* + * platform_sst_audio.h: sst audio platform data header file + * + * Copyright (C) 2012-14 Intel Corporation + * Author: Jeeja KP jeeja.kp@intel.com + * 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. + */ +#ifndef _PLATFORM_SST_AUDIO_H_ +#define _PLATFORM_SST_AUDIO_H_ + +#include <linux/sfi.h> + +enum sst_audio_task_id_mrfld { + SST_TASK_ID_NONE = 0, + SST_TASK_ID_SBA = 1, + SST_TASK_ID_MEDIA = 3, + SST_TASK_ID_MAX = SST_TASK_ID_MEDIA, +}; + +/* Device IDs for Merrifield are Pipe IDs, + * ref: DSP spec v0.75 */ +enum sst_audio_device_id_mrfld { + /* Output pipeline IDs */ + PIPE_ID_OUT_START = 0x0, + PIPE_CODEC_OUT0 = 0x2, + PIPE_CODEC_OUT1 = 0x3, + PIPE_SPROT_LOOP_OUT = 0x4, + PIPE_MEDIA_LOOP1_OUT = 0x5, + PIPE_MEDIA_LOOP2_OUT = 0x6, + PIPE_VOIP_OUT = 0xC, + PIPE_PCM0_OUT = 0xD, + PIPE_PCM1_OUT = 0xE, + PIPE_PCM2_OUT = 0xF, + PIPE_MEDIA0_OUT = 0x12, + PIPE_MEDIA1_OUT = 0x13, +/* Input Pipeline IDs */ + PIPE_ID_IN_START = 0x80, + PIPE_CODEC_IN0 = 0x82, + PIPE_CODEC_IN1 = 0x83, + PIPE_SPROT_LOOP_IN = 0x84, + PIPE_MEDIA_LOOP1_IN = 0x85, + PIPE_MEDIA_LOOP2_IN = 0x86, + PIPE_VOIP_IN = 0x8C, + PIPE_PCM0_IN = 0x8D, + PIPE_PCM1_IN = 0x8E, + PIPE_MEDIA0_IN = 0x8F, + PIPE_MEDIA1_IN = 0x90, + PIPE_MEDIA2_IN = 0x91, + PIPE_RSVD = 0xFF, +}; + +/* The stream map for each platform consists of an array of the below + * stream map structure. + */ +struct sst_dev_stream_map { + u8 dev_num; /* device id */ + u8 subdev_num; /* substream */ + u8 direction; + u8 device_id; /* fw id */ + u8 task_id; /* fw task */ + u8 status; +}; + +struct sst_platform_data { + /* Intel software platform id*/ + struct sst_dev_stream_map *pdev_strm_map; + unsigned int strm_map_size; +}; + +int add_sst_platform_device(void); +#endif + diff --git a/sound/soc/intel/sst-atom-controls.h b/sound/soc/intel/sst-atom-controls.h new file mode 100644 index 0000000..14063ab --- /dev/null +++ b/sound/soc/intel/sst-atom-controls.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2013-14 Intel Corp + * Author: Ramesh Babu ramesh.babu.koul@intel.com + * Omair M Abdullah omair.m.abdullah@intel.com + * Samreen Nilofer samreen.nilofer@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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#ifndef __SST_CONTROLS_V2_H__ +#define __SST_CONTROLS_V2_H__ + +enum { + MERR_DPCM_AUDIO = 0, + MERR_DPCM_COMPR, +}; + + +#endif diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index 6e7bfb1..7de8788 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -1,7 +1,7 @@ /* * sst_mfld_platform.c - Intel MID Platform driver * - * Copyright (C) 2010-2013 Intel Corp + * Copyright (C) 2010-2014 Intel Corp * Author: Vinod Koul vinod.koul@intel.com * Author: Harsha Priya priya.harsha@intel.com * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -27,7 +27,9 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/compress_driver.h> +#include <asm/platform_sst_audio.h> #include "sst-mfld-platform.h" +#include "sst-atom-controls.h"
struct sst_device *sst; static DEFINE_MUTEX(sst_lock); @@ -92,6 +94,13 @@ static struct snd_pcm_hardware sst_platform_pcm_hw = { .fifo_size = SST_FIFO_SIZE, };
+static struct sst_dev_stream_map dpcm_strm_map[] = { + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* Reserved, not in use */ + {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA1_IN, SST_TASK_ID_MEDIA, 0}, + {MERR_DPCM_COMPR, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA0_IN, SST_TASK_ID_MEDIA, 0}, + {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[] = { { @@ -175,12 +184,36 @@ static void sst_fill_pcm_params(struct snd_pcm_substream *substream, memset(param->uc.pcm_params.channel_map, 0, sizeof(u8));
} + +static int sst_get_stream_mapping(int dev, int sdev, int dir, + struct sst_dev_stream_map *map, int size) +{ + int i; + + if (map == NULL) + return -EINVAL; + + + /* index 0 is not used in stream map */ + for (i = 1; i < size; i++) { + if ((map[i].dev_num == dev) && (map[i].direction == dir)) + return i; + } + return 0; +} + int sst_fill_stream_params(void *substream, - struct snd_sst_params *str_params, bool is_compress) + const struct sst_data *ctx, struct snd_sst_params *str_params, bool is_compress) { + int map_size; + int index; + struct sst_dev_stream_map *map; struct snd_pcm_substream *pstream = NULL; struct snd_compr_stream *cstream = NULL;
+ map = ctx->pdata->pdev_strm_map; + map_size = ctx->pdata->strm_map_size; + if (is_compress == true) cstream = (struct snd_compr_stream *)substream; else @@ -189,11 +222,32 @@ int sst_fill_stream_params(void *substream, str_params->stream_type = SST_STREAM_TYPE_MUSIC;
/* For pcm streams */ - if (pstream) + if (pstream) { + index = sst_get_stream_mapping(pstream->pcm->device, + pstream->number, pstream->stream, + map, map_size); + if (index <= 0) + return -EINVAL; + + str_params->stream_id = index; + str_params->device_type = map[index].device_id; + str_params->task = map[index].task_id; + str_params->ops = (u8)pstream->stream; - if (cstream) - str_params->ops = (u8)cstream->direction; + } + + if (cstream) { + index = sst_get_stream_mapping(cstream->device->device, + 0, cstream->direction, + map, map_size); + if (index <= 0) + return -EINVAL; + str_params->stream_id = index; + str_params->device_type = map[index].device_id; + str_params->task = map[index].task_id;
+ str_params->ops = (u8)cstream->direction; + } return 0; }
@@ -206,6 +260,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);
/* set codec params and inform SST driver the same */ sst_fill_pcm_params(substream, ¶m); @@ -216,7 +271,7 @@ static int sst_platform_alloc_stream(struct snd_pcm_substream *substream, str_params.codec = SST_CODEC_TYPE_PCM;
/* fill the device type and stream id to pass to SST driver */ - ret_val = sst_fill_stream_params(substream, &str_params, false); + ret_val = sst_fill_stream_params(substream, ctx, &str_params, false); if (ret_val < 0) return ret_val;
@@ -321,7 +376,22 @@ static void sst_media_close(struct snd_pcm_substream *substream, ret_val = stream->ops->close(str_id); module_put(sst->dev->driver->owner); kfree(stream); - return; +} + +static inline unsigned int get_current_pipe_id(struct snd_soc_platform *platform, + struct snd_pcm_substream *substream) +{ + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map; + struct sst_runtime_stream *stream = + substream->runtime->private_data; + u32 str_id = stream->stream_info.str_id; + unsigned int pipe_id; + pipe_id = map[str_id].device_id; + + pr_debug("%s: got pipe_id = %#x for str_id = %d\n", + __func__, pipe_id, str_id); + return pipe_id; }
static int sst_media_prepare(struct snd_pcm_substream *substream, @@ -498,10 +568,22 @@ static const struct snd_soc_component_driver sst_component = {
static int sst_platform_probe(struct platform_device *pdev) { + struct sst_data *drv; int ret; + struct sst_platform_data *pdata = pdev->dev.platform_data; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (sst == NULL) { + pr_err("kzalloc failed\n"); + return -ENOMEM; + } + + pdata->pdev_strm_map = dpcm_strm_map; + pdata->strm_map_size = ARRAY_SIZE(dpcm_strm_map); + drv->pdata = pdata; + mutex_init(&drv->lock); + dev_set_drvdata(&pdev->dev, drv);
- pr_debug("sst_platform_probe called\n"); - sst = NULL; ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv); if (ret) { pr_err("registering soc platform failed\n"); diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h index aa5ddbb..33891a8 100644 --- a/sound/soc/intel/sst-mfld-platform.h +++ b/sound/soc/intel/sst-mfld-platform.h @@ -144,10 +144,28 @@ struct sst_device { char *name; struct device *dev; struct sst_ops *ops; + struct platform_device *pdev; struct compress_sst_ops *compr_ops; };
+struct sst_data; + void sst_set_stream_status(struct sst_runtime_stream *stream, int state); +struct sst_algo_int_control_v2 { + struct soc_mixer_control mc; + u16 module_id; /* module identifieer */ + u16 pipe_id; /* location info: pipe_id + instance_id */ + u16 instance_id; + unsigned int value; /* Value received is stored here */ +}; + +struct sst_data { + struct platform_device *pdev; + struct sst_platform_data *pdata; + struct mutex lock; +}; + int sst_register_dsp(struct sst_device *sst); int sst_unregister_dsp(struct sst_device *sst); + #endif
On Fri, Jun 13, 2014 at 06:03:56PM +0530, Vinod Koul wrote:
Merrifield DSP used various pipelines to identify the streams and processing modules. Add these defination in the pcm driver and also add a table for device entries to firmware pipeline id conversion
Applied, thanks.
As added in previosu patch along with stream to piep conversion si required for compressed audio too
Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/sst-mfld-platform-compress.c | 11 ++++++++--- sound/soc/intel/sst-mfld-platform.h | 7 +++---- 2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/sound/soc/intel/sst-mfld-platform-compress.c b/sound/soc/intel/sst-mfld-platform-compress.c index 02abd19..29c059c 100644 --- a/sound/soc/intel/sst-mfld-platform-compress.c +++ b/sound/soc/intel/sst-mfld-platform-compress.c @@ -100,14 +100,19 @@ static int sst_platform_compr_set_params(struct snd_compr_stream *cstream, int retval; struct snd_sst_params str_params; struct sst_compress_cb cb; + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_platform *platform = rtd->platform; + struct sst_data *ctx = snd_soc_platform_get_drvdata(platform);
stream = cstream->runtime->private_data; /* construct fw structure for this*/ memset(&str_params, 0, sizeof(str_params));
- str_params.ops = STREAM_OPS_PLAYBACK; - str_params.stream_type = SST_STREAM_TYPE_MUSIC; - str_params.device_type = SND_SST_DEVICE_COMPRESS; + /* fill the device type and stream id to pass to SST driver */ + retval = sst_fill_stream_params(cstream, ctx, &str_params, true); + pr_debug("compr_set_params: fill stream params ret_val = 0x%x\n", retval); + if (retval < 0) + return retval;
switch (params->codec.id) { case SND_AUDIOCODEC_MP3: { diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h index 33891a8..9dc962f 100644 --- a/sound/soc/intel/sst-mfld-platform.h +++ b/sound/soc/intel/sst-mfld-platform.h @@ -149,8 +149,10 @@ struct sst_device { };
struct sst_data; - 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); + struct sst_algo_int_control_v2 { struct soc_mixer_control mc; u16 module_id; /* module identifieer */ @@ -158,14 +160,11 @@ struct sst_algo_int_control_v2 { u16 instance_id; unsigned int value; /* Value received is stored here */ }; - struct sst_data { struct platform_device *pdev; struct sst_platform_data *pdata; struct mutex lock; }; - int sst_register_dsp(struct sst_device *sst); int sst_unregister_dsp(struct sst_device *sst); - #endif
On Fri, Jun 13, 2014 at 06:03:57PM +0530, Vinod Koul wrote:
As added in previosu patch along with stream to piep conversion si required for compressed audio too
Applied, thanks.
Now that we have added code for managing DPS piplelines we need to add the code for DSPs FrontEnd and Backend dai.
Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/sst-mfld-platform-pcm.c | 113 +++++++++++++++++++++++-------- 1 files 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 7de8788..3fcd35c 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, @@ -444,6 +415,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 Fri, Jun 13, 2014 at 06:03:58PM +0530, Vinod Koul wrote:
Now that we have added code for managing DPS piplelines we need to add the code for DSPs FrontEnd and Backend dai.
Does this bisect OK? I'm not seeing anything that hooks the DPCM stuff up here but I might be missing something.
The add the registers space for MRFLD DSP. The initialization is done in soc_probe of the platform. This will be used in subsequent patches to add platform widgets
Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/Makefile | 3 +- sound/soc/intel/sst-atom-controls.c | 90 +++++++++ sound/soc/intel/sst-atom-controls.h | 318 +++++++++++++++++++++++++++++++ sound/soc/intel/sst-mfld-platform-pcm.c | 10 +- sound/soc/intel/sst-mfld-platform.h | 10 + 5 files changed, 429 insertions(+), 2 deletions(-) create mode 100644 sound/soc/intel/sst-atom-controls.c
diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index 4bfca79..9480b5d 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 0000000..f710888 --- /dev/null +++ b/sound/soc/intel/sst-atom-controls.c @@ -0,0 +1,90 @@ +/* + * 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" + +unsigned int sst_soc_read(struct snd_soc_platform *platform, + unsigned int reg) +{ + struct sst_data *drv = snd_soc_platform_get_drvdata(platform); + + pr_debug("%s: reg[%d] = %#x\n", __func__, reg, drv->widget[reg]); + BUG_ON(reg > (SST_NUM_WIDGETS - 1)); + return drv->widget[reg]; +} + +int sst_soc_write(struct snd_soc_platform *platform, + unsigned int reg, unsigned int val) +{ + struct sst_data *drv = snd_soc_platform_get_drvdata(platform); + + pr_debug("%s: reg[%d] = %#x\n", __func__, reg, val); + BUG_ON(reg > (SST_NUM_WIDGETS - 1)); + drv->widget[reg] = val; + return 0; +} + +unsigned int sst_reg_read(struct sst_data *drv, unsigned int reg, + unsigned int shift, unsigned int max) +{ + unsigned int mask = (1 << fls(max)) - 1; + + return (drv->widget[reg] >> shift) & mask; +} + +unsigned int sst_reg_write(struct sst_data *drv, unsigned int reg, + unsigned int shift, unsigned int max, unsigned int val) +{ + unsigned int mask = (1 << fls(max)) - 1; + + val &= mask; + val <<= shift; + drv->widget[reg] &= ~(mask << shift); + drv->widget[reg] |= val; + return val; +} + + +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) { + pr_err("%s: kzalloc failed\n", __func__); + return -ENOMEM; + } + drv->widget = devm_kzalloc(platform->dev, + SST_NUM_WIDGETS * sizeof(*drv->widget), + GFP_KERNEL); + if (!drv->widget) { + pr_err("%s: kzalloc failed\n", __func__); + return -ENOMEM; + } + + return ret; +} diff --git a/sound/soc/intel/sst-atom-controls.h b/sound/soc/intel/sst-atom-controls.h index 14063ab..8c35946 100644 --- a/sound/soc/intel/sst-atom-controls.h +++ b/sound/soc/intel/sst-atom-controls.h @@ -1,4 +1,6 @@ /* + * controls_v2.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 @@ -25,6 +27,322 @@ enum { MERR_DPCM_AUDIO = 0, MERR_DPCM_COMPR, }; +/* + * This section defines the map for the mixer widgets. + * + * Each mixer will be represented by single value and that value will have each + * bit corresponding to one input + * + * Each out_id will correspond to one mixer and one path. Each input will be + * represented by single bit in the register. + */ + +/* mixer register ids here */ +#define SST_MIX(x) (x) + +#define SST_MIX_CODEC0 SST_MIX(2) +#define SST_MIX_CODEC1 SST_MIX(3) +#define SST_MIX_LOOP0 SST_MIX(4) +#define SST_MIX_LOOP1 SST_MIX(5) +#define SST_MIX_LOOP2 SST_MIX(6) +#define SST_MIX_VOIP SST_MIX(12) +#define SST_MIX_PCM0 SST_MIX(13) +#define SST_MIX_PCM1 SST_MIX(14) +#define SST_MIX_PCM2 SST_MIX(15) + +#define SST_MIX_MEDIA0 SST_MIX(19) +#define SST_MIX_MEDIA1 SST_MIX(20) + +#define SST_NUM_MIX (SST_MIX_MEDIA1 + 1) + +#define SST_MIX_SWITCH (SST_NUM_MIX + 1) +#define SST_OUT_SWITCH (SST_NUM_MIX + 2) +#define SST_IN_SWITCH (SST_NUM_MIX + 3) +#define SST_MUX_REG (SST_NUM_MIX + 4) +#define SST_REG_LAST (SST_MUX_REG) + +/* last entry defines array size */ +#define SST_NUM_WIDGETS (SST_REG_LAST + 1) + +/* in each mixer register we will define one bit for each 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 3fcd35c..8df5aca 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -609,11 +609,19 @@ 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, .pcm_free = sst_pcm_free, + .read = sst_soc_read, + .write = sst_soc_write, };
static const struct snd_soc_component_driver sst_component = { diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h index 9dc962f..dc60f86 100644 --- a/sound/soc/intel/sst-mfld-platform.h +++ b/sound/soc/intel/sst-mfld-platform.h @@ -149,6 +149,14 @@ struct sst_device { };
struct sst_data; + +int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform); +unsigned int sst_soc_read(struct snd_soc_platform *platform, unsigned int reg); +int sst_soc_write(struct snd_soc_platform *platform, unsigned int reg, unsigned int val); +unsigned int sst_reg_read(struct sst_data *sst, unsigned int reg, + unsigned int shift, unsigned int max); +unsigned int sst_reg_write(struct sst_data *sst, unsigned int reg, + unsigned int shift, unsigned int max, unsigned int val); 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); @@ -163,6 +171,8 @@ struct sst_algo_int_control_v2 { struct sst_data { struct platform_device *pdev; struct sst_platform_data *pdata; + u32 *widget; + char *byte_stream; struct mutex lock; }; int sst_register_dsp(struct sst_device *sst);
On 06/13/2014 02:33 PM, Vinod Koul wrote:
The add the registers space for MRFLD DSP. The initialization is done in soc_probe of the platform. This will be used in subsequent patches to add platform widgets
Signed-off-by: Vinod Koul vinod.koul@intel.com
sound/soc/intel/Makefile | 3 +- sound/soc/intel/sst-atom-controls.c | 90 +++++++++ sound/soc/intel/sst-atom-controls.h | 318 +++++++++++++++++++++++++++++++ sound/soc/intel/sst-mfld-platform-pcm.c | 10 +- sound/soc/intel/sst-mfld-platform.h | 10 + 5 files changed, 429 insertions(+), 2 deletions(-) create mode 100644 sound/soc/intel/sst-atom-controls.c
diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index 4bfca79..9480b5d 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 0000000..f710888 --- /dev/null +++ b/sound/soc/intel/sst-atom-controls.c @@ -0,0 +1,90 @@ +/*
- 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"
+unsigned int sst_soc_read(struct snd_soc_platform *platform,
unsigned int reg)
+{
- struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
- pr_debug("%s: reg[%d] = %#x\n", __func__, reg, drv->widget[reg]);
- BUG_ON(reg > (SST_NUM_WIDGETS - 1));
- return drv->widget[reg];
+}
+int sst_soc_write(struct snd_soc_platform *platform,
unsigned int reg, unsigned int val)
+{
- struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
- pr_debug("%s: reg[%d] = %#x\n", __func__, reg, val);
- BUG_ON(reg > (SST_NUM_WIDGETS - 1));
- drv->widget[reg] = val;
- return 0;
+}
These seem to be purely virtual registers, what is this about? The DAPM core is able to handle widgets and controls without any register backing just fine. There is no need to emulate virtual registers.
+unsigned int sst_reg_read(struct sst_data *drv, unsigned int reg,
unsigned int shift, unsigned int max)
+{
[...]
+}
+unsigned int sst_reg_write(struct sst_data *drv, unsigned int reg,
unsigned int shift, unsigned int max, unsigned int val)
+{
[..]
+}
How are these functions different from snd_soc_platform_{read,update_bits}()?
- Lars
On Fri, Jun 20, 2014 at 10:22:30AM +0200, Lars-Peter Clausen wrote:
On 06/13/2014 02:33 PM, Vinod Koul wrote:
+unsigned int sst_soc_read(struct snd_soc_platform *platform,
unsigned int reg)
+{
- struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
- pr_debug("%s: reg[%d] = %#x\n", __func__, reg, drv->widget[reg]);
- BUG_ON(reg > (SST_NUM_WIDGETS - 1));
- return drv->widget[reg];
+}
+int sst_soc_write(struct snd_soc_platform *platform,
unsigned int reg, unsigned int val)
+{
- struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
- pr_debug("%s: reg[%d] = %#x\n", __func__, reg, val);
- BUG_ON(reg > (SST_NUM_WIDGETS - 1));
- drv->widget[reg] = val;
- return 0;
+}
These seem to be purely virtual registers, what is this about? The DAPM core is able to handle widgets and controls without any register backing just fine. There is no need to emulate virtual registers.
But we need to store the mixer configuration for sending IPCs to DSP. So virtual register file is very much required
+unsigned int sst_reg_read(struct sst_data *drv, unsigned int reg,
unsigned int shift, unsigned int max)
+{
[...]
+}
+unsigned int sst_reg_write(struct sst_data *drv, unsigned int reg,
unsigned int shift, unsigned int max, unsigned int val)
+{
[..]
+}
How are these functions different from snd_soc_platform_{read,update_bits}()?
Should be possible, I will check!
On 06/20/2014 01:32 PM, Vinod Koul wrote:
On Fri, Jun 20, 2014 at 10:22:30AM +0200, Lars-Peter Clausen wrote:
On 06/13/2014 02:33 PM, Vinod Koul wrote:
+unsigned int sst_soc_read(struct snd_soc_platform *platform,
unsigned int reg)
+{
- struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
- pr_debug("%s: reg[%d] = %#x\n", __func__, reg, drv->widget[reg]);
- BUG_ON(reg > (SST_NUM_WIDGETS - 1));
- return drv->widget[reg];
+}
+int sst_soc_write(struct snd_soc_platform *platform,
unsigned int reg, unsigned int val)
+{
- struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
- pr_debug("%s: reg[%d] = %#x\n", __func__, reg, val);
- BUG_ON(reg > (SST_NUM_WIDGETS - 1));
- drv->widget[reg] = val;
- return 0;
+}
These seem to be purely virtual registers, what is this about? The DAPM core is able to handle widgets and controls without any register backing just fine. There is no need to emulate virtual registers.
But we need to store the mixer configuration for sending IPCs to DSP. So virtual register file is very much required
Hm, ok. But how does this work, when is the IPC triggered and why can't the IPC be done from within the write function?
- Lars
On Fri, Jun 20, 2014 at 02:33:19PM +0200, Lars-Peter Clausen wrote:
On 06/20/2014 01:32 PM, Vinod Koul wrote:
On Fri, Jun 20, 2014 at 10:22:30AM +0200, Lars-Peter Clausen wrote:
On 06/13/2014 02:33 PM, Vinod Koul wrote:
+unsigned int sst_soc_read(struct snd_soc_platform *platform,
unsigned int reg)
+{
- struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
- pr_debug("%s: reg[%d] = %#x\n", __func__, reg, drv->widget[reg]);
- BUG_ON(reg > (SST_NUM_WIDGETS - 1));
- return drv->widget[reg];
+}
+int sst_soc_write(struct snd_soc_platform *platform,
unsigned int reg, unsigned int val)
+{
- struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
- pr_debug("%s: reg[%d] = %#x\n", __func__, reg, val);
- BUG_ON(reg > (SST_NUM_WIDGETS - 1));
- drv->widget[reg] = val;
- return 0;
+}
These seem to be purely virtual registers, what is this about? The DAPM core is able to handle widgets and controls without any register backing just fine. There is no need to emulate virtual registers.
But we need to store the mixer configuration for sending IPCs to DSP. So virtual register file is very much required
Hm, ok. But how does this work, when is the IPC triggered and why can't the IPC be done from within the write function?
Write can come at any time, even when DSP is inactive. Also we want to tell DSP about the paths which are active
So most of IPCs are sent from DAPM widget handlers with exception of controls which are active. For those we send IPC during get/put handlers.
Fwiw, please note the register file is in driver to keep the values for controls. It is not availble to DSP.
On 06/21/2014 08:22 AM, Vinod Koul wrote:
On Fri, Jun 20, 2014 at 02:33:19PM +0200, Lars-Peter Clausen wrote:
On 06/20/2014 01:32 PM, Vinod Koul wrote:
On Fri, Jun 20, 2014 at 10:22:30AM +0200, Lars-Peter Clausen wrote:
On 06/13/2014 02:33 PM, Vinod Koul wrote:
+unsigned int sst_soc_read(struct snd_soc_platform *platform,
unsigned int reg)
+{
- struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
- pr_debug("%s: reg[%d] = %#x\n", __func__, reg, drv->widget[reg]);
- BUG_ON(reg > (SST_NUM_WIDGETS - 1));
- return drv->widget[reg];
+}
+int sst_soc_write(struct snd_soc_platform *platform,
unsigned int reg, unsigned int val)
+{
- struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
- pr_debug("%s: reg[%d] = %#x\n", __func__, reg, val);
- BUG_ON(reg > (SST_NUM_WIDGETS - 1));
- drv->widget[reg] = val;
- return 0;
+}
These seem to be purely virtual registers, what is this about? The DAPM core is able to handle widgets and controls without any register backing just fine. There is no need to emulate virtual registers.
But we need to store the mixer configuration for sending IPCs to DSP. So virtual register file is very much required
Hm, ok. But how does this work, when is the IPC triggered and why can't the IPC be done from within the write function?
Write can come at any time, even when DSP is inactive. Also we want to tell DSP about the paths which are active
So most of IPCs are sent from DAPM widget handlers with exception of controls which are active. For those we send IPC during get/put handlers.
This sounds very similar to the auto-mute controls that are supported by the ASoC core. Auto-mute controls will update the value in the put handler, but only if the widget it is attached to is powered up. If the widget is not powered up the value is stored and will be written once the widget powers up. Try to see if you can adopt the auto-mute controls for your usecase. I think this should allow to remove some of the parts from the driver that peek into core internal data structures.
- Lars
On Sat, Jun 21, 2014 at 08:56:42AM +0200, Lars-Peter Clausen wrote:
On 06/21/2014 08:22 AM, Vinod Koul wrote:
On Fri, Jun 20, 2014 at 02:33:19PM +0200, Lars-Peter Clausen wrote:
On 06/20/2014 01:32 PM, Vinod Koul wrote:
On Fri, Jun 20, 2014 at 10:22:30AM +0200, Lars-Peter Clausen wrote:
On 06/13/2014 02:33 PM, Vinod Koul wrote:
+unsigned int sst_soc_read(struct snd_soc_platform *platform,
unsigned int reg)
+{
- struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
- pr_debug("%s: reg[%d] = %#x\n", __func__, reg, drv->widget[reg]);
- BUG_ON(reg > (SST_NUM_WIDGETS - 1));
- return drv->widget[reg];
+}
+int sst_soc_write(struct snd_soc_platform *platform,
unsigned int reg, unsigned int val)
+{
- struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
- pr_debug("%s: reg[%d] = %#x\n", __func__, reg, val);
- BUG_ON(reg > (SST_NUM_WIDGETS - 1));
- drv->widget[reg] = val;
- return 0;
+}
These seem to be purely virtual registers, what is this about? The DAPM core is able to handle widgets and controls without any register backing just fine. There is no need to emulate virtual registers.
But we need to store the mixer configuration for sending IPCs to DSP. So virtual register file is very much required
Hm, ok. But how does this work, when is the IPC triggered and why can't the IPC be done from within the write function?
Write can come at any time, even when DSP is inactive. Also we want to tell DSP about the paths which are active
So most of IPCs are sent from DAPM widget handlers with exception of controls which are active. For those we send IPC during get/put handlers.
This sounds very similar to the auto-mute controls that are supported by the ASoC core. Auto-mute controls will update the value in the put handler, but only if the widget it is attached to is powered up. If the widget is not powered up the value is stored and will be written once the widget powers up. Try to see if you can adopt the auto-mute controls for your usecase. I think this should allow to remove some of the parts from the driver that peek into core internal data structures.
Do you mean autodisable bit? Btw this seems to be used only in kcontrol creation and not in runtime. Actually I wont find using this feature if we have it :)
On 06/23/2014 06:27 AM, Vinod Koul wrote:
On Sat, Jun 21, 2014 at 08:56:42AM +0200, Lars-Peter Clausen wrote:
On 06/21/2014 08:22 AM, Vinod Koul wrote:
On Fri, Jun 20, 2014 at 02:33:19PM +0200, Lars-Peter Clausen wrote:
On 06/20/2014 01:32 PM, Vinod Koul wrote:
On Fri, Jun 20, 2014 at 10:22:30AM +0200, Lars-Peter Clausen wrote:
On 06/13/2014 02:33 PM, Vinod Koul wrote: > +unsigned int sst_soc_read(struct snd_soc_platform *platform, > + unsigned int reg) > +{ > + struct sst_data *drv = snd_soc_platform_get_drvdata(platform); > + > + pr_debug("%s: reg[%d] = %#x\n", __func__, reg, drv->widget[reg]); > + BUG_ON(reg > (SST_NUM_WIDGETS - 1)); > + return drv->widget[reg]; > +} > + > +int sst_soc_write(struct snd_soc_platform *platform, > + unsigned int reg, unsigned int val) > +{ > + struct sst_data *drv = snd_soc_platform_get_drvdata(platform); > + > + pr_debug("%s: reg[%d] = %#x\n", __func__, reg, val); > + BUG_ON(reg > (SST_NUM_WIDGETS - 1)); > + drv->widget[reg] = val; > + return 0; > +}
These seem to be purely virtual registers, what is this about? The DAPM core is able to handle widgets and controls without any register backing just fine. There is no need to emulate virtual registers.
But we need to store the mixer configuration for sending IPCs to DSP. So virtual register file is very much required
Hm, ok. But how does this work, when is the IPC triggered and why can't the IPC be done from within the write function?
Write can come at any time, even when DSP is inactive. Also we want to tell DSP about the paths which are active
So most of IPCs are sent from DAPM widget handlers with exception of controls which are active. For those we send IPC during get/put handlers.
This sounds very similar to the auto-mute controls that are supported by the ASoC core. Auto-mute controls will update the value in the put handler, but only if the widget it is attached to is powered up. If the widget is not powered up the value is stored and will be written once the widget powers up. Try to see if you can adopt the auto-mute controls for your usecase. I think this should allow to remove some of the parts from the driver that peek into core internal data structures.
Do you mean autodisable bit? Btw this seems to be used only in kcontrol creation and not in runtime. Actually I wont find using this feature if we have it :)
Auto-disable is also used at runtime. If the auto-disable bit in a control is set to 1, the control will have the behavior as described above.
- Lars
On Wed, Jun 25, 2014 at 06:31:17AM +0200, Lars-Peter Clausen wrote:
On 06/23/2014 06:27 AM, Vinod Koul wrote:
On Sat, Jun 21, 2014 at 08:56:42AM +0200, Lars-Peter Clausen wrote:
On 06/21/2014 08:22 AM, Vinod Koul wrote:
On Fri, Jun 20, 2014 at 02:33:19PM +0200, Lars-Peter Clausen wrote:
On 06/20/2014 01:32 PM, Vinod Koul wrote:
On Fri, Jun 20, 2014 at 10:22:30AM +0200, Lars-Peter Clausen wrote: >On 06/13/2014 02:33 PM, Vinod Koul wrote: >>+unsigned int sst_soc_read(struct snd_soc_platform *platform, >>+ unsigned int reg) >>+{ >>+ struct sst_data *drv = snd_soc_platform_get_drvdata(platform); >>+ >>+ pr_debug("%s: reg[%d] = %#x\n", __func__, reg, drv->widget[reg]); >>+ BUG_ON(reg > (SST_NUM_WIDGETS - 1)); >>+ return drv->widget[reg]; >>+} >>+ >>+int sst_soc_write(struct snd_soc_platform *platform, >>+ unsigned int reg, unsigned int val) >>+{ >>+ struct sst_data *drv = snd_soc_platform_get_drvdata(platform); >>+ >>+ pr_debug("%s: reg[%d] = %#x\n", __func__, reg, val); >>+ BUG_ON(reg > (SST_NUM_WIDGETS - 1)); >>+ drv->widget[reg] = val; >>+ return 0; >>+} > >These seem to be purely virtual registers, what is this about? The >DAPM core is able to handle widgets and controls without any >register backing just fine. There is no need to emulate virtual >registers. But we need to store the mixer configuration for sending IPCs to DSP. So virtual register file is very much required
Hm, ok. But how does this work, when is the IPC triggered and why can't the IPC be done from within the write function?
Write can come at any time, even when DSP is inactive. Also we want to tell DSP about the paths which are active
So most of IPCs are sent from DAPM widget handlers with exception of controls which are active. For those we send IPC during get/put handlers.
This sounds very similar to the auto-mute controls that are supported by the ASoC core. Auto-mute controls will update the value in the put handler, but only if the widget it is attached to is powered up. If the widget is not powered up the value is stored and will be written once the widget powers up. Try to see if you can adopt the auto-mute controls for your usecase. I think this should allow to remove some of the parts from the driver that peek into core internal data structures.
Do you mean autodisable bit? Btw this seems to be used only in kcontrol creation and not in runtime. Actually I wont find using this feature if we have it :)
Auto-disable is also used at runtime. If the auto-disable bit in a control is set to 1, the control will have the behavior as described above.
Ah now understood it, took a while and git history helped :)
Yes this is indeed very interesting, will explore it now
Thanks
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 --- sound/soc/intel/sst-mfld-platform-pcm.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index 8df5aca..0fbb688 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/sst-mfld-platform-pcm.c @@ -519,6 +519,8 @@ static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, int str_cmd, 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) {
This will be used by subsequent patches to send DSP cmds on DAPM widgets enable/disable
Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/sst-mfld-platform.h | 8 +++++++- 1 files changed, 7 insertions(+), 1 deletions(-)
diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h index dc60f86..5cfe947 100644 --- a/sound/soc/intel/sst-mfld-platform.h +++ b/sound/soc/intel/sst-mfld-platform.h @@ -63,7 +63,12 @@ enum sst_controls { SST_SND_BUFFER_POINTER = 0x05, SST_SND_STREAM_INIT = 0x06, SST_SND_START = 0x07, - SST_MAX_CONTROLS = 0x07, + SST_SET_RUNTIME_PARAMS = 0x08, + SST_SET_ALGO_PARAMS = 0x09, + SST_SET_BYTE_STREAM = 0x0A, + SST_GET_BYTE_STREAM = 0x0B, + + SST_MAX_CONTROLS = SST_GET_BYTE_STREAM, };
enum sst_stream_ops { @@ -127,6 +132,7 @@ struct compress_sst_ops { struct sst_ops { int (*open) (struct snd_sst_params *str_param); int (*device_control) (int cmd, void *arg); + int (*set_generic_params) (enum sst_controls cmd, void *arg); int (*close) (unsigned int str_id); };
This patch add support for various modules like eq etc for mrfld DSP. All these moduels will be exposed to usermode as bytes controls.
Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/sst-atom-controls.c | 172 +++++++++++++++++++++++++++++++++++ sound/soc/intel/sst-atom-controls.h | 129 ++++++++++++++++++++++++++ 2 files changed, 301 insertions(+), 0 deletions(-)
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index f710888..c72d3aa 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -66,6 +66,176 @@ unsigned int sst_reg_write(struct sst_data *drv, unsigned int reg, return val; }
+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)) { + pr_err("%s: command length too big (%u)", __func__, len); + len = SST_MAX_BIN_BYTES - sizeof(*byte_data); + WARN_ON(1); /* this happens only if code is wrong */ + } + 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->set_generic_params(SST_SET_BYTE_STREAM, + 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) { + pr_err("Failed to send cmd, kzalloc failed\n"); + 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(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_platform *platform = 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(platform->dev, bc->max, GFP_KERNEL); + if (bc->params == NULL) { + pr_err("kzalloc failed\n"); + 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_kcontrol_chip(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); + 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: + pr_err("Invalid Input- algo type:%ld\n", ucontrol->value.integer.value[0]); + 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); + + 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) { @@ -86,5 +256,7 @@ int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) 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 8c35946..448fdff 100644 --- a/sound/soc/intel/sst-atom-controls.h +++ b/sound/soc/intel/sst-atom-controls.h @@ -345,4 +345,133 @@ 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 06/13/2014 02:34 PM, Vinod Koul wrote: [...]
+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_platform *platform = snd_kcontrol_chip(kcontrol);
This won't work in asoc/for-next. Use snd_soc_kcontrol_platform(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(platform->dev, bc->max, GFP_KERNEL);
if (bc->params == NULL) {
pr_err("kzalloc failed\n");
return -ENOMEM;
}
- }
- return 0;
+}
+static int sst_algo_control_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+{
[...]
+}
+static int sst_algo_control_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+{
[...]
+}
You probably want some kind of locking around the put and get handlers.
+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),
You are creating a lot of global non-const variables here that are later modified in the put and get handlers and also elsewhere.
+};
On Fri, Jun 20, 2014 at 10:11:23AM +0200, Lars-Peter Clausen wrote:
On 06/13/2014 02:34 PM, Vinod Koul wrote:
+static int sst_algo_control_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+{
[...]
+}
You probably want some kind of locking around the put and get handlers.
Is that for prevting get/set races? I though the mixer implemention in sound/core would lock against that?
+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),
You are creating a lot of global non-const variables here that are later modified in the put and get handlers and also elsewhere.
Sorry which ones above are modfied. Above values are information for headers of IPCs which we send to DSPs
On 06/20/2014 01:30 PM, Vinod Koul wrote:
On Fri, Jun 20, 2014 at 10:11:23AM +0200, Lars-Peter Clausen wrote:
On 06/13/2014 02:34 PM, Vinod Koul wrote:
+static int sst_algo_control_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+{
[...]
+}
You probably want some kind of locking around the put and get handlers.
Is that for prevting get/set races? I though the mixer implemention in sound/core would lock against that?
There core doesn't do any locking on the put and get handlers. If you need locking you need to do it by hand.
+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),
You are creating a lot of global non-const variables here that are later modified in the put and get handlers and also elsewhere.
Sorry which ones above are modfied. Above values are information for headers of IPCs which we send to DSPs
The SST_ALGO_CTL_VALUE() macro uses compound literals to create a global (nameless) struct. A pointer to this struct is assigned to the kcontrols private_value field. This is later read and the struct is modified.
- Lars
On Fri, Jun 20, 2014 at 02:27:52PM +0200, Lars-Peter Clausen wrote:
On 06/20/2014 01:30 PM, Vinod Koul wrote:
On Fri, Jun 20, 2014 at 10:11:23AM +0200, Lars-Peter Clausen wrote:
On 06/13/2014 02:34 PM, Vinod Koul wrote:
+static int sst_algo_control_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+{
[...]
+}
You probably want some kind of locking around the put and get handlers.
Is that for prevting get/set races? I though the mixer implemention in sound/core would lock against that?
There core doesn't do any locking on the put and get handlers. If you need locking you need to do it by hand.
+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),
You are creating a lot of global non-const variables here that are later modified in the put and get handlers and also elsewhere.
Sorry which ones above are modfied. Above values are information for headers of IPCs which we send to DSPs
The SST_ALGO_CTL_VALUE() macro uses compound literals to create a global (nameless) struct. A pointer to this struct is assigned to the kcontrols private_value field. This is later read and the struct is modified.
Yes but not the above values as these as IPC header info which DSP needs.
On 06/21/2014 08:16 AM, Vinod Koul wrote:
On Fri, Jun 20, 2014 at 02:27:52PM +0200, Lars-Peter Clausen wrote:
On 06/20/2014 01:30 PM, Vinod Koul wrote:
On Fri, Jun 20, 2014 at 10:11:23AM +0200, Lars-Peter Clausen wrote:
On 06/13/2014 02:34 PM, Vinod Koul wrote:
+static int sst_algo_control_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+{
[...]
+}
You probably want some kind of locking around the put and get handlers.
Is that for prevting get/set races? I though the mixer implemention in sound/core would lock against that?
There core doesn't do any locking on the put and get handlers. If you need locking you need to do it by hand.
+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),
You are creating a lot of global non-const variables here that are later modified in the put and get handlers and also elsewhere.
Sorry which ones above are modfied. Above values are information for headers of IPCs which we send to DSPs
The SST_ALGO_CTL_VALUE() macro uses compound literals to create a global (nameless) struct. A pointer to this struct is assigned to the kcontrols private_value field. This is later read and the struct is modified.
Yes but not the above values as these as IPC header info which DSP needs.
The SST_ALGO_KCONTROL_BYTES() macro calls SST_ALGO_KCONTROL() which in return calls SST_ALGO_CTL_VALUE()
On Sat, Jun 21, 2014 at 08:19:13AM +0200, Lars-Peter Clausen wrote:
+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),
You are creating a lot of global non-const variables here that are later modified in the put and get handlers and also elsewhere.
Sorry which ones above are modfied. Above values are information for headers of IPCs which we send to DSPs
The SST_ALGO_CTL_VALUE() macro uses compound literals to create a global (nameless) struct. A pointer to this struct is assigned to the kcontrols private_value field. This is later read and the struct is modified.
Yes but not the above values as these as IPC header info which DSP needs.
The SST_ALGO_KCONTROL_BYTES() macro calls SST_ALGO_KCONTROL() which in return calls SST_ALGO_CTL_VALUE()
Yes and #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,\ }
So these values passed above are filled here and put in private_data as you rightly noticed. But later on get/put handlers will not modify these specfic value but other private value. Above are used _only_ for IPC headers to DSP so we cant afford to modify them. We cna try making these values above as consts, static
On 06/23/2014 06:15 AM, Vinod Koul wrote:
On Sat, Jun 21, 2014 at 08:19:13AM +0200, Lars-Peter Clausen wrote:
> +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),
You are creating a lot of global non-const variables here that are later modified in the put and get handlers and also elsewhere.
Sorry which ones above are modfied. Above values are information for headers of IPCs which we send to DSPs
The SST_ALGO_CTL_VALUE() macro uses compound literals to create a global (nameless) struct. A pointer to this struct is assigned to the kcontrols private_value field. This is later read and the struct is modified.
Yes but not the above values as these as IPC header info which DSP needs.
The SST_ALGO_KCONTROL_BYTES() macro calls SST_ALGO_KCONTROL() which in return calls SST_ALGO_CTL_VALUE()
Yes and #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,\ }
So these values passed above are filled here and put in private_data as you rightly noticed. But later on get/put handlers will not modify these specfic value but other private value. Above are used _only_ for IPC headers to DSP so we cant afford to modify them. We cna try making these values above as consts, static
Ok, but how does this work, where does the different private_value come from?
- Lars
The DSP has various gain modules in the path, add these as ALSA gain controls
Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/sst-atom-controls.c | 164 ++++++++++++++++++++++++++++++++++- sound/soc/intel/sst-atom-controls.h | 119 +++++++++++++++++++++++++ 2 files changed, 282 insertions(+), 1 deletions(-)
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index c72d3aa..0d9c72c 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -215,6 +215,157 @@ 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(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_kcontrol_chip(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; + + 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: + 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); + 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), @@ -239,7 +390,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, @@ -256,6 +407,17 @@ int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) 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 448fdff..9f04e43 100644 --- a/sound/soc/intel/sst-atom-controls.h +++ b/sound/soc/intel/sst-atom-controls.h @@ -396,16 +396,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,
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 --- sound/soc/intel/sst-mfld-platform-pcm.c | 14 ++++++++++++++ sound/soc/intel/sst-mfld-platform.h | 1 + 2 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index 0fbb688..346f972 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) return ret_val;
} + +static inline int power_up_sst(struct sst_runtime_stream *sst) +{ + return sst->ops->power(true); +} + +static inline int power_down_sst(struct sst_runtime_stream *sst) +{ + return sst->ops->power(false); +} /* end -- helper functions */
static int sst_media_open(struct snd_pcm_substream *substream, @@ -323,6 +333,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); @@ -342,6 +354,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(str_id); diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h index 5cfe947..4acdd32 100644 --- a/sound/soc/intel/sst-mfld-platform.h +++ b/sound/soc/intel/sst-mfld-platform.h @@ -134,6 +134,7 @@ struct sst_ops { int (*device_control) (int cmd, void *arg); int (*set_generic_params) (enum sst_controls cmd, void *arg); int (*close) (unsigned int str_id); + int (*power) (bool state); };
struct sst_runtime_stream {
This patch adds core controls like interleavers, SSPs, and also logic of sending pipeline and module commands to the DSP
Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/sst-atom-controls.c | 660 ++++++++++++++++++++++++++++++++++- sound/soc/intel/sst-atom-controls.h | 310 ++++++++++++++++ 2 files changed, 969 insertions(+), 1 deletions(-)
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index 0d9c72c..2938105 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -119,6 +119,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(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_kcontrol_chip(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 = 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) + 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); + return 0; +} + +/* assumes a boolean mux */ +static inline bool get_mux_state(struct sst_data *drv, unsigned int reg, unsigned int shift) +{ + return sst_reg_read(drv, reg, shift, 1) == 1; +} + static void sst_send_algo_cmd(struct sst_data *drv, struct sst_algo_control *bc) { @@ -146,7 +293,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; @@ -325,8 +493,295 @@ 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 sst_data *drv = snd_soc_platform_get_drvdata(w->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); + 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, +}; +void sst_handle_vb_timer(struct snd_soc_platform *p, bool enable) +{ + struct sst_cmd_generic cmd; + struct sst_data *drv = snd_soc_platform_get_drvdata(p); + 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(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(false); +} + +#define SST_SSP_CODEC_MUX 0 +#define SST_SSP_CODEC_DOMAIN 0 + +static const int sst_ssp_mux_shift[SST_NUM_SSPS] = { + [SST_SSP2] = -1, +}; + +static const int sst_ssp_domain_shift[SST_NUM_SSPS][SST_MAX_SSP_MUX] = { + [SST_SSP2][0] = -1, +}; + +/** + * sst_ssp_config - contains SSP configuration for different UCs + * + * The 3-D array contains SSP configuration for different SSPs for different + * domains (e.g. NB, WB), as well as muxed SSPs. + * + * The first dimension has SSP number + * The second dimension has SSP Muxing (e.g. BT/FM muxed on same SSP) + * The third dimension has SSP domains (e.g. NB/WB for BT) + */ +static const struct sst_ssp_config +sst_ssp_configs[SST_NUM_SSPS][SST_MAX_SSP_MUX][SST_MAX_SSP_DOMAINS] = { + [SST_SSP2] = { + [SST_SSP_CODEC_MUX] = { + [SST_SSP_CODEC_DOMAIN] = { + .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, + }, + }, + }, +}; + +#define SST_SSP_CFG(wssp_no) \ + (const struct sst_ssp_cfg){ .ssp_config = &sst_ssp_configs[wssp_no], \ + .ssp_number = wssp_no, \ + .mux_shift = &sst_ssp_mux_shift[wssp_no], \ + .domain_shift = &sst_ssp_domain_shift[wssp_no], } + +void send_ssp_cmd(struct snd_soc_platform *platform, const char *id, bool enable) +{ + struct sst_cmd_sba_hw_set_ssp cmd; + struct sst_data *drv = snd_soc_platform_get_drvdata(platform); + unsigned int domain, mux; + int domain_shift, mux_shift, ssp_no; + const struct sst_ssp_config *config; + const struct sst_ssp_cfg *ssp; + + + pr_err("Enter:%s, enable=%d port_name=%s\n", __func__, enable, id); + + if (strcmp(id, "ssp2-port") == 0) + ssp_no = SST_SSP2; + else + return; + + ssp = &SST_SSP_CFG(ssp_no); + + 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); + mux_shift = *ssp->mux_shift; + mux = (mux_shift == -1) ? 0 : get_mux_state(drv, SST_MUX_REG, mux_shift); + domain_shift = (*ssp->domain_shift)[mux]; + domain = (domain_shift == -1) ? 0 : get_mux_state(drv, SST_MUX_REG, domain_shift); + + config = &(*ssp->ssp_config)[mux][domain]; + pr_debug("%s: ssp_id: %u, mux: %d, domain: %d\n", __func__, + config->ssp_id, mux, domain); + + 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 sst_data *drv = snd_soc_platform_get_drvdata(w->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); + } + 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 sst_data *drv = snd_soc_platform_get_drvdata(w->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); + 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 sst_data *drv = snd_soc_platform_get_drvdata(w->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); + 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, \ @@ -388,6 +843,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 snd_soc_platform *platform = dai->platform; + struct sst_data *drv = snd_soc_platform_get_drvdata(platform); + 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 sst_ids *ids = w->priv; + + module = devm_kzalloc(w->platform->dev, sizeof(*module), GFP_KERNEL); + if (!module) { + pr_err("kzalloc block failed\n"); + 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->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; + struct snd_soc_dapm_context *dapm = &platform->dapm; + + unsigned int len = strlen(ids->parent_wname); + list_for_each_entry(w, &dapm->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; + struct snd_soc_dapm_context *dapm = &platform->dapm; + int ret = 0; + + list_for_each_entry(w, &dapm->card->widgets, list) { + if (w->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; @@ -420,5 +1073,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 9f04e43..88d89a9 100644 --- a/sound/soc/intel/sst-atom-controls.h +++ b/sound/soc/intel/sst-atom-controls.h @@ -397,6 +397,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; @@ -415,10 +447,169 @@ 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, +}; + +int sst_mix_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int sst_mix_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); + /**** 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; @@ -431,6 +622,95 @@ 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, \ + .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, \ + .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, \ + .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, \ + .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, \ + .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, \ + .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, \ + .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, @@ -593,4 +873,34 @@ struct sst_enum { const char * const *texts; 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){ .reg = xreg, .texts = xtexts, .shift_l = xshift, \ + .shift_r = xshift, .items = ARRAY_SIZE(xtexts), } + +#define SST_SSP_MUX_CTL(xpname, xinstance, xreg, xshift, xtexts, xget, xput) \ + SOC_DAPM_ENUM_EXT(SST_MUX_CTL_NAME(xpname, xinstance), \ + SST_SSP_MUX_ENUM(xreg, xshift, xtexts), \ + xget, xput) + + + #endif
This patchs 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 --- sound/soc/intel/sst-atom-controls.c | 237 +++++++++++++++++++++++++++++++++++ sound/soc/intel/sst-mfld-platform.h | 4 + 2 files changed, 241 insertions(+), 0 deletions(-)
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index 2938105..2e733f8 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -528,6 +528,161 @@ 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; + 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); + } +} + +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 sst_data *drv = snd_soc_platform_get_drvdata(w->platform); + struct sst_ids *ids = w->priv; + bool set_mixer = false; + int val = drv->widget[ids->reg]; + + pr_debug("%s: widget = %s\n", __func__, w->name); + pr_debug("%s: reg[%d] = %#x\n", __func__, ids->reg, 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, mixer_reg) \ + static const struct snd_kcontrol_new kctl_name[] = { \ + SOC_SINGLE_EXT("codec_in0", mixer_reg, SST_IP_CODEC0, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("codec_in1", mixer_reg, SST_IP_CODEC1, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("sprot_loop_in", mixer_reg, SST_IP_LOOP0, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("media_loop1_in", mixer_reg, SST_IP_LOOP1, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("media_loop2_in", mixer_reg, SST_IP_LOOP2, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("pcm0_in", mixer_reg, SST_IP_PCM0, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("pcm1_in", mixer_reg, SST_IP_PCM1, 1, 0, \ + sst_mix_get, sst_mix_put), \ + } + +#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, mixer_reg) \ + static const struct snd_kcontrol_new kctl_name[] = { \ + SOC_SINGLE_EXT("media0_in", mixer_reg, SST_IP_MEDIA0, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("media1_in", mixer_reg, SST_IP_MEDIA1, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("media2_in", mixer_reg, SST_IP_MEDIA2, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("media3_in", mixer_reg, SST_IP_MEDIA3, 1, 0, \ + sst_mix_get, sst_mix_put), \ + } + +SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media0_controls, SST_MIX_MEDIA0); +SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media1_controls, SST_MIX_MEDIA1); + +/* 18 SBA mixers */ +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm0_controls, SST_MIX_PCM0); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm1_controls, SST_MIX_PCM1); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm2_controls, SST_MIX_PCM2); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_sprot_l0_controls, SST_MIX_LOOP0); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l1_controls, SST_MIX_LOOP1); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls, SST_MIX_LOOP2); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_voip_controls, SST_MIX_VOIP); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls, SST_MIX_CODEC0); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls, SST_MIX_CODEC1); + void sst_handle_vb_timer(struct snd_soc_platform *p, bool enable) { struct sst_cmd_generic cmd; @@ -751,6 +906,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", @@ -1060,6 +1292,11 @@ int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) return -ENOMEM; }
+ snd_soc_dapm_new_controls(&platform->dapm, sst_dapm_widgets, + ARRAY_SIZE(sst_dapm_widgets)); + snd_soc_dapm_add_routes(&platform->dapm, intercon, + ARRAY_SIZE(intercon)); + snd_soc_dapm_new_widgets(platform->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 4acdd32..fee03df 100644 --- a/sound/soc/intel/sst-mfld-platform.h +++ b/sound/soc/intel/sst-mfld-platform.h @@ -158,6 +158,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_platform *platform, const char *id, bool enable); +void sst_handle_vb_timer(struct snd_soc_platform *platform, bool enable); + unsigned int sst_soc_read(struct snd_soc_platform *platform, unsigned int reg); int sst_soc_write(struct snd_soc_platform *platform, unsigned int reg, unsigned int val); unsigned int sst_reg_read(struct sst_data *sst, unsigned int reg,
Now that the DSP toplogy is added, add the FE and BE dai ops for controls DSP as well mute/unmute of FE thru ASoC
Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/sst-mfld-platform-pcm.c | 38 +++++++++++++++++++++++++++++++ 1 files changed, 38 insertions(+), 0 deletions(-)
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c index 346f972..007bfcd 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, @@ -421,12 +426,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->platform, true); + send_ssp_cmd(dai->platform, 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->platform, dai->name, 0); + sst_handle_vb_timer(dai->platform, false); + } +} + static struct snd_soc_dai_ops sst_media_dai_ops = { .startup = sst_media_open, .shutdown = sst_media_close, .prepare = sst_media_prepare, .hw_params = sst_media_hw_params, .hw_free = sst_media_hw_free, + .mute_stream = sst_media_digital_mute, +}; + +static struct snd_soc_dai_ops sst_compr_dai_ops = { + .mute_stream = sst_media_digital_mute, +}; + +static struct snd_soc_dai_ops sst_be_dai_ops = { + .startup = sst_enable_ssp, + .shutdown = sst_disable_ssp, };
static struct snd_soc_dai_driver sst_platform_dai[] = { @@ -451,6 +485,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, @@ -462,6 +497,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, @@ -479,6 +515,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, @@ -496,6 +533,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,
The is RFC patch for adding the platform mixer controls. This requires the dapm_set-get to be exported. Or changes required after component series is merged
Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/sst-atom-controls.c | 67 +++++++++++++++++++++++++++++++++++ 1 files changed, 67 insertions(+), 0 deletions(-)
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index 2e733f8..81a24eb 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -66,6 +66,47 @@ unsigned int sst_reg_write(struct sst_data *drv, unsigned int reg, return val; }
+int sst_mix_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist;// = dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct sst_data *drv = snd_soc_platform_get_drvdata(widget->platform); + unsigned int mask = (1 << fls(mc->max)) - 1; + unsigned int val; + int connect; + struct snd_soc_dapm_update update; + + pr_debug("%s called set %#lx for %s\n", __func__, + ucontrol->value.integer.value[0], widget->name); + val = sst_reg_write(drv, mc->reg, mc->shift, mc->max, ucontrol->value.integer.value[0]); + connect = !!val; + + //dapm_kcontrol_set_value(kcontrol, val); + update.kcontrol = kcontrol; + update.reg = mc->reg; + update.mask = mask; + update.val = val; + + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, connect, &update); + return 0; +} + +int sst_mix_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist;// = dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *w = wlist->widgets[0]; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct sst_data *drv = snd_soc_platform_get_drvdata(w->platform); + + ucontrol->value.integer.value[0] = !!sst_reg_read(drv, mc->reg, mc->shift, mc->max); + return 0; +} + static inline void sst_fill_byte_control(char *param, u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, @@ -937,6 +978,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", SST_MIX_MEDIA0, SST_TASK_MMX, SST_SWM_OUT_MEDIA0, + sst_mix_media0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("media1_out mix 0", SST_MIX_MEDIA1, 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", SST_MIX_PCM0, SST_TASK_SBA, SST_SWM_OUT_PCM0, + sst_mix_pcm0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("pcm1_out mix 0", SST_MIX_PCM1, SST_TASK_SBA, SST_SWM_OUT_PCM1, + sst_mix_pcm1_controls, sst_swm_mixer_event), + SST_SWM_MIXER("pcm2_out mix 0", SST_MIX_PCM2, 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", SST_MIX_LOOP0, 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", SST_MIX_LOOP1, 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", SST_MIX_LOOP2, 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", SST_MIX_CODEC0, SST_TASK_SBA, SST_SWM_OUT_CODEC0, + sst_mix_codec0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("codec_out1 mix 0", SST_MIX_CODEC1, SST_TASK_SBA, SST_SWM_OUT_CODEC1, + sst_mix_codec1_controls, sst_swm_mixer_event), };
static const struct snd_soc_dapm_route intercon[] = {
On 06/13/2014 02:34 PM, Vinod Koul wrote:
The is RFC patch for adding the platform mixer controls. This requires the dapm_set-get to be exported. Or changes required after component series is merged
You are not using dapm_kcontrol_get_value() in this patch and without that calling dapm_kcontrol_set_value() is pretty pointless as the value is never read again. But it is still a good idea to use the generic controls rather than having a custom copy&pasted version.
Signed-off-by: Vinod Koul vinod.koul@intel.com
sound/soc/intel/sst-atom-controls.c | 67 +++++++++++++++++++++++++++++++++++ 1 files changed, 67 insertions(+), 0 deletions(-)
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index 2e733f8..81a24eb 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -66,6 +66,47 @@ unsigned int sst_reg_write(struct sst_data *drv, unsigned int reg, return val; }
+int sst_mix_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+{
- struct snd_soc_dapm_widget_list *wlist;// = dapm_kcontrol_get_wlist(kcontrol);
- struct snd_soc_dapm_widget *widget = wlist->widgets[0];
- struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct sst_data *drv = snd_soc_platform_get_drvdata(widget->platform);
- unsigned int mask = (1 << fls(mc->max)) - 1;
- unsigned int val;
- int connect;
- struct snd_soc_dapm_update update;
- pr_debug("%s called set %#lx for %s\n", __func__,
ucontrol->value.integer.value[0], widget->name);
- val = sst_reg_write(drv, mc->reg, mc->shift, mc->max, ucontrol->value.integer.value[0]);
- connect = !!val;
- //dapm_kcontrol_set_value(kcontrol, val);
- update.kcontrol = kcontrol;
- update.reg = mc->reg;
- update.mask = mask;
- update.val = val;
- snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, connect, &update);
- return 0;
+}
+int sst_mix_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+{
- struct snd_soc_dapm_widget_list *wlist;// = dapm_kcontrol_get_wlist(kcontrol);
- struct snd_soc_dapm_widget *w = wlist->widgets[0];
- struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct sst_data *drv = snd_soc_platform_get_drvdata(w->platform);
- ucontrol->value.integer.value[0] = !!sst_reg_read(drv, mc->reg, mc->shift, mc->max);
- return 0;
+}
- static inline void sst_fill_byte_control(char *param, u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id,
@@ -937,6 +978,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", SST_MIX_MEDIA0, SST_TASK_MMX, SST_SWM_OUT_MEDIA0,
sst_mix_media0_controls, sst_swm_mixer_event),
SST_SWM_MIXER("media1_out mix 0", SST_MIX_MEDIA1, 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", SST_MIX_PCM0, SST_TASK_SBA, SST_SWM_OUT_PCM0,
sst_mix_pcm0_controls, sst_swm_mixer_event),
SST_SWM_MIXER("pcm1_out mix 0", SST_MIX_PCM1, SST_TASK_SBA, SST_SWM_OUT_PCM1,
sst_mix_pcm1_controls, sst_swm_mixer_event),
SST_SWM_MIXER("pcm2_out mix 0", SST_MIX_PCM2, 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", SST_MIX_LOOP0, 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", SST_MIX_LOOP1, 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", SST_MIX_LOOP2, 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", SST_MIX_CODEC0, SST_TASK_SBA, SST_SWM_OUT_CODEC0,
sst_mix_codec0_controls, sst_swm_mixer_event),
SST_SWM_MIXER("codec_out1 mix 0", SST_MIX_CODEC1, SST_TASK_SBA, SST_SWM_OUT_CODEC1,
sst_mix_codec1_controls, sst_swm_mixer_event),
};
static const struct snd_soc_dapm_route intercon[] = {
On Sat, Jun 14, 2014 at 05:39:09PM +0200, Lars-Peter Clausen wrote:
On 06/13/2014 02:34 PM, Vinod Koul wrote:
The is RFC patch for adding the platform mixer controls. This requires the dapm_set-get to be exported. Or changes required after component series is merged
You are not using dapm_kcontrol_get_value() in this patch and without that calling dapm_kcontrol_set_value() is pretty pointless as the value is never read again. But it is still a good idea to use the generic controls rather than having a custom copy&pasted version.
Hey Lars,
Circling back on this one..
We tried implementing based on the feedback. One question though remains.
We were able to remove the get/put widget handlers. Now only the callback is present. With this when widget is powered up for a mixer, we need to know the value of the mixer so that we can inform DSP using IPC.
Now potentially looks like we should be able to remove the register file as well, but only problem would be how to get the mixer control value (how many and which inputs are on/off and tell DSP). This would need me to export the dapm_get function, is that approach fine, or do we have any other way to get the value of this in driver.
On 07/04/2014 06:46 AM, Vinod Koul wrote:
On Sat, Jun 14, 2014 at 05:39:09PM +0200, Lars-Peter Clausen wrote:
On 06/13/2014 02:34 PM, Vinod Koul wrote:
The is RFC patch for adding the platform mixer controls. This requires the dapm_set-get to be exported. Or changes required after component series is merged
You are not using dapm_kcontrol_get_value() in this patch and without that calling dapm_kcontrol_set_value() is pretty pointless as the value is never read again. But it is still a good idea to use the generic controls rather than having a custom copy&pasted version.
Hey Lars,
Circling back on this one..
We tried implementing based on the feedback. One question though remains.
We were able to remove the get/put widget handlers. Now only the callback is present. With this when widget is powered up for a mixer, we need to know the value of the mixer so that we can inform DSP using IPC.
Now potentially looks like we should be able to remove the register file as well, but only problem would be how to get the mixer control value (how many and which inputs are on/off and tell DSP). This would need me to export the dapm_get function, is that approach fine, or do we have any other way to get the value of this in driver.
I think it should be fine, if I correctly understand what you want to do. The main issue with all of this is that DAPM internally is very much tailored towards registers IO based systems. And using it for IPC is a bit bumpy. You can do it but the implementation is not always nice. DAPM could probably be adopted to better support non register IO based systems, but that would require a bit of effort.
- Lars
On Fri, Jul 04, 2014 at 01:21:17PM +0200, Lars-Peter Clausen wrote:
On 07/04/2014 06:46 AM, Vinod Koul wrote:
On Sat, Jun 14, 2014 at 05:39:09PM +0200, Lars-Peter Clausen wrote:
On 06/13/2014 02:34 PM, Vinod Koul wrote:
The is RFC patch for adding the platform mixer controls. This requires the dapm_set-get to be exported. Or changes required after component series is merged
You are not using dapm_kcontrol_get_value() in this patch and without that calling dapm_kcontrol_set_value() is pretty pointless as the value is never read again. But it is still a good idea to use the generic controls rather than having a custom copy&pasted version.
Hey Lars,
Circling back on this one..
We tried implementing based on the feedback. One question though remains.
We were able to remove the get/put widget handlers. Now only the callback is present. With this when widget is powered up for a mixer, we need to know the value of the mixer so that we can inform DSP using IPC.
Now potentially looks like we should be able to remove the register file as well, but only problem would be how to get the mixer control value (how many and which inputs are on/off and tell DSP). This would need me to export the dapm_get function, is that approach fine, or do we have any other way to get the value of this in driver.
I think it should be fine, if I correctly understand what you want to do. The main issue with all of this is that DAPM internally is very much tailored towards registers IO based systems. And using it for IPC is a bit bumpy. You can do it but the implementation is not always nice. DAPM could probably be adopted to better support non register IO based systems, but that would require a bit of effort.
For now with this we are removing most of register code. Am optimistic right now that we can get it to work... Stay tuned :)
On 06/13/2014 02:33 PM, Vinod Koul wrote:
Here is the split patch series for adding DSP support for Intel's merrfield platform.
The last patch si mxer update patch which we have been discussing over the list. This is only for discussion and sake of complteness. For that patch we need the dapm_kcontrol_get/set series to do merged OR patch to be reworked after compenent series
Hi,
Some general feedback on this series.
You are often accessing data structures which are more or less internal to the DAPM core, all without doing any of the necessary locking. Those are in particular the widget and path structures. This access in the driver will make it harder to make modifications to the core as you'd not only understand the core but also the special semantics that this driver puts on those data structures. If you think the core does not provide what you need try to extend the core with helper functions, with well defined semantics, that do what you need.
Also this series is a bit of a step backwards in terms of compartmentalization as it re-introduces things that we've been trying to get rid of over the last few months: * Don't access widget->platform or dapm->platform, use snd_soc_dapm_to_platform() * Don't access dai->platform, use rtd->platform. * Use regmap for IO instead of the read/write callbacks in the platform_driver struct.
- Lars
On Fri, Jun 20, 2014 at 10:04:51AM +0200, Lars-Peter Clausen wrote:
On 06/13/2014 02:33 PM, Vinod Koul wrote:
Here is the split patch series for adding DSP support for Intel's merrfield platform.
The last patch si mxer update patch which we have been discussing over the list. This is only for discussion and sake of complteness. For that patch we need the dapm_kcontrol_get/set series to do merged OR patch to be reworked after compenent series
Hi,
Some general feedback on this series.
Thanks a bunch for that :)
You are often accessing data structures which are more or less internal to the DAPM core, all without doing any of the necessary locking. Those are in particular the widget and path structures. This access in the driver will make it harder to make modifications to the core as you'd not only understand the core but also the special semantics that this driver puts on those data structures. If you think the core does not provide what you need try to extend the core with helper functions, with well defined semantics, that do what you need.
Also this series is a bit of a step backwards in terms of compartmentalization as it re-introduces things that we've been trying to get rid of over the last few months:
Well the series was developed on 3.10 and forward ported for upstreaming. So yes you can get that feel. I will try to clean these bits up. Where ever missed please do point it out.
- Don't access widget->platform or dapm->platform, use
snd_soc_dapm_to_platform()
- Don't access dai->platform, use rtd->platform.
Will update above two
- Use regmap for IO instead of the read/write callbacks in the
platform_driver struct.
I dont think we need that for our DSP. The read/write are writing to driver memory which is later used to send data to DSP using IPCs. So how will regmap help here?
On 06/20/2014 10:20 AM, Vinod Koul wrote: [...]
- Use regmap for IO instead of the read/write callbacks in the
platform_driver struct.
I dont think we need that for our DSP. The read/write are writing to driver memory which is later used to send data to DSP using IPCs. So how will regmap help here?
The idea is to get rid of the IO abstraction layer inside ASoC and only rely on regmap for this. This is a process this has been going on ever since the regmap framework was added to the kernel and we are almost there now. The best is to implement a regmap bus or a bus-less regmap instance that implements the IPC for the DSP. If the IPC does not map nicely onto a register map, we need to come up with something better. It's basically the same problem that that HDA CODEC by RT has.
- Lars
On Fri, Jun 20, 2014 at 10:57:25AM +0200, Lars-Peter Clausen wrote:
The idea is to get rid of the IO abstraction layer inside ASoC and only rely on regmap for this. This is a process this has been going on ever since the regmap framework was added to the kernel and we are almost there now. The best is to implement a regmap bus or a bus-less regmap instance that implements the IPC for the DSP. If the IPC does not map nicely onto a register map, we need to come up with something better. It's basically the same problem that that HDA CODEC by RT has.
Well, it depends. If the device looks like it has a register map that's sensible but if it looks nothing like that then we should be implementing the widgets differently. It may be that we need to be constructing and destructing objects on the DSP for example.
On 06/23/2014 12:09 PM, Mark Brown wrote:
On Fri, Jun 20, 2014 at 10:57:25AM +0200, Lars-Peter Clausen wrote:
The idea is to get rid of the IO abstraction layer inside ASoC and only rely on regmap for this. This is a process this has been going on ever since the regmap framework was added to the kernel and we are almost there now. The best is to implement a regmap bus or a bus-less regmap instance that implements the IPC for the DSP. If the IPC does not map nicely onto a register map, we need to come up with something better. It's basically the same problem that that HDA CODEC by RT has.
Well, it depends. If the device looks like it has a register map that's sensible but if it looks nothing like that then we should be implementing the widgets differently. It may be that we need to be constructing and destructing objects on the DSP for example.
What do you mean by 'constructing/destructing on the DSP'?
- Lars
On Wed, Jun 25, 2014 at 06:27:44AM +0200, Lars-Peter Clausen wrote:
On 06/23/2014 12:09 PM, Mark Brown wrote:
On Fri, Jun 20, 2014 at 10:57:25AM +0200, Lars-Peter Clausen wrote:
The idea is to get rid of the IO abstraction layer inside ASoC and only rely on regmap for this. This is a process this has been going on ever since the regmap framework was added to the kernel and we are almost there now. The best is to implement a regmap bus or a bus-less regmap instance that implements the IPC for the DSP. If the IPC does not map nicely onto a register map, we need to come up with something better. It's basically the same problem that that HDA CODEC by RT has.
No the register file _does_ not map to IPCs, as I have explained previosuly. The file is just to store the informationa nd dpam widget handling triggers IPCs.
Since we are NOT writing to device, I still dont think regmap will help
Well, it depends. If the device looks like it has a register map that's sensible but if it looks nothing like that then we should be implementing the widgets differently. It may be that we need to be constructing and destructing objects on the DSP for example.
What do you mean by 'constructing/destructing on the DSP'?
I think Mark refers to modules and processing elements on DSP. Since this is DSP (firmware) we can tell it to create a link b/w FE and BE, throw in another link etc. Unlike codec, everything is driver controlled and instances created and destroyed dynamically.
yes upating this to latest dapm (thanks to Lars for that), will help in moving code away from driver
participants (3)
-
Lars-Peter Clausen
-
Mark Brown
-
Vinod Koul