The following commits has been backported from kernel 4.9 with local modification to fit to kernel 4.4: e4e2d2f45284f620eeb9eea25570838b2a42c968 f7590d4f1565b0d609d25113c37b9bea8a13e9f7
Signed-off-by: Guoqing Zhang <guoqing.zh...@intel.com> --- include/uapi/sound/asoc.h | 44 +- include/uapi/sound/snd_sst_tokens.h | 214 ++++ sound/soc/intel/skylake/skl-pcm.c | 60 +- sound/soc/intel/skylake/skl-sst-dsp.h | 14 +- sound/soc/intel/skylake/skl-sst-utils.c | 249 ++++- sound/soc/intel/skylake/skl-topology.c | 1343 ++++++++++++++++++++------ sound/soc/intel/skylake/skl-topology.h | 22 +- sound/soc/intel/skylake/skl-tplg-interface.h | 25 +- sound/soc/intel/skylake/skl.h | 3 + 9 files changed, 1670 insertions(+), 304 deletions(-) create mode 100644 include/uapi/sound/snd_sst_tokens.h diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index c4cc1e4..e4701a3 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -116,6 +116,14 @@ #define SND_SOC_TPLG_STREAM_PLAYBACK 0 #define SND_SOC_TPLG_STREAM_CAPTURE 1 +/* vendor tuple types */ +#define SND_SOC_TPLG_TUPLE_TYPE_UUID 0 +#define SND_SOC_TPLG_TUPLE_TYPE_STRING 1 +#define SND_SOC_TPLG_TUPLE_TYPE_BOOL 2 +#define SND_SOC_TPLG_TUPLE_TYPE_BYTE 3 +#define SND_SOC_TPLG_TUPLE_TYPE_WORD 4 +#define SND_SOC_TPLG_TUPLE_TYPE_SHORT 5 + /* * Block Header. * This header precedes all object and object arrays below. @@ -132,6 +140,35 @@ struct snd_soc_tplg_hdr { __le32 count; /* number of elements in block */ } __attribute__((packed)); +/* vendor tuple for uuid */ +struct snd_soc_tplg_vendor_uuid_elem { + __le32 token; + char uuid[16]; +} __attribute__((packed)); + +/* vendor tuple for a bool/byte/short/word value */ +struct snd_soc_tplg_vendor_value_elem { + __le32 token; + __le32 value; +} __attribute__((packed)); + +/* vendor tuple for string */ +struct snd_soc_tplg_vendor_string_elem { + __le32 token; + char string[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; +} __attribute__((packed)); + +struct snd_soc_tplg_vendor_array { + __le32 size; /* size in bytes of the array, including all elements */ + __le32 type; /* SND_SOC_TPLG_TUPLE_TYPE_ */ + __le32 num_elems; /* number of elements in array */ + union { + struct snd_soc_tplg_vendor_uuid_elem uuid[0]; + struct snd_soc_tplg_vendor_value_elem value[0]; + struct snd_soc_tplg_vendor_string_elem string[0]; + }; +} __attribute__((packed)); + /* * Private data. * All topology objects may have private data that can be used by the driver or @@ -139,7 +176,10 @@ struct snd_soc_tplg_hdr { */ struct snd_soc_tplg_private { __le32 size; /* in bytes of private data */ - char data[0]; + union { + char data[0]; + struct snd_soc_tplg_vendor_array array[0]; + }; } __attribute__((packed)); /* @@ -383,7 +423,7 @@ struct snd_soc_tplg_pcm { __le32 size; /* in bytes of this structure */ char pcm_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; char dai_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - __le32 pcm_id; /* unique ID - used to match */ + __le32 pcm_id; /* unique ID - used to match with DAI link */ __le32 dai_id; /* unique ID - used to match */ __le32 playback; /* supports playback mode */ __le32 capture; /* supports capture mode */ diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h new file mode 100644 index 0000000..1ee2e94 --- /dev/null +++ b/include/uapi/sound/snd_sst_tokens.h @@ -0,0 +1,214 @@ +/* + * snd_sst_tokens.h - Intel SST tokens definition + * + * Copyright (C) 2016 Intel Corp + * Author: Shreyas NC <shreyas...@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 version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __SND_SST_TOKENS_H__ +#define __SND_SST_TOKENS_H__ + +/** + * %SKL_TKN_UUID: Module UUID + * + * %SKL_TKN_U8_BLOCK_TYPE: Type of the private data block.Can be: + * tuples, bytes, short and words + * + * %SKL_TKN_U8_IN_PIN_TYPE: Input pin type, + * homogenous=0, heterogenous=1 + * + * %SKL_TKN_U8_OUT_PIN_TYPE: Output pin type, + * homogenous=0, heterogenous=1 + * %SKL_TKN_U8_DYN_IN_PIN: Configure Input pin dynamically + * if true + * + * %SKL_TKN_U8_DYN_OUT_PIN: Configure Output pin dynamically + * if true + * + * %SKL_TKN_U8_IN_QUEUE_COUNT: Store the number of Input pins + * + * %SKL_TKN_U8_OUT_QUEUE_COUNT: Store the number of Output pins + * + * %SKL_TKN_U8_TIME_SLOT: TDM slot number + * + * %SKL_TKN_U8_CORE_ID: Stores module affinity value.Can take + * the values: + * SKL_AFFINITY_CORE_0 = 0, + * SKL_AFFINITY_CORE_1, + * SKL_AFFINITY_CORE_MAX + * + * %SKL_TKN_U8_MOD_TYPE: Module type value. + * + * %SKL_TKN_U8_CONN_TYPE: Module connection type can be a FE, + * BE or NONE as defined : + * SKL_PIPE_CONN_TYPE_NONE = 0, + * SKL_PIPE_CONN_TYPE_FE = 1 (HOST_DMA) + * SKL_PIPE_CONN_TYPE_BE = 2 (LINK_DMA) + * + * %SKL_TKN_U8_DEV_TYPE: Type of device to which the module is + * connected + * Can take the values: + * SKL_DEVICE_BT = 0x0, + * SKL_DEVICE_DMIC = 0x1, + * SKL_DEVICE_I2S = 0x2, + * SKL_DEVICE_SLIMBUS = 0x3, + * SKL_DEVICE_HDALINK = 0x4, + * SKL_DEVICE_HDAHOST = 0x5, + * SKL_DEVICE_NONE + * + * %SKL_TKN_U8_HW_CONN_TYPE: Connection type of the HW to which the + * module is connected + * SKL_CONN_NONE = 0, + * SKL_CONN_SOURCE = 1, + * SKL_CONN_SINK = 2 + * + * %SKL_TKN_U16_PIN_INST_ID: Stores the pin instance id + * + * %SKL_TKN_U16_MOD_INST_ID: Stores the mdule instance id + * + * %SKL_TKN_U32_MAX_MCPS: Module max mcps value + * + * %SKL_TKN_U32_MEM_PAGES: Module resource pages + * + * %SKL_TKN_U32_OBS: Stores Output Buffer size + * + * %SKL_TKN_U32_IBS: Stores input buffer size + * + * %SKL_TKN_U32_VBUS_ID: Module VBUS_ID. PDM=0, SSP0=0, + * SSP1=1,SSP2=2, + * SSP3=3, SSP4=4, + * SSP5=5, SSP6=6,INVALID + * + * %SKL_TKN_U32_PARAMS_FIXUP: Module Params fixup mask + * %SKL_TKN_U32_CONVERTER: Module params converter mask + * %SKL_TKN_U32_PIPE_ID: Stores the pipe id + * + * %SKL_TKN_U32_PIPE_CONN_TYPE: Type of the token to which the pipe is + * connected to. It can be + * SKL_PIPE_CONN_TYPE_NONE = 0, + * SKL_PIPE_CONN_TYPE_FE = 1 (HOST_DMA), + * SKL_PIPE_CONN_TYPE_BE = 2 (LINK_DMA), + * + * %SKL_TKN_U32_PIPE_PRIORITY: Pipe priority value + * %SKL_TKN_U32_PIPE_MEM_PGS: Pipe resource pages + * + * %SKL_TKN_U32_DIR_PIN_COUNT: Value for the direction to set input/output + * formats and the pin count. + * The first 4 bits have the direction + * value and the next 4 have + * the pin count value. + * SKL_DIR_IN = 0, SKL_DIR_OUT = 1. + * The input and output formats + * share the same set of tokens + * with the distinction between input + * and output made by reading direction + * token. + * + * %SKL_TKN_U32_FMT_CH: Supported channel count + * + * %SKL_TKN_U32_FMT_FREQ: Supported frequency/sample rate + * + * %SKL_TKN_U32_FMT_BIT_DEPTH: Supported container size + * + * %SKL_TKN_U32_FMT_SAMPLE_SIZE:Number of samples in the container + * + * %SKL_TKN_U32_FMT_CH_CONFIG: Supported channel configurations for the + * input/output. + * + * %SKL_TKN_U32_FMT_INTERLEAVE: Interleaving style which can be per + * channel or per sample. The values can be : + * SKL_INTERLEAVING_PER_CHANNEL = 0, + * SKL_INTERLEAVING_PER_SAMPLE = 1, + * + * %SKL_TKN_U32_FMT_SAMPLE_TYPE: + * Specifies the sample type. Can take the + * values: SKL_SAMPLE_TYPE_INT_MSB = 0, + * SKL_SAMPLE_TYPE_INT_LSB = 1, + * SKL_SAMPLE_TYPE_INT_SIGNED = 2, + * SKL_SAMPLE_TYPE_INT_UNSIGNED = 3, + * SKL_SAMPLE_TYPE_FLOAT = 4 + * + * %SKL_TKN_U32_CH_MAP: Channel map values + * %SKL_TKN_U32_MOD_SET_PARAMS: It can take these values: + * SKL_PARAM_DEFAULT, SKL_PARAM_INIT, + * SKL_PARAM_SET, SKL_PARAM_BIND + * + * %SKL_TKN_U32_MOD_PARAM_ID: ID of the module params + * + * %SKL_TKN_U32_CAPS_SET_PARAMS: + * Set params value + * + * %SKL_TKN_U32_CAPS_PARAMS_ID: Params ID + * + * %SKL_TKN_U32_CAPS_SIZE: Caps size + * + * %SKL_TKN_U32_PROC_DOMAIN: Specify processing domain + * + * %SKL_TKN_U32_LIB_COUNT: Specifies the number of libraries + * + * %SKL_TKN_STR_LIB_NAME: Specifies the library name + * + * module_id and loadable flags dont have tokens as these values will be + * read from the DSP FW manifest + */ +enum SKL_TKNS { + SKL_TKN_UUID = 1, + SKL_TKN_U8_NUM_BLOCKS, + SKL_TKN_U8_BLOCK_TYPE, + SKL_TKN_U8_IN_PIN_TYPE, + SKL_TKN_U8_OUT_PIN_TYPE, + SKL_TKN_U8_DYN_IN_PIN, + SKL_TKN_U8_DYN_OUT_PIN, + SKL_TKN_U8_IN_QUEUE_COUNT, + SKL_TKN_U8_OUT_QUEUE_COUNT, + SKL_TKN_U8_TIME_SLOT, + SKL_TKN_U8_CORE_ID, + SKL_TKN_U8_MOD_TYPE, + SKL_TKN_U8_CONN_TYPE, + SKL_TKN_U8_DEV_TYPE, + SKL_TKN_U8_HW_CONN_TYPE, + SKL_TKN_U16_MOD_INST_ID, + SKL_TKN_U16_BLOCK_SIZE, + SKL_TKN_U32_MAX_MCPS, + SKL_TKN_U32_MEM_PAGES, + SKL_TKN_U32_OBS, + SKL_TKN_U32_IBS, + SKL_TKN_U32_VBUS_ID, + SKL_TKN_U32_PARAMS_FIXUP, + SKL_TKN_U32_CONVERTER, + SKL_TKN_U32_PIPE_ID, + SKL_TKN_U32_PIPE_CONN_TYPE, + SKL_TKN_U32_PIPE_PRIORITY, + SKL_TKN_U32_PIPE_MEM_PGS, + SKL_TKN_U32_DIR_PIN_COUNT, + SKL_TKN_U32_FMT_CH, + SKL_TKN_U32_FMT_FREQ, + SKL_TKN_U32_FMT_BIT_DEPTH, + SKL_TKN_U32_FMT_SAMPLE_SIZE, + SKL_TKN_U32_FMT_CH_CONFIG, + SKL_TKN_U32_FMT_INTERLEAVE, + SKL_TKN_U32_FMT_SAMPLE_TYPE, + SKL_TKN_U32_FMT_CH_MAP, + SKL_TKN_U32_PIN_MOD_ID, + SKL_TKN_U32_PIN_INST_ID, + SKL_TKN_U32_MOD_SET_PARAMS, + SKL_TKN_U32_MOD_PARAM_ID, + SKL_TKN_U32_CAPS_SET_PARAMS, + SKL_TKN_U32_CAPS_PARAMS_ID, + SKL_TKN_U32_CAPS_SIZE, + SKL_TKN_U32_PROC_DOMAIN, + SKL_TKN_U32_LIB_COUNT, + SKL_TKN_STR_LIB_NAME, + SKL_TKN_MAX = SKL_TKN_STR_LIB_NAME, +}; + +#endif diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 7c81b31..acbc219 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -219,7 +219,6 @@ static int skl_be_prepare(struct snd_pcm_substream *substream, mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream); if (mconfig == NULL) return -EINVAL; - return skl_dsp_set_dma_control(ctx, mconfig); } @@ -227,11 +226,14 @@ static int skl_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + struct skl *skl = get_skl_ctx(dai->dev); unsigned int format_val; int err; + struct skl_module_cfg *mconfig; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); format_val = skl_get_format(substream, dai); dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d\n", hdac_stream(stream)->stream_tag, format_val); @@ -338,7 +340,6 @@ static int skl_be_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct skl_pipe_params p_params = {0}; - p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); p_params.s_freq = params_rate(params); @@ -520,6 +521,8 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream, unsigned int format_val = 0; struct skl_dma_params *dma_params; struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_module_cfg *mconfig = NULL; struct hdac_ext_link *link; dma_params = (struct skl_dma_params *) @@ -535,6 +538,7 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream, snd_hdac_ext_link_stream_reset(link_dev); + mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream); snd_hdac_ext_link_stream_setup(link_dev, format_val); snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_dev)->stream_tag); @@ -890,6 +894,17 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }, { + .name = "DMIC23 Pin", + .ops = &skl_dmic_dai_ops, + .capture = { + .stream_name = "DMIC23 Rx", + .channels_min = HDA_MONO, + .channels_max = HDA_QUAD, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ .name = "HD-Codec Pin", .ops = &skl_link_dai_ops, .playback = { @@ -1025,7 +1040,6 @@ static int skl_get_delay_from_lpib(struct hdac_ext_bus *ebus, delay = pos - lpib_pos; else delay = lpib_pos - pos; - if (delay < 0) { if (delay >= hstream->delay_negative_threshold) delay = 0; @@ -1037,12 +1051,12 @@ static int skl_get_delay_from_lpib(struct hdac_ext_bus *ebus, delay = 0; if (delay >= hstream->period_bytes) { - dev_info(bus->dev, + /* The following line add extra delay to ring buffer updates, remove it */ + /*dev_info(bus->dev, "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n", - delay, hstream->period_bytes); + delay, hstream->period_bytes);*/ delay = 0; } - return bytes_to_frames(substream->runtime, delay); } @@ -1177,13 +1191,44 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd) return retval; } + +static int skl_populate_modules(struct skl *skl) +{ + struct skl_pipeline *p; + struct skl_pipe_module *m; + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mconfig; + int ret; + + list_for_each_entry(p, &skl->ppl_list, node) { + list_for_each_entry(m, &p->pipe->w_list, node) { + w = m->w; + mconfig = w->priv; + ret = snd_skl_get_module_info(skl->skl_sst, mconfig); + if (ret < 0) { + dev_err(skl->skl_sst->dev, + "query module info failed:%d\n", ret); + goto err; + } + } + } +err: + return ret; +} + + static int skl_platform_soc_probe(struct snd_soc_platform *platform) { struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev); + int ret = 0; + struct skl *skl = ebus_to_skl(ebus); if (ebus->ppcap) - return skl_tplg_init(platform, ebus); + ret = skl_tplg_init(platform, ebus); + if (ret) + return ret; + skl_populate_modules(skl); return 0; } static struct snd_soc_platform_driver skl_platform_drv = { @@ -1204,7 +1249,6 @@ int skl_platform_register(struct device *dev) struct skl *skl = ebus_to_skl(ebus); INIT_LIST_HEAD(&skl->ppl_list); - ret = snd_soc_register_platform(dev, &skl_platform_drv); if (ret) { dev_err(dev, "soc platform registration failed %d\n", ret); diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 7efaf64..1690a74 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -20,6 +20,7 @@ #include <sound/memalloc.h> #include "skl-sst-cldma.h" #include "skl-tplg-interface.h" +#include "skl-topology.h" struct sst_dsp; struct skl_sst; @@ -176,9 +177,18 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); -int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid, - struct skl_dfw_module *dfw_config); +int snd_skl_get_module_info(struct skl_sst *ctx, struct skl_module_cfg *mconfig); + int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset); + +int skl_get_pvt_id(struct skl_sst *ctx, + struct skl_module_cfg *mconfig); +int skl_put_pvt_id(struct skl_sst *ctx, + struct skl_module_cfg *mconfig); +int skl_get_pvt_instance_id_map(struct skl_sst *ctx, + int module_id, int instance_id); + + void skl_freeup_uuid_list(struct skl_sst *ctx); int skl_dsp_strip_extended_manifest(struct firmware *fw); diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 25fcb79..d906701 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -28,11 +28,6 @@ /* FW Extended Manifest Header id = $AE1 */ #define SKL_EXT_MANIFEST_HEADER_MAGIC 0x31454124 -struct skl_dfw_module_mod { - char name[100]; - struct skl_dfw_module skl_dfw_mod; -}; - struct UUID { u8 id[16]; }; @@ -99,10 +94,15 @@ struct adsp_fw_hdr { u32 load_offset; } __packed; +#define MAX_INSTANCE_BUFF 2 + struct uuid_module { uuid_le uuid; int id; int is_loadable; + int max_instance; + u64 pvt_id[MAX_INSTANCE_BUFF]; + int *instance_id; struct list_head list; }; @@ -115,19 +115,26 @@ struct skl_ext_manifest_hdr { u32 entries; }; -int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid, - struct skl_dfw_module *dfw_config) +int snd_skl_get_module_info(struct skl_sst *ctx, struct skl_module_cfg *mconfig) { struct uuid_module *module; uuid_le *uuid_mod; - uuid_mod = (uuid_le *)uuid; + uuid_mod = (uuid_le *)mconfig->guid; + + if (list_empty(&ctx->uuid_list)) { + dev_err(ctx->dev, "Module list is empty\n"); + return -EINVAL; + } list_for_each_entry(module, &ctx->uuid_list, list) { - if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) { - dfw_config->module_id = module->id; - dfw_config->is_loadable = module->is_loadable; + dev_dbg(ctx->dev, + "uuid_tplg :%pUL uuid_fw:%pUL\n", + uuid_mod, &module->uuid ); + if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) { + mconfig->id.module_id = module->id; + mconfig->is_loadable = module->is_loadable; return 0; } } @@ -136,6 +143,219 @@ int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid, } EXPORT_SYMBOL_GPL(snd_skl_get_module_info); +static int skl_get_pvtid_map(struct uuid_module *module, int instance_id) +{ + int pvt_id; + + for (pvt_id = 0; pvt_id < module->max_instance; pvt_id++) { + if (module->instance_id[pvt_id] == instance_id) + return pvt_id; + } + return -EINVAL; +} + +int skl_get_pvt_instance_id_map(struct skl_sst *ctx, + int module_id, int instance_id) +{ + struct uuid_module *module; + + list_for_each_entry(module, &ctx->uuid_list, list) { + if (module->id == module_id) + return skl_get_pvtid_map(module, instance_id); + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(skl_get_pvt_instance_id_map); + +static inline int skl_getid_32(struct uuid_module *module, u64 *val, + int word1_mask, int word2_mask) +{ + int index, max_inst, pvt_id; + u32 mask_val; + + max_inst = module->max_instance; + mask_val = (u32)(*val >> word1_mask); + if (mask_val != 0xffffffff) { + index = ffz(mask_val); + pvt_id = index + word1_mask + word2_mask; + if (pvt_id <= (max_inst - 1)) { + *val |= 1 << (index + word1_mask); + return pvt_id; + } + } + + return -EINVAL; +} + +static inline int skl_pvtid_128(struct uuid_module *module) +{ + int j, i, word1_mask, word2_mask = 0, pvt_id; + + for (j = 0; j < MAX_INSTANCE_BUFF; j++) { + word1_mask = 0; + + for (i = 0; i < 2; i++) { + pvt_id = skl_getid_32(module, &module->pvt_id[j], + word1_mask, word2_mask); + if (pvt_id >= 0) + return pvt_id; + + word1_mask += 32; + if ((word1_mask + word2_mask) >= module->max_instance) + return -EINVAL; + } + + word2_mask += 64; + if (word2_mask >= module->max_instance) + return -EINVAL; + } + + return -EINVAL; +} + +/** + * skl_get_pvt_id: generate a private id for use as module id + * + * @ctx: driver context + * @mconfig: module configuration data + * + * This generates a 128 bit private unique id for a module TYPE so that + * module instance is unique + */ +int skl_get_pvt_id(struct skl_sst *ctx, struct skl_module_cfg *mconfig) +{ + struct uuid_module *module; + uuid_le *uuid_mod; + int pvt_id; + + uuid_mod = (uuid_le *)mconfig->guid; + + list_for_each_entry(module, &ctx->uuid_list, list) { + if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) { + pvt_id = skl_pvtid_128(module); + if (pvt_id >= 0) { + module->instance_id[pvt_id] = + mconfig->id.instance_id; + return pvt_id; + } + } + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(skl_get_pvt_id); + +/** + * skl_put_pvt_id: free up the private id allocated + * + * @ctx: driver context + * @mconfig: module configuration data + * + * This frees a 128 bit private unique id previously generated + */ +int skl_put_pvt_id(struct skl_sst *ctx, struct skl_module_cfg *mconfig) +{ + int i; + uuid_le *uuid_mod; + struct uuid_module *module; + + uuid_mod = (uuid_le *)mconfig->guid; + list_for_each_entry(module, &ctx->uuid_list, list) { + if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) { + + if (mconfig->id.pvt_id != 0) + i = (mconfig->id.pvt_id) / 64; + else + i = 0; + + module->pvt_id[i] &= ~(1 << (mconfig->id.pvt_id)); + mconfig->id.pvt_id = -1; + return 0; + } + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(skl_put_pvt_id); + +/* + * Parse the firmware binary to get the UUID, module id + * and loadable flags + */ +/*int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, + unsigned int offset, int index) +{ + struct adsp_fw_hdr *adsp_hdr; + struct adsp_module_entry *mod_entry; + int i, num_entry, size; + uuid_le *uuid_bin; + const char *buf; + struct skl_sst *skl = ctx->thread_context; + struct uuid_module *module; + struct firmware stripped_fw; + unsigned int safe_file; + + stripped_fw.data = fw->data; + stripped_fw.size = fw->size; + + skl_dsp_strip_extended_manifest(&stripped_fw); + + buf = stripped_fw.data; + + safe_file = sizeof(*adsp_hdr) + offset; + if (stripped_fw.size <= safe_file) { + dev_err(ctx->dev, "Small fw file size, No space for hdr\n"); + return -EINVAL; + } + + adsp_hdr = (struct adsp_fw_hdr *)(buf + offset); + + safe_file += adsp_hdr->len + sizeof(*mod_entry); + if (stripped_fw.size <= safe_file) { + dev_err(ctx->dev, "Small fw file size, No module entry\n"); + return -EINVAL; + } + + mod_entry = (struct adsp_module_entry *) + (buf + offset + adsp_hdr->len); + + num_entry = adsp_hdr->num_modules; + + safe_file += num_entry * sizeof(*mod_entry); + if (stripped_fw.size <= safe_file) { + dev_err(ctx->dev, "Small fw file size, No modules\n"); + return -EINVAL; + } + + + + for (i = 0; i < num_entry; i++, mod_entry++) { + module = kzalloc(sizeof(*module), GFP_KERNEL); + if (!module) + return -ENOMEM; + + uuid_bin = (uuid_le *)mod_entry->uuid.id; + memcpy(&module->uuid, uuid_bin, sizeof(module->uuid)); + + module->id = (i | (index << 12)); + module->is_loadable = mod_entry->type.load_type; + module->max_instance = mod_entry->instance_max_count; + size = sizeof(int) * mod_entry->instance_max_count; + module->instance_id = devm_kzalloc(ctx->dev, size, GFP_KERNEL); + if (!module->instance_id) + return -ENOMEM; + + list_add_tail(&module->list, &skl->uuid_list); + + dev_dbg(ctx->dev, + "Adding uuid :%pUL mod id: %d Loadable: %d\n", + &module->uuid, module->id, module->is_loadable); + } + + return 0; +}*/ + /* * Parse the firmware binary to get the UUID, module id * and loadable flags @@ -144,7 +364,7 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset) { struct adsp_fw_hdr *adsp_hdr; struct adsp_module_entry *mod_entry; - int i, num_entry; + int i, num_entry, size; uuid_le *uuid_bin; const char *buf; struct skl_sst *skl = ctx->thread_context; @@ -207,6 +427,11 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset) module->id = i; module->is_loadable = mod_entry->type.load_type; + module->max_instance = mod_entry->instance_max_count; + size = sizeof(int) * mod_entry->instance_max_count; + module->instance_id = devm_kzalloc(ctx->dev, size, GFP_KERNEL); + if (!module->instance_id) + return -ENOMEM; list_add_tail(&module->list, &skl->uuid_list); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 44b62e1..7e93d4f 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -21,6 +21,7 @@ #include <linux/firmware.h> #include <sound/soc.h> #include <sound/soc-topology.h> +#include <uapi/sound/snd_sst_tokens.h> #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" #include "skl-topology.h" @@ -32,6 +33,8 @@ #define SKL_CH_FIXUP_MASK (1 << 0) #define SKL_RATE_FIXUP_MASK (1 << 1) #define SKL_FMT_FIXUP_MASK (1 << 2) +#define SKL_IN_DIR_BIT_MASK BIT(0) +#define SKL_PIN_COUNT_MASK GENMASK(7, 4) /* * SKL DSP driver modelling uses only few DAPM widgets so for rest we will @@ -297,7 +300,6 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, u32 ch, s_freq, s_fmt; struct nhlt_specific_cfg *cfg; struct skl *skl = get_skl_ctx(ctx->dev); - /* check if we already have blob */ if (m_cfg->formats_config.caps_size > 0) return 0; @@ -379,43 +381,6 @@ static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w, } /* - * A pipe can have multiple modules, each of them will be a DAPM widget as - * well. While managing a pipeline we need to get the list of all the - * widgets in a pipelines, so this helper - skl_tplg_get_pipe_widget() helps - * to get the SKL type widgets in that pipeline - */ -static int skl_tplg_alloc_pipe_widget(struct device *dev, - struct snd_soc_dapm_widget *w, struct skl_pipe *pipe) -{ - struct skl_module_cfg *src_module = NULL; - struct snd_soc_dapm_path *p = NULL; - struct skl_pipe_module *p_module = NULL; - - p_module = devm_kzalloc(dev, sizeof(*p_module), GFP_KERNEL); - if (!p_module) - return -ENOMEM; - - p_module->w = w; - list_add_tail(&p_module->node, &pipe->w_list); - - snd_soc_dapm_widget_for_each_sink_path(w, p) { - if ((p->sink->priv == NULL) - && (!is_skl_dsp_widget_type(w))) - continue; - - if ((p->sink->priv != NULL) && p->connect - && is_skl_dsp_widget_type(p->sink)) { - - src_module = p->sink->priv; - if (pipe->ppl_id == src_module->pipe->ppl_id) - skl_tplg_alloc_pipe_widget(dev, - p->sink, pipe); - } - } - return 0; -} - -/* * some modules can have multiple params set from user control and * need to be set after module is initialized. If set_param flag is * set module params will be done after module is initialised. @@ -448,7 +413,7 @@ static int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w, if (bc->set_params == SKL_PARAM_SET) { ret = skl_set_module_params(ctx, - (u32 *)bc->params, bc->max, + (u32 *)bc->params, bc->size, bc->param_id, mconfig); if (ret < 0) return ret; @@ -483,7 +448,7 @@ static int skl_tplg_set_module_init_data(struct snd_soc_dapm_widget *w) continue; mconfig->formats_config.caps = (u32 *)&bc->params; - mconfig->formats_config.caps_size = bc->max; + mconfig->formats_config.caps_size = bc->size; break; } @@ -509,13 +474,22 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) list_for_each_entry(w_module, &pipe->w_list, node) { w = w_module->w; mconfig = w->priv; + /* check if module ids are populated */ + if (mconfig->id.module_id < 0) { + dev_err(skl->skl_sst->dev, + "module %pUL id not populated\n", + (uuid_le *)mconfig->guid); + return -EIO; + } else { + dev_err(skl->skl_sst->dev, + "module %pUL id is populated\n", + (uuid_le *)mconfig->guid); + } /* check resource available */ if (!skl_is_pipe_mcps_avail(skl, mconfig)) return -ENOMEM; - skl_tplg_alloc_pipe_mcps(skl, mconfig); - if (mconfig->is_loadable && ctx->dsp->fw_ops.load_mod) { ret = ctx->dsp->fw_ops.load_mod(ctx->dsp, mconfig->id.module_id, mconfig->guid); @@ -533,13 +507,21 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) * FE/BE params */ skl_tplg_update_module_params(w, ctx); + mconfig->id.pvt_id = skl_get_pvt_id(ctx, mconfig); + if (mconfig->id.pvt_id < 0) + return ret; skl_tplg_set_module_init_data(w); + ret = skl_init_module(ctx, mconfig); - if (ret < 0) + if (ret < 0) { + skl_put_pvt_id(ctx, mconfig); return ret; + } + skl_tplg_alloc_pipe_mcps(skl, mconfig); ret = skl_tplg_set_module_params(w, ctx); + if (ret < 0) return ret; } @@ -550,6 +532,7 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx, struct skl_pipe *pipe) { + int ret; struct skl_pipe_module *w_module = NULL; struct skl_module_cfg *mconfig = NULL; @@ -557,9 +540,13 @@ static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx, mconfig = w_module->w->priv; if (mconfig->is_loadable && ctx->dsp->fw_ops.unload_mod && - mconfig->m_state > SKL_MODULE_UNINIT) - return ctx->dsp->fw_ops.unload_mod(ctx->dsp, + mconfig->m_state > SKL_MODULE_UNINIT) { + ret = ctx->dsp->fw_ops.unload_mod(ctx->dsp, mconfig->id.module_id); + if (ret < 0) + return -EIO; + } + skl_put_pvt_id(ctx, mconfig); } /* no modules to unload in this path, so return */ @@ -591,9 +578,6 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, if (!skl_is_pipe_mem_avail(skl, mconfig)) return -ENOMEM; - skl_tplg_alloc_pipe_mem(skl, mconfig); - skl_tplg_alloc_pipe_mcps(skl, mconfig); - /* * Create a list of modules for pipe. * This list contains modules from source to sink @@ -602,19 +586,8 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, if (ret < 0) return ret; - /* - * we create a w_list of all widgets in that pipe. This list is not - * freed on PMD event as widgets within a pipe are static. This - * saves us cycles to get widgets in pipe every time. - * - * So if we have already initialized all the widgets of a pipeline - * we skip, so check for list_empty and create the list if empty - */ - if (list_empty(&s_pipe->w_list)) { - ret = skl_tplg_alloc_pipe_widget(ctx->dev, w, s_pipe); - if (ret < 0) - return ret; - } + skl_tplg_alloc_pipe_mem(skl, mconfig); + skl_tplg_alloc_pipe_mcps(skl, mconfig); /* Init all pipe modules from source to sink */ ret = skl_tplg_init_pipe_modules(skl, s_pipe); @@ -640,6 +613,26 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, return 0; } +static int skl_fill_sink_instance_id(struct skl_sst *ctx, + struct skl_algo_data *alg_data) +{ + struct skl_kpb_params *params = (struct skl_kpb_params *)alg_data->params; + struct skl_mod_inst_map *inst; + int i, pvt_id; + + inst = params->map; + + for (i = 0; i < params->num_modules; i++) { + pvt_id = skl_get_pvt_instance_id_map(ctx, + inst->mod_id, inst->inst_id); + if (pvt_id < 0) + return -EINVAL; + inst->inst_id = pvt_id; + inst++; + } + return 0; +} + /* * Some modules require params to be set after the module is bound to * all pins connected. @@ -688,6 +681,8 @@ static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w, bc = (struct skl_algo_data *)sb->dobj.private; if (bc->set_params == SKL_PARAM_BIND) { + if (mconfig->m_type == SKL_MODULE_TYPE_KPB) + skl_fill_sink_instance_id(ctx, bc); ret = skl_set_module_params(ctx, (u32 *)bc->params, bc->max, bc->param_id, mconfig); @@ -781,7 +776,6 @@ static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, struct skl_module_cfg *src_mconfig; struct skl_sst *ctx = skl->skl_sst; int ret = 0; - src_mconfig = w->priv; /* @@ -949,13 +943,17 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, struct skl_pipe *s_pipe = mconfig->pipe; int ret = 0; + if (s_pipe->state == SKL_PIPE_INVALID) + return -EINVAL; + skl_tplg_free_pipe_mcps(skl, mconfig); skl_tplg_free_pipe_mem(skl, mconfig); list_for_each_entry(w_module, &s_pipe->w_list, node) { dst_module = w_module->w->priv; - skl_tplg_free_pipe_mcps(skl, dst_module); + if (mconfig->m_state >= SKL_MODULE_INIT_DONE) + skl_tplg_free_pipe_mcps(skl, dst_module); if (src_module == NULL) { src_module = dst_module; continue; @@ -1102,7 +1100,7 @@ static int skl_tplg_tlv_control_get(struct snd_kcontrol *kcontrol, if (w->power) skl_get_module_params(skl->skl_sst, (u32 *)bc->params, - bc->max, bc->param_id, mconfig); + bc->size, bc->param_id, mconfig); /* decrement size for TLV header */ size -= 2 * sizeof(u32); @@ -1136,6 +1134,10 @@ static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol, struct skl *skl = get_skl_ctx(w->dapm->dev); if (ac->params) { + if (size > ac->max) + return -EINVAL; + + ac->size = size; /* * if the param_is is of type Vendor, firmware expects actual * parameter id and size from the control. @@ -1151,7 +1153,7 @@ static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol, if (w->power) return skl_set_module_params(skl->skl_sst, - (u32 *)ac->params, ac->max, + (u32 *)ac->params, ac->size, ac->param_id, mconfig); } @@ -1159,6 +1161,39 @@ static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol, } /* + * Fill the dma id for host and link. In case of passthrough + * pipeline, this will both host and link in the same + * pipeline, so need to copy the link and host based on dev_type + */ +static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg, + struct skl_pipe_params *params) +{ + struct skl_pipe *pipe = mcfg->pipe; + + if (pipe->passthru) { + switch (mcfg->dev_type) { + case SKL_DEVICE_HDALINK: + pipe->p_params->link_dma_id = params->link_dma_id; + break; + + case SKL_DEVICE_HDAHOST: + pipe->p_params->host_dma_id = params->host_dma_id; + break; + + default: + break; + } + pipe->p_params->s_fmt = params->s_fmt; + pipe->p_params->ch = params->ch; + pipe->p_params->s_freq = params->s_freq; + pipe->p_params->stream = params->stream; + + } else { + memcpy(pipe->p_params, params, sizeof(*params)); + } +} + +/* * The FE params are passed by hw_params of the DAI. * On hw_params, the params are stored in Gateway module of the FE and we * need to calculate the format in DSP module configuration, that @@ -1168,10 +1203,9 @@ int skl_tplg_update_pipe_params(struct device *dev, struct skl_module_cfg *mconfig, struct skl_pipe_params *params) { - struct skl_pipe *pipe = mconfig->pipe; struct skl_module_fmt *format = NULL; - memcpy(pipe->p_params, params, sizeof(*params)); + skl_tplg_fill_dma_id(mconfig, params); if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) format = &mconfig->in_fmt[0]; @@ -1358,12 +1392,12 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, struct skl_module_cfg *mconfig, struct skl_pipe_params *params) { - struct skl_pipe *pipe = mconfig->pipe; struct nhlt_specific_cfg *cfg; struct skl *skl = get_skl_ctx(dai->dev); int link_type = skl_tplg_be_link_type(mconfig->dev_type); + u32 *ssp_blob; - memcpy(pipe->p_params, params, sizeof(*params)); + skl_tplg_fill_dma_id(mconfig, params); if (link_type == NHLT_LINK_HDA) return 0; @@ -1383,6 +1417,14 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, params->ch, params->s_freq, params->s_fmt); return -EINVAL; } + if ((link_type == NHLT_LINK_SSP) && (mconfig->vbus_id == 0)) { /*Assuming you are using SSP0 */ + dev_info(dai->dev, "%s:overridimg MDIVR to bypass MCLK division\n", __func__); + ssp_blob = (u32*)mconfig->formats_config.caps; + /* Set bypass in mdivr */ + ssp_blob[9] = 0x83D00337; // 4 channel, 24 data size + ssp_blob[20] = 0xFFF; + ssp_blob[12] = ssp_blob[12] & 0xFFFFFFFC; + } return 0; } @@ -1450,7 +1492,6 @@ int skl_tplg_be_update_params(struct snd_soc_dai *dai, if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) { w = dai->playback_widget; - return skl_tplg_be_set_src_pipe_params(dai, w, params); } else { @@ -1473,297 +1514,1041 @@ static const struct snd_soc_tplg_bytes_ext_ops skl_tlv_ops[] = { skl_tplg_tlv_control_set}, }; -/* - * The topology binary passes the pin info for a module so initialize the pin - * info passed into module instance - */ -static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin, - struct skl_module_pin *m_pin, - bool is_dynamic, int max_pin) +static int skl_tplg_fill_pipe_tkn(struct device *dev, + struct skl_pipe *pipe, u32 tkn, + u32 tkn_val) { - int i; - for (i = 0; i < max_pin; i++) { - m_pin[i].id.module_id = dfw_pin[i].module_id; - m_pin[i].id.instance_id = dfw_pin[i].instance_id; - m_pin[i].in_use = false; - m_pin[i].is_dynamic = is_dynamic; - m_pin[i].pin_state = SKL_PIN_UNBIND; + switch (tkn) { + case SKL_TKN_U32_PIPE_CONN_TYPE: + pipe->conn_type = tkn_val; + break; + + case SKL_TKN_U32_PIPE_PRIORITY: + pipe->pipe_priority = tkn_val; + break; + + case SKL_TKN_U32_PIPE_MEM_PGS: + pipe->memory_pages = tkn_val; + break; + + default: + dev_err(dev, "Token not handled %d\n", tkn); + return -EINVAL; } + + return 0; } /* - * Add pipeline from topology binary into driver pipeline list - * - * If already added we return that instance - * Otherwise we create a new instance and add into driver list + * Add pipeline by parsing the relevant tokens + * Return an existing pipe if the pipe already exists. */ -static struct skl_pipe *skl_tplg_add_pipe(struct device *dev, - struct skl *skl, struct skl_dfw_pipe *dfw_pipe) +static int skl_tplg_add_pipe(struct device *dev, + struct skl_module_cfg *mconfig, struct skl *skl, + struct snd_soc_tplg_vendor_value_elem *tkn_elem) { struct skl_pipeline *ppl; struct skl_pipe *pipe; struct skl_pipe_params *params; list_for_each_entry(ppl, &skl->ppl_list, node) { - if (ppl->pipe->ppl_id == dfw_pipe->pipe_id) - return ppl->pipe; + if (ppl->pipe->ppl_id == tkn_elem->value) { + mconfig->pipe = ppl->pipe; + return EEXIST; + } } ppl = devm_kzalloc(dev, sizeof(*ppl), GFP_KERNEL); if (!ppl) - return NULL; + return -ENOMEM; pipe = devm_kzalloc(dev, sizeof(*pipe), GFP_KERNEL); if (!pipe) - return NULL; + return -ENOMEM; params = devm_kzalloc(dev, sizeof(*params), GFP_KERNEL); if (!params) - return NULL; + return -ENOMEM; - pipe->ppl_id = dfw_pipe->pipe_id; - pipe->memory_pages = dfw_pipe->memory_pages; - pipe->pipe_priority = dfw_pipe->pipe_priority; - pipe->conn_type = dfw_pipe->conn_type; - pipe->state = SKL_PIPE_INVALID; pipe->p_params = params; + pipe->ppl_id = tkn_elem->value; INIT_LIST_HEAD(&pipe->w_list); ppl->pipe = pipe; list_add(&ppl->node, &skl->ppl_list); - return ppl->pipe; + mconfig->pipe = pipe; + mconfig->pipe->state = SKL_PIPE_INVALID; + + return 0; } -static void skl_tplg_fill_fmt(struct skl_module_fmt *dst_fmt, - struct skl_dfw_module_fmt *src_fmt, - int pins) +static int skl_tplg_fill_pin(struct device *dev, u32 tkn, + struct skl_module_pin *m_pin, + int pin_index, u32 value) { - int i; + switch (tkn) { + case SKL_TKN_U32_PIN_MOD_ID: + m_pin[pin_index].id.module_id = value; + break; + + case SKL_TKN_U32_PIN_INST_ID: + m_pin[pin_index].id.instance_id = value; + break; - for (i = 0; i < pins; i++) { - dst_fmt[i].channels = src_fmt[i].channels; - dst_fmt[i].s_freq = src_fmt[i].freq; - dst_fmt[i].bit_depth = src_fmt[i].bit_depth; - dst_fmt[i].valid_bit_depth = src_fmt[i].valid_bit_depth; - dst_fmt[i].ch_cfg = src_fmt[i].ch_cfg; - dst_fmt[i].ch_map = src_fmt[i].ch_map; - dst_fmt[i].interleaving_style = src_fmt[i].interleaving_style; - dst_fmt[i].sample_type = src_fmt[i].sample_type; + default: + dev_err(dev, "%d Not a pin token\n", value); + return -EINVAL; } + + return 0; } /* - * Topology core widget load callback - * - * This is used to save the private data for each widget which gives - * information to the driver about module and pipeline parameters which DSP - * FW expects like ids, resource values, formats etc + * Parse for pin config specific tokens to fill up the + * module private data */ -static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, - struct snd_soc_dapm_widget *w, - struct snd_soc_tplg_dapm_widget *tplg_w) +static int skl_tplg_fill_pins_info(struct device *dev, + struct skl_module_cfg *mconfig, + struct snd_soc_tplg_vendor_value_elem *tkn_elem, + int dir, int pin_count) { int ret; - struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); - struct skl *skl = ebus_to_skl(ebus); - struct hdac_bus *bus = ebus_to_hbus(ebus); - struct skl_module_cfg *mconfig; - struct skl_pipe *pipe; - struct skl_dfw_module *dfw_config = - (struct skl_dfw_module *)tplg_w->priv.data; + struct skl_module_pin *m_pin; - if (!tplg_w->priv.size) - goto bind_event; + switch (dir) { + case SKL_DIR_IN: + m_pin = mconfig->m_in_pin; + break; - mconfig = devm_kzalloc(bus->dev, sizeof(*mconfig), GFP_KERNEL); + case SKL_DIR_OUT: + m_pin = mconfig->m_out_pin; + break; - if (!mconfig) - return -ENOMEM; + default: + dev_err(dev, "Invalid direction value\n"); + return -EINVAL; + } - w->priv = mconfig; - memcpy(&mconfig->guid, &dfw_config->uuid, 16); + ret = skl_tplg_fill_pin(dev, tkn_elem->token, + m_pin, pin_count, tkn_elem->value); - ret = snd_skl_get_module_info(skl->skl_sst, mconfig->guid, dfw_config); if (ret < 0) return ret; - mconfig->id.module_id = dfw_config->module_id; - mconfig->id.instance_id = dfw_config->instance_id; - mconfig->mcps = dfw_config->max_mcps; - mconfig->ibs = dfw_config->ibs; - mconfig->obs = dfw_config->obs; - mconfig->core_id = dfw_config->core_id; - mconfig->max_in_queue = dfw_config->max_in_queue; - mconfig->max_out_queue = dfw_config->max_out_queue; - mconfig->is_loadable = dfw_config->is_loadable; - skl_tplg_fill_fmt(mconfig->in_fmt, dfw_config->in_fmt, - MODULE_MAX_IN_PINS); - skl_tplg_fill_fmt(mconfig->out_fmt, dfw_config->out_fmt, - MODULE_MAX_OUT_PINS); - - mconfig->params_fixup = dfw_config->params_fixup; - mconfig->converter = dfw_config->converter; - mconfig->m_type = dfw_config->module_type; - mconfig->vbus_id = dfw_config->vbus_id; - mconfig->mem_pages = dfw_config->mem_pages; - - pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe); - if (pipe) - mconfig->pipe = pipe; - - mconfig->dev_type = dfw_config->dev_type; - mconfig->hw_conn_type = dfw_config->hw_conn_type; - mconfig->time_slot = dfw_config->time_slot; - mconfig->formats_config.caps_size = dfw_config->caps.caps_size; - - mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * - sizeof(*mconfig->m_in_pin), - GFP_KERNEL); - if (!mconfig->m_in_pin) - return -ENOMEM; + m_pin[pin_count].in_use = false; + m_pin[pin_count].pin_state = SKL_PIN_UNBIND; - mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_out_queue) * - sizeof(*mconfig->m_out_pin), - GFP_KERNEL); - if (!mconfig->m_out_pin) - return -ENOMEM; + return 0; +} - skl_fill_module_pin_info(dfw_config->in_pin, mconfig->m_in_pin, - dfw_config->is_dynamic_in_pin, - mconfig->max_in_queue); +/* + * Fill up input/output module config format based + * on the direction + */ +static int skl_tplg_fill_fmt(struct device *dev, + struct skl_module_cfg *mconfig, u32 tkn, + u32 value, u32 dir, u32 pin_count) +{ + struct skl_module_fmt *dst_fmt; - skl_fill_module_pin_info(dfw_config->out_pin, mconfig->m_out_pin, - dfw_config->is_dynamic_out_pin, - mconfig->max_out_queue); + switch (dir) { + case SKL_DIR_IN: + dst_fmt = mconfig->in_fmt; + dst_fmt += pin_count; + break; + case SKL_DIR_OUT: + dst_fmt = mconfig->out_fmt; + dst_fmt += pin_count; + break; - if (mconfig->formats_config.caps_size == 0) - goto bind_event; + default: + dev_err(dev, "Invalid direction value\n"); + return -EINVAL; + } - mconfig->formats_config.caps = (u32 *)devm_kzalloc(bus->dev, - mconfig->formats_config.caps_size, GFP_KERNEL); + switch (tkn) { + case SKL_TKN_U32_FMT_CH: + dst_fmt->channels = value; + break; - if (mconfig->formats_config.caps == NULL) - return -ENOMEM; + case SKL_TKN_U32_FMT_FREQ: + dst_fmt->s_freq = value; + break; - memcpy(mconfig->formats_config.caps, dfw_config->caps.caps, - dfw_config->caps.caps_size); - mconfig->formats_config.param_id = dfw_config->caps.param_id; - mconfig->formats_config.set_params = dfw_config->caps.set_params; + case SKL_TKN_U32_FMT_BIT_DEPTH: + dst_fmt->bit_depth = value; + break; -bind_event: - if (tplg_w->event_type == 0) { - dev_dbg(bus->dev, "ASoC: No event handler required\n"); - return 0; + case SKL_TKN_U32_FMT_SAMPLE_SIZE: + dst_fmt->valid_bit_depth = value; + break; + + case SKL_TKN_U32_FMT_CH_CONFIG: + dst_fmt->ch_cfg = value; + break; + + case SKL_TKN_U32_FMT_INTERLEAVE: + dst_fmt->interleaving_style = value; + break; + + case SKL_TKN_U32_FMT_SAMPLE_TYPE: + dst_fmt->sample_type = value; + break; + + case SKL_TKN_U32_FMT_CH_MAP: + dst_fmt->ch_map = value; + break; + + default: + dev_err(dev, "Invalid token %d\n", tkn); + return -EINVAL; } - ret = snd_soc_tplg_widget_bind_event(w, skl_tplg_widget_ops, - ARRAY_SIZE(skl_tplg_widget_ops), - tplg_w->event_type); + return 0; +} - if (ret) { - dev_err(bus->dev, "%s: No matching event handlers found for %d\n", - __func__, tplg_w->event_type); +static int skl_tplg_get_uuid(struct device *dev, struct skl_module_cfg *mconfig, + struct snd_soc_tplg_vendor_uuid_elem *uuid_tkn) +{ + if (uuid_tkn->token == SKL_TKN_UUID) + memcpy(&mconfig->guid, &uuid_tkn->uuid, 16); + else { + dev_err(dev, "Not an UUID token tkn %d\n", uuid_tkn->token); return -EINVAL; } return 0; } -static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be, - struct snd_soc_tplg_bytes_control *bc) +static void skl_tplg_fill_pin_dynamic_val( + struct skl_module_pin *mpin, u32 pin_count, u32 value) { - struct skl_algo_data *ac; - struct skl_dfw_algo_data *dfw_ac = - (struct skl_dfw_algo_data *)bc->priv.data; + int i; - ac = devm_kzalloc(dev, sizeof(*ac), GFP_KERNEL); - if (!ac) - return -ENOMEM; + for (i = 0; i < pin_count; i++) + mpin[i].is_dynamic = value; +} - /* Fill private data */ - ac->max = dfw_ac->max; - ac->param_id = dfw_ac->param_id; - ac->set_params = dfw_ac->set_params; +/* + * Parse tokens to fill up the module private data + */ +static int skl_tplg_get_token(struct device *dev, + struct snd_soc_tplg_vendor_value_elem *tkn_elem, + struct skl *skl, struct skl_module_cfg *mconfig) +{ + int tkn_count = 0; + int ret; + static int is_pipe_exists; + static int pin_index, dir; + if (tkn_elem->token > SKL_TKN_MAX) + return -EINVAL; - if (ac->max) { - ac->params = (char *) devm_kzalloc(dev, ac->max, GFP_KERNEL); - if (!ac->params) + switch (tkn_elem->token) { + case SKL_TKN_U8_IN_QUEUE_COUNT: + mconfig->max_in_queue = tkn_elem->value; + mconfig->m_in_pin = devm_kzalloc(dev, mconfig->max_in_queue * + sizeof(*mconfig->m_in_pin), + GFP_KERNEL); + if (!mconfig->m_in_pin) return -ENOMEM; - memcpy(ac->params, dfw_ac->params, ac->max); - } + break; - be->dobj.private = ac; - return 0; -} + case SKL_TKN_U8_OUT_QUEUE_COUNT: + mconfig->max_out_queue = tkn_elem->value; + mconfig->m_out_pin = devm_kzalloc(dev, mconfig->max_out_queue * + sizeof(*mconfig->m_out_pin), + GFP_KERNEL); -static int skl_tplg_control_load(struct snd_soc_component *cmpnt, - struct snd_kcontrol_new *kctl, - struct snd_soc_tplg_ctl_hdr *hdr) -{ - struct soc_bytes_ext *sb; - struct snd_soc_tplg_bytes_control *tplg_bc; - struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); - struct hdac_bus *bus = ebus_to_hbus(ebus); + if (!mconfig->m_out_pin) + return -ENOMEM; - switch (hdr->ops.info) { - case SND_SOC_TPLG_CTL_BYTES: - tplg_bc = container_of(hdr, - struct snd_soc_tplg_bytes_control, hdr); - if (kctl->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { - sb = (struct soc_bytes_ext *)kctl->private_value; - if (tplg_bc->priv.size) - return skl_init_algo_data( - bus->dev, sb, tplg_bc); - } break; - default: - dev_warn(bus->dev, "Control load not supported %d:%d:%d\n", - hdr->ops.get, hdr->ops.put, hdr->ops.info); + case SKL_TKN_U8_DYN_IN_PIN: + if (!mconfig->m_in_pin) + return -ENOMEM; + + skl_tplg_fill_pin_dynamic_val(mconfig->m_in_pin, + mconfig->max_in_queue, tkn_elem->value); + break; - } - return 0; -} + case SKL_TKN_U8_DYN_OUT_PIN: + if (!mconfig->m_out_pin) + return -ENOMEM; -static struct snd_soc_tplg_ops skl_tplg_ops = { - .widget_load = skl_tplg_widget_load, - .control_load = skl_tplg_control_load, - .bytes_ext_ops = skl_tlv_ops, - .bytes_ext_ops_count = ARRAY_SIZE(skl_tlv_ops), -}; + skl_tplg_fill_pin_dynamic_val(mconfig->m_out_pin, + mconfig->max_out_queue, tkn_elem->value); -/* This will be read from topology manifest, currently defined here */ -#define SKL_MAX_MCPS 30000000 -#define SKL_FW_MAX_MEM 1000000 + break; -/* - * SKL topology init routine - */ -int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) -{ - int ret; - const struct firmware *fw; - struct hdac_bus *bus = ebus_to_hbus(ebus); - struct skl *skl = ebus_to_skl(ebus); + case SKL_TKN_U8_TIME_SLOT: + mconfig->time_slot = tkn_elem->value; + break; - ret = request_firmware(&fw, skl->tplg_name, bus->dev); - if (ret < 0) { - dev_err(bus->dev, "tplg fw %s load failed with %d\n", - skl->tplg_name, ret); - ret = request_firmware(&fw, "dfw_sst.bin", bus->dev); - if (ret < 0) { - dev_err(bus->dev, "Fallback tplg fw %s load failed with %d\n", - "dfw_sst.bin", ret); - return ret; - } - } + case SKL_TKN_U8_CORE_ID: + mconfig->core_id = tkn_elem->value; - /* + case SKL_TKN_U8_MOD_TYPE: + mconfig->m_type = tkn_elem->value; + break; + + case SKL_TKN_U8_DEV_TYPE: + mconfig->dev_type = tkn_elem->value; + break; + + case SKL_TKN_U8_HW_CONN_TYPE: + mconfig->hw_conn_type = tkn_elem->value; + break; + + case SKL_TKN_U16_MOD_INST_ID: + mconfig->id.instance_id = + tkn_elem->value; + break; + + case SKL_TKN_U32_MEM_PAGES: + mconfig->mem_pages = tkn_elem->value; + break; + + case SKL_TKN_U32_MAX_MCPS: + mconfig->mcps = tkn_elem->value; + break; + + case SKL_TKN_U32_OBS: + mconfig->obs = tkn_elem->value; + break; + + case SKL_TKN_U32_IBS: + mconfig->ibs = tkn_elem->value; + break; + + case SKL_TKN_U32_VBUS_ID: + mconfig->vbus_id = tkn_elem->value; + break; + + case SKL_TKN_U32_PARAMS_FIXUP: + mconfig->params_fixup = tkn_elem->value; + break; + + case SKL_TKN_U32_CONVERTER: + mconfig->converter = tkn_elem->value; + break; + + case SKL_TKN_U32_PIPE_ID: + ret = skl_tplg_add_pipe(dev, + mconfig, skl, tkn_elem); + + if (ret < 0) + return is_pipe_exists; + + if (ret == EEXIST) + is_pipe_exists = 1; + + break; + + case SKL_TKN_U32_PIPE_CONN_TYPE: + case SKL_TKN_U32_PIPE_PRIORITY: + case SKL_TKN_U32_PIPE_MEM_PGS: + if (is_pipe_exists) { + ret = skl_tplg_fill_pipe_tkn(dev, mconfig->pipe, + tkn_elem->token, tkn_elem->value); + if (ret < 0) + return ret; + } + + break; + + /* + * SKL_TKN_U32_DIR_PIN_COUNT token has the value for both + * direction and the pin count. The first four bits represent + * direction and next four the pin count. + */ + case SKL_TKN_U32_DIR_PIN_COUNT: + dir = tkn_elem->value & SKL_IN_DIR_BIT_MASK; + pin_index = (tkn_elem->value & + SKL_PIN_COUNT_MASK) >> 4; + + break; + + case SKL_TKN_U32_FMT_CH: + case SKL_TKN_U32_FMT_FREQ: + case SKL_TKN_U32_FMT_BIT_DEPTH: + case SKL_TKN_U32_FMT_SAMPLE_SIZE: + case SKL_TKN_U32_FMT_CH_CONFIG: + case SKL_TKN_U32_FMT_INTERLEAVE: + case SKL_TKN_U32_FMT_SAMPLE_TYPE: + case SKL_TKN_U32_FMT_CH_MAP: + ret = skl_tplg_fill_fmt(dev, mconfig, tkn_elem->token, + tkn_elem->value, dir, pin_index); + + if (ret < 0) + return ret; + + break; + + case SKL_TKN_U32_PIN_MOD_ID: + case SKL_TKN_U32_PIN_INST_ID: + ret = skl_tplg_fill_pins_info(dev, + mconfig, tkn_elem, dir, + pin_index); + if (ret < 0) + return ret; + + break; + + case SKL_TKN_U32_CAPS_SIZE: + mconfig->formats_config.caps_size = + tkn_elem->value; + + break; + + case SKL_TKN_U32_PROC_DOMAIN: + mconfig->domain = + tkn_elem->value; + + break; + + case SKL_TKN_U8_IN_PIN_TYPE: + case SKL_TKN_U8_OUT_PIN_TYPE: + case SKL_TKN_U8_CONN_TYPE: + break; + + default: + dev_err(dev, "Token %d not handled\n", + tkn_elem->token); + return -EINVAL; + } + + tkn_count++; + + return tkn_count; +} + +/* + * Parse the vendor array for specific tokens to construct + * module private data + */ +static int skl_tplg_get_tokens(struct device *dev, + char *pvt_data, struct skl *skl, + struct skl_module_cfg *mconfig, int block_size) +{ + struct snd_soc_tplg_vendor_array *array; + struct snd_soc_tplg_vendor_value_elem *tkn_elem; + int tkn_count = 0, ret; + int off = 0, tuple_size = 0; + if (block_size <= 0) + return -EINVAL; + + while (tuple_size < block_size) { + array = (struct snd_soc_tplg_vendor_array *)(pvt_data + off); + + off += array->size; + + switch (array->type) { + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + dev_warn(dev, "no string tokens expected for skl tplg\n"); + continue; + + case SND_SOC_TPLG_TUPLE_TYPE_UUID: + ret = skl_tplg_get_uuid(dev, mconfig, array->uuid); + if (ret < 0) + return ret; + + tuple_size += sizeof(*array->uuid); + + continue; + + default: + tkn_elem = array->value; + tkn_count = 0; + break; + } + + while (tkn_count <= (array->num_elems - 1)) { + ret = skl_tplg_get_token(dev, tkn_elem, + skl, mconfig); + + if (ret < 0) + return ret; + + tkn_count = tkn_count + ret; + tkn_elem++; + } + + tuple_size += tkn_count * sizeof(*tkn_elem); + } + + return 0; +} + +/* + * Every data block is preceded by a descriptor to read the number + * of data blocks, they type of the block and it's size + */ +static int skl_tplg_get_desc_blocks(struct device *dev, + struct snd_soc_tplg_vendor_array *array) +{ + struct snd_soc_tplg_vendor_value_elem *tkn_elem; + + tkn_elem = array->value; + + switch (tkn_elem->token) { + case SKL_TKN_U8_NUM_BLOCKS: + case SKL_TKN_U8_BLOCK_TYPE: + case SKL_TKN_U16_BLOCK_SIZE: + return tkn_elem->value; + + default: + dev_err(dev, "Invalid descriptor token %d\n", tkn_elem->token); + break; + } + + return -EINVAL; +} + +/* + * Parse the private data for the token and corresponding value. + * The private data can have multiple data blocks. So, a data block + * is preceded by a descriptor for number of blocks and a descriptor + * for the type and size of the suceeding data block. + */ +static int skl_tplg_get_pvt_data(struct snd_soc_tplg_dapm_widget *tplg_w, + struct skl *skl, struct device *dev, + struct skl_module_cfg *mconfig) +{ + struct snd_soc_tplg_vendor_array *array; + int num_blocks, block_size = 0, block_type, off = 0; + char *data; + int ret; + /* Read the NUM_DATA_BLOCKS descriptor */ + array = (struct snd_soc_tplg_vendor_array *)tplg_w->priv.data; + ret = skl_tplg_get_desc_blocks(dev, array); + if (ret < 0) + return ret; + num_blocks = ret; + + off += array->size; + array = (struct snd_soc_tplg_vendor_array *)(tplg_w->priv.data + off); + + /* Read the BLOCK_TYPE and BLOCK_SIZE descriptor */ + while (num_blocks > 0) { + ret = skl_tplg_get_desc_blocks(dev, array); + + if (ret < 0) + return ret; + block_type = ret; + off += array->size; + + array = (struct snd_soc_tplg_vendor_array *) + (tplg_w->priv.data + off); + + ret = skl_tplg_get_desc_blocks(dev, array); + + if (ret < 0) + return ret; + block_size = ret; + off += array->size; + + array = (struct snd_soc_tplg_vendor_array *) + (tplg_w->priv.data + off); + + data = (tplg_w->priv.data + off); + + if (block_type == SKL_TYPE_TUPLE) { + ret = skl_tplg_get_tokens(dev, data, + skl, mconfig, block_size); + + if (ret < 0) + return ret; + + --num_blocks; + } else { + if (mconfig->formats_config.caps_size > 0) + memcpy(mconfig->formats_config.caps, data, + mconfig->formats_config.caps_size); + --num_blocks; + } + } + + return 0; +} + +static void skl_clear_pin_config(struct snd_soc_platform *platform, + struct snd_soc_dapm_widget *w) +{ + int i; + struct skl_module_cfg *mconfig; + struct skl_pipe *pipe; + + if (!strncmp(w->dapm->component->name, platform->component.name, + strlen(platform->component.name))) { + mconfig = w->priv; + pipe = mconfig->pipe; + for (i = 0; i < mconfig->max_in_queue; i++) { + mconfig->m_in_pin[i].in_use = false; + mconfig->m_in_pin[i].pin_state = SKL_PIN_UNBIND; + } + for (i = 0; i < mconfig->max_out_queue; i++) { + mconfig->m_out_pin[i].in_use = false; + mconfig->m_out_pin[i].pin_state = SKL_PIN_UNBIND; + } + pipe->state = SKL_PIPE_INVALID; + mconfig->m_state = SKL_MODULE_UNINIT; + } +} + +void skl_cleanup_resources(struct skl *skl) +{ + struct skl_sst *ctx = skl->skl_sst; + struct snd_soc_platform *soc_platform = skl->platform; + struct snd_soc_dapm_widget *w; + struct snd_soc_card *card; + + if (soc_platform == NULL) + return; + + card = soc_platform->component.card; + if (!card || !card->instantiated) + return; + + skl->resource.mem = 0; + skl->resource.mcps = 0; + + list_for_each_entry(w, &card->widgets, list) { + if (is_skl_dsp_widget_type(w) && (w->priv != NULL)) + skl_clear_pin_config(soc_platform, w); + } + + //skl_clear_module_cnt(ctx->dsp); +} + +/* + * Topology core widget load callback + * + * This is used to save the private data for each widget which gives + * information to the driver about module and pipeline parameters which DSP + * FW expects like ids, resource values, formats etc + */ +static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tplg_w) +{ + int ret; + struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); + struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl_module_cfg *mconfig; + + if (!tplg_w->priv.size) + goto bind_event; + + mconfig = devm_kzalloc(bus->dev, sizeof(*mconfig), GFP_KERNEL); + + if (!mconfig) + return -ENOMEM; + + w->priv = mconfig; + + /* + * module binary can be loaded later, so set it to query when + * module is load for a use case + */ + mconfig->id.module_id = -1; + /* Parse private data for tuples */ + ret = skl_tplg_get_pvt_data(tplg_w, skl, bus->dev, mconfig); + if (ret < 0) + return ret; +bind_event: + if (tplg_w->event_type == 0) { + dev_dbg(bus->dev, "ASoC: No event handler required\n"); + return 0; + } + + ret = snd_soc_tplg_widget_bind_event(w, skl_tplg_widget_ops, + ARRAY_SIZE(skl_tplg_widget_ops), + tplg_w->event_type); + + if (ret) { + dev_err(bus->dev, "%s: No matching event handlers found for %d\n", + __func__, tplg_w->event_type); + return -EINVAL; + } + + return 0; +} + +static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be, + struct snd_soc_tplg_bytes_control *bc) +{ + struct skl_algo_data *ac; + struct skl_dfw_algo_data *dfw_ac = + (struct skl_dfw_algo_data *)bc->priv.data; + + ac = devm_kzalloc(dev, sizeof(*ac), GFP_KERNEL); + if (!ac) + return -ENOMEM; + + /* Fill private data */ + ac->max = dfw_ac->max; + ac->param_id = dfw_ac->param_id; + ac->set_params = dfw_ac->set_params; + ac->size = dfw_ac->max; + + if (ac->max) { + ac->params = (char *) devm_kzalloc(dev, ac->max, GFP_KERNEL); + if (!ac->params) + return -ENOMEM; + + memcpy(ac->params, dfw_ac->params, ac->max); + } + + be->dobj.private = ac; + return 0; +} + +static int skl_tplg_control_load(struct snd_soc_component *cmpnt, + struct snd_kcontrol_new *kctl, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct soc_bytes_ext *sb; + struct snd_soc_tplg_bytes_control *tplg_bc; + struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); + struct hdac_bus *bus = ebus_to_hbus(ebus); + + switch (hdr->ops.info) { + case SND_SOC_TPLG_CTL_BYTES: + tplg_bc = container_of(hdr, + struct snd_soc_tplg_bytes_control, hdr); + if (kctl->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + sb = (struct soc_bytes_ext *)kctl->private_value; + if (tplg_bc->priv.size) + return skl_init_algo_data( + bus->dev, sb, tplg_bc); + } + break; + + default: + dev_warn(bus->dev, "Control load not supported %d:%d:%d\n", + hdr->ops.get, hdr->ops.put, hdr->ops.info); + break; + } + + return 0; +} + +static int skl_tplg_fill_str_mfest_tkn(struct device *dev, + struct snd_soc_tplg_vendor_string_elem *str_elem, + struct skl_dfw_manifest *minfo) +{ + int tkn_count = 0; + static int ref_count; + + switch (str_elem->token) { + case SKL_TKN_STR_LIB_NAME: + if (ref_count > minfo->lib_count - 1) { + ref_count = 0; + return -EINVAL; + } + + strncpy(minfo->lib[ref_count].name, str_elem->string, + ARRAY_SIZE(minfo->lib[ref_count].name)); + ref_count++; + tkn_count++; + break; + + default: + dev_err(dev, "Not a string token %d\n", str_elem->token); + break; + } + + return tkn_count; +} + +static int skl_tplg_get_str_tkn(struct device *dev, + struct snd_soc_tplg_vendor_array *array, + struct skl_dfw_manifest *minfo) +{ + int tkn_count = 0, ret; + struct snd_soc_tplg_vendor_string_elem *str_elem; + + str_elem = (struct snd_soc_tplg_vendor_string_elem *)array->value; + while (tkn_count < array->num_elems) { + ret = skl_tplg_fill_str_mfest_tkn(dev, str_elem, minfo); + str_elem++; + + if (ret < 0) + return ret; + + tkn_count = tkn_count + ret; + } + + return tkn_count; +} + +static int skl_tplg_get_int_tkn(struct device *dev, + struct snd_soc_tplg_vendor_value_elem *tkn_elem, + struct skl_dfw_manifest *minfo) +{ + int tkn_count = 0; + + switch (tkn_elem->token) { + case SKL_TKN_U32_LIB_COUNT: + minfo->lib_count = tkn_elem->value; + tkn_count++; + break; + + default: + dev_err(dev, "Not a manifest token %d\n", tkn_elem->token); + return -EINVAL; + } + + return tkn_count; +} + +/* + * Fill the manifest structure by parsing the tokens based on the + * type. + */ +static int skl_tplg_get_manifest_tkn(struct device *dev, + char *pvt_data, struct skl_dfw_manifest *minfo, + int block_size) +{ + int tkn_count = 0, ret; + int off = 0, tuple_size = 0; + struct snd_soc_tplg_vendor_array *array; + struct snd_soc_tplg_vendor_value_elem *tkn_elem; + + if (block_size <= 0) + return -EINVAL; + + while (tuple_size < block_size) { + array = (struct snd_soc_tplg_vendor_array *)(pvt_data + off); + off += array->size; + switch (array->type) { + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + ret = skl_tplg_get_str_tkn(dev, array, minfo); + + if (ret < 0) + return ret; + tkn_count += ret; + + tuple_size += tkn_count * + sizeof(struct snd_soc_tplg_vendor_string_elem); + continue; + + case SND_SOC_TPLG_TUPLE_TYPE_UUID: + dev_warn(dev, "no uuid tokens for skl tplf manifest\n"); + continue; + + default: + tkn_elem = array->value; + tkn_count = 0; + break; + } + + while (tkn_count <= array->num_elems - 1) { + ret = skl_tplg_get_int_tkn(dev, + tkn_elem, minfo); + if (ret < 0) + return ret; + + tkn_count = tkn_count + ret; + tkn_elem++; + tuple_size += tkn_count * + sizeof(struct snd_soc_tplg_vendor_value_elem); + break; + } + tkn_count = 0; + } + + return 0; +} + +/* + * Parse manifest private data for tokens. The private data block is + * preceded by descriptors for type and size of data block. + */ +static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest, + struct device *dev, struct skl_dfw_manifest *minfo) +{ + struct snd_soc_tplg_vendor_array *array; + int num_blocks, block_size = 0, block_type, off = 0; + char *data; + int ret; + + /* Read the NUM_DATA_BLOCKS descriptor */ + array = (struct snd_soc_tplg_vendor_array *)manifest->priv.data; + ret = skl_tplg_get_desc_blocks(dev, array); + if (ret < 0) + return ret; + num_blocks = ret; + + off += array->size; + array = (struct snd_soc_tplg_vendor_array *) + (manifest->priv.data + off); + + /* Read the BLOCK_TYPE and BLOCK_SIZE descriptor */ + while (num_blocks > 0) { + ret = skl_tplg_get_desc_blocks(dev, array); + + if (ret < 0) + return ret; + block_type = ret; + off += array->size; + + array = (struct snd_soc_tplg_vendor_array *) + (manifest->priv.data + off); + + ret = skl_tplg_get_desc_blocks(dev, array); + + if (ret < 0) + return ret; + block_size = ret; + off += array->size; + + array = (struct snd_soc_tplg_vendor_array *) + (manifest->priv.data + off); + + data = (manifest->priv.data + off); + + if (block_type == SKL_TYPE_TUPLE) { + ret = skl_tplg_get_manifest_tkn(dev, data, minfo, + block_size); + + if (ret < 0) + return ret; + + --num_blocks; + } else { + return -EINVAL; + } + } + + return 0; +} + +static struct skl_dfw_manifest gManifest; + +static int skl_manifest_load(struct snd_soc_component *cmpnt, + struct snd_soc_tplg_manifest *manifest) +{ + struct skl_dfw_manifest *minfo; + struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = ebus_to_skl(ebus); + int ret = 0; + + /* proceed only if we have private data defined */ + if (manifest->priv.size == 0) + return 0; + + minfo = &gManifest; //skl->skl_sst->manifest; + + skl_tplg_get_manifest_data(manifest, bus->dev, minfo); + + if (minfo->lib_count > HDA_MAX_LIB) { + dev_err(bus->dev, "Exceeding max Library count. Got:%d\n", + minfo->lib_count); + ret = -EINVAL; + } + + return ret; +} + +static struct snd_soc_tplg_ops skl_tplg_ops = { + .widget_load = skl_tplg_widget_load, + .control_load = skl_tplg_control_load, + .bytes_ext_ops = skl_tlv_ops, + .bytes_ext_ops_count = ARRAY_SIZE(skl_tlv_ops), + .manifest = skl_manifest_load, +}; + +/* + * A pipe can have multiple modules, each of them will be a DAPM widget as + * well. While managing a pipeline we need to get the list of all the + * widgets in a pipelines, so this helper - skl_tplg_create_pipe_widget_list() + * helps to get the SKL type widgets in that pipeline + */ +static int skl_tplg_create_pipe_widget_list(struct snd_soc_platform *platform) +{ + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mcfg = NULL; + struct skl_pipe_module *p_module = NULL; + struct skl_pipe *pipe; + + list_for_each_entry(w, &platform->component.card->widgets, list) { + if (is_skl_dsp_widget_type(w) && w->priv != NULL) { + mcfg = w->priv; + pipe = mcfg->pipe; + + p_module = devm_kzalloc(platform->dev, + sizeof(*p_module), GFP_KERNEL); + if (!p_module) + return -ENOMEM; + + p_module->w = w; + list_add_tail(&p_module->node, &pipe->w_list); + } + } + + return 0; +} + +static void skl_tplg_set_pipe_type(struct skl *skl, struct skl_pipe *pipe) +{ + struct skl_pipe_module *w_module; + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mconfig; + bool host_found = false, link_found = false; + + list_for_each_entry(w_module, &pipe->w_list, node) { + w = w_module->w; + mconfig = w->priv; + + if (mconfig->dev_type == SKL_DEVICE_HDAHOST) + host_found = true; + else if (mconfig->dev_type != SKL_DEVICE_NONE) + link_found = true; + } + + if (host_found && link_found) + pipe->passthru = true; + else + pipe->passthru = false; +} + +/* This will be read from topology manifest, currently defined here */ +#define SKL_MAX_MCPS 30000000 +#define SKL_FW_MAX_MEM 1000000 + +/* + * SKL topology init routine + */ +int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) +{ + int ret; + const struct firmware *fw; + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = ebus_to_skl(ebus); + struct skl_pipeline *ppl; + + ret = request_firmware(&fw, skl->tplg_name, bus->dev); + if (ret < 0) { + dev_err(bus->dev, "tplg fw %s load failed with %d\n", + skl->tplg_name, ret); + ret = request_firmware(&fw, "dfw_sst.bin", bus->dev); + if (ret < 0) { + dev_err(bus->dev, "Fallback tplg fw %s load failed with %d\n", + "dfw_sst.bin", ret); + return ret; + } + } + + /* * The complete tplg for SKL is loaded as index 0, we don't use * any other index */ @@ -1779,6 +2564,12 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) skl->resource.max_mem = SKL_FW_MAX_MEM; skl->tplg = fw; + ret = skl_tplg_create_pipe_widget_list(platform); + if (ret < 0) + return ret; + + list_for_each_entry(ppl, &skl->ppl_list, node) + skl_tplg_set_pipe_type(skl, ppl->pipe); return 0; } diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index e4b399c..a519360 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -133,7 +133,7 @@ struct skl_i2s_config_blob { struct skl_dma_control { u32 node_id; u32 config_length; - u32 config_data[1]; + u32 config_data[0]; } __packed; struct skl_cpr_cfg { @@ -215,9 +215,20 @@ struct skl_module_fmt { struct skl_module_cfg; +struct skl_mod_inst_map { + u16 mod_id; + u16 inst_id; +}; + +struct skl_kpb_params { + u32 num_modules; + struct skl_mod_inst_map map[0]; +}; + struct skl_module_inst_id { - u32 module_id; + int module_id; u32 instance_id; + int pvt_id; }; enum skl_module_pin_state { @@ -244,7 +255,8 @@ enum skl_pipe_state { SKL_PIPE_INVALID = 0, SKL_PIPE_CREATED = 1, SKL_PIPE_PAUSED = 2, - SKL_PIPE_STARTED = 3 + SKL_PIPE_STARTED = 3, + SKL_PIPE_RESET = 4 }; struct skl_pipe_module { @@ -270,6 +282,7 @@ struct skl_pipe { struct skl_pipe_params *p_params; enum skl_pipe_state state; struct list_head w_list; + bool passthru; }; enum skl_module_state { @@ -319,6 +332,7 @@ struct skl_algo_data { u32 param_id; u32 set_params; u32 max; + u32 size; char *params; }; @@ -357,6 +371,8 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); +int skl_reset_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); + int skl_init_module(struct skl_sst *ctx, struct skl_module_cfg *module_config); int skl_bind_modules(struct skl_sst *ctx, struct skl_module_cfg diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index a32e5e9..44f723c 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -80,7 +80,8 @@ enum skl_module_type { SKL_MODULE_TYPE_UPDWMIX, SKL_MODULE_TYPE_SRCINT, SKL_MODULE_TYPE_ALGO, - SKL_MODULE_TYPE_BASE_OUTFMT + SKL_MODULE_TYPE_BASE_OUTFMT, + SKL_MODULE_TYPE_KPB, }; enum skl_core_affinity { @@ -228,4 +229,26 @@ struct skl_dfw_algo_data { char params[0]; } __packed; +#define LIB_NAME_LENGTH 128 +#define HDA_MAX_LIB 16 + +struct lib_info { + char name[LIB_NAME_LENGTH]; +} __packed; + +struct skl_dfw_manifest { + u32 lib_count; + struct lib_info lib[HDA_MAX_LIB]; +} __packed; + +enum skl_tkn_dir { + SKL_DIR_IN, + SKL_DIR_OUT +}; + +enum skl_tuple_type { + SKL_TYPE_TUPLE, + SKL_TYPE_DATA +}; + #endif diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 4b4b387..3ddf1b3 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -66,6 +66,7 @@ struct skl { struct platform_device *dmic_dev; struct platform_device *i2s_dev; + struct snd_soc_platform *platform; struct nhlt_acpi_table *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ @@ -113,4 +114,6 @@ int skl_init_dsp(struct skl *skl); int skl_free_dsp(struct skl *skl); int skl_suspend_dsp(struct skl *skl); int skl_resume_dsp(struct skl *skl); + +void skl_cleanup_resources(struct skl *skl); #endif /* __SOUND_SOC_SKL_H */ -- 2.5.0 -- _______________________________________________ linux-yocto mailing list linux-yocto@yoctoproject.org https://lists.yoctoproject.org/listinfo/linux-yocto