summaryrefslogtreecommitdiffstats
path: root/sound/soc/intel/atom
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
commit2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch)
tree848558de17fb3008cdf4d861b01ac7781903ce39 /sound/soc/intel/atom
parentInitial commit. (diff)
downloadlinux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz
linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip
Adding upstream version 6.1.76.upstream/6.1.76
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sound/soc/intel/atom')
-rw-r--r--sound/soc/intel/atom/Makefile9
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.c1583
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.h875
-rw-r--r--sound/soc/intel/atom/sst-mfld-dsp.h525
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-compress.c271
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-pcm.c826
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform.h178
-rw-r--r--sound/soc/intel/atom/sst/Makefile8
-rw-r--r--sound/soc/intel/atom/sst/sst.c573
-rw-r--r--sound/soc/intel/atom/sst/sst.h533
-rw-r--r--sound/soc/intel/atom/sst/sst_acpi.c372
-rw-r--r--sound/soc/intel/atom/sst/sst_drv_interface.c712
-rw-r--r--sound/soc/intel/atom/sst/sst_ipc.c375
-rw-r--r--sound/soc/intel/atom/sst/sst_loader.c454
-rw-r--r--sound/soc/intel/atom/sst/sst_pci.c201
-rw-r--r--sound/soc/intel/atom/sst/sst_pvt.c406
-rw-r--r--sound/soc/intel/atom/sst/sst_stream.c471
17 files changed, 8372 insertions, 0 deletions
diff --git a/sound/soc/intel/atom/Makefile b/sound/soc/intel/atom/Makefile
new file mode 100644
index 000000000..c66f03f5d
--- /dev/null
+++ b/sound/soc/intel/atom/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-soc-sst-atom-hifi2-platform-objs := sst-mfld-platform-pcm.o \
+ sst-mfld-platform-compress.o \
+ sst-atom-controls.o
+
+obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += snd-soc-sst-atom-hifi2-platform.o
+
+# DSP driver
+obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += sst/
diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c
new file mode 100644
index 000000000..fd59b35a6
--- /dev/null
+++ b/sound/soc/intel/atom/sst-atom-controls.c
@@ -0,0 +1,1583 @@
+// SPDX-License-Identifier: GPL-2.0-only
+ /*
+ * 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>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * In the dpcm driver modelling when a particular FE/BE/Mixer/Pipe is active
+ * we forward the settings and parameters, rest we keep the values in
+ * driver and forward when DAPM enables them
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#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"
+
+static int sst_fill_byte_control(struct sst_data *drv,
+ u8 ipc_msg, u8 block,
+ u8 task_id, u8 pipe_id,
+ u16 len, void *cmd_data)
+{
+ struct snd_sst_bytes_v2 *byte_data = drv->byte_stream;
+
+ byte_data->type = SST_CMD_BYTES_SET;
+ byte_data->ipc_msg = ipc_msg;
+ byte_data->block = block;
+ byte_data->task_id = task_id;
+ byte_data->pipe_id = pipe_id;
+
+ if (len > SST_MAX_BIN_BYTES - sizeof(*byte_data)) {
+ dev_err(&drv->pdev->dev, "command length too big (%u)", len);
+ return -EINVAL;
+ }
+ byte_data->len = len;
+ memcpy(byte_data->bytes, cmd_data, len);
+ print_hex_dump_bytes("writing to lpe: ", DUMP_PREFIX_OFFSET,
+ byte_data, len + sizeof(*byte_data));
+ return 0;
+}
+
+static int sst_fill_and_send_cmd_unlocked(struct sst_data *drv,
+ u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id,
+ void *cmd_data, u16 len)
+{
+ int ret = 0;
+
+ WARN_ON(!mutex_is_locked(&drv->lock));
+
+ ret = sst_fill_byte_control(drv, ipc_msg,
+ block, task_id, pipe_id, len, cmd_data);
+ if (ret < 0)
+ return ret;
+ return sst->ops->send_byte_stream(sst->dev, drv->byte_stream);
+}
+
+/**
+ * sst_fill_and_send_cmd - generate the IPC message and send it to the FW
+ * @drv: sst_data
+ * @ipc_msg: type of IPC (CMD, SET_PARAMS, GET_PARAMS)
+ * @block: block index
+ * @task_id: task index
+ * @pipe_id: pipe index
+ * @cmd_data: the IPC payload
+ * @len: length of data to be sent
+ */
+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;
+}
+
+/*
+ * tx 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_tx_map[SST_MAX_TDM_SLOTS] = {
+ 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default rx map */
+};
+
+/*
+ * rx 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_rx_map[SST_MAX_TDM_SLOTS] = {
+ 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default tx map */
+};
+
+/*
+ * NOTE: this is invoked with lock held
+ */
+static int sst_send_slot_map(struct sst_data *drv)
+{
+ struct sst_param_sba_ssp_slot_map cmd;
+
+ 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_tx_map[0], sizeof(cmd.rx_slot_map));
+ memcpy(cmd.tx_slot_map, &sst_ssp_rx_map[0], sizeof(cmd.tx_slot_map));
+
+ return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
+ SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,
+ sizeof(cmd.header) + cmd.header.length);
+}
+
+static 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
+ * @kcontrol: control pointer
+ * @ucontrol: User data
+ * Searches the map where the control status is stored, and gets the
+ * channel/slot which is currently set for this enumerated control. Since it is
+ * an enumerated control, there is only one possible value.
+ */
+static int sst_slot_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct sst_enum *e = (void *)kcontrol->private_value;
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct sst_data *drv = snd_soc_component_get_drvdata(c);
+ unsigned int ctl_no = e->reg;
+ unsigned int is_tx = e->tx;
+ unsigned int val, mux;
+ u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;
+
+ mutex_lock(&drv->lock);
+ 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;
+ mutex_unlock(&drv->lock);
+
+ dev_dbg(c->dev, "%s - %s map = %#x\n",
+ is_tx ? "tx channel" : "rx slot",
+ e->texts[mux], mux ? map[mux - 1] : -1);
+ return 0;
+}
+
+/* sst_check_and_send_slot_map - helper for checking power state and sending
+ * slot map cmd
+ *
+ * called with lock held
+ */
+static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol *kcontrol)
+{
+ struct sst_enum *e = (void *)kcontrol->private_value;
+ int ret = 0;
+
+ if (e->w && e->w->power)
+ ret = sst_send_slot_map(drv);
+ else if (!e->w)
+ dev_err(&drv->pdev->dev, "Slot control: %s doesn't have DAPM widget!!!\n",
+ kcontrol->id.name);
+ return ret;
+}
+
+/**
+ * sst_slot_put - set the status of interleaver/deinterleaver control
+ * @kcontrol: control pointer
+ * @ucontrol: User data
+ * (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_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct sst_data *drv = snd_soc_component_get_drvdata(c);
+ struct sst_enum *e = (void *)kcontrol->private_value;
+ int i, ret = 0;
+ unsigned int ctl_no = e->reg;
+ unsigned int is_tx = e->tx;
+ unsigned int slot_channel_no;
+ unsigned int val, mux;
+ u8 *map;
+
+ map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;
+
+ val = 1 << ctl_no;
+ mux = ucontrol->value.enumerated.item[0];
+ if (mux > e->max - 1)
+ return -EINVAL;
+
+ mutex_lock(&drv->lock);
+ /* first clear all registers of this bit */
+ for (i = 0; i < e->max; i++)
+ map[i] &= ~val;
+
+ if (mux == 0) {
+ /* kctl set to 'none' and we reset the bits so send IPC */
+ ret = sst_check_and_send_slot_map(drv, kcontrol);
+
+ mutex_unlock(&drv->lock);
+ return ret;
+ }
+
+ /* offset by one to take "None" into account */
+ slot_channel_no = mux - 1;
+ map[slot_channel_no] |= val;
+
+ dev_dbg(c->dev, "%s %s map = %#x\n",
+ is_tx ? "tx channel" : "rx slot",
+ e->texts[mux], map[slot_channel_no]);
+
+ ret = sst_check_and_send_slot_map(drv, kcontrol);
+
+ mutex_unlock(&drv->lock);
+ return ret;
+}
+
+static int sst_send_algo_cmd(struct sst_data *drv,
+ struct sst_algo_control *bc)
+{
+ int len, ret = 0;
+ struct sst_cmd_set_params *cmd;
+
+ /*bc->max includes sizeof algos + length field*/
+ len = sizeof(cmd->dst) + sizeof(cmd->command_id) + bc->max;
+
+ cmd = kzalloc(len, GFP_KERNEL);
+ if (cmd == NULL)
+ return -ENOMEM;
+
+ SST_FILL_DESTINATION(2, cmd->dst, bc->pipe_id, bc->module_id);
+ cmd->command_id = bc->cmd_id;
+ memcpy(cmd->params, bc->params, bc->max);
+
+ ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
+ SST_FLAG_BLOCKED, bc->task_id, 0, cmd, len);
+ kfree(cmd);
+ return ret;
+}
+
+/**
+ * sst_find_and_send_pipe_algo - send all the algo parameters for a pipe
+ * @drv: sst_data
+ * @pipe: string identifier
+ * @ids: list of algorithms
+ * The algos which are in each pipeline are sent to the firmware one by one
+ *
+ * Called with lock held
+ */
+static int sst_find_and_send_pipe_algo(struct sst_data *drv,
+ const char *pipe, struct sst_ids *ids)
+{
+ int ret = 0;
+ struct sst_algo_control *bc;
+ struct sst_module *algo;
+
+ dev_dbg(&drv->pdev->dev, "Enter: widget=%s\n", pipe);
+
+ list_for_each_entry(algo, &ids->algo_list, node) {
+ bc = (void *)algo->kctl->private_value;
+
+ dev_dbg(&drv->pdev->dev, "Found algo control name=%s pipe=%s\n",
+ algo->kctl->id.name, pipe);
+ ret = sst_send_algo_cmd(drv, bc);
+ if (ret)
+ return ret;
+ }
+ return ret;
+}
+
+static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct sst_algo_control *bc = (void *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = bc->max;
+
+ return 0;
+}
+
+static int sst_algo_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct sst_algo_control *bc = (void *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+
+ switch (bc->type) {
+ case SST_ALGO_PARAMS:
+ memcpy(ucontrol->value.bytes.data, bc->params, bc->max);
+ break;
+ default:
+ dev_err(component->dev, "Invalid Input- algo type:%d\n",
+ bc->type);
+ return -EINVAL;
+
+ }
+ return 0;
+}
+
+static int sst_algo_control_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
+ struct sst_algo_control *bc = (void *)kcontrol->private_value;
+
+ dev_dbg(cmpnt->dev, "control_name=%s\n", kcontrol->id.name);
+ mutex_lock(&drv->lock);
+ switch (bc->type) {
+ case SST_ALGO_PARAMS:
+ memcpy(bc->params, ucontrol->value.bytes.data, bc->max);
+ break;
+ default:
+ mutex_unlock(&drv->lock);
+ dev_err(cmpnt->dev, "Invalid Input- algo type:%d\n",
+ bc->type);
+ return -EINVAL;
+ }
+ /*if pipe is enabled, need to send the algo params from here*/
+ if (bc->w && bc->w->power)
+ ret = sst_send_algo_cmd(drv, bc);
+ mutex_unlock(&drv->lock);
+
+ return ret;
+}
+
+static 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
+ * @drv: sst_data
+ * @gv:the stored value of gain (also contains rampduration)
+ * @task_id: task index
+ * @loc_id: location/position index
+ * @module_id: module index
+ * @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.
+ *
+ * Called with sst_data.lock held
+ *
+ * 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 int sst_send_gain_cmd(struct sst_data *drv, struct sst_gain_value *gv,
+ u16 task_id, u16 loc_id, u16 module_id, int mute)
+{
+ struct sst_cmd_set_gain_dual cmd;
+
+ dev_dbg(&drv->pdev->dev, "Enter\n");
+
+ cmd.header.command_id = MMX_SET_GAIN;
+ SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+ cmd.gain_cell_num = 1;
+
+ if (mute || gv->mute) {
+ cmd.cell_gains[0].cell_gain_left = SST_GAIN_MIN_VALUE;
+ cmd.cell_gains[0].cell_gain_right = SST_GAIN_MIN_VALUE;
+ } else {
+ cmd.cell_gains[0].cell_gain_left = gv->l_gain;
+ cmd.cell_gains[0].cell_gain_right = gv->r_gain;
+ }
+
+ SST_FILL_DESTINATION(2, cmd.cell_gains[0].dest,
+ loc_id, module_id);
+ cmd.cell_gains[0].gain_time_constant = gv->ramp_duration;
+
+ cmd.header.length = sizeof(struct sst_cmd_set_gain_dual)
+ - sizeof(struct sst_dsp_header);
+
+ /* we are with lock held, so call the unlocked api to send */
+ return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
+ SST_FLAG_BLOCKED, task_id, 0, &cmd,
+ sizeof(cmd.header) + cmd.header.length);
+}
+
+static int sst_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+ struct sst_gain_value *gv = mc->gain_val;
+
+ switch (mc->type) {
+ case SST_GAIN_TLV:
+ ucontrol->value.integer.value[0] = gv->l_gain;
+ ucontrol->value.integer.value[1] = gv->r_gain;
+ break;
+
+ case SST_GAIN_MUTE:
+ ucontrol->value.integer.value[0] = gv->mute ? 0 : 1;
+ break;
+
+ case SST_GAIN_RAMP_DURATION:
+ ucontrol->value.integer.value[0] = gv->ramp_duration;
+ break;
+
+ default:
+ dev_err(component->dev, "Invalid Input- gain type:%d\n",
+ mc->type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sst_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
+ struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+ struct sst_gain_value *gv = mc->gain_val;
+
+ mutex_lock(&drv->lock);
+
+ switch (mc->type) {
+ case SST_GAIN_TLV:
+ gv->l_gain = ucontrol->value.integer.value[0];
+ gv->r_gain = ucontrol->value.integer.value[1];
+ dev_dbg(cmpnt->dev, "%s: Volume %d, %d\n",
+ mc->pname, gv->l_gain, gv->r_gain);
+ break;
+
+ case SST_GAIN_MUTE:
+ gv->mute = !ucontrol->value.integer.value[0];
+ dev_dbg(cmpnt->dev, "%s: Mute %d\n", mc->pname, gv->mute);
+ break;
+
+ case SST_GAIN_RAMP_DURATION:
+ gv->ramp_duration = ucontrol->value.integer.value[0];
+ dev_dbg(cmpnt->dev, "%s: Ramp Delay%d\n",
+ mc->pname, gv->ramp_duration);
+ break;
+
+ default:
+ mutex_unlock(&drv->lock);
+ dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n",
+ mc->type);
+ return -EINVAL;
+ }
+
+ if (mc->w && mc->w->power)
+ ret = sst_send_gain_cmd(drv, gv, mc->task_id,
+ mc->pipe_id | mc->instance_id, mc->module_id, 0);
+ mutex_unlock(&drv->lock);
+
+ return ret;
+}
+
+static int sst_set_pipe_gain(struct sst_ids *ids,
+ struct sst_data *drv, int mute);
+
+static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol)
+{
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct sst_data *drv = snd_soc_component_get_drvdata(c);
+ struct sst_ids *ids = w->priv;
+
+ mutex_lock(&drv->lock);
+ sst_find_and_send_pipe_algo(drv, w->name, ids);
+ sst_set_pipe_gain(ids, drv, 0);
+ mutex_unlock(&drv->lock);
+
+ return 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))
+ return sst_send_pipe_module_params(w, k);
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0);
+
+/* Look up table to convert MIXER SW bit regs to SWM inputs */
+static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = {
+ [SST_IP_MODEM] = SST_SWM_IN_MODEM,
+ [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,
+};
+
+/**
+ * fill_swm_input - fill in the SWM input ids given the register
+ * @cmpnt: ASoC component
+ * @swm_input: array of swm_input_ids
+ * @reg: the register value is a bit-field inicated which mixer inputs are ON.
+ *
+ * Use the lookup table to get the input-id and fill it in the
+ * structure.
+ */
+static int fill_swm_input(struct snd_soc_component *cmpnt,
+ struct swm_input_ids *swm_input, unsigned int reg)
+{
+ uint i, is_set, nb_inputs = 0;
+ u16 input_loc_id;
+
+ dev_dbg(cmpnt->dev, "reg: %#x\n", reg);
+ for (i = 0; i < SST_SWM_INPUT_COUNT; i++) {
+ is_set = reg & BIT(i);
+ if (!is_set)
+ continue;
+
+ input_loc_id = swm_mixer_input_ids[i];
+ SST_FILL_DESTINATION(2, swm_input->input_id,
+ input_loc_id, SST_DEFAULT_MODULE_ID);
+ nb_inputs++;
+ swm_input++;
+ dev_dbg(cmpnt->dev, "input id: %#x, nb_inputs: %d\n",
+ input_loc_id, nb_inputs);
+
+ if (nb_inputs == SST_CMD_SWM_MAX_INPUTS) {
+ dev_warn(cmpnt->dev, "SET_SWM cmd max inputs reached");
+ break;
+ }
+ }
+ return nb_inputs;
+}
+
+
+/*
+ * called with lock held
+ */
+static int sst_set_pipe_gain(struct sst_ids *ids,
+ struct sst_data *drv, int mute)
+{
+ int ret = 0;
+ struct sst_gain_mixer_control *mc;
+ struct sst_gain_value *gv;
+ struct sst_module *gain;
+
+ list_for_each_entry(gain, &ids->gain_list, node) {
+ struct snd_kcontrol *kctl = gain->kctl;
+
+ dev_dbg(&drv->pdev->dev, "control name=%s\n", kctl->id.name);
+ mc = (void *)kctl->private_value;
+ gv = mc->gain_val;
+
+ ret = sst_send_gain_cmd(drv, gv, mc->task_id,
+ mc->pipe_id | mc->instance_id, mc->module_id, mute);
+ if (ret)
+ return ret;
+ }
+ return ret;
+}
+
+static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct sst_cmd_set_swm cmd;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
+ struct sst_ids *ids = w->priv;
+ bool set_mixer = false;
+ struct soc_mixer_control *mc;
+ int val = 0;
+ int i = 0;
+
+ dev_dbg(cmpnt->dev, "widget = %s\n", w->name);
+ /*
+ * Identify which mixer input is on and send the bitmap of the
+ * inputs as an IPC to the DSP.
+ */
+ for (i = 0; i < w->num_kcontrols; i++) {
+ if (dapm_kcontrol_get_value(w->kcontrols[i])) {
+ mc = (struct soc_mixer_control *)(w->kcontrols[i])->private_value;
+ val |= 1 << mc->shift;
+ }
+ }
+ dev_dbg(cmpnt->dev, "val = %#x\n", val);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ set_mixer = true;
+ break;
+ case SND_SOC_DAPM_POST_REG:
+ if (w->power)
+ set_mixer = true;
+ break;
+ default:
+ set_mixer = false;
+ }
+
+ if (!set_mixer)
+ 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(cmpnt, &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]));
+
+ return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+ ids->task_id, 0, &cmd,
+ sizeof(cmd.header) + cmd.header.length);
+}
+
+/* SBA mixers - 16 inputs */
+#define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name) \
+ static const struct snd_kcontrol_new kctl_name[] = { \
+ SOC_DAPM_SINGLE("modem_in Switch", SND_SOC_NOPM, SST_IP_MODEM, 1, 0), \
+ SOC_DAPM_SINGLE("codec_in0 Switch", SND_SOC_NOPM, SST_IP_CODEC0, 1, 0), \
+ SOC_DAPM_SINGLE("codec_in1 Switch", SND_SOC_NOPM, SST_IP_CODEC1, 1, 0), \
+ SOC_DAPM_SINGLE("sprot_loop_in Switch", SND_SOC_NOPM, SST_IP_LOOP0, 1, 0), \
+ SOC_DAPM_SINGLE("media_loop1_in Switch", SND_SOC_NOPM, SST_IP_LOOP1, 1, 0), \
+ SOC_DAPM_SINGLE("media_loop2_in Switch", SND_SOC_NOPM, SST_IP_LOOP2, 1, 0), \
+ SOC_DAPM_SINGLE("pcm0_in Switch", SND_SOC_NOPM, SST_IP_PCM0, 1, 0), \
+ SOC_DAPM_SINGLE("pcm1_in Switch", SND_SOC_NOPM, SST_IP_PCM1, 1, 0), \
+ }
+
+#define SST_SBA_MIXER_GRAPH_MAP(mix_name) \
+ { mix_name, "modem_in Switch", "modem_in" }, \
+ { mix_name, "codec_in0 Switch", "codec_in0" }, \
+ { mix_name, "codec_in1 Switch", "codec_in1" }, \
+ { mix_name, "sprot_loop_in Switch", "sprot_loop_in" }, \
+ { mix_name, "media_loop1_in Switch", "media_loop1_in" }, \
+ { mix_name, "media_loop2_in Switch", "media_loop2_in" }, \
+ { mix_name, "pcm0_in Switch", "pcm0_in" }, \
+ { mix_name, "pcm1_in Switch", "pcm1_in" }
+
+#define SST_MMX_DECLARE_MIX_CONTROLS(kctl_name) \
+ static const struct snd_kcontrol_new kctl_name[] = { \
+ SOC_DAPM_SINGLE("media0_in Switch", SND_SOC_NOPM, SST_IP_MEDIA0, 1, 0), \
+ SOC_DAPM_SINGLE("media1_in Switch", SND_SOC_NOPM, SST_IP_MEDIA1, 1, 0), \
+ SOC_DAPM_SINGLE("media2_in Switch", SND_SOC_NOPM, SST_IP_MEDIA2, 1, 0), \
+ SOC_DAPM_SINGLE("media3_in Switch", SND_SOC_NOPM, SST_IP_MEDIA3, 1, 0), \
+ }
+
+SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media0_controls);
+SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media1_controls);
+
+/* 18 SBA mixers */
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm1_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm2_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_sprot_l0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l1_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(__maybe_unused sst_mix_voip_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_modem_controls);
+
+/*
+ * sst_handle_vb_timer - Start/Stop the DSP scheduler
+ *
+ * The DSP expects first cmd to be SBA_VB_START, so at first startup send
+ * that.
+ * DSP expects last cmd to be SBA_VB_IDLE, so at last shutdown send that.
+ *
+ * Do refcount internally so that we send command only at first start
+ * and last end. Since SST driver does its own ref count, invoke sst's
+ * power ops always!
+ */
+int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable)
+{
+ int ret = 0;
+ struct sst_cmd_generic cmd;
+ struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+ static int timer_usage;
+
+ if (enable)
+ cmd.header.command_id = SBA_VB_START;
+ else
+ cmd.header.command_id = SBA_IDLE;
+ dev_dbg(dai->dev, "enable=%u, usage=%d\n", enable, timer_usage);
+
+ SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+ cmd.header.length = 0;
+
+ if (enable) {
+ ret = sst->ops->power(sst->dev, true);
+ if (ret < 0)
+ return ret;
+ }
+
+ 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))) {
+ ret = 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);
+ if (ret && enable) {
+ timer_usage--;
+ enable = false;
+ }
+ }
+ mutex_unlock(&drv->lock);
+
+ if (!enable)
+ sst->ops->power(sst->dev, false);
+ return ret;
+}
+
+int sst_fill_ssp_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);
+
+ ctx->ssp_cmd.nb_slots = slots;
+ ctx->ssp_cmd.active_tx_slot_map = tx_mask;
+ ctx->ssp_cmd.active_rx_slot_map = rx_mask;
+ ctx->ssp_cmd.nb_bits_per_slots = slot_width;
+
+ return 0;
+}
+
+static int sst_get_frame_sync_polarity(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ int format;
+
+ format = fmt & SND_SOC_DAIFMT_INV_MASK;
+ dev_dbg(dai->dev, "Enter:%s, format=%x\n", __func__, format);
+
+ switch (format) {
+ case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_IB_NF:
+ return SSP_FS_ACTIVE_HIGH;
+ case SND_SOC_DAIFMT_NB_IF:
+ case SND_SOC_DAIFMT_IB_IF:
+ return SSP_FS_ACTIVE_LOW;
+ default:
+ dev_err(dai->dev, "Invalid frame sync polarity %d\n", format);
+ }
+
+ return -EINVAL;
+}
+
+static int sst_get_ssp_mode(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ int format;
+
+ format = (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK);
+ dev_dbg(dai->dev, "Enter:%s, format=%x\n", __func__, format);
+
+ switch (format) {
+ case SND_SOC_DAIFMT_BP_FP:
+ return SSP_MODE_PROVIDER;
+ case SND_SOC_DAIFMT_BC_FC:
+ return SSP_MODE_CONSUMER;
+ default:
+ dev_err(dai->dev, "Invalid ssp protocol: %d\n", format);
+ }
+
+ return -EINVAL;
+}
+
+
+int sst_fill_ssp_config(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ unsigned int mode;
+ int fs_polarity;
+ struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);
+
+ mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ switch (mode) {
+ case SND_SOC_DAIFMT_DSP_B:
+ ctx->ssp_cmd.ssp_protocol = SSP_MODE_PCM;
+ ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NETWORK << 1);
+ ctx->ssp_cmd.start_delay = 0;
+ ctx->ssp_cmd.data_polarity = 1;
+ ctx->ssp_cmd.frame_sync_width = 1;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ ctx->ssp_cmd.ssp_protocol = SSP_MODE_PCM;
+ ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NETWORK << 1);
+ ctx->ssp_cmd.start_delay = 1;
+ ctx->ssp_cmd.data_polarity = 1;
+ ctx->ssp_cmd.frame_sync_width = 1;
+ break;
+
+ case SND_SOC_DAIFMT_I2S:
+ ctx->ssp_cmd.ssp_protocol = SSP_MODE_I2S;
+ ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NORMAL << 1);
+ ctx->ssp_cmd.start_delay = 1;
+ ctx->ssp_cmd.data_polarity = 0;
+ ctx->ssp_cmd.frame_sync_width = ctx->ssp_cmd.nb_bits_per_slots;
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctx->ssp_cmd.ssp_protocol = SSP_MODE_I2S;
+ ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NORMAL << 1);
+ ctx->ssp_cmd.start_delay = 0;
+ ctx->ssp_cmd.data_polarity = 0;
+ ctx->ssp_cmd.frame_sync_width = ctx->ssp_cmd.nb_bits_per_slots;
+ break;
+
+ default:
+ dev_dbg(dai->dev, "using default ssp configs\n");
+ }
+
+ fs_polarity = sst_get_frame_sync_polarity(dai, fmt);
+ if (fs_polarity < 0)
+ return fs_polarity;
+
+ ctx->ssp_cmd.frame_sync_polarity = fs_polarity;
+
+ return 0;
+}
+
+/*
+ * sst_ssp_config - contains SSP configuration for media UC
+ * this can be overwritten by set_dai_xxx APIs
+ */
+static const struct sst_ssp_config sst_ssp_configs = {
+ .ssp_id = SSP_CODEC,
+ .bits_per_slot = 24,
+ .slots = 4,
+ .ssp_mode = SSP_MODE_PROVIDER,
+ .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,
+ .frame_sync_polarity = SSP_FS_ACTIVE_HIGH,
+ .data_polarity = 1,
+};
+
+void sst_fill_ssp_defaults(struct snd_soc_dai *dai)
+{
+ const struct sst_ssp_config *config;
+ struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);
+
+ config = &sst_ssp_configs;
+
+ ctx->ssp_cmd.selection = config->ssp_id;
+ ctx->ssp_cmd.nb_bits_per_slots = config->bits_per_slot;
+ ctx->ssp_cmd.nb_slots = config->slots;
+ ctx->ssp_cmd.mode = config->ssp_mode | (config->pcm_mode << 1);
+ ctx->ssp_cmd.duplex = config->duplex;
+ ctx->ssp_cmd.active_tx_slot_map = config->active_slot_map;
+ ctx->ssp_cmd.active_rx_slot_map = config->active_slot_map;
+ ctx->ssp_cmd.frame_sync_frequency = config->fs_frequency;
+ ctx->ssp_cmd.frame_sync_polarity = config->frame_sync_polarity;
+ ctx->ssp_cmd.data_polarity = config->data_polarity;
+ ctx->ssp_cmd.frame_sync_width = config->fs_width;
+ ctx->ssp_cmd.ssp_protocol = config->ssp_protocol;
+ ctx->ssp_cmd.start_delay = config->start_delay;
+ ctx->ssp_cmd.reserved1 = ctx->ssp_cmd.reserved2 = 0xFF;
+}
+
+int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable)
+{
+ struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+ int ssp_id;
+
+ dev_dbg(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id);
+
+ if (strcmp(id, "ssp0-port") == 0)
+ ssp_id = SSP_MODEM;
+ else if (strcmp(id, "ssp2-port") == 0)
+ ssp_id = SSP_CODEC;
+ else {
+ dev_dbg(dai->dev, "port %s is not supported\n", id);
+ return -1;
+ }
+
+ SST_FILL_DEFAULT_DESTINATION(drv->ssp_cmd.header.dst);
+ drv->ssp_cmd.header.command_id = SBA_HW_SET_SSP;
+ drv->ssp_cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp)
+ - sizeof(struct sst_dsp_header);
+
+ drv->ssp_cmd.selection = ssp_id;
+ dev_dbg(dai->dev, "ssp_id: %u\n", ssp_id);
+
+ if (enable)
+ drv->ssp_cmd.switch_state = SST_SWITCH_ON;
+ else
+ drv->ssp_cmd.switch_state = SST_SWITCH_OFF;
+
+ return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+ SST_TASK_SBA, 0, &drv->ssp_cmd,
+ sizeof(drv->ssp_cmd.header) + drv->ssp_cmd.header.length);
+}
+
+static int sst_set_be_modules(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ int ret = 0;
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct sst_data *drv = snd_soc_component_get_drvdata(c);
+
+ dev_dbg(c->dev, "Enter: widget=%s\n", w->name);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ mutex_lock(&drv->lock);
+ ret = sst_send_slot_map(drv);
+ mutex_unlock(&drv->lock);
+ if (ret)
+ return ret;
+ ret = sst_send_pipe_module_params(w, k);
+ }
+ return ret;
+}
+
+static int sst_set_media_path(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ int ret = 0;
+ struct sst_cmd_set_media_path cmd;
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct sst_data *drv = snd_soc_component_get_drvdata(c);
+ struct sst_ids *ids = w->priv;
+
+ dev_dbg(c->dev, "widget=%s\n", w->name);
+ dev_dbg(c->dev, "task=%u, location=%#x\n",
+ ids->task_id, ids->location_id);
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ cmd.switch_state = SST_PATH_ON;
+ else
+ cmd.switch_state = SST_PATH_OFF;
+
+ SST_FILL_DESTINATION(2, cmd.header.dst,
+ ids->location_id, SST_DEFAULT_MODULE_ID);
+
+ /* MMX_SET_MEDIA_PATH == SBA_SET_MEDIA_PATH */
+ cmd.header.command_id = MMX_SET_MEDIA_PATH;
+ cmd.header.length = sizeof(struct sst_cmd_set_media_path)
+ - sizeof(struct sst_dsp_header);
+
+ ret = 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 (ret)
+ return ret;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ ret = sst_send_pipe_module_params(w, k);
+ return ret;
+}
+
+static int sst_set_media_loop(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ int ret = 0;
+ struct sst_cmd_sba_set_media_loop_map cmd;
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct sst_data *drv = snd_soc_component_get_drvdata(c);
+ struct sst_ids *ids = w->priv;
+
+ dev_dbg(c->dev, "Enter:widget=%s\n", w->name);
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ cmd.switch_state = SST_SWITCH_ON;
+ else
+ cmd.switch_state = SST_SWITCH_OFF;
+
+ SST_FILL_DESTINATION(2, cmd.header.dst,
+ ids->location_id, SST_DEFAULT_MODULE_ID);
+
+ cmd.header.command_id = SBA_SET_MEDIA_LOOP_MAP;
+ cmd.header.length = sizeof(struct sst_cmd_sba_set_media_loop_map)
+ - sizeof(struct sst_dsp_header);
+ cmd.param.part.cfg.rate = 2; /* 48khz */
+
+ cmd.param.part.cfg.format = ids->format; /* stereo/Mono */
+ cmd.param.part.cfg.s_length = 1; /* 24bit left justified */
+ cmd.map = 0; /* Algo sequence: Gain - DRP - FIR - IIR */
+
+ ret = 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 (ret)
+ return ret;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ ret = sst_send_pipe_module_params(w, k);
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget sst_dapm_widgets[] = {
+ SST_AIF_IN("modem_in", sst_set_be_modules),
+ SST_AIF_IN("codec_in0", sst_set_be_modules),
+ SST_AIF_IN("codec_in1", sst_set_be_modules),
+ SST_AIF_OUT("modem_out", 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_STEREO, sst_set_media_loop),
+ SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_STEREO, 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 */
+ SST_SWM_MIXER("media0_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA0,
+ sst_mix_media0_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("media1_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA1,
+ sst_mix_media1_controls, sst_swm_mixer_event),
+
+ /* SBA PCM mixers */
+ SST_SWM_MIXER("pcm0_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM0,
+ sst_mix_pcm0_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("pcm1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM1,
+ sst_mix_pcm1_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("pcm2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM2,
+ sst_mix_pcm2_controls, sst_swm_mixer_event),
+
+ /* SBA Loop mixers */
+ SST_SWM_MIXER("sprot_loop_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP,
+ sst_mix_sprot_l0_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("media_loop1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1,
+ sst_mix_media_l1_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("media_loop2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2,
+ sst_mix_media_l2_controls, sst_swm_mixer_event),
+
+ /* SBA Backend mixers */
+ SST_SWM_MIXER("codec_out0 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC0,
+ sst_mix_codec0_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("codec_out1 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC1,
+ sst_mix_codec1_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("modem_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MODEM,
+ sst_mix_modem_controls, sst_swm_mixer_event),
+
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"media0_in", NULL, "Compress Playback"},
+ {"media1_in", NULL, "Headset Playback"},
+ {"media2_in", NULL, "pcm0_out"},
+ {"media3_in", NULL, "Deepbuffer Playback"},
+
+ {"media0_out mix 0", "media0_in Switch", "media0_in"},
+ {"media0_out mix 0", "media1_in Switch", "media1_in"},
+ {"media0_out mix 0", "media2_in Switch", "media2_in"},
+ {"media0_out mix 0", "media3_in Switch", "media3_in"},
+ {"media1_out mix 0", "media0_in Switch", "media0_in"},
+ {"media1_out mix 0", "media1_in Switch", "media1_in"},
+ {"media1_out mix 0", "media2_in Switch", "media2_in"},
+ {"media1_out mix 0", "media3_in Switch", "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"),
+ {"modem_out", NULL, "modem_out mix 0"},
+ SST_SBA_MIXER_GRAPH_MAP("modem_out mix 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, "tx 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, "rx 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, \
+ 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)
+
+static struct sst_gain_value sst_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[6]),
+ SST_GAIN("pcm2_out", SST_PATH_INDEX_PCM2_OUT, SST_TASK_SBA, 0, &sst_gains[7]),
+
+ SST_GAIN("codec_in0", SST_PATH_INDEX_CODEC_IN0, SST_TASK_SBA, 0, &sst_gains[8]),
+ SST_GAIN("codec_in1", SST_PATH_INDEX_CODEC_IN1, SST_TASK_SBA, 0, &sst_gains[9]),
+ SST_GAIN("codec_out0", SST_PATH_INDEX_CODEC_OUT0, SST_TASK_SBA, 0, &sst_gains[10]),
+ SST_GAIN("codec_out1", SST_PATH_INDEX_CODEC_OUT1, SST_TASK_SBA, 0, &sst_gains[11]),
+ SST_GAIN("media_loop1_out", SST_PATH_INDEX_MEDIA_LOOP1_OUT, SST_TASK_SBA, 0, &sst_gains[12]),
+ SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[13]),
+ SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[14]),
+ SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[15]),
+ SST_GAIN("modem_in", SST_PATH_INDEX_MODEM_IN, SST_TASK_SBA, 0, &sst_gains[16]),
+ SST_GAIN("modem_out", SST_PATH_INDEX_MODEM_OUT, SST_TASK_SBA, 0, &sst_gains[17]),
+
+};
+
+#define SST_GAIN_NUM_CONTROLS 3
+/* the SST_GAIN macro above will create three alsa controls for each
+ * instance invoked, gain, mute and ramp duration, which use the same gain
+ * cell sst_gain to keep track of data
+ * To calculate number of gain cell instances we need to device by 3 in
+ * below caulcation for gain cell memory.
+ * This gets rid of static number and issues while adding new controls
+ */
+static struct sst_gain_value sst_gains[ARRAY_SIZE(sst_gain_controls)/SST_GAIN_NUM_CONTROLS];
+
+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),
+
+};
+
+static int sst_algo_control_init(struct device *dev)
+{
+ int i = 0;
+ struct sst_algo_control *bc;
+ /*allocate space to cache the algo parameters in the driver*/
+ for (i = 0; i < ARRAY_SIZE(sst_algo_controls); i++) {
+ bc = (struct sst_algo_control *)sst_algo_controls[i].private_value;
+ bc->params = devm_kzalloc(dev, bc->max, GFP_KERNEL);
+ if (bc->params == NULL)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w)
+{
+ switch (w->id) {
+ case snd_soc_dapm_pga:
+ case snd_soc_dapm_aif_in:
+ case snd_soc_dapm_aif_out:
+ case snd_soc_dapm_input:
+ case snd_soc_dapm_output:
+ case snd_soc_dapm_mixer:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * sst_send_pipe_gains - send gains for the front-end DAIs
+ * @dai: front-end dai
+ * @stream: direction
+ * @mute: boolean indicating mute status
+ *
+ * The gains in the pipes connected to the front-ends are muted/unmuted
+ * automatically via the digital_mute() DAPM callback. This function sends the
+ * gains for the front-end pipes.
+ */
+int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
+{
+ struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_path *p;
+
+ dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream);
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dev_dbg(dai->dev, "Stream name=%s\n",
+ dai->playback_widget->name);
+ w = dai->playback_widget;
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if (p->connected && !p->connected(w, p->sink))
+ continue;
+
+ if (p->connect && p->sink->power &&
+ is_sst_dapm_widget(p->sink)) {
+ struct sst_ids *ids = p->sink->priv;
+
+ dev_dbg(dai->dev, "send gains for widget=%s\n",
+ p->sink->name);
+ mutex_lock(&drv->lock);
+ sst_set_pipe_gain(ids, drv, mute);
+ mutex_unlock(&drv->lock);
+ }
+ }
+ } else {
+ dev_dbg(dai->dev, "Stream name=%s\n",
+ dai->capture_widget->name);
+ w = dai->capture_widget;
+ snd_soc_dapm_widget_for_each_source_path(w, p) {
+ if (p->connected && !p->connected(w, p->source))
+ continue;
+
+ if (p->connect && p->source->power &&
+ is_sst_dapm_widget(p->source)) {
+ struct sst_ids *ids = p->source->priv;
+
+ dev_dbg(dai->dev, "send gain for widget=%s\n",
+ p->source->name);
+ mutex_lock(&drv->lock);
+ sst_set_pipe_gain(ids, drv, mute);
+ mutex_unlock(&drv->lock);
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * sst_fill_module_list - populate the list of modules/gains for a pipe
+ * @kctl: kcontrol pointer
+ * @w: dapm widget
+ * @type: widget type
+ *
+ * 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;
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct sst_ids *ids = w->priv;
+ int ret = 0;
+
+ module = devm_kzalloc(c->dev, sizeof(*module), GFP_KERNEL);
+ if (!module)
+ return -ENOMEM;
+
+ if (type == SST_MODULE_GAIN) {
+ struct sst_gain_mixer_control *mc = (void *)kctl->private_value;
+
+ mc->w = w;
+ module->kctl = kctl;
+ list_add_tail(&module->node, &ids->gain_list);
+ } else if (type == SST_MODULE_ALGO) {
+ struct sst_algo_control *bc = (void *)kctl->private_value;
+
+ bc->w = w;
+ module->kctl = kctl;
+ list_add_tail(&module->node, &ids->algo_list);
+ } else {
+ dev_err(c->dev, "invoked for unknown type %d module %s",
+ type, kctl->id.name);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/**
+ * sst_fill_widget_module_info - fill list of gains/algos for the pipe
+ * @w: pipe modeled as a DAPM widget
+ * @component: ASoC component
+ *
+ * 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_component *component)
+{
+ struct snd_kcontrol *kctl;
+ int index, ret = 0;
+ struct snd_card *card = component->card->snd_card;
+ char *idx;
+
+ down_read(&card->controls_rwsem);
+
+ list_for_each_entry(kctl, &card->controls, list) {
+ idx = strchr(kctl->id.name, ' ');
+ if (idx == NULL)
+ continue;
+ index = idx - (char*)kctl->id.name;
+ if (strncmp(kctl->id.name, w->name, index))
+ continue;
+
+ if (strstr(kctl->id.name, "Volume"))
+ ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN);
+
+ else if (strstr(kctl->id.name, "params"))
+ ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO);
+
+ else if (strstr(kctl->id.name, "Switch") &&
+ strstr(kctl->id.name, "Gain")) {
+ struct sst_gain_mixer_control *mc =
+ (void *)kctl->private_value;
+
+ mc->w = w;
+
+ } else if (strstr(kctl->id.name, "interleaver")) {
+ struct sst_enum *e = (void *)kctl->private_value;
+
+ e->w = w;
+
+ } else if (strstr(kctl->id.name, "deinterleaver")) {
+ 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
+ * @component: ASoC component
+ * @ids: sst_ids array
+ */
+static void sst_fill_linked_widgets(struct snd_soc_component *component,
+ struct sst_ids *ids)
+{
+ struct snd_soc_dapm_widget *w;
+ unsigned int len = strlen(ids->parent_wname);
+
+ list_for_each_entry(w, &component->card->widgets, list) {
+ if (!strncmp(ids->parent_wname, w->name, len)) {
+ ids->parent_w = w;
+ break;
+ }
+ }
+}
+
+/**
+ * sst_map_modules_to_pipe - fill algo/gains list for all pipes
+ * @component: ASoC component
+ */
+static int sst_map_modules_to_pipe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_widget *w;
+ int ret = 0;
+
+ list_for_each_entry(w, &component->card->widgets, list) {
+ if (is_sst_dapm_widget(w) && (w->priv)) {
+ struct sst_ids *ids = w->priv;
+
+ dev_dbg(component->dev, "widget type=%d name=%s\n",
+ w->id, w->name);
+ INIT_LIST_HEAD(&ids->algo_list);
+ INIT_LIST_HEAD(&ids->gain_list);
+ ret = sst_fill_widget_module_info(w, component);
+
+ if (ret < 0)
+ return ret;
+
+ /* fill linked widgets */
+ if (ids->parent_wname != NULL)
+ sst_fill_linked_widgets(component, ids);
+ }
+ }
+ return 0;
+}
+
+int sst_dsp_init_v2_dpcm(struct snd_soc_component *component)
+{
+ int i, ret = 0;
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ struct sst_data *drv = snd_soc_component_get_drvdata(component);
+ unsigned int gains = ARRAY_SIZE(sst_gain_controls)/3;
+
+ drv->byte_stream = devm_kzalloc(component->dev,
+ SST_MAX_BIN_BYTES, GFP_KERNEL);
+ if (!drv->byte_stream)
+ return -ENOMEM;
+
+ snd_soc_dapm_new_controls(dapm, sst_dapm_widgets,
+ ARRAY_SIZE(sst_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, intercon,
+ ARRAY_SIZE(intercon));
+ snd_soc_dapm_new_widgets(dapm->card);
+
+ for (i = 0; i < 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;
+ }
+
+ ret = snd_soc_add_component_controls(component, sst_gain_controls,
+ ARRAY_SIZE(sst_gain_controls));
+ if (ret)
+ return ret;
+
+ /* Initialize algo control params */
+ ret = sst_algo_control_init(component->dev);
+ if (ret)
+ return ret;
+ ret = snd_soc_add_component_controls(component, sst_algo_controls,
+ ARRAY_SIZE(sst_algo_controls));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_add_component_controls(component, sst_slot_controls,
+ ARRAY_SIZE(sst_slot_controls));
+ if (ret)
+ return ret;
+
+ ret = sst_map_modules_to_pipe(component);
+
+ return ret;
+}
diff --git a/sound/soc/intel/atom/sst-atom-controls.h b/sound/soc/intel/atom/sst-atom-controls.h
new file mode 100644
index 000000000..23bf37544
--- /dev/null
+++ b/sound/soc/intel/atom/sst-atom-controls.h
@@ -0,0 +1,875 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * sst-atom-controls.h - Intel MID Platform driver header file
+ *
+ * Copyright (C) 2013-14 Intel Corp
+ * Author: Ramesh Babu <ramesh.babu.koul@intel.com>
+ * Omair M Abdullah <omair.m.abdullah@intel.com>
+ * Samreen Nilofer <samreen.nilofer@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#ifndef __SST_ATOM_CONTROLS_H__
+#define __SST_ATOM_CONTROLS_H__
+
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+enum {
+ MERR_DPCM_AUDIO = 0,
+ MERR_DPCM_DEEP_BUFFER,
+ MERR_DPCM_COMPR,
+};
+
+/* define a bit for each mixer input */
+#define SST_MIX_IP(x) (x)
+
+#define SST_IP_MODEM SST_MIX_IP(0)
+#define SST_IP_BT SST_MIX_IP(1)
+#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_MODEM_OUT = (0x00 << SST_PATH_ID_SHIFT),
+ 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_MODEM_IN = (0x80 << SST_PATH_ID_SHIFT),
+ 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_MODEM = (SST_PATH_INDEX_MODEM_IN | SST_DEFAULT_CELL_NBR),
+ 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_MODEM = (SST_PATH_INDEX_MODEM_OUT | SST_DEFAULT_CELL_NBR),
+ 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 = 3,
+};
+
+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,
+};
+
+#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 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;
+ 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[];
+} __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_PROVIDER = 0,
+ SSP_MODE_CONSUMER = 1,
+};
+
+enum sst_ssp_pcm_mode {
+ SSP_PCM_MODE_NORMAL = 0,
+ SSP_PCM_MODE_NETWORK = 1,
+};
+
+enum sst_ssp_duplex {
+ SSP_DUPLEX = 0,
+ SSP_RX = 1,
+ SSP_TX = 2,
+};
+
+enum sst_ssp_fs_frequency {
+ SSP_FS_8_KHZ = 0,
+ SSP_FS_16_KHZ = 1,
+ SSP_FS_44_1_KHZ = 2,
+ SSP_FS_48_KHZ = 3,
+};
+
+enum sst_ssp_fs_polarity {
+ SSP_FS_ACTIVE_LOW = 0,
+ SSP_FS_ACTIVE_HIGH = 1,
+};
+
+enum sst_ssp_protocol {
+ SSP_MODE_PCM = 0,
+ SSP_MODE_I2S = 1,
+};
+
+enum sst_ssp_port_id {
+ SSP_MODEM = 0,
+ SSP_BT = 1,
+ SSP_FM = 2,
+ SSP_CODEC = 3,
+};
+
+struct sst_cmd_sba_hw_set_ssp {
+ struct sst_dsp_header header;
+ u16 selection; /* 0:SSP0(def), 1:SSP1, 2:SSP2 */
+
+ u16 switch_state;
+
+ u16 nb_bits_per_slots:6; /* 0-32 bits, 24 (def) */
+ u16 nb_slots:4; /* 0-8: slots per frame */
+ u16 mode:3; /* 0:Master, 1: Slave */
+ u16 duplex:3;
+
+ u16 active_tx_slot_map:8; /* Bit map, 0:off, 1:on */
+ u16 reserved1:8;
+
+ u16 active_rx_slot_map:8; /* Bit map 0: Off, 1:On */
+ u16 reserved2:8;
+
+ u16 frame_sync_frequency;
+
+ u16 frame_sync_polarity:8;
+ u16 data_polarity:8;
+
+ u16 frame_sync_width; /* 1 to N clocks */
+ u16 ssp_protocol:8;
+ u16 start_delay:8; /* Start delay in terms of clock ticks */
+} __packed;
+
+#define SST_MAX_TDM_SLOTS 8
+
+struct sst_param_sba_ssp_slot_map {
+ struct sst_dsp_header header;
+
+ u16 param_id;
+ u16 param_len;
+ u16 ssp_index;
+
+ u8 rx_slot_map[SST_MAX_TDM_SLOTS];
+ u8 tx_slot_map[SST_MAX_TDM_SLOTS];
+} __packed;
+
+enum {
+ SST_PROBE_EXTRACTOR = 0,
+ SST_PROBE_INJECTOR = 1,
+};
+
+/**** widget defines *****/
+
+#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;
+ u8 frame_sync_polarity;
+ u8 data_polarity;
+};
+
+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;
+ 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;
+};
+
+
+#define SST_AIF_IN(wname, wevent) \
+{ .id = snd_soc_dapm_aif_in, .name = wname, .sname = NULL, \
+ .reg = SND_SOC_NOPM, .shift = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
+ .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \
+}
+
+#define SST_AIF_OUT(wname, wevent) \
+{ .id = snd_soc_dapm_aif_out, .name = wname, .sname = NULL, \
+ .reg = SND_SOC_NOPM, .shift = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
+ .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \
+}
+
+#define SST_INPUT(wname, wevent) \
+{ .id = snd_soc_dapm_input, .name = wname, .sname = NULL, \
+ .reg = SND_SOC_NOPM, .shift = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
+ .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \
+}
+
+#define SST_OUTPUT(wname, wevent) \
+{ .id = snd_soc_dapm_output, .name = wname, .sname = NULL, \
+ .reg = SND_SOC_NOPM, .shift = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
+ .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \
+}
+
+#define SST_DAPM_OUTPUT(wname, wloc_id, wtask_id, wformat, wevent) \
+{ .id = snd_soc_dapm_output, .name = wname, .sname = NULL, \
+ .reg = SND_SOC_NOPM, .shift = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
+ .priv = (void *)&(struct sst_ids) { .location_id = wloc_id, .task_id = wtask_id,\
+ .pcm_fmt = wformat, } \
+}
+
+#define SST_PATH(wname, wtask, wloc_id, wevent, wflags) \
+{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \
+ .kcontrol_news = NULL, .num_kcontrols = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = wflags, \
+ .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, } \
+}
+
+#define SST_LINKED_PATH(wname, wtask, wloc_id, linked_wname, wevent, wflags) \
+{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \
+ .kcontrol_news = NULL, .num_kcontrols = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = wflags, \
+ .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \
+ .parent_wname = linked_wname} \
+}
+
+#define SST_PATH_MEDIA_LOOP(wname, wtask, wloc_id, wformat, wevent, wflags) \
+{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \
+ .kcontrol_news = NULL, .num_kcontrols = 0, \
+ .event = wevent, .event_flags = wflags, \
+ .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \
+ .format = wformat,} \
+}
+
+/* output is triggered before input */
+#define SST_PATH_INPUT(name, task_id, loc_id, event) \
+ SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
+
+#define SST_PATH_LINKED_INPUT(name, task_id, loc_id, linked_wname, event) \
+ SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event, \
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
+
+#define SST_PATH_OUTPUT(name, task_id, loc_id, event) \
+ SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+#define SST_PATH_LINKED_OUTPUT(name, task_id, loc_id, linked_wname, event) \
+ SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event, \
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+#define SST_PATH_MEDIA_LOOP_OUTPUT(name, task_id, loc_id, format, event) \
+ SST_PATH_MEDIA_LOOP(name, task_id, loc_id, format, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+
+#define SST_SWM_MIXER(wname, wreg, wtask, wloc_id, wcontrols, wevent) \
+{ .id = snd_soc_dapm_mixer, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \
+ .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols),\
+ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD | \
+ SND_SOC_DAPM_POST_REG, \
+ .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \
+ .reg = wreg } \
+}
+
+enum sst_gain_kcontrol_type {
+ SST_GAIN_TLV,
+ SST_GAIN_MUTE,
+ 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[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ 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 Ramp Delay
+ * - pcm0_in Gain 0 Switch
+ */
+#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, "Ramp Delay"), \
+ 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, "Switch"), \
+ 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,
+};
+
+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;
+};
+
+/* only 4 slots/channels supported atm */
+#define SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts) \
+ (struct sst_enum){ .reg = s_ch_no, .tx = is_tx, .max = 4+1, .texts = xtexts, }
+
+#define SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name) \
+ xpname " " xmname " " s_ch_name
+
+#define SST_SSP_SLOT_CTL(xpname, xmname, s_ch_name, s_ch_no, is_tx, xtexts, xget, xput) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name), \
+ .info = sst_slot_enum_info, \
+ .get = xget, .put = xput, \
+ .private_value = (unsigned long)&SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts), \
+}
+
+#define SST_MUX_CTL_NAME(xpname, xinstance) \
+ xpname " " #xinstance
+
+#define SST_SSP_MUX_ENUM(xreg, xshift, xtexts) \
+ (struct soc_enum) SOC_ENUM_DOUBLE(xreg, xshift, xshift, ARRAY_SIZE(xtexts), xtexts)
+
+#define SST_SSP_MUX_CTL(xpname, xinstance, xreg, xshift, xtexts) \
+ SOC_DAPM_ENUM(SST_MUX_CTL_NAME(xpname, xinstance), \
+ SST_SSP_MUX_ENUM(xreg, xshift, xtexts))
+
+int sst_fill_ssp_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width);
+int sst_fill_ssp_config(struct snd_soc_dai *dai, unsigned int fmt);
+void sst_fill_ssp_defaults(struct snd_soc_dai *dai);
+
+#endif
diff --git a/sound/soc/intel/atom/sst-mfld-dsp.h b/sound/soc/intel/atom/sst-mfld-dsp.h
new file mode 100644
index 000000000..c8f0816ed
--- /dev/null
+++ b/sound/soc/intel/atom/sst-mfld-dsp.h
@@ -0,0 +1,525 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __SST_MFLD_DSP_H__
+#define __SST_MFLD_DSP_H__
+/*
+ * sst_mfld_dsp.h - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corporation
+ * Authors: Vinod Koul <vinod.koul@linux.intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#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,
+ SST_CODEC_TYPE_PCM, /* Pass through Audio codec */
+ SST_CODEC_TYPE_MP3,
+ SST_CODEC_TYPE_MP24,
+ SST_CODEC_TYPE_AAC,
+ SST_CODEC_TYPE_AACP,
+ SST_CODEC_TYPE_eAACP,
+};
+
+enum stream_type {
+ SST_STREAM_TYPE_NONE = 0,
+ SST_STREAM_TYPE_MUSIC = 1,
+};
+
+enum sst_error_codes {
+ /* Error code,response to msgId: Description */
+ /* Common error codes */
+ SST_SUCCESS = 0, /* Success */
+ SST_ERR_INVALID_STREAM_ID = 1,
+ SST_ERR_INVALID_MSG_ID = 2,
+ SST_ERR_INVALID_STREAM_OP = 3,
+ SST_ERR_INVALID_PARAMS = 4,
+ SST_ERR_INVALID_CODEC = 5,
+ SST_ERR_INVALID_MEDIA_TYPE = 6,
+ SST_ERR_STREAM_ERR = 7,
+
+ SST_ERR_STREAM_IN_USE = 15,
+};
+
+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 structure 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*/
+ u8 use_offload_path; /* 0-PCM using period elpased & ALSA interfaces
+ 1-PCM stream via compressed interface */
+ u8 reserved2;
+ u32 sfreq; /* Sampling rate in Hz */
+ u8 channel_map[8];
+} __packed;
+
+/* MP3 Music Parameters Message */
+struct snd_mp3_params {
+ u8 num_chan; /* 1=Mono, 2=Stereo */
+ u8 pcm_wd_sz; /* 16/24 - bit*/
+ u8 crc_check; /* crc_check - disable (0) or enable (1) */
+ u8 reserved1; /* unused*/
+ u16 reserved2; /* Unused */
+} __packed;
+
+#define AAC_BIT_STREAM_ADTS 0
+#define AAC_BIT_STREAM_ADIF 1
+#define AAC_BIT_STREAM_RAW 2
+
+/* AAC Music Parameters Message */
+struct snd_aac_params {
+ u8 num_chan; /* 1=Mono, 2=Stereo*/
+ u8 pcm_wd_sz; /* 16/24 - bit*/
+ u8 bdownsample; /*SBR downsampling 0 - disable 1 -enabled AAC+ only */
+ u8 bs_format; /* input bit stream format adts=0, adif=1, raw=2 */
+ u16 reser2;
+ u32 externalsr; /*sampling rate of basic AAC raw bit stream*/
+ u8 sbr_signalling;/*disable/enable/set automode the SBR tool.AAC+*/
+ u8 reser1;
+ u16 reser3;
+} __packed;
+
+/* WMA Music Parameters Message */
+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 */
+ u16 format_tag; /* Format Tag */
+ u16 block_align; /* packet size */
+ u16 wma_encode_opt;/* Encoder option */
+ u8 op_align; /* op align 0- 16 bit, 1- MSB, 2 LSB */
+ u8 reserved; /* reserved */
+} __packed;
+
+/* Codec params structure */
+union snd_sst_codec_params {
+ struct snd_pcm_params pcm_params;
+ struct snd_mp3_params mp3_params;
+ struct snd_aac_params aac_params;
+ struct snd_wma_params wma_params;
+} __packed;
+
+/* Address and size info of a frame buffer */
+struct sst_address_info {
+ u32 addr; /* Address at IA */
+ u32 size; /* Size of the buffer */
+};
+
+struct snd_sst_alloc_params_ext {
+ __u16 sg_count;
+ __u16 reserved;
+ __u32 frag_size; /*Number of samples after which period elapsed
+ message is sent valid only if path = 0*/
+ 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[];
+};
+
+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[];
+};
+
+#define MAX_VTSV_FILES 2
+struct snd_sst_vtsv_info {
+ struct sst_address_info vfiles[MAX_VTSV_FILES];
+} __packed;
+
+#endif /* __SST_MFLD_DSP_H__ */
diff --git a/sound/soc/intel/atom/sst-mfld-platform-compress.c b/sound/soc/intel/atom/sst-mfld-platform-compress.c
new file mode 100644
index 000000000..89c9c5ad6
--- /dev/null
+++ b/sound/soc/intel/atom/sst-mfld-platform-compress.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * sst_mfld_platform.c - Intel MID Platform driver
+ *
+ * Copyright (C) 2010-2014 Intel Corp
+ * Author: Vinod Koul <vinod.koul@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include "sst-mfld-platform.h"
+
+/* compress stream operations */
+static void sst_compr_fragment_elapsed(void *arg)
+{
+ struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;
+
+ pr_debug("fragment elapsed by driver\n");
+ if (cstream)
+ snd_compr_fragment_elapsed(cstream);
+}
+
+static void sst_drain_notify(void *arg)
+{
+ struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;
+
+ pr_debug("drain notify by driver\n");
+ if (cstream)
+ snd_compr_drain_notify(cstream);
+}
+
+static int sst_platform_compr_open(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream)
+{
+ int ret_val;
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct sst_runtime_stream *stream;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+
+ spin_lock_init(&stream->status_lock);
+
+ /* get the sst ops */
+ if (!sst || !try_module_get(sst->dev->driver->owner)) {
+ pr_err("no device available to run\n");
+ ret_val = -ENODEV;
+ goto out_ops;
+ }
+ stream->compr_ops = sst->compr_ops;
+ stream->id = 0;
+
+ /* Turn on LPE */
+ sst->compr_ops->power(sst->dev, true);
+
+ sst_set_stream_status(stream, SST_PLATFORM_INIT);
+ runtime->private_data = stream;
+ return 0;
+out_ops:
+ kfree(stream);
+ return ret_val;
+}
+
+static int sst_platform_compr_free(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream)
+{
+ struct sst_runtime_stream *stream;
+ int ret_val = 0, str_id;
+
+ stream = cstream->runtime->private_data;
+ /* Turn off LPE */
+ sst->compr_ops->power(sst->dev, false);
+
+ /*need to check*/
+ str_id = stream->id;
+ if (str_id)
+ ret_val = stream->compr_ops->close(sst->dev, str_id);
+ module_put(sst->dev->driver->owner);
+ kfree(stream);
+ pr_debug("%s: %d\n", __func__, ret_val);
+ return 0;
+}
+
+static int sst_platform_compr_set_params(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ struct snd_compr_params *params)
+{
+ struct sst_runtime_stream *stream;
+ int retval;
+ struct snd_sst_params str_params;
+ struct sst_compress_cb cb;
+ struct sst_data *ctx = snd_soc_component_get_drvdata(component);
+
+ stream = cstream->runtime->private_data;
+ /* construct fw structure for this*/
+ memset(&str_params, 0, sizeof(str_params));
+
+ /* 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: {
+ str_params.codec = SST_CODEC_TYPE_MP3;
+ str_params.sparams.uc.mp3_params.num_chan = params->codec.ch_in;
+ str_params.sparams.uc.mp3_params.pcm_wd_sz = 16;
+ break;
+ }
+
+ case SND_AUDIOCODEC_AAC: {
+ str_params.codec = SST_CODEC_TYPE_AAC;
+ str_params.sparams.uc.aac_params.num_chan = params->codec.ch_in;
+ str_params.sparams.uc.aac_params.pcm_wd_sz = 16;
+ if (params->codec.format == SND_AUDIOSTREAMFORMAT_MP4ADTS)
+ str_params.sparams.uc.aac_params.bs_format =
+ AAC_BIT_STREAM_ADTS;
+ else if (params->codec.format == SND_AUDIOSTREAMFORMAT_RAW)
+ str_params.sparams.uc.aac_params.bs_format =
+ AAC_BIT_STREAM_RAW;
+ else {
+ pr_err("Undefined format%d\n", params->codec.format);
+ return -EINVAL;
+ }
+ str_params.sparams.uc.aac_params.externalsr =
+ params->codec.sample_rate;
+ break;
+ }
+
+ default:
+ pr_err("codec not supported, id =%d\n", params->codec.id);
+ return -EINVAL;
+ }
+
+ str_params.aparams.ring_buf_info[0].addr =
+ virt_to_phys(cstream->runtime->buffer);
+ str_params.aparams.ring_buf_info[0].size =
+ cstream->runtime->buffer_size;
+ str_params.aparams.sg_count = 1;
+ str_params.aparams.frag_size = cstream->runtime->fragment_size;
+
+ cb.param = cstream;
+ cb.compr_cb = sst_compr_fragment_elapsed;
+ cb.drain_cb_param = cstream;
+ cb.drain_notify = sst_drain_notify;
+
+ retval = stream->compr_ops->open(sst->dev, &str_params, &cb);
+ if (retval < 0) {
+ pr_err("stream allocation failed %d\n", retval);
+ return retval;
+ }
+
+ stream->id = retval;
+ return 0;
+}
+
+static int sst_platform_compr_trigger(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream, int cmd)
+{
+ struct sst_runtime_stream *stream = cstream->runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (stream->compr_ops->stream_start)
+ return stream->compr_ops->stream_start(sst->dev, stream->id);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (stream->compr_ops->stream_drop)
+ return stream->compr_ops->stream_drop(sst->dev, stream->id);
+ break;
+ case SND_COMPR_TRIGGER_DRAIN:
+ if (stream->compr_ops->stream_drain)
+ return stream->compr_ops->stream_drain(sst->dev, stream->id);
+ break;
+ case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
+ if (stream->compr_ops->stream_partial_drain)
+ return stream->compr_ops->stream_partial_drain(sst->dev, stream->id);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (stream->compr_ops->stream_pause)
+ return stream->compr_ops->stream_pause(sst->dev, stream->id);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (stream->compr_ops->stream_pause_release)
+ return stream->compr_ops->stream_pause_release(sst->dev, stream->id);
+ break;
+ }
+ return -EINVAL;
+}
+
+static int sst_platform_compr_pointer(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp *tstamp)
+{
+ struct sst_runtime_stream *stream;
+
+ stream = cstream->runtime->private_data;
+ stream->compr_ops->tstamp(sst->dev, stream->id, tstamp);
+ tstamp->byte_offset = tstamp->copied_total %
+ (u32)cstream->runtime->buffer_size;
+ pr_debug("calc bytes offset/copied bytes as %d\n", tstamp->byte_offset);
+ return 0;
+}
+
+static int sst_platform_compr_ack(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ size_t bytes)
+{
+ struct sst_runtime_stream *stream;
+
+ stream = cstream->runtime->private_data;
+ stream->compr_ops->ack(sst->dev, stream->id, (unsigned long)bytes);
+ stream->bytes_written += bytes;
+
+ return 0;
+}
+
+static int sst_platform_compr_get_caps(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ struct snd_compr_caps *caps)
+{
+ struct sst_runtime_stream *stream =
+ cstream->runtime->private_data;
+
+ return stream->compr_ops->get_caps(caps);
+}
+
+static int sst_platform_compr_get_codec_caps(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ struct snd_compr_codec_caps *codec)
+{
+ struct sst_runtime_stream *stream =
+ cstream->runtime->private_data;
+
+ return stream->compr_ops->get_codec_caps(codec);
+}
+
+static int sst_platform_compr_set_metadata(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ struct snd_compr_metadata *metadata)
+{
+ struct sst_runtime_stream *stream =
+ cstream->runtime->private_data;
+
+ return stream->compr_ops->set_metadata(sst->dev, stream->id, metadata);
+}
+
+const struct snd_compress_ops sst_platform_compress_ops = {
+
+ .open = sst_platform_compr_open,
+ .free = sst_platform_compr_free,
+ .set_params = sst_platform_compr_set_params,
+ .set_metadata = sst_platform_compr_set_metadata,
+ .trigger = sst_platform_compr_trigger,
+ .pointer = sst_platform_compr_pointer,
+ .ack = sst_platform_compr_ack,
+ .get_caps = sst_platform_compr_get_caps,
+ .get_codec_caps = sst_platform_compr_get_codec_caps,
+};
diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
new file mode 100644
index 000000000..c75616a5f
--- /dev/null
+++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
@@ -0,0 +1,826 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * sst_mfld_platform.c - Intel MID Platform driver
+ *
+ * Copyright (C) 2010-2014 Intel Corp
+ * Author: Vinod Koul <vinod.koul@intel.com>
+ * Author: Harsha Priya <priya.harsha@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#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);
+
+int sst_register_dsp(struct sst_device *dev)
+{
+ if (WARN_ON(!dev))
+ return -EINVAL;
+ if (!try_module_get(dev->dev->driver->owner))
+ return -ENODEV;
+ mutex_lock(&sst_lock);
+ if (sst) {
+ dev_err(dev->dev, "we already have a device %s\n", sst->name);
+ module_put(dev->dev->driver->owner);
+ mutex_unlock(&sst_lock);
+ return -EEXIST;
+ }
+ dev_dbg(dev->dev, "registering device %s\n", dev->name);
+ sst = dev;
+ mutex_unlock(&sst_lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_register_dsp);
+
+int sst_unregister_dsp(struct sst_device *dev)
+{
+ if (WARN_ON(!dev))
+ return -EINVAL;
+ if (dev != sst)
+ return -EINVAL;
+
+ mutex_lock(&sst_lock);
+
+ if (!sst) {
+ mutex_unlock(&sst_lock);
+ return -EIO;
+ }
+
+ module_put(sst->dev->driver->owner);
+ dev_dbg(dev->dev, "unreg %s\n", sst->name);
+ sst = NULL;
+ mutex_unlock(&sst_lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_unregister_dsp);
+
+static const struct snd_pcm_hardware sst_platform_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_DOUBLE |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_MMAP|
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_SYNC_START),
+ .buffer_bytes_max = SST_MAX_BUFFER,
+ .period_bytes_min = SST_MIN_PERIOD_BYTES,
+ .period_bytes_max = SST_MAX_PERIOD_BYTES,
+ .periods_min = SST_MIN_PERIODS,
+ .periods_max = SST_MAX_PERIODS,
+ .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},
+ {MERR_DPCM_DEEP_BUFFER, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA3_IN, 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,
+ int state)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&stream->status_lock, flags);
+ stream->stream_status = state;
+ spin_unlock_irqrestore(&stream->status_lock, flags);
+}
+
+static inline int sst_get_stream_status(struct sst_runtime_stream *stream)
+{
+ int state;
+ unsigned long flags;
+
+ spin_lock_irqsave(&stream->status_lock, flags);
+ state = stream->stream_status;
+ spin_unlock_irqrestore(&stream->status_lock, flags);
+ 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 = substream->runtime->dma_addr;
+
+ 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 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;
+
+ /* 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));
+
+}
+
+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,
+ 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)
+ 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) {
+ 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) {
+ 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;
+}
+
+static int sst_platform_alloc_stream(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sst_runtime_stream *stream =
+ substream->runtime->private_data;
+ 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;
+ struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);
+
+ /* set codec params and inform SST driver the same */
+ sst_fill_pcm_params(substream, &param);
+ sst_fill_alloc_params(substream, &alloc_params);
+ str_params.sparams = param;
+ 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, ctx, &str_params, false);
+ if (ret_val < 0)
+ return ret_val;
+
+ stream->stream_info.str_id = str_params.stream_id;
+
+ ret_val = stream->ops->open(sst->dev, &str_params);
+ if (ret_val <= 0)
+ return ret_val;
+
+
+ return ret_val;
+}
+
+static void sst_period_elapsed(void *arg)
+{
+ struct snd_pcm_substream *substream = arg;
+ struct sst_runtime_stream *stream;
+ int status;
+
+ if (!substream || !substream->runtime)
+ return;
+ stream = substream->runtime->private_data;
+ if (!stream)
+ return;
+ status = sst_get_stream_status(stream);
+ if (status != SST_PLATFORM_RUNNING)
+ return;
+ snd_pcm_period_elapsed(substream);
+}
+
+static int sst_platform_init_stream(struct snd_pcm_substream *substream)
+{
+ struct sst_runtime_stream *stream =
+ substream->runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ int ret_val;
+
+ dev_dbg(rtd->dev, "setting buffer ptr param\n");
+ sst_set_stream_status(stream, SST_PLATFORM_INIT);
+ stream->stream_info.period_elapsed = sst_period_elapsed;
+ stream->stream_info.arg = substream;
+ stream->stream_info.buffer_ptr = 0;
+ stream->stream_info.sfreq = substream->runtime->rate;
+ ret_val = stream->ops->stream_init(sst->dev, &stream->stream_info);
+ if (ret_val)
+ dev_err(rtd->dev, "control_set ret error %d\n", ret_val);
+ return ret_val;
+
+}
+
+static int power_up_sst(struct sst_runtime_stream *stream)
+{
+ return stream->ops->power(sst->dev, true);
+}
+
+static void power_down_sst(struct sst_runtime_stream *stream)
+{
+ stream->ops->power(sst->dev, false);
+}
+
+static int sst_media_open(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int ret_val = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sst_runtime_stream *stream;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+ spin_lock_init(&stream->status_lock);
+
+ /* get the sst ops */
+ mutex_lock(&sst_lock);
+ if (!sst ||
+ !try_module_get(sst->dev->driver->owner)) {
+ dev_err(dai->dev, "no device available to run\n");
+ ret_val = -ENODEV;
+ goto out_ops;
+ }
+ stream->ops = sst->ops;
+ mutex_unlock(&sst_lock);
+
+ stream->stream_info.str_id = 0;
+
+ stream->stream_info.arg = substream;
+ /* allocate memory for SST API set */
+ runtime->private_data = stream;
+
+ ret_val = power_up_sst(stream);
+ if (ret_val < 0)
+ goto out_power_up;
+
+ /*
+ * Make sure the period to be multiple of 1ms to align the
+ * design of firmware. Apply same rule to buffer size to make
+ * sure alsa could always find a value for period size
+ * regardless the buffer size given by user space.
+ */
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 48);
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 48);
+
+ /* 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:
+ mutex_unlock(&sst_lock);
+out_power_up:
+ kfree(stream);
+ return ret_val;
+}
+
+static void sst_media_close(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sst_runtime_stream *stream;
+ int str_id;
+
+ stream = substream->runtime->private_data;
+ power_down_sst(stream);
+
+ str_id = stream->stream_info.str_id;
+ if (str_id)
+ stream->ops->close(sst->dev, str_id);
+ module_put(sst->dev->driver->owner);
+ kfree(stream);
+}
+
+static int sst_media_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sst_runtime_stream *stream;
+ int ret_val, str_id;
+
+ stream = substream->runtime->private_data;
+ str_id = stream->stream_info.str_id;
+ if (stream->stream_info.str_id) {
+ ret_val = stream->ops->stream_drop(sst->dev, str_id);
+ return ret_val;
+ }
+
+ ret_val = sst_platform_alloc_stream(substream, dai);
+ if (ret_val <= 0)
+ return ret_val;
+ snprintf(substream->pcm->id, sizeof(substream->pcm->id),
+ "%d", stream->stream_info.str_id);
+
+ ret_val = sst_platform_init_stream(substream);
+ if (ret_val)
+ return ret_val;
+ substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER;
+ return 0;
+}
+
+static int sst_enable_ssp(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+
+ if (!snd_soc_dai_active(dai)) {
+ ret = sst_handle_vb_timer(dai, true);
+ sst_fill_ssp_defaults(dai);
+ }
+ return ret;
+}
+
+static int sst_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+
+ if (snd_soc_dai_active(dai) == 1)
+ ret = send_ssp_cmd(dai, dai->name, 1);
+ return ret;
+}
+
+static int sst_set_format(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ int ret = 0;
+
+ if (!snd_soc_dai_active(dai))
+ return 0;
+
+ ret = sst_fill_ssp_config(dai, fmt);
+ if (ret < 0)
+ dev_err(dai->dev, "sst_set_format failed..\n");
+
+ return ret;
+}
+
+static int sst_platform_set_ssp_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width) {
+ int ret = 0;
+
+ if (!snd_soc_dai_active(dai))
+ return ret;
+
+ ret = sst_fill_ssp_slot(dai, tx_mask, rx_mask, slots, slot_width);
+ if (ret < 0)
+ dev_err(dai->dev, "sst_fill_ssp_slot failed..%d\n", ret);
+
+ return ret;
+}
+
+static void sst_disable_ssp(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ if (!snd_soc_dai_active(dai)) {
+ send_ssp_cmd(dai, dai->name, 0);
+ sst_handle_vb_timer(dai, false);
+ }
+}
+
+static const struct snd_soc_dai_ops sst_media_dai_ops = {
+ .startup = sst_media_open,
+ .shutdown = sst_media_close,
+ .prepare = sst_media_prepare,
+ .mute_stream = sst_media_digital_mute,
+};
+
+static const struct snd_soc_dai_ops sst_compr_dai_ops = {
+ .mute_stream = sst_media_digital_mute,
+};
+
+static const struct snd_soc_dai_ops sst_be_dai_ops = {
+ .startup = sst_enable_ssp,
+ .hw_params = sst_be_hw_params,
+ .set_fmt = sst_set_format,
+ .set_tdm_slot = sst_platform_set_ssp_slot,
+ .shutdown = sst_disable_ssp,
+};
+
+static struct snd_soc_dai_driver sst_platform_dai[] = {
+{
+ .name = "media-cpu-dai",
+ .ops = &sst_media_dai_ops,
+ .playback = {
+ .stream_name = "Headset Playback",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Headset Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "deepbuffer-cpu-dai",
+ .ops = &sst_media_dai_ops,
+ .playback = {
+ .stream_name = "Deepbuffer Playback",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "compress-cpu-dai",
+ .compress_new = snd_soc_new_compress,
+ .ops = &sst_compr_dai_ops,
+ .playback = {
+ .stream_name = "Compress Playback",
+ .channels_min = 1,
+ },
+},
+/* BE CPU Dais */
+{
+ .name = "ssp0-port",
+ .ops = &sst_be_dai_ops,
+ .playback = {
+ .stream_name = "ssp0 Tx",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "ssp0 Rx",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "ssp1-port",
+ .ops = &sst_be_dai_ops,
+ .playback = {
+ .stream_name = "ssp1 Tx",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "ssp1 Rx",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "ssp2-port",
+ .ops = &sst_be_dai_ops,
+ .playback = {
+ .stream_name = "ssp2 Tx",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "ssp2 Rx",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+};
+
+static int sst_soc_open(struct snd_soc_component *component,
+ 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_soc_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ int ret_val = 0, str_id;
+ struct sst_runtime_stream *stream;
+ int status;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+
+ dev_dbg(rtd->dev, "%s called\n", __func__);
+ if (substream->pcm->internal)
+ return 0;
+ stream = substream->runtime->private_data;
+ str_id = stream->stream_info.str_id;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ dev_dbg(rtd->dev, "sst: Trigger Start\n");
+ status = SST_PLATFORM_RUNNING;
+ stream->stream_info.arg = substream;
+ ret_val = stream->ops->stream_start(sst->dev, str_id);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ dev_dbg(rtd->dev, "sst: in stop\n");
+ status = SST_PLATFORM_DROPPED;
+ ret_val = stream->ops->stream_drop(sst->dev, str_id);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ dev_dbg(rtd->dev, "sst: in pause\n");
+ status = SST_PLATFORM_PAUSED;
+ ret_val = stream->ops->stream_pause(sst->dev, str_id);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ dev_dbg(rtd->dev, "sst: in pause release\n");
+ status = SST_PLATFORM_RUNNING;
+ ret_val = stream->ops->stream_pause_release(sst->dev, str_id);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!ret_val)
+ sst_set_stream_status(stream, status);
+
+ return ret_val;
+}
+
+
+static snd_pcm_uframes_t sst_soc_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct sst_runtime_stream *stream;
+ int ret_val, status;
+ struct pcm_stream_info *str_info;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+
+ stream = substream->runtime->private_data;
+ status = sst_get_stream_status(stream);
+ if (status == SST_PLATFORM_INIT)
+ return 0;
+ str_info = &stream->stream_info;
+ ret_val = stream->ops->stream_read_tstamp(sst->dev, str_info);
+ if (ret_val) {
+ dev_err(rtd->dev, "sst: error code = %d\n", ret_val);
+ return ret_val;
+ }
+ return str_info->buffer_ptr;
+}
+
+static snd_pcm_sframes_t sst_soc_delay(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct sst_runtime_stream *stream = substream->runtime->private_data;
+ struct pcm_stream_info *str_info = &stream->stream_info;
+
+ if (sst_get_stream_status(stream) == SST_PLATFORM_INIT)
+ return 0;
+
+ return str_info->pcm_delay;
+}
+
+static int sst_soc_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
+ struct snd_pcm *pcm = rtd->pcm;
+
+ if (dai->driver->playback.channels_min ||
+ dai->driver->capture.channels_min) {
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ pcm->card->dev,
+ SST_MIN_BUFFER, SST_MAX_BUFFER);
+ }
+ return 0;
+}
+
+static int sst_soc_probe(struct snd_soc_component *component)
+{
+ struct sst_data *drv = dev_get_drvdata(component->dev);
+
+ drv->soc_card = component->card;
+ return sst_dsp_init_v2_dpcm(component);
+}
+
+static void sst_soc_remove(struct snd_soc_component *component)
+{
+ struct sst_data *drv = dev_get_drvdata(component->dev);
+
+ drv->soc_card = NULL;
+}
+
+static const struct snd_soc_component_driver sst_soc_platform_drv = {
+ .name = DRV_NAME,
+ .probe = sst_soc_probe,
+ .remove = sst_soc_remove,
+ .open = sst_soc_open,
+ .trigger = sst_soc_trigger,
+ .pointer = sst_soc_pointer,
+ .delay = sst_soc_delay,
+ .compress_ops = &sst_platform_compress_ops,
+ .pcm_construct = sst_soc_pcm_new,
+};
+
+static int sst_platform_probe(struct platform_device *pdev)
+{
+ struct sst_data *drv;
+ int ret;
+ struct sst_platform_data *pdata;
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (drv == NULL) {
+ return -ENOMEM;
+ }
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (pdata == NULL) {
+ return -ENOMEM;
+ }
+
+ pdata->pdev_strm_map = dpcm_strm_map;
+ pdata->strm_map_size = ARRAY_SIZE(dpcm_strm_map);
+ drv->pdata = pdata;
+ drv->pdev = pdev;
+ mutex_init(&drv->lock);
+ dev_set_drvdata(&pdev->dev, drv);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &sst_soc_platform_drv,
+ sst_platform_dai, ARRAY_SIZE(sst_platform_dai));
+ if (ret)
+ dev_err(&pdev->dev, "registering cpu dais failed\n");
+
+ return ret;
+}
+
+static int sst_platform_remove(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "sst_platform_remove success\n");
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int sst_soc_prepare(struct device *dev)
+{
+ struct sst_data *drv = dev_get_drvdata(dev);
+ struct snd_soc_pcm_runtime *rtd;
+
+ if (!drv->soc_card)
+ return 0;
+
+ /* suspend all pcms first */
+ snd_soc_suspend(drv->soc_card->dev);
+ snd_soc_poweroff(drv->soc_card->dev);
+
+ /* set the SSPs to idle */
+ for_each_card_rtds(drv->soc_card, rtd) {
+ struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
+
+ if (snd_soc_dai_active(dai)) {
+ send_ssp_cmd(dai, dai->name, 0);
+ sst_handle_vb_timer(dai, false);
+ }
+ }
+
+ return 0;
+}
+
+static void sst_soc_complete(struct device *dev)
+{
+ struct sst_data *drv = dev_get_drvdata(dev);
+ struct snd_soc_pcm_runtime *rtd;
+
+ if (!drv->soc_card)
+ return;
+
+ /* restart SSPs */
+ for_each_card_rtds(drv->soc_card, rtd) {
+ struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
+
+ if (snd_soc_dai_active(dai)) {
+ sst_handle_vb_timer(dai, true);
+ send_ssp_cmd(dai, dai->name, 1);
+ }
+ }
+ snd_soc_resume(drv->soc_card->dev);
+}
+
+#else
+
+#define sst_soc_prepare NULL
+#define sst_soc_complete NULL
+
+#endif
+
+
+static const struct dev_pm_ops sst_platform_pm = {
+ .prepare = sst_soc_prepare,
+ .complete = sst_soc_complete,
+};
+
+static struct platform_driver sst_platform_driver = {
+ .driver = {
+ .name = "sst-mfld-platform",
+ .pm = &sst_platform_pm,
+ },
+ .probe = sst_platform_probe,
+ .remove = sst_platform_remove,
+};
+
+module_platform_driver(sst_platform_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sst-atom-hifi2-platform");
+MODULE_ALIAS("platform:sst-mfld-platform");
diff --git a/sound/soc/intel/atom/sst-mfld-platform.h b/sound/soc/intel/atom/sst-mfld-platform.h
new file mode 100644
index 000000000..8b5777d32
--- /dev/null
+++ b/sound/soc/intel/atom/sst-mfld-platform.h
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * sst_mfld_platform.h - Intel MID Platform driver header file
+ *
+ * Copyright (C) 2010 Intel Corp
+ * Author: Vinod Koul <vinod.koul@intel.com>
+ * Author: Harsha Priya <priya.harsha@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#ifndef __SST_PLATFORMDRV_H__
+#define __SST_PLATFORMDRV_H__
+
+#include "sst-mfld-dsp.h"
+#include "sst-atom-controls.h"
+
+extern struct sst_device *sst;
+extern const struct snd_compress_ops sst_platform_compress_ops;
+
+#define DRV_NAME "sst"
+
+#define SST_MONO 1
+#define SST_STEREO 2
+#define SST_MAX_CAP 5
+
+#define SST_MAX_BUFFER (800*1024)
+#define SST_MIN_BUFFER (800*1024)
+#define SST_MIN_PERIOD_BYTES 32
+#define SST_MAX_PERIOD_BYTES SST_MAX_BUFFER
+#define SST_MIN_PERIODS 2
+#define SST_MAX_PERIODS (1024*2)
+#define SST_FIFO_SIZE 0
+
+struct pcm_stream_info {
+ int str_id;
+ void *arg;
+ void (*period_elapsed) (void *arg);
+ unsigned long long buffer_ptr;
+ unsigned long long pcm_delay;
+ int sfreq;
+};
+
+enum sst_drv_status {
+ SST_PLATFORM_INIT = 1,
+ SST_PLATFORM_STARTED,
+ SST_PLATFORM_RUNNING,
+ SST_PLATFORM_PAUSED,
+ SST_PLATFORM_DROPPED,
+};
+
+enum sst_stream_ops {
+ STREAM_OPS_PLAYBACK = 0,
+ STREAM_OPS_CAPTURE,
+};
+
+enum sst_audio_device_type {
+ SND_SST_DEVICE_HEADSET = 1,
+ SND_SST_DEVICE_IHF,
+ SND_SST_DEVICE_VIBRA,
+ SND_SST_DEVICE_HAPTIC,
+ SND_SST_DEVICE_CAPTURE,
+ SND_SST_DEVICE_COMPRESS,
+};
+
+/* PCM Parameters */
+struct sst_pcm_params {
+ u16 codec; /* codec type */
+ 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 */
+ u32 ring_buffer_size;
+ u32 period_count; /* period elapsed in samples*/
+ u32 ring_buffer_addr;
+};
+
+struct sst_stream_params {
+ u32 result;
+ u32 stream_id;
+ u8 codec;
+ u8 ops;
+ u8 stream_type;
+ u8 device_type;
+ struct sst_pcm_params sparams;
+};
+
+struct sst_compress_cb {
+ void *param;
+ void (*compr_cb)(void *param);
+ void *drain_cb_param;
+ void (*drain_notify)(void *param);
+};
+
+struct compress_sst_ops {
+ const char *name;
+ int (*open)(struct device *dev,
+ struct snd_sst_params *str_params, struct sst_compress_cb *cb);
+ int (*stream_start)(struct device *dev, unsigned int str_id);
+ int (*stream_drop)(struct device *dev, unsigned int str_id);
+ int (*stream_drain)(struct device *dev, unsigned int str_id);
+ int (*stream_partial_drain)(struct device *dev, unsigned int str_id);
+ int (*stream_pause)(struct device *dev, unsigned int str_id);
+ int (*stream_pause_release)(struct device *dev, unsigned int str_id);
+
+ int (*tstamp)(struct device *dev, unsigned int str_id,
+ struct snd_compr_tstamp *tstamp);
+ int (*ack)(struct device *dev, unsigned int str_id,
+ unsigned long bytes);
+ int (*close)(struct device *dev, unsigned int str_id);
+ int (*get_caps)(struct snd_compr_caps *caps);
+ int (*get_codec_caps)(struct snd_compr_codec_caps *codec);
+ int (*set_metadata)(struct device *dev, unsigned int str_id,
+ struct snd_compr_metadata *mdata);
+ int (*power)(struct device *dev, bool state);
+};
+
+struct sst_ops {
+ int (*open)(struct device *dev, struct snd_sst_params *str_param);
+ int (*stream_init)(struct device *dev, struct pcm_stream_info *str_info);
+ int (*stream_start)(struct device *dev, int str_id);
+ int (*stream_drop)(struct device *dev, int str_id);
+ int (*stream_pause)(struct device *dev, int str_id);
+ int (*stream_pause_release)(struct device *dev, int str_id);
+ int (*stream_read_tstamp)(struct device *dev, struct pcm_stream_info *str_info);
+ int (*send_byte_stream)(struct device *dev, struct snd_sst_bytes_v2 *bytes);
+ int (*close)(struct device *dev, unsigned int str_id);
+ int (*power)(struct device *dev, bool state);
+};
+
+struct sst_runtime_stream {
+ int stream_status;
+ unsigned int id;
+ size_t bytes_written;
+ struct pcm_stream_info stream_info;
+ struct sst_ops *ops;
+ struct compress_sst_ops *compr_ops;
+ spinlock_t status_lock;
+};
+
+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;
+
+int sst_dsp_init_v2_dpcm(struct snd_soc_component *component);
+int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute);
+int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable);
+int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable);
+
+void sst_set_stream_status(struct sst_runtime_stream *stream, int state);
+int sst_fill_stream_params(void *substream, const struct sst_data *ctx,
+ struct snd_sst_params *str_params, bool is_compress);
+
+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 snd_sst_bytes_v2 *byte_stream;
+ struct mutex lock;
+ struct snd_soc_card *soc_card;
+ struct sst_cmd_sba_hw_set_ssp ssp_cmd;
+};
+int sst_register_dsp(struct sst_device *dev);
+int sst_unregister_dsp(struct sst_device *dev);
+#endif
diff --git a/sound/soc/intel/atom/sst/Makefile b/sound/soc/intel/atom/sst/Makefile
new file mode 100644
index 000000000..5761d30a5
--- /dev/null
+++ b/sound/soc/intel/atom/sst/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-intel-sst-core-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o
+snd-intel-sst-pci-objs += sst_pci.o
+snd-intel-sst-acpi-objs += sst_acpi.o
+
+obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += snd-intel-sst-core.o
+obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_PCI) += snd-intel-sst-pci.o
+obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI) += snd-intel-sst-acpi.o
diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c
new file mode 100644
index 000000000..a0d29510d
--- /dev/null
+++ b/sound/soc/intel/atom/sst/sst.c
@@ -0,0 +1,573 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * sst.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corp
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/async.h>
+#include <linux/acpi.h>
+#include <linux/sysfs.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine Driver");
+MODULE_LICENSE("GPL v2");
+
+static inline bool sst_is_process_reply(u32 msg_id)
+{
+ return ((msg_id & PROCESS_MSG) ? true : false);
+}
+
+static inline bool sst_validate_mailbox_size(unsigned int size)
+{
+ return ((size <= SST_MAILBOX_SIZE) ? true : false);
+}
+
+static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context)
+{
+ union interrupt_reg_mrfld isr;
+ union ipc_header_mrfld header;
+ union sst_imr_reg_mrfld imr;
+ struct ipc_post *msg = NULL;
+ unsigned int size;
+ struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
+ irqreturn_t retval = IRQ_HANDLED;
+
+ /* Interrupt arrived, check src */
+ isr.full = sst_shim_read64(drv->shim, SST_ISRX);
+
+ if (isr.part.done_interrupt) {
+ /* Clear done bit */
+ spin_lock(&drv->ipc_spin_lock);
+ header.full = sst_shim_read64(drv->shim,
+ drv->ipc_reg.ipcx);
+ header.p.header_high.part.done = 0;
+ sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full);
+
+ /* write 1 to clear status register */;
+ isr.part.done_interrupt = 1;
+ sst_shim_write64(drv->shim, SST_ISRX, isr.full);
+ spin_unlock(&drv->ipc_spin_lock);
+
+ /* we can send more messages to DSP so trigger work */
+ queue_work(drv->post_msg_wq, &drv->ipc_post_msg_wq);
+ retval = IRQ_HANDLED;
+ }
+
+ if (isr.part.busy_interrupt) {
+ /* message from dsp so copy that */
+ spin_lock(&drv->ipc_spin_lock);
+ imr.full = sst_shim_read64(drv->shim, SST_IMRX);
+ imr.part.busy_interrupt = 1;
+ sst_shim_write64(drv->shim, SST_IMRX, imr.full);
+ spin_unlock(&drv->ipc_spin_lock);
+ header.full = sst_shim_read64(drv->shim, drv->ipc_reg.ipcd);
+
+ if (sst_create_ipc_msg(&msg, header.p.header_high.part.large)) {
+ drv->ops->clear_interrupt(drv);
+ return IRQ_HANDLED;
+ }
+
+ if (header.p.header_high.part.large) {
+ size = header.p.header_low_payload;
+ if (sst_validate_mailbox_size(size)) {
+ memcpy_fromio(msg->mailbox_data,
+ drv->mailbox + drv->mailbox_recv_offset, size);
+ } else {
+ dev_err(drv->dev,
+ "Mailbox not copied, payload size is: %u\n", size);
+ header.p.header_low_payload = 0;
+ }
+ }
+
+ msg->mrfld_header = header;
+ msg->is_process_reply =
+ sst_is_process_reply(header.p.header_high.part.msg_id);
+ spin_lock(&drv->rx_msg_lock);
+ list_add_tail(&msg->node, &drv->rx_list);
+ spin_unlock(&drv->rx_msg_lock);
+ drv->ops->clear_interrupt(drv);
+ retval = IRQ_WAKE_THREAD;
+ }
+ return retval;
+}
+
+static irqreturn_t intel_sst_irq_thread_mrfld(int irq, void *context)
+{
+ struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
+ struct ipc_post *__msg, *msg;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
+ if (list_empty(&drv->rx_list)) {
+ spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+ return IRQ_HANDLED;
+ }
+
+ list_for_each_entry_safe(msg, __msg, &drv->rx_list, node) {
+ list_del(&msg->node);
+ spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+ if (msg->is_process_reply)
+ drv->ops->process_message(msg);
+ else
+ drv->ops->process_reply(drv, msg);
+
+ if (msg->is_large)
+ kfree(msg->mailbox_data);
+ kfree(msg);
+ spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
+ }
+ spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+ return IRQ_HANDLED;
+}
+
+static int sst_save_dsp_context_v2(struct intel_sst_drv *sst)
+{
+ int ret = 0;
+
+ ret = sst_prepare_and_post_msg(sst, SST_TASK_ID_MEDIA, IPC_CMD,
+ IPC_PREP_D3, PIPE_RSVD, 0, NULL, NULL,
+ true, true, false, true);
+
+ if (ret < 0) {
+ dev_err(sst->dev, "not suspending FW!!, Err: %d\n", ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+static struct intel_sst_ops mrfld_ops = {
+ .interrupt = intel_sst_interrupt_mrfld,
+ .irq_thread = intel_sst_irq_thread_mrfld,
+ .clear_interrupt = intel_sst_clear_intr_mrfld,
+ .start = sst_start_mrfld,
+ .reset = intel_sst_reset_dsp_mrfld,
+ .post_message = sst_post_message_mrfld,
+ .process_reply = sst_process_reply_mrfld,
+ .save_dsp_context = sst_save_dsp_context_v2,
+ .alloc_stream = sst_alloc_stream_mrfld,
+ .post_download = sst_post_download_mrfld,
+};
+
+int sst_driver_ops(struct intel_sst_drv *sst)
+{
+
+ switch (sst->dev_id) {
+ case SST_MRFLD_PCI_ID:
+ case SST_BYT_ACPI_ID:
+ case SST_CHV_ACPI_ID:
+ sst->tstamp = SST_TIME_STAMP_MRFLD;
+ sst->ops = &mrfld_ops;
+ return 0;
+
+ default:
+ dev_err(sst->dev,
+ "SST Driver capabilities missing for dev_id: %x",
+ sst->dev_id);
+ return -EINVAL;
+ }
+}
+
+void sst_process_pending_msg(struct work_struct *work)
+{
+ struct intel_sst_drv *ctx = container_of(work,
+ struct intel_sst_drv, ipc_post_msg_wq);
+
+ ctx->ops->post_message(ctx, NULL, false);
+}
+
+static int sst_workqueue_init(struct intel_sst_drv *ctx)
+{
+ INIT_LIST_HEAD(&ctx->memcpy_list);
+ INIT_LIST_HEAD(&ctx->rx_list);
+ INIT_LIST_HEAD(&ctx->ipc_dispatch_list);
+ INIT_LIST_HEAD(&ctx->block_list);
+ INIT_WORK(&ctx->ipc_post_msg_wq, sst_process_pending_msg);
+ init_waitqueue_head(&ctx->wait_queue);
+
+ ctx->post_msg_wq =
+ create_singlethread_workqueue("sst_post_msg_wq");
+ if (!ctx->post_msg_wq)
+ return -EBUSY;
+ return 0;
+}
+
+static void sst_init_locks(struct intel_sst_drv *ctx)
+{
+ mutex_init(&ctx->sst_lock);
+ spin_lock_init(&ctx->rx_msg_lock);
+ spin_lock_init(&ctx->ipc_spin_lock);
+ spin_lock_init(&ctx->block_lock);
+}
+
+int sst_alloc_drv_context(struct intel_sst_drv **ctx,
+ struct device *dev, unsigned int dev_id)
+{
+ *ctx = devm_kzalloc(dev, sizeof(struct intel_sst_drv), GFP_KERNEL);
+ if (!(*ctx))
+ return -ENOMEM;
+
+ (*ctx)->dev = dev;
+ (*ctx)->dev_id = dev_id;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_alloc_drv_context);
+
+static ssize_t firmware_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (ctx->fw_version.type == 0 && ctx->fw_version.major == 0 &&
+ ctx->fw_version.minor == 0 && ctx->fw_version.build == 0)
+ return sysfs_emit(buf, "FW not yet loaded\n");
+ else
+ return sysfs_emit(buf, "v%02x.%02x.%02x.%02x\n",
+ ctx->fw_version.type, ctx->fw_version.major,
+ ctx->fw_version.minor, ctx->fw_version.build);
+
+}
+
+static DEVICE_ATTR_RO(firmware_version);
+
+static const struct attribute *sst_fw_version_attrs[] = {
+ &dev_attr_firmware_version.attr,
+ NULL,
+};
+
+static const struct attribute_group sst_fw_version_attr_group = {
+ .attrs = (struct attribute **)sst_fw_version_attrs,
+};
+
+int sst_context_init(struct intel_sst_drv *ctx)
+{
+ int ret = 0, i;
+
+ if (!ctx->pdata)
+ return -EINVAL;
+
+ if (!ctx->pdata->probe_data)
+ return -EINVAL;
+
+ memcpy(&ctx->info, ctx->pdata->probe_data, sizeof(ctx->info));
+
+ ret = sst_driver_ops(ctx);
+ if (ret != 0)
+ return -EINVAL;
+
+ sst_init_locks(ctx);
+ sst_set_fw_state_locked(ctx, SST_RESET);
+
+ /* pvt_id 0 reserved for async messages */
+ ctx->pvt_id = 1;
+ ctx->stream_cnt = 0;
+ ctx->fw_in_mem = NULL;
+ /* we use memcpy, so set to 0 */
+ ctx->use_dma = 0;
+ ctx->use_lli = 0;
+
+ if (sst_workqueue_init(ctx))
+ return -EINVAL;
+
+ ctx->mailbox_recv_offset = ctx->pdata->ipc_info->mbox_recv_off;
+ ctx->ipc_reg.ipcx = SST_IPCX + ctx->pdata->ipc_info->ipc_offset;
+ ctx->ipc_reg.ipcd = SST_IPCD + ctx->pdata->ipc_info->ipc_offset;
+
+ dev_info(ctx->dev, "Got drv data max stream %d\n",
+ ctx->info.max_streams);
+
+ for (i = 1; i <= ctx->info.max_streams; i++) {
+ struct stream_info *stream = &ctx->streams[i];
+
+ memset(stream, 0, sizeof(*stream));
+ stream->pipe_id = PIPE_RSVD;
+ mutex_init(&stream->lock);
+ }
+
+ /* Register the ISR */
+ ret = devm_request_threaded_irq(ctx->dev, ctx->irq_num, ctx->ops->interrupt,
+ ctx->ops->irq_thread, 0, SST_DRV_NAME,
+ ctx);
+ if (ret)
+ goto do_free_mem;
+
+ dev_dbg(ctx->dev, "Registered IRQ %#x\n", ctx->irq_num);
+
+ /* default intr are unmasked so set this as masked */
+ sst_shim_write64(ctx->shim, SST_IMRX, 0xFFFF0038);
+
+ ctx->qos = devm_kzalloc(ctx->dev,
+ sizeof(struct pm_qos_request), GFP_KERNEL);
+ if (!ctx->qos) {
+ ret = -ENOMEM;
+ goto do_free_mem;
+ }
+ cpu_latency_qos_add_request(ctx->qos, PM_QOS_DEFAULT_VALUE);
+
+ dev_dbg(ctx->dev, "Requesting FW %s now...\n", ctx->firmware_name);
+ ret = request_firmware_nowait(THIS_MODULE, true, ctx->firmware_name,
+ ctx->dev, GFP_KERNEL, ctx, sst_firmware_load_cb);
+ if (ret) {
+ dev_err(ctx->dev, "Firmware download failed:%d\n", ret);
+ goto do_free_mem;
+ }
+
+ ret = sysfs_create_group(&ctx->dev->kobj,
+ &sst_fw_version_attr_group);
+ if (ret) {
+ dev_err(ctx->dev,
+ "Unable to create sysfs\n");
+ goto err_sysfs;
+ }
+
+ sst_register(ctx->dev);
+ return 0;
+err_sysfs:
+ sysfs_remove_group(&ctx->dev->kobj, &sst_fw_version_attr_group);
+
+do_free_mem:
+ destroy_workqueue(ctx->post_msg_wq);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sst_context_init);
+
+void sst_context_cleanup(struct intel_sst_drv *ctx)
+{
+ pm_runtime_get_noresume(ctx->dev);
+ pm_runtime_disable(ctx->dev);
+ sst_unregister(ctx->dev);
+ sst_set_fw_state_locked(ctx, SST_SHUTDOWN);
+ sysfs_remove_group(&ctx->dev->kobj, &sst_fw_version_attr_group);
+ destroy_workqueue(ctx->post_msg_wq);
+ cpu_latency_qos_remove_request(ctx->qos);
+ kfree(ctx->fw_sg_list.src);
+ kfree(ctx->fw_sg_list.dst);
+ ctx->fw_sg_list.list_len = 0;
+ kfree(ctx->fw_in_mem);
+ ctx->fw_in_mem = NULL;
+ sst_memcpy_free_resources(ctx);
+}
+EXPORT_SYMBOL_GPL(sst_context_cleanup);
+
+void sst_configure_runtime_pm(struct intel_sst_drv *ctx)
+{
+ pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY);
+ pm_runtime_use_autosuspend(ctx->dev);
+ /*
+ * For acpi devices, the actual physical device state is
+ * initially active. So change the state to active before
+ * enabling the pm
+ */
+
+ if (!acpi_disabled)
+ pm_runtime_set_active(ctx->dev);
+
+ pm_runtime_enable(ctx->dev);
+
+ if (acpi_disabled)
+ pm_runtime_set_active(ctx->dev);
+ else
+ pm_runtime_put_noidle(ctx->dev);
+}
+EXPORT_SYMBOL_GPL(sst_configure_runtime_pm);
+
+static int intel_sst_runtime_suspend(struct device *dev)
+{
+ int ret = 0;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (ctx->sst_state == SST_RESET) {
+ dev_dbg(dev, "LPE is already in RESET state, No action\n");
+ return 0;
+ }
+ /* save fw context */
+ if (ctx->ops->save_dsp_context(ctx))
+ return -EBUSY;
+
+ /* Move the SST state to Reset */
+ sst_set_fw_state_locked(ctx, SST_RESET);
+
+ synchronize_irq(ctx->irq_num);
+ flush_workqueue(ctx->post_msg_wq);
+
+ ctx->ops->reset(ctx);
+
+ return ret;
+}
+
+static int intel_sst_suspend(struct device *dev)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+ struct sst_fw_save *fw_save;
+ int i, ret;
+
+ /* check first if we are already in SW reset */
+ if (ctx->sst_state == SST_RESET)
+ return 0;
+
+ /*
+ * check if any stream is active and running
+ * they should already by suspend by soc_suspend
+ */
+ for (i = 1; i <= ctx->info.max_streams; i++) {
+ struct stream_info *stream = &ctx->streams[i];
+
+ if (stream->status == STREAM_RUNNING) {
+ dev_err(dev, "stream %d is running, can't suspend, abort\n", i);
+ return -EBUSY;
+ }
+
+ if (ctx->pdata->streams_lost_on_suspend) {
+ stream->resume_status = stream->status;
+ stream->resume_prev = stream->prev;
+ if (stream->status != STREAM_UN_INIT)
+ sst_free_stream(ctx, i);
+ }
+ }
+ synchronize_irq(ctx->irq_num);
+ flush_workqueue(ctx->post_msg_wq);
+
+ /* Move the SST state to Reset */
+ sst_set_fw_state_locked(ctx, SST_RESET);
+
+ /* tell DSP we are suspending */
+ if (ctx->ops->save_dsp_context(ctx))
+ return -EBUSY;
+
+ /* save the memories */
+ fw_save = kzalloc(sizeof(*fw_save), GFP_KERNEL);
+ if (!fw_save)
+ return -ENOMEM;
+ fw_save->iram = kvzalloc(ctx->iram_end - ctx->iram_base, GFP_KERNEL);
+ if (!fw_save->iram) {
+ ret = -ENOMEM;
+ goto iram;
+ }
+ fw_save->dram = kvzalloc(ctx->dram_end - ctx->dram_base, GFP_KERNEL);
+ if (!fw_save->dram) {
+ ret = -ENOMEM;
+ goto dram;
+ }
+ fw_save->sram = kvzalloc(SST_MAILBOX_SIZE, GFP_KERNEL);
+ if (!fw_save->sram) {
+ ret = -ENOMEM;
+ goto sram;
+ }
+
+ fw_save->ddr = kvzalloc(ctx->ddr_end - ctx->ddr_base, GFP_KERNEL);
+ if (!fw_save->ddr) {
+ ret = -ENOMEM;
+ goto ddr;
+ }
+
+ memcpy32_fromio(fw_save->iram, ctx->iram, ctx->iram_end - ctx->iram_base);
+ memcpy32_fromio(fw_save->dram, ctx->dram, ctx->dram_end - ctx->dram_base);
+ memcpy32_fromio(fw_save->sram, ctx->mailbox, SST_MAILBOX_SIZE);
+ memcpy32_fromio(fw_save->ddr, ctx->ddr, ctx->ddr_end - ctx->ddr_base);
+
+ ctx->fw_save = fw_save;
+ ctx->ops->reset(ctx);
+ return 0;
+ddr:
+ kvfree(fw_save->sram);
+sram:
+ kvfree(fw_save->dram);
+dram:
+ kvfree(fw_save->iram);
+iram:
+ kfree(fw_save);
+ return ret;
+}
+
+static int intel_sst_resume(struct device *dev)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+ struct sst_fw_save *fw_save = ctx->fw_save;
+ struct sst_block *block;
+ int i, ret = 0;
+
+ if (!fw_save)
+ return 0;
+
+ sst_set_fw_state_locked(ctx, SST_FW_LOADING);
+
+ /* we have to restore the memory saved */
+ ctx->ops->reset(ctx);
+
+ ctx->fw_save = NULL;
+
+ memcpy32_toio(ctx->iram, fw_save->iram, ctx->iram_end - ctx->iram_base);
+ memcpy32_toio(ctx->dram, fw_save->dram, ctx->dram_end - ctx->dram_base);
+ memcpy32_toio(ctx->mailbox, fw_save->sram, SST_MAILBOX_SIZE);
+ memcpy32_toio(ctx->ddr, fw_save->ddr, ctx->ddr_end - ctx->ddr_base);
+
+ kvfree(fw_save->sram);
+ kvfree(fw_save->dram);
+ kvfree(fw_save->iram);
+ kvfree(fw_save->ddr);
+ kfree(fw_save);
+
+ block = sst_create_block(ctx, 0, FW_DWNL_ID);
+ if (block == NULL)
+ return -ENOMEM;
+
+
+ /* start and wait for ack */
+ ctx->ops->start(ctx);
+ ret = sst_wait_timeout(ctx, block);
+ if (ret) {
+ dev_err(ctx->dev, "fw download failed %d\n", ret);
+ /* FW download failed due to timeout */
+ ret = -EBUSY;
+
+ } else {
+ sst_set_fw_state_locked(ctx, SST_FW_RUNNING);
+ }
+
+ if (ctx->pdata->streams_lost_on_suspend) {
+ for (i = 1; i <= ctx->info.max_streams; i++) {
+ struct stream_info *stream = &ctx->streams[i];
+
+ if (stream->resume_status != STREAM_UN_INIT) {
+ dev_dbg(ctx->dev, "Re-allocing stream %d status %d prev %d\n",
+ i, stream->resume_status,
+ stream->resume_prev);
+ sst_realloc_stream(ctx, i);
+ stream->status = stream->resume_status;
+ stream->prev = stream->resume_prev;
+ }
+ }
+ }
+
+ sst_free_block(ctx, block);
+ return ret;
+}
+
+const struct dev_pm_ops intel_sst_pm = {
+ .suspend = intel_sst_suspend,
+ .resume = intel_sst_resume,
+ .runtime_suspend = intel_sst_runtime_suspend,
+};
+EXPORT_SYMBOL_GPL(intel_sst_pm);
diff --git a/sound/soc/intel/atom/sst/sst.h b/sound/soc/intel/atom/sst/sst.h
new file mode 100644
index 000000000..4d37d39fd
--- /dev/null
+++ b/sound/soc/intel/atom/sst/sst.h
@@ -0,0 +1,533 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * sst.h - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corporation
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Common private declarations for SST
+ */
+#ifndef __SST_H__
+#define __SST_H__
+
+#include <linux/firmware.h>
+
+/* driver names */
+#define SST_DRV_NAME "intel_sst_driver"
+#define SST_MRFLD_PCI_ID 0x119A
+#define SST_BYT_ACPI_ID 0x80860F28
+#define SST_CHV_ACPI_ID 0x808622A8
+
+#define SST_SUSPEND_DELAY 2000
+#define FW_CONTEXT_MEM (64*1024)
+#define SST_ICCM_BOUNDARY 4
+#define SST_CONFIG_SSP_SIGN 0x7ffe8001
+
+#define MRFLD_FW_VIRTUAL_BASE 0xC0000000
+#define MRFLD_FW_DDR_BASE_OFFSET 0x0
+#define MRFLD_FW_FEATURE_BASE_OFFSET 0x4
+#define MRFLD_FW_BSS_RESET_BIT 0
+
+/* SST Shim register map */
+#define SST_CSR 0x00
+#define SST_ISRX 0x18
+#define SST_IMRX 0x28
+#define SST_IPCX 0x38 /* IPC IA -> SST */
+#define SST_IPCD 0x40 /* IPC SST -> IA */
+
+extern const struct dev_pm_ops intel_sst_pm;
+enum sst_states {
+ SST_FW_LOADING = 1,
+ SST_FW_RUNNING,
+ SST_RESET,
+ SST_SHUTDOWN,
+};
+
+enum sst_algo_ops {
+ SST_SET_ALGO = 0,
+ SST_GET_ALGO = 1,
+};
+
+#define SST_BLOCK_TIMEOUT 1000
+
+#define FW_SIGNATURE_SIZE 4
+#define FW_NAME_SIZE 32
+
+/* stream states */
+enum sst_stream_states {
+ STREAM_UN_INIT = 0, /* Freed/Not used stream */
+ STREAM_RUNNING = 1, /* Running */
+ STREAM_PAUSED = 2, /* Paused stream */
+ STREAM_INIT = 3, /* stream init, waiting for data */
+};
+
+enum sst_ram_type {
+ SST_IRAM = 1,
+ SST_DRAM = 2,
+ SST_DDR = 5,
+ SST_CUSTOM_INFO = 7, /* consists of FW binary information */
+};
+
+/* SST shim registers to structure mapping */
+union interrupt_reg {
+ struct {
+ u64 done_interrupt:1;
+ u64 busy_interrupt:1;
+ u64 rsvd:62;
+ } part;
+ u64 full;
+};
+
+union sst_pisr_reg {
+ struct {
+ u32 pssp0:1;
+ u32 pssp1:1;
+ u32 rsvd0:3;
+ u32 dmac:1;
+ u32 rsvd1:26;
+ } part;
+ u32 full;
+};
+
+union sst_pimr_reg {
+ struct {
+ u32 ssp0:1;
+ u32 ssp1:1;
+ u32 rsvd0:3;
+ u32 dmac:1;
+ u32 rsvd1:10;
+ u32 ssp0_sc:1;
+ u32 ssp1_sc:1;
+ u32 rsvd2:3;
+ u32 dmac_sc:1;
+ u32 rsvd3:10;
+ } part;
+ u32 full;
+};
+
+union config_status_reg_mrfld {
+ struct {
+ u64 lpe_reset:1;
+ u64 lpe_reset_vector:1;
+ u64 runstall:1;
+ u64 pwaitmode:1;
+ u64 clk_sel:3;
+ u64 rsvd2:1;
+ u64 sst_clk:3;
+ u64 xt_snoop:1;
+ u64 rsvd3:4;
+ u64 clk_sel1:6;
+ u64 clk_enable:3;
+ u64 rsvd4:6;
+ u64 slim0baseclk:1;
+ u64 rsvd:32;
+ } part;
+ u64 full;
+};
+
+union interrupt_reg_mrfld {
+ struct {
+ u64 done_interrupt:1;
+ u64 busy_interrupt:1;
+ u64 rsvd:62;
+ } part;
+ u64 full;
+};
+
+union sst_imr_reg_mrfld {
+ struct {
+ u64 done_interrupt:1;
+ u64 busy_interrupt:1;
+ u64 rsvd:62;
+ } part;
+ u64 full;
+};
+
+/**
+ * struct sst_block - This structure is used to block a user/fw data call to another
+ * fw/user call
+ *
+ * @condition: condition for blocking check
+ * @ret_code: ret code when block is released
+ * @data: data ptr
+ * @size: size of data
+ * @on: block condition
+ * @msg_id: msg_id = msgid in mfld/ctp, mrfld = NULL
+ * @drv_id: str_id in mfld/ctp, = drv_id in mrfld
+ * @node: list head node
+ */
+struct sst_block {
+ bool condition;
+ int ret_code;
+ void *data;
+ u32 size;
+ bool on;
+ u32 msg_id;
+ u32 drv_id;
+ struct list_head node;
+};
+
+/**
+ * struct stream_info - structure that holds the stream information
+ *
+ * @status : stream current state
+ * @prev : stream prev state
+ * @resume_status : stream current state to restore on resume
+ * @resume_prev : stream prev state to restore on resume
+ * @lock : stream mutex for protecting state
+ * @alloc_param : parameters used for stream (re-)allocation
+ * @pcm_substream : PCM substream
+ * @period_elapsed : PCM period elapsed callback
+ * @sfreq : stream sampling freq
+ * @cumm_bytes : cummulative bytes decoded
+ */
+struct stream_info {
+ unsigned int status;
+ unsigned int prev;
+ unsigned int resume_status;
+ unsigned int resume_prev;
+ struct mutex lock;
+ struct snd_sst_alloc_mrfld alloc_param;
+
+ void *pcm_substream;
+ void (*period_elapsed)(void *pcm_substream);
+
+ unsigned int sfreq;
+ u32 cumm_bytes;
+
+ void *compr_cb_param;
+ void (*compr_cb)(void *compr_cb_param);
+
+ void *drain_cb_param;
+ void (*drain_notify)(void *drain_cb_param);
+
+ unsigned int num_ch;
+ unsigned int pipe_id;
+ unsigned int task_id;
+};
+
+#define SST_FW_SIGN "$SST"
+#define SST_FW_LIB_SIGN "$LIB"
+
+/**
+ * struct sst_fw_header - FW file headers
+ *
+ * @signature : FW signature
+ * @file_size: size of fw image
+ * @modules : # of modules
+ * @file_format : version of header format
+ * @reserved : reserved fields
+ */
+struct sst_fw_header {
+ unsigned char signature[FW_SIGNATURE_SIZE];
+ u32 file_size;
+ u32 modules;
+ u32 file_format;
+ u32 reserved[4];
+};
+
+/**
+ * struct fw_module_header - module header in FW
+ *
+ * @signature: module signature
+ * @mod_size: size of module
+ * @blocks: block count
+ * @type: block type
+ * @entry_point: module netry point
+ */
+struct fw_module_header {
+ unsigned char signature[FW_SIGNATURE_SIZE];
+ u32 mod_size;
+ u32 blocks;
+ u32 type;
+ u32 entry_point;
+};
+
+/**
+ * struct fw_block_info - block header for FW
+ *
+ * @type: block ram type I/D
+ * @size: size of block
+ * @ram_offset: offset in ram
+ */
+struct fw_block_info {
+ enum sst_ram_type type;
+ u32 size;
+ u32 ram_offset;
+ u32 rsvd;
+};
+
+struct sst_runtime_param {
+ struct snd_sst_runtime_params param;
+};
+
+struct sst_sg_list {
+ struct scatterlist *src;
+ struct scatterlist *dst;
+ int list_len;
+ unsigned int sg_idx;
+};
+
+struct sst_memcpy_list {
+ struct list_head memcpylist;
+ void *dstn;
+ const void *src;
+ u32 size;
+ bool is_io;
+};
+
+/*Firmware Module Information*/
+enum sst_lib_dwnld_status {
+ SST_LIB_NOT_FOUND = 0,
+ SST_LIB_FOUND,
+ SST_LIB_DOWNLOADED,
+};
+
+struct sst_module_info {
+ const char *name; /*Library name*/
+ u32 id; /*Module ID*/
+ u32 entry_pt; /*Module entry point*/
+ u8 status; /*module status*/
+ u8 rsvd1;
+ u16 rsvd2;
+};
+
+/*
+ * Structure for managing the Library Region(1.5MB)
+ * in DDR in Merrifield
+ */
+struct sst_mem_mgr {
+ phys_addr_t current_base;
+ int avail;
+ unsigned int count;
+};
+
+struct sst_ipc_reg {
+ int ipcx;
+ int ipcd;
+};
+
+struct sst_fw_save {
+ void *iram; /* allocated via kvmalloc() */
+ void *dram; /* allocated via kvmalloc() */
+ void *sram; /* allocated via kvmalloc() */
+ void *ddr; /* allocated via kvmalloc() */
+};
+
+/**
+ * struct intel_sst_drv - driver ops
+ *
+ * @sst_state : current sst device state
+ * @dev_id : device identifier, pci_id for pci devices and acpi_id for acpi
+ * devices
+ * @shim : SST shim pointer
+ * @mailbox : SST mailbox pointer
+ * @iram : SST IRAM pointer
+ * @dram : SST DRAM pointer
+ * @pdata : SST info passed as a part of pci platform data
+ * @shim_phy_add : SST shim phy addr
+ * @ipc_dispatch_list : ipc messages dispatched
+ * @rx_list : to copy the process_reply/process_msg from DSP
+ * @ipc_post_msg_wq : wq to post IPC messages context
+ * @mad_ops : MAD driver operations registered
+ * @mad_wq : MAD driver wq
+ * @post_msg_wq : wq to post IPC messages
+ * @streams : sst stream contexts
+ * @list_lock : sst driver list lock (deprecated)
+ * @ipc_spin_lock : spin lock to handle audio shim access and ipc queue
+ * @block_lock : spin lock to add block to block_list and assign pvt_id
+ * @rx_msg_lock : spin lock to handle the rx messages from the DSP
+ * @scard_ops : sst card ops
+ * @pci : sst pci device struture
+ * @dev : pointer to current device struct
+ * @sst_lock : sst device lock
+ * @pvt_id : sst private id
+ * @stream_cnt : total sst active stream count
+ * @pb_streams : total active pb streams
+ * @cp_streams : total active cp streams
+ * @audio_start : audio status
+ * @qos : PM Qos struct
+ * firmware_name : Firmware / Library name
+ */
+struct intel_sst_drv {
+ int sst_state;
+ int irq_num;
+ unsigned int dev_id;
+ void __iomem *ddr;
+ void __iomem *shim;
+ void __iomem *mailbox;
+ void __iomem *iram;
+ void __iomem *dram;
+ unsigned int mailbox_add;
+ unsigned int iram_base;
+ unsigned int dram_base;
+ unsigned int shim_phy_add;
+ unsigned int iram_end;
+ unsigned int dram_end;
+ unsigned int ddr_end;
+ unsigned int ddr_base;
+ unsigned int mailbox_recv_offset;
+ struct list_head block_list;
+ struct list_head ipc_dispatch_list;
+ struct sst_platform_info *pdata;
+ struct list_head rx_list;
+ struct work_struct ipc_post_msg_wq;
+ wait_queue_head_t wait_queue;
+ struct workqueue_struct *post_msg_wq;
+ unsigned int tstamp;
+ /* str_id 0 is not used */
+ struct stream_info streams[MAX_NUM_STREAMS+1];
+ spinlock_t ipc_spin_lock;
+ spinlock_t block_lock;
+ spinlock_t rx_msg_lock;
+ struct pci_dev *pci;
+ struct device *dev;
+ volatile long unsigned pvt_id;
+ struct mutex sst_lock;
+ unsigned int stream_cnt;
+ unsigned int csr_value;
+ void *fw_in_mem;
+ struct sst_sg_list fw_sg_list, library_list;
+ struct intel_sst_ops *ops;
+ struct sst_info info;
+ struct pm_qos_request *qos;
+ unsigned int use_dma;
+ unsigned int use_lli;
+ atomic_t fw_clear_context;
+ bool lib_dwnld_reqd;
+ struct list_head memcpy_list;
+ struct sst_ipc_reg ipc_reg;
+ struct sst_mem_mgr lib_mem_mgr;
+ /*
+ * Holder for firmware name. Due to async call it needs to be
+ * persistent till worker thread gets called
+ */
+ char firmware_name[FW_NAME_SIZE];
+
+ struct snd_sst_fw_version fw_version;
+ struct sst_fw_save *fw_save;
+};
+
+/* misc definitions */
+#define FW_DWNL_ID 0x01
+
+struct intel_sst_ops {
+ irqreturn_t (*interrupt)(int, void *);
+ irqreturn_t (*irq_thread)(int, void *);
+ void (*clear_interrupt)(struct intel_sst_drv *ctx);
+ int (*start)(struct intel_sst_drv *ctx);
+ int (*reset)(struct intel_sst_drv *ctx);
+ void (*process_reply)(struct intel_sst_drv *ctx, struct ipc_post *msg);
+ int (*post_message)(struct intel_sst_drv *ctx,
+ struct ipc_post *msg, bool sync);
+ void (*process_message)(struct ipc_post *msg);
+ void (*set_bypass)(bool set);
+ int (*save_dsp_context)(struct intel_sst_drv *sst);
+ void (*restore_dsp_context)(void);
+ int (*alloc_stream)(struct intel_sst_drv *ctx, void *params);
+ void (*post_download)(struct intel_sst_drv *sst);
+};
+
+int sst_realloc_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
+int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
+int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
+int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
+int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
+int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
+int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ struct snd_sst_bytes_v2 *bytes);
+int sst_set_stream_param(int str_id, struct snd_sst_params *str_param);
+int sst_set_metadata(int str_id, char *params);
+int sst_get_stream(struct intel_sst_drv *ctx,
+ struct snd_sst_params *str_param);
+int sst_get_stream_allocated(struct intel_sst_drv *ctx,
+ struct snd_sst_params *str_param,
+ struct snd_sst_lib_download **lib_dnld);
+int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
+ int str_id, bool partial_drain);
+int sst_post_message_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ struct ipc_post *ipc_msg, bool sync);
+void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx, struct ipc_post *msg);
+int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx);
+int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx);
+void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx);
+
+int sst_load_fw(struct intel_sst_drv *sst_drv_ctx);
+int sst_load_library(struct snd_sst_lib_download *lib, u8 ops);
+void sst_post_download_mrfld(struct intel_sst_drv *ctx);
+int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx);
+void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx);
+
+int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
+ struct sst_block *block);
+int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx,
+ struct sst_block *block);
+int sst_create_ipc_msg(struct ipc_post **arg, bool large);
+int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id);
+void sst_clean_stream(struct stream_info *stream);
+int intel_sst_register_compress(struct intel_sst_drv *sst);
+int intel_sst_remove_compress(struct intel_sst_drv *sst);
+void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id);
+int sst_send_sync_msg(int ipc, int str_id);
+int sst_get_num_channel(struct snd_sst_params *str_param);
+int sst_get_sfreq(struct snd_sst_params *str_param);
+int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params);
+void sst_restore_fw_context(void);
+struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
+ u32 msg_id, u32 drv_id);
+int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large,
+ struct intel_sst_drv *sst_drv_ctx, struct sst_block **block,
+ u32 msg_id, u32 drv_id);
+int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed);
+int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
+ u32 drv_id, u32 ipc, void *data, u32 size);
+int sst_request_firmware_async(struct intel_sst_drv *ctx);
+int sst_driver_ops(struct intel_sst_drv *sst);
+struct sst_platform_info *sst_get_acpi_driver_data(const char *hid);
+void sst_firmware_load_cb(const struct firmware *fw, void *context);
+int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
+ int task_id, int ipc_msg, int cmd_id, int pipe_id,
+ size_t mbox_data_len, const void *mbox_data, void **data,
+ bool large, bool fill_dsp, bool sync, bool response);
+
+void sst_process_pending_msg(struct work_struct *work);
+int sst_assign_pvt_id(struct intel_sst_drv *drv);
+int sst_validate_strid(struct intel_sst_drv *sst_drv_ctx, int str_id);
+struct stream_info *get_stream_info(struct intel_sst_drv *sst_drv_ctx,
+ int str_id);
+int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ u32 pipe_id);
+u32 relocate_imr_addr_mrfld(u32 base_addr);
+void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst,
+ struct ipc_post *msg);
+int sst_pm_runtime_put(struct intel_sst_drv *sst_drv);
+int sst_shim_write(void __iomem *addr, int offset, int value);
+u32 sst_shim_read(void __iomem *addr, int offset);
+u64 sst_reg_read64(void __iomem *addr, int offset);
+int sst_shim_write64(void __iomem *addr, int offset, u64 value);
+u64 sst_shim_read64(void __iomem *addr, int offset);
+void sst_set_fw_state_locked(
+ struct intel_sst_drv *sst_drv_ctx, int sst_state);
+void sst_fill_header_mrfld(union ipc_header_mrfld *header,
+ int msg, int task_id, int large, int drv_id);
+void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg,
+ int pipe_id, int len);
+
+int sst_register(struct device *);
+int sst_unregister(struct device *);
+
+int sst_alloc_drv_context(struct intel_sst_drv **ctx,
+ struct device *dev, unsigned int dev_id);
+int sst_context_init(struct intel_sst_drv *ctx);
+void sst_context_cleanup(struct intel_sst_drv *ctx);
+void sst_configure_runtime_pm(struct intel_sst_drv *ctx);
+void memcpy32_toio(void __iomem *dst, const void *src, int count);
+void memcpy32_fromio(void *dst, const void __iomem *src, int count);
+
+#endif
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
new file mode 100644
index 000000000..3be64430c
--- /dev/null
+++ b/sound/soc/intel/atom/sst/sst_acpi.c
@@ -0,0 +1,372 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * sst_acpi.c - SST (LPE) driver init file for ACPI enumeration.
+ *
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * Authors: Ramesh Babu K V <Ramesh.Babu@intel.com>
+ * Authors: Omair Mohammed Abdullah <omair.m.abdullah@intel.com>
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+#include <asm/platform_sst_audio.h>
+#include <sound/core.h>
+#include <sound/intel-dsp-config.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <acpi/acbuffer.h>
+#include <acpi/platform/acenv.h>
+#include <acpi/platform/aclinux.h>
+#include <acpi/actypes.h>
+#include <acpi/acpi_bus.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include "../sst-mfld-platform.h"
+#include "../../common/soc-intel-quirks.h"
+#include "sst.h"
+
+/* LPE viewpoint addresses */
+#define SST_BYT_IRAM_PHY_START 0xff2c0000
+#define SST_BYT_IRAM_PHY_END 0xff2d4000
+#define SST_BYT_DRAM_PHY_START 0xff300000
+#define SST_BYT_DRAM_PHY_END 0xff320000
+#define SST_BYT_IMR_VIRT_START 0xc0000000 /* virtual addr in LPE */
+#define SST_BYT_IMR_VIRT_END 0xc01fffff
+#define SST_BYT_SHIM_PHY_ADDR 0xff340000
+#define SST_BYT_MBOX_PHY_ADDR 0xff344000
+#define SST_BYT_DMA0_PHY_ADDR 0xff298000
+#define SST_BYT_DMA1_PHY_ADDR 0xff29c000
+#define SST_BYT_SSP0_PHY_ADDR 0xff2a0000
+#define SST_BYT_SSP2_PHY_ADDR 0xff2a2000
+
+#define BYT_FW_MOD_TABLE_OFFSET 0x80000
+#define BYT_FW_MOD_TABLE_SIZE 0x100
+#define BYT_FW_MOD_OFFSET (BYT_FW_MOD_TABLE_OFFSET + BYT_FW_MOD_TABLE_SIZE)
+
+static const struct sst_info byt_fwparse_info = {
+ .use_elf = false,
+ .max_streams = 25,
+ .iram_start = SST_BYT_IRAM_PHY_START,
+ .iram_end = SST_BYT_IRAM_PHY_END,
+ .iram_use = true,
+ .dram_start = SST_BYT_DRAM_PHY_START,
+ .dram_end = SST_BYT_DRAM_PHY_END,
+ .dram_use = true,
+ .imr_start = SST_BYT_IMR_VIRT_START,
+ .imr_end = SST_BYT_IMR_VIRT_END,
+ .imr_use = true,
+ .mailbox_start = SST_BYT_MBOX_PHY_ADDR,
+ .num_probes = 0,
+ .lpe_viewpt_rqd = true,
+};
+
+static const struct sst_ipc_info byt_ipc_info = {
+ .ipc_offset = 0,
+ .mbox_recv_off = 0x400,
+};
+
+static const struct sst_lib_dnld_info byt_lib_dnld_info = {
+ .mod_base = SST_BYT_IMR_VIRT_START,
+ .mod_end = SST_BYT_IMR_VIRT_END,
+ .mod_table_offset = BYT_FW_MOD_TABLE_OFFSET,
+ .mod_table_size = BYT_FW_MOD_TABLE_SIZE,
+ .mod_ddr_dnld = false,
+};
+
+static const struct sst_res_info byt_rvp_res_info = {
+ .shim_offset = 0x140000,
+ .shim_size = 0x000100,
+ .shim_phy_addr = SST_BYT_SHIM_PHY_ADDR,
+ .ssp0_offset = 0xa0000,
+ .ssp0_size = 0x1000,
+ .dma0_offset = 0x98000,
+ .dma0_size = 0x4000,
+ .dma1_offset = 0x9c000,
+ .dma1_size = 0x4000,
+ .iram_offset = 0x0c0000,
+ .iram_size = 0x14000,
+ .dram_offset = 0x100000,
+ .dram_size = 0x28000,
+ .mbox_offset = 0x144000,
+ .mbox_size = 0x1000,
+ .acpi_lpe_res_index = 0,
+ .acpi_ddr_index = 2,
+ .acpi_ipc_irq_index = 5,
+};
+
+/* BYTCR has different BIOS from BYT */
+static const struct sst_res_info bytcr_res_info = {
+ .shim_offset = 0x140000,
+ .shim_size = 0x000100,
+ .shim_phy_addr = SST_BYT_SHIM_PHY_ADDR,
+ .ssp0_offset = 0xa0000,
+ .ssp0_size = 0x1000,
+ .dma0_offset = 0x98000,
+ .dma0_size = 0x4000,
+ .dma1_offset = 0x9c000,
+ .dma1_size = 0x4000,
+ .iram_offset = 0x0c0000,
+ .iram_size = 0x14000,
+ .dram_offset = 0x100000,
+ .dram_size = 0x28000,
+ .mbox_offset = 0x144000,
+ .mbox_size = 0x1000,
+ .acpi_lpe_res_index = 0,
+ .acpi_ddr_index = 2,
+ .acpi_ipc_irq_index = 0
+};
+
+static struct sst_platform_info byt_rvp_platform_data = {
+ .probe_data = &byt_fwparse_info,
+ .ipc_info = &byt_ipc_info,
+ .lib_info = &byt_lib_dnld_info,
+ .res_info = &byt_rvp_res_info,
+ .platform = "sst-mfld-platform",
+ .streams_lost_on_suspend = true,
+};
+
+/* Cherryview (Cherrytrail and Braswell) uses same mrfld dpcm fw as Baytrail,
+ * so pdata is same as Baytrail, minus the streams_lost_on_suspend quirk.
+ */
+static struct sst_platform_info chv_platform_data = {
+ .probe_data = &byt_fwparse_info,
+ .ipc_info = &byt_ipc_info,
+ .lib_info = &byt_lib_dnld_info,
+ .res_info = &byt_rvp_res_info,
+ .platform = "sst-mfld-platform",
+};
+
+static int sst_platform_get_resources(struct intel_sst_drv *ctx)
+{
+ struct resource *rsrc;
+ struct platform_device *pdev = to_platform_device(ctx->dev);
+
+ /* All ACPI resource request here */
+ /* Get Shim addr */
+ rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
+ ctx->pdata->res_info->acpi_lpe_res_index);
+ if (!rsrc) {
+ dev_err(ctx->dev, "Invalid SHIM base from IFWI\n");
+ return -EIO;
+ }
+ dev_info(ctx->dev, "LPE base: %#x size:%#x", (unsigned int) rsrc->start,
+ (unsigned int)resource_size(rsrc));
+
+ ctx->iram_base = rsrc->start + ctx->pdata->res_info->iram_offset;
+ ctx->iram_end = ctx->iram_base + ctx->pdata->res_info->iram_size - 1;
+ dev_info(ctx->dev, "IRAM base: %#x", ctx->iram_base);
+ ctx->iram = devm_ioremap(ctx->dev, ctx->iram_base,
+ ctx->pdata->res_info->iram_size);
+ if (!ctx->iram) {
+ dev_err(ctx->dev, "unable to map IRAM\n");
+ return -EIO;
+ }
+
+ ctx->dram_base = rsrc->start + ctx->pdata->res_info->dram_offset;
+ ctx->dram_end = ctx->dram_base + ctx->pdata->res_info->dram_size - 1;
+ dev_info(ctx->dev, "DRAM base: %#x", ctx->dram_base);
+ ctx->dram = devm_ioremap(ctx->dev, ctx->dram_base,
+ ctx->pdata->res_info->dram_size);
+ if (!ctx->dram) {
+ dev_err(ctx->dev, "unable to map DRAM\n");
+ return -EIO;
+ }
+
+ ctx->shim_phy_add = rsrc->start + ctx->pdata->res_info->shim_offset;
+ dev_info(ctx->dev, "SHIM base: %#x", ctx->shim_phy_add);
+ ctx->shim = devm_ioremap(ctx->dev, ctx->shim_phy_add,
+ ctx->pdata->res_info->shim_size);
+ if (!ctx->shim) {
+ dev_err(ctx->dev, "unable to map SHIM\n");
+ return -EIO;
+ }
+
+ /* reassign physical address to LPE viewpoint address */
+ ctx->shim_phy_add = ctx->pdata->res_info->shim_phy_addr;
+
+ /* Get mailbox addr */
+ ctx->mailbox_add = rsrc->start + ctx->pdata->res_info->mbox_offset;
+ dev_info(ctx->dev, "Mailbox base: %#x", ctx->mailbox_add);
+ ctx->mailbox = devm_ioremap(ctx->dev, ctx->mailbox_add,
+ ctx->pdata->res_info->mbox_size);
+ if (!ctx->mailbox) {
+ dev_err(ctx->dev, "unable to map mailbox\n");
+ return -EIO;
+ }
+
+ /* reassign physical address to LPE viewpoint address */
+ ctx->mailbox_add = ctx->info.mailbox_start;
+
+ rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
+ ctx->pdata->res_info->acpi_ddr_index);
+ if (!rsrc) {
+ dev_err(ctx->dev, "Invalid DDR base from IFWI\n");
+ return -EIO;
+ }
+ ctx->ddr_base = rsrc->start;
+ ctx->ddr_end = rsrc->end;
+ dev_info(ctx->dev, "DDR base: %#x", ctx->ddr_base);
+ ctx->ddr = devm_ioremap(ctx->dev, ctx->ddr_base,
+ resource_size(rsrc));
+ if (!ctx->ddr) {
+ dev_err(ctx->dev, "unable to map DDR\n");
+ return -EIO;
+ }
+
+ /* Find the IRQ */
+ ctx->irq_num = platform_get_irq(pdev,
+ ctx->pdata->res_info->acpi_ipc_irq_index);
+ if (ctx->irq_num <= 0)
+ return ctx->irq_num < 0 ? ctx->irq_num : -EIO;
+
+ return 0;
+}
+
+static int sst_acpi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int ret = 0;
+ struct intel_sst_drv *ctx;
+ const struct acpi_device_id *id;
+ struct snd_soc_acpi_mach *mach;
+ struct platform_device *mdev;
+ struct platform_device *plat_dev;
+ struct sst_platform_info *pdata;
+ unsigned int dev_id;
+
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return -ENODEV;
+
+ ret = snd_intel_acpi_dsp_driver_probe(dev, id->id);
+ if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_SST) {
+ dev_dbg(dev, "SST ACPI driver not selected, aborting probe\n");
+ return -ENODEV;
+ }
+
+ dev_dbg(dev, "for %s\n", id->id);
+
+ mach = (struct snd_soc_acpi_mach *)id->driver_data;
+ mach = snd_soc_acpi_find_machine(mach);
+ if (mach == NULL) {
+ dev_err(dev, "No matching machine driver found\n");
+ return -ENODEV;
+ }
+
+ if (soc_intel_is_byt())
+ mach->pdata = &byt_rvp_platform_data;
+ else
+ mach->pdata = &chv_platform_data;
+ pdata = mach->pdata;
+
+ ret = kstrtouint(id->id, 16, &dev_id);
+ if (ret < 0) {
+ dev_err(dev, "Unique device id conversion error: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "ACPI device id: %x\n", dev_id);
+
+ ret = sst_alloc_drv_context(&ctx, dev, dev_id);
+ if (ret < 0)
+ return ret;
+
+ if (soc_intel_is_byt_cr(pdev)) {
+ /* override resource info */
+ byt_rvp_platform_data.res_info = &bytcr_res_info;
+ }
+
+ /* update machine parameters */
+ mach->mach_params.acpi_ipc_irq_index =
+ pdata->res_info->acpi_ipc_irq_index;
+
+ plat_dev = platform_device_register_data(dev, pdata->platform, -1,
+ NULL, 0);
+ if (IS_ERR(plat_dev)) {
+ dev_err(dev, "Failed to create machine device: %s\n",
+ pdata->platform);
+ return PTR_ERR(plat_dev);
+ }
+
+ /*
+ * Create platform device for sst machine driver,
+ * pass machine info as pdata
+ */
+ mdev = platform_device_register_data(dev, mach->drv_name, -1,
+ (const void *)mach, sizeof(*mach));
+ if (IS_ERR(mdev)) {
+ dev_err(dev, "Failed to create machine device: %s\n",
+ mach->drv_name);
+ return PTR_ERR(mdev);
+ }
+
+ /* Fill sst platform data */
+ ctx->pdata = pdata;
+ strcpy(ctx->firmware_name, mach->fw_filename);
+
+ ret = sst_platform_get_resources(ctx);
+ if (ret)
+ return ret;
+
+ ret = sst_context_init(ctx);
+ if (ret < 0)
+ return ret;
+
+ sst_configure_runtime_pm(ctx);
+ platform_set_drvdata(pdev, ctx);
+ return ret;
+}
+
+/**
+* sst_acpi_remove - remove function
+*
+* @pdev: platform device structure
+*
+* This function is called by OS when a device is unloaded
+* This frees the interrupt etc
+*/
+static int sst_acpi_remove(struct platform_device *pdev)
+{
+ struct intel_sst_drv *ctx;
+
+ ctx = platform_get_drvdata(pdev);
+ sst_context_cleanup(ctx);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static const struct acpi_device_id sst_acpi_ids[] = {
+ { "80860F28", (unsigned long)&snd_soc_acpi_intel_baytrail_machines},
+ { "808622A8", (unsigned long)&snd_soc_acpi_intel_cherrytrail_machines},
+ { },
+};
+
+MODULE_DEVICE_TABLE(acpi, sst_acpi_ids);
+
+static struct platform_driver sst_acpi_driver = {
+ .driver = {
+ .name = "intel_sst_acpi",
+ .acpi_match_table = ACPI_PTR(sst_acpi_ids),
+ .pm = &intel_sst_pm,
+ },
+ .probe = sst_acpi_probe,
+ .remove = sst_acpi_remove,
+};
+
+module_platform_driver(sst_acpi_driver);
+
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine ACPI Driver");
+MODULE_AUTHOR("Ramesh Babu K V");
+MODULE_AUTHOR("Omair Mohammed Abdullah");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("sst");
diff --git a/sound/soc/intel/atom/sst/sst_drv_interface.c b/sound/soc/intel/atom/sst/sst_drv_interface.c
new file mode 100644
index 000000000..dc31c2c8f
--- /dev/null
+++ b/sound/soc/intel/atom/sst/sst_drv_interface.c
@@ -0,0 +1,712 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * sst_drv_interface.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corp
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com)
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/math64.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+
+#define NUM_CODEC 2
+#define MIN_FRAGMENT 2
+#define MAX_FRAGMENT 4
+#define MIN_FRAGMENT_SIZE (50 * 1024)
+#define MAX_FRAGMENT_SIZE (1024 * 1024)
+#define SST_GET_BYTES_PER_SAMPLE(pcm_wd_sz) (((pcm_wd_sz + 15) >> 4) << 1)
+#ifdef CONFIG_PM
+#define GET_USAGE_COUNT(dev) (atomic_read(&dev->power.usage_count))
+#else
+#define GET_USAGE_COUNT(dev) 1
+#endif
+
+int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id)
+{
+ struct stream_info *stream;
+ int ret = 0;
+
+ stream = get_stream_info(ctx, str_id);
+ if (stream) {
+ /* str_id is valid, so stream is alloacted */
+ ret = sst_free_stream(ctx, str_id);
+ if (ret)
+ sst_clean_stream(&ctx->streams[str_id]);
+ return ret;
+ } else {
+ dev_err(ctx->dev, "we tried to free stream context %d which was freed!!!\n", str_id);
+ }
+ return ret;
+}
+
+int sst_get_stream_allocated(struct intel_sst_drv *ctx,
+ struct snd_sst_params *str_param,
+ struct snd_sst_lib_download **lib_dnld)
+{
+ int retval;
+
+ retval = ctx->ops->alloc_stream(ctx, str_param);
+ if (retval > 0)
+ dev_dbg(ctx->dev, "Stream allocated %d\n", retval);
+ return retval;
+
+}
+
+/*
+ * sst_get_sfreq - this function returns the frequency of the stream
+ *
+ * @str_param : stream params
+ */
+int sst_get_sfreq(struct snd_sst_params *str_param)
+{
+ switch (str_param->codec) {
+ case SST_CODEC_TYPE_PCM:
+ return str_param->sparams.uc.pcm_params.sfreq;
+ case SST_CODEC_TYPE_AAC:
+ return str_param->sparams.uc.aac_params.externalsr;
+ case SST_CODEC_TYPE_MP3:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * sst_get_num_channel - get number of channels for the stream
+ *
+ * @str_param : stream params
+ */
+int sst_get_num_channel(struct snd_sst_params *str_param)
+{
+ switch (str_param->codec) {
+ case SST_CODEC_TYPE_PCM:
+ return str_param->sparams.uc.pcm_params.num_chan;
+ case SST_CODEC_TYPE_MP3:
+ return str_param->sparams.uc.mp3_params.num_chan;
+ case SST_CODEC_TYPE_AAC:
+ return str_param->sparams.uc.aac_params.num_chan;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * sst_get_stream - this function prepares for stream allocation
+ *
+ * @str_param : stream param
+ */
+int sst_get_stream(struct intel_sst_drv *ctx,
+ struct snd_sst_params *str_param)
+{
+ int retval;
+ struct stream_info *str_info;
+
+ /* stream is not allocated, we are allocating */
+ retval = ctx->ops->alloc_stream(ctx, str_param);
+ if (retval <= 0) {
+ return -EIO;
+ }
+ /* store sampling freq */
+ str_info = &ctx->streams[retval];
+ str_info->sfreq = sst_get_sfreq(str_param);
+
+ return retval;
+}
+
+static int sst_power_control(struct device *dev, bool state)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+ int ret = 0;
+ int usage_count = 0;
+
+ if (state) {
+ ret = pm_runtime_resume_and_get(dev);
+ usage_count = GET_USAGE_COUNT(dev);
+ dev_dbg(ctx->dev, "Enable: pm usage count: %d\n", usage_count);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Runtime get failed with err: %d\n", ret);
+ return ret;
+ }
+ if ((ctx->sst_state == SST_RESET) && (usage_count == 1)) {
+ ret = sst_load_fw(ctx);
+ if (ret) {
+ dev_err(dev, "FW download fail %d\n", ret);
+ sst_set_fw_state_locked(ctx, SST_RESET);
+ ret = sst_pm_runtime_put(ctx);
+ }
+ }
+ } else {
+ usage_count = GET_USAGE_COUNT(dev);
+ dev_dbg(ctx->dev, "Disable: pm usage count: %d\n", usage_count);
+ return sst_pm_runtime_put(ctx);
+ }
+ return ret;
+}
+
+/*
+ * sst_open_pcm_stream - Open PCM interface
+ *
+ * @str_param: parameters of pcm stream
+ *
+ * This function is called by MID sound card driver to open
+ * a new pcm interface
+ */
+static int sst_open_pcm_stream(struct device *dev,
+ struct snd_sst_params *str_param)
+{
+ int retval;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (!str_param)
+ return -EINVAL;
+
+ retval = sst_get_stream(ctx, str_param);
+ if (retval > 0)
+ ctx->stream_cnt++;
+ else
+ dev_err(ctx->dev, "sst_get_stream returned err %d\n", retval);
+
+ return retval;
+}
+
+static int sst_cdev_open(struct device *dev,
+ struct snd_sst_params *str_params, struct sst_compress_cb *cb)
+{
+ int str_id, retval;
+ struct stream_info *stream;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ retval = pm_runtime_resume_and_get(ctx->dev);
+ if (retval < 0)
+ return retval;
+
+ str_id = sst_get_stream(ctx, str_params);
+ if (str_id > 0) {
+ dev_dbg(dev, "stream allocated in sst_cdev_open %d\n", str_id);
+ stream = &ctx->streams[str_id];
+ stream->compr_cb = cb->compr_cb;
+ stream->compr_cb_param = cb->param;
+ stream->drain_notify = cb->drain_notify;
+ stream->drain_cb_param = cb->drain_cb_param;
+ } else {
+ dev_err(dev, "stream encountered error during alloc %d\n", str_id);
+ str_id = -EINVAL;
+ sst_pm_runtime_put(ctx);
+ }
+ return str_id;
+}
+
+static int sst_cdev_close(struct device *dev, unsigned int str_id)
+{
+ int retval;
+ struct stream_info *stream;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ stream = get_stream_info(ctx, str_id);
+ if (!stream) {
+ dev_err(dev, "stream info is NULL for str %d!!!\n", str_id);
+ return -EINVAL;
+ }
+
+ retval = sst_free_stream(ctx, str_id);
+ stream->compr_cb_param = NULL;
+ stream->compr_cb = NULL;
+
+ if (retval)
+ dev_err(dev, "free stream returned err %d\n", retval);
+
+ dev_dbg(dev, "End\n");
+ return retval;
+}
+
+static int sst_cdev_ack(struct device *dev, unsigned int str_id,
+ unsigned long bytes)
+{
+ struct stream_info *stream;
+ struct snd_sst_tstamp fw_tstamp = {0,};
+ int offset;
+ void __iomem *addr;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ stream = get_stream_info(ctx, str_id);
+ if (!stream)
+ return -EINVAL;
+
+ /* update bytes sent */
+ stream->cumm_bytes += bytes;
+ dev_dbg(dev, "bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes);
+
+ addr = ((void __iomem *)(ctx->mailbox + ctx->tstamp)) +
+ (str_id * sizeof(fw_tstamp));
+
+ memcpy_fromio(&fw_tstamp, addr, sizeof(fw_tstamp));
+
+ fw_tstamp.bytes_copied = stream->cumm_bytes;
+ dev_dbg(dev, "bytes sent to fw %llu inc by %ld\n",
+ fw_tstamp.bytes_copied, bytes);
+
+ offset = offsetof(struct snd_sst_tstamp, bytes_copied);
+ sst_shim_write(addr, offset, fw_tstamp.bytes_copied);
+ return 0;
+}
+
+static int sst_cdev_set_metadata(struct device *dev,
+ unsigned int str_id, struct snd_compr_metadata *metadata)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "set metadata for stream %d\n", str_id);
+
+ str_info = get_stream_info(ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+
+ dev_dbg(dev, "pipe id = %d\n", str_info->pipe_id);
+ retval = sst_prepare_and_post_msg(ctx, str_info->task_id, IPC_CMD,
+ IPC_IA_SET_STREAM_PARAMS_MRFLD, str_info->pipe_id,
+ sizeof(*metadata), metadata, NULL,
+ true, true, true, false);
+
+ return retval;
+}
+
+static int sst_cdev_stream_pause(struct device *dev, unsigned int str_id)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ return sst_pause_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_pause_release(struct device *dev,
+ unsigned int str_id)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ return sst_resume_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_start(struct device *dev, unsigned int str_id)
+{
+ struct stream_info *str_info;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ str_info = get_stream_info(ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ str_info->prev = str_info->status;
+ str_info->status = STREAM_RUNNING;
+ return sst_start_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_drop(struct device *dev, unsigned int str_id)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ return sst_drop_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_drain(struct device *dev, unsigned int str_id)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ return sst_drain_stream(ctx, str_id, false);
+}
+
+static int sst_cdev_stream_partial_drain(struct device *dev,
+ unsigned int str_id)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ return sst_drain_stream(ctx, str_id, true);
+}
+
+static int sst_cdev_tstamp(struct device *dev, unsigned int str_id,
+ struct snd_compr_tstamp *tstamp)
+{
+ struct snd_sst_tstamp fw_tstamp = {0,};
+ struct stream_info *stream;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+ void __iomem *addr;
+
+ addr = (void __iomem *)(ctx->mailbox + ctx->tstamp) +
+ (str_id * sizeof(fw_tstamp));
+
+ memcpy_fromio(&fw_tstamp, addr, sizeof(fw_tstamp));
+
+ stream = get_stream_info(ctx, str_id);
+ if (!stream)
+ return -EINVAL;
+ dev_dbg(dev, "rb_counter %llu in bytes\n", fw_tstamp.ring_buffer_counter);
+
+ tstamp->copied_total = fw_tstamp.ring_buffer_counter;
+ tstamp->pcm_frames = fw_tstamp.frames_decoded;
+ tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter,
+ (u64)stream->num_ch * SST_GET_BYTES_PER_SAMPLE(24));
+ tstamp->sampling_rate = fw_tstamp.sampling_frequency;
+
+ dev_dbg(dev, "PCM = %u\n", tstamp->pcm_io_frames);
+ dev_dbg(dev, "Ptr Query on strid = %d copied_total %d, decodec %d\n",
+ str_id, tstamp->copied_total, tstamp->pcm_frames);
+ dev_dbg(dev, "rendered %d\n", tstamp->pcm_io_frames);
+
+ return 0;
+}
+
+static int sst_cdev_caps(struct snd_compr_caps *caps)
+{
+ caps->num_codecs = NUM_CODEC;
+ caps->min_fragment_size = MIN_FRAGMENT_SIZE; /* 50KB */
+ caps->max_fragment_size = MAX_FRAGMENT_SIZE; /* 1024KB */
+ caps->min_fragments = MIN_FRAGMENT;
+ caps->max_fragments = MAX_FRAGMENT;
+ caps->codecs[0] = SND_AUDIOCODEC_MP3;
+ caps->codecs[1] = SND_AUDIOCODEC_AAC;
+ return 0;
+}
+
+static const struct snd_compr_codec_caps caps_mp3 = {
+ .num_descriptors = 1,
+ .descriptor[0].max_ch = 2,
+ .descriptor[0].sample_rates[0] = 48000,
+ .descriptor[0].sample_rates[1] = 44100,
+ .descriptor[0].sample_rates[2] = 32000,
+ .descriptor[0].sample_rates[3] = 16000,
+ .descriptor[0].sample_rates[4] = 8000,
+ .descriptor[0].num_sample_rates = 5,
+ .descriptor[0].bit_rate[0] = 320,
+ .descriptor[0].bit_rate[1] = 192,
+ .descriptor[0].num_bitrates = 2,
+ .descriptor[0].profiles = 0,
+ .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO,
+ .descriptor[0].formats = 0,
+};
+
+static const struct snd_compr_codec_caps caps_aac = {
+ .num_descriptors = 2,
+ .descriptor[1].max_ch = 2,
+ .descriptor[0].sample_rates[0] = 48000,
+ .descriptor[0].sample_rates[1] = 44100,
+ .descriptor[0].sample_rates[2] = 32000,
+ .descriptor[0].sample_rates[3] = 16000,
+ .descriptor[0].sample_rates[4] = 8000,
+ .descriptor[0].num_sample_rates = 5,
+ .descriptor[1].bit_rate[0] = 320,
+ .descriptor[1].bit_rate[1] = 192,
+ .descriptor[1].num_bitrates = 2,
+ .descriptor[1].profiles = 0,
+ .descriptor[1].modes = 0,
+ .descriptor[1].formats =
+ (SND_AUDIOSTREAMFORMAT_MP4ADTS |
+ SND_AUDIOSTREAMFORMAT_RAW),
+};
+
+static int sst_cdev_codec_caps(struct snd_compr_codec_caps *codec)
+{
+ if (codec->codec == SND_AUDIOCODEC_MP3)
+ *codec = caps_mp3;
+ else if (codec->codec == SND_AUDIOCODEC_AAC)
+ *codec = caps_aac;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id)
+{
+ struct stream_info *stream;
+
+ dev_dbg(ctx->dev, "fragment elapsed from firmware for str_id %d\n",
+ str_id);
+ stream = &ctx->streams[str_id];
+ if (stream->compr_cb)
+ stream->compr_cb(stream->compr_cb_param);
+}
+
+/*
+ * sst_close_pcm_stream - Close PCM interface
+ *
+ * @str_id: stream id to be closed
+ *
+ * This function is called by MID sound card driver to close
+ * an existing pcm interface
+ */
+static int sst_close_pcm_stream(struct device *dev, unsigned int str_id)
+{
+ struct stream_info *stream;
+ int retval = 0;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ stream = get_stream_info(ctx, str_id);
+ if (!stream) {
+ dev_err(ctx->dev, "stream info is NULL for str %d!!!\n", str_id);
+ return -EINVAL;
+ }
+
+ retval = free_stream_context(ctx, str_id);
+ stream->pcm_substream = NULL;
+ stream->status = STREAM_UN_INIT;
+ stream->period_elapsed = NULL;
+ ctx->stream_cnt--;
+
+ if (retval)
+ dev_err(ctx->dev, "free stream returned err %d\n", retval);
+
+ dev_dbg(ctx->dev, "Exit\n");
+ return 0;
+}
+
+static inline int sst_calc_tstamp(struct intel_sst_drv *ctx,
+ struct pcm_stream_info *info,
+ struct snd_pcm_substream *substream,
+ struct snd_sst_tstamp *fw_tstamp)
+{
+ size_t delay_bytes, delay_frames;
+ size_t buffer_sz;
+ u32 pointer_bytes, pointer_samples;
+
+ dev_dbg(ctx->dev, "mrfld ring_buffer_counter %llu in bytes\n",
+ fw_tstamp->ring_buffer_counter);
+ dev_dbg(ctx->dev, "mrfld hardware_counter %llu in bytes\n",
+ fw_tstamp->hardware_counter);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ delay_bytes = (size_t) (fw_tstamp->ring_buffer_counter -
+ fw_tstamp->hardware_counter);
+ else
+ delay_bytes = (size_t) (fw_tstamp->hardware_counter -
+ fw_tstamp->ring_buffer_counter);
+ delay_frames = bytes_to_frames(substream->runtime, delay_bytes);
+ buffer_sz = snd_pcm_lib_buffer_bytes(substream);
+ div_u64_rem(fw_tstamp->ring_buffer_counter, buffer_sz, &pointer_bytes);
+ pointer_samples = bytes_to_samples(substream->runtime, pointer_bytes);
+
+ dev_dbg(ctx->dev, "pcm delay %zu in bytes\n", delay_bytes);
+
+ info->buffer_ptr = pointer_samples / substream->runtime->channels;
+
+ info->pcm_delay = delay_frames;
+ dev_dbg(ctx->dev, "buffer ptr %llu pcm_delay rep: %llu\n",
+ info->buffer_ptr, info->pcm_delay);
+ return 0;
+}
+
+static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info)
+{
+ struct stream_info *stream;
+ struct snd_pcm_substream *substream;
+ struct snd_sst_tstamp fw_tstamp;
+ unsigned int str_id;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+ void __iomem *addr;
+
+ str_id = info->str_id;
+ stream = get_stream_info(ctx, str_id);
+ if (!stream)
+ return -EINVAL;
+
+ if (!stream->pcm_substream)
+ return -EINVAL;
+ substream = stream->pcm_substream;
+
+ addr = (void __iomem *)(ctx->mailbox + ctx->tstamp) +
+ (str_id * sizeof(fw_tstamp));
+
+ memcpy_fromio(&fw_tstamp, addr, sizeof(fw_tstamp));
+
+ return sst_calc_tstamp(ctx, info, substream, &fw_tstamp);
+}
+
+static int sst_stream_start(struct device *dev, int str_id)
+{
+ struct stream_info *str_info;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (ctx->sst_state != SST_FW_RUNNING)
+ return 0;
+ str_info = get_stream_info(ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ str_info->prev = str_info->status;
+ str_info->status = STREAM_RUNNING;
+ sst_start_stream(ctx, str_id);
+
+ return 0;
+}
+
+static int sst_stream_drop(struct device *dev, int str_id)
+{
+ struct stream_info *str_info;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (ctx->sst_state != SST_FW_RUNNING)
+ return 0;
+
+ str_info = get_stream_info(ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ str_info->prev = STREAM_UN_INIT;
+ str_info->status = STREAM_INIT;
+ return sst_drop_stream(ctx, str_id);
+}
+
+static int sst_stream_pause(struct device *dev, int str_id)
+{
+ struct stream_info *str_info;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (ctx->sst_state != SST_FW_RUNNING)
+ return 0;
+
+ str_info = get_stream_info(ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+
+ return sst_pause_stream(ctx, str_id);
+}
+
+static int sst_stream_resume(struct device *dev, int str_id)
+{
+ struct stream_info *str_info;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (ctx->sst_state != SST_FW_RUNNING)
+ return 0;
+
+ str_info = get_stream_info(ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ return sst_resume_stream(ctx, str_id);
+}
+
+static int sst_stream_init(struct device *dev, struct pcm_stream_info *str_info)
+{
+ int str_id = 0;
+ struct stream_info *stream;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ str_id = str_info->str_id;
+
+ if (ctx->sst_state != SST_FW_RUNNING)
+ return 0;
+
+ stream = get_stream_info(ctx, str_id);
+ if (!stream)
+ return -EINVAL;
+
+ dev_dbg(ctx->dev, "setting the period ptrs\n");
+ stream->pcm_substream = str_info->arg;
+ stream->period_elapsed = str_info->period_elapsed;
+ stream->sfreq = str_info->sfreq;
+ stream->prev = stream->status;
+ stream->status = STREAM_INIT;
+ dev_dbg(ctx->dev,
+ "pcm_substream %p, period_elapsed %p, sfreq %d, status %d\n",
+ stream->pcm_substream, stream->period_elapsed,
+ stream->sfreq, stream->status);
+
+ return 0;
+}
+
+/*
+ * sst_set_byte_stream - Set generic params
+ *
+ * @cmd: control cmd to be set
+ * @arg: command argument
+ *
+ * This function is called by MID sound card driver to configure
+ * SST runtime params.
+ */
+static int sst_send_byte_stream(struct device *dev,
+ struct snd_sst_bytes_v2 *bytes)
+{
+ int ret_val = 0;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (NULL == bytes)
+ return -EINVAL;
+ ret_val = pm_runtime_resume_and_get(ctx->dev);
+ if (ret_val < 0)
+ return ret_val;
+
+ ret_val = sst_send_byte_stream_mrfld(ctx, bytes);
+ sst_pm_runtime_put(ctx);
+
+ return ret_val;
+}
+
+static struct sst_ops pcm_ops = {
+ .open = sst_open_pcm_stream,
+ .stream_init = sst_stream_init,
+ .stream_start = sst_stream_start,
+ .stream_drop = sst_stream_drop,
+ .stream_pause = sst_stream_pause,
+ .stream_pause_release = sst_stream_resume,
+ .stream_read_tstamp = sst_read_timestamp,
+ .send_byte_stream = sst_send_byte_stream,
+ .close = sst_close_pcm_stream,
+ .power = sst_power_control,
+};
+
+static struct compress_sst_ops compr_ops = {
+ .open = sst_cdev_open,
+ .close = sst_cdev_close,
+ .stream_pause = sst_cdev_stream_pause,
+ .stream_pause_release = sst_cdev_stream_pause_release,
+ .stream_start = sst_cdev_stream_start,
+ .stream_drop = sst_cdev_stream_drop,
+ .stream_drain = sst_cdev_stream_drain,
+ .stream_partial_drain = sst_cdev_stream_partial_drain,
+ .tstamp = sst_cdev_tstamp,
+ .ack = sst_cdev_ack,
+ .get_caps = sst_cdev_caps,
+ .get_codec_caps = sst_cdev_codec_caps,
+ .set_metadata = sst_cdev_set_metadata,
+ .power = sst_power_control,
+};
+
+static struct sst_device sst_dsp_device = {
+ .name = "Intel(R) SST LPE",
+ .dev = NULL,
+ .ops = &pcm_ops,
+ .compr_ops = &compr_ops,
+};
+
+/*
+ * sst_register - function to register DSP
+ *
+ * This functions registers DSP with the platform driver
+ */
+int sst_register(struct device *dev)
+{
+ int ret_val;
+
+ sst_dsp_device.dev = dev;
+ ret_val = sst_register_dsp(&sst_dsp_device);
+ if (ret_val)
+ dev_err(dev, "Unable to register DSP with platform driver\n");
+
+ return ret_val;
+}
+
+int sst_unregister(struct device *dev)
+{
+ return sst_unregister_dsp(&sst_dsp_device);
+}
diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c
new file mode 100644
index 000000000..4e039c717
--- /dev/null
+++ b/sound/soc/intel/atom/sst/sst_ipc.c
@@ -0,0 +1,375 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * sst_ipc.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corporation
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/intel-mid.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+
+struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
+ u32 msg_id, u32 drv_id)
+{
+ struct sst_block *msg;
+
+ dev_dbg(ctx->dev, "Enter\n");
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return NULL;
+ msg->condition = false;
+ msg->on = true;
+ msg->msg_id = msg_id;
+ msg->drv_id = drv_id;
+ spin_lock_bh(&ctx->block_lock);
+ list_add_tail(&msg->node, &ctx->block_list);
+ spin_unlock_bh(&ctx->block_lock);
+
+ return msg;
+}
+
+/*
+ * while handling the interrupts, we need to check for message status and
+ * then if we are blocking for a message
+ *
+ * here we are unblocking the blocked ones, this is based on id we have
+ * passed and search that for block threads.
+ * We will not find block in two cases
+ * a) when its small message and block in not there, so silently ignore
+ * them
+ * b) when we are actually not able to find the block (bug perhaps)
+ *
+ * Since we have bit of small messages we can spam kernel log with err
+ * print on above so need to keep as debug prints which should be enabled
+ * via dynamic debug while debugging IPC issues
+ */
+int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
+ u32 drv_id, u32 ipc, void *data, u32 size)
+{
+ struct sst_block *block;
+
+ dev_dbg(ctx->dev, "Enter\n");
+
+ spin_lock_bh(&ctx->block_lock);
+ list_for_each_entry(block, &ctx->block_list, node) {
+ dev_dbg(ctx->dev, "Block ipc %d, drv_id %d\n", block->msg_id,
+ block->drv_id);
+ if (block->msg_id == ipc && block->drv_id == drv_id) {
+ dev_dbg(ctx->dev, "free up the block\n");
+ block->ret_code = result;
+ block->data = data;
+ block->size = size;
+ block->condition = true;
+ spin_unlock_bh(&ctx->block_lock);
+ wake_up(&ctx->wait_queue);
+ return 0;
+ }
+ }
+ spin_unlock_bh(&ctx->block_lock);
+ dev_dbg(ctx->dev,
+ "Block not found or a response received for a short msg for ipc %d, drv_id %d\n",
+ ipc, drv_id);
+ return -EINVAL;
+}
+
+int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed)
+{
+ struct sst_block *block, *__block;
+
+ dev_dbg(ctx->dev, "Enter\n");
+ spin_lock_bh(&ctx->block_lock);
+ list_for_each_entry_safe(block, __block, &ctx->block_list, node) {
+ if (block == freed) {
+ pr_debug("pvt_id freed --> %d\n", freed->drv_id);
+ /* toggle the index position of pvt_id */
+ list_del(&freed->node);
+ spin_unlock_bh(&ctx->block_lock);
+ kfree(freed->data);
+ freed->data = NULL;
+ kfree(freed);
+ return 0;
+ }
+ }
+ spin_unlock_bh(&ctx->block_lock);
+ dev_err(ctx->dev, "block is already freed!!!\n");
+ return -EINVAL;
+}
+
+int sst_post_message_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ struct ipc_post *ipc_msg, bool sync)
+{
+ struct ipc_post *msg = ipc_msg;
+ union ipc_header_mrfld header;
+ unsigned int loop_count = 0;
+ int retval = 0;
+ unsigned long irq_flags;
+
+ dev_dbg(sst_drv_ctx->dev, "Enter: sync: %d\n", sync);
+ spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+ header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX);
+ if (sync) {
+ while (header.p.header_high.part.busy) {
+ if (loop_count > 25) {
+ dev_err(sst_drv_ctx->dev,
+ "sst: Busy wait failed, can't send this msg\n");
+ retval = -EBUSY;
+ goto out;
+ }
+ cpu_relax();
+ loop_count++;
+ header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX);
+ }
+ } else {
+ if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) {
+ /* queue is empty, nothing to send */
+ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+ dev_dbg(sst_drv_ctx->dev,
+ "Empty msg queue... NO Action\n");
+ return 0;
+ }
+
+ if (header.p.header_high.part.busy) {
+ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+ dev_dbg(sst_drv_ctx->dev, "Busy not free... post later\n");
+ return 0;
+ }
+
+ /* copy msg from list */
+ msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next,
+ struct ipc_post, node);
+ list_del(&msg->node);
+ }
+ dev_dbg(sst_drv_ctx->dev, "sst: Post message: header = %x\n",
+ msg->mrfld_header.p.header_high.full);
+ dev_dbg(sst_drv_ctx->dev, "sst: size = 0x%x\n",
+ msg->mrfld_header.p.header_low_payload);
+
+ if (msg->mrfld_header.p.header_high.part.large)
+ memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND,
+ msg->mailbox_data,
+ msg->mrfld_header.p.header_low_payload);
+
+ sst_shim_write64(sst_drv_ctx->shim, SST_IPCX, msg->mrfld_header.full);
+
+out:
+ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+ kfree(msg->mailbox_data);
+ kfree(msg);
+ return retval;
+}
+
+void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+ union interrupt_reg_mrfld isr;
+ union interrupt_reg_mrfld imr;
+ union ipc_header_mrfld clear_ipc;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+ imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX);
+ isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX);
+
+ /* write 1 to clear*/
+ isr.part.busy_interrupt = 1;
+ sst_shim_write64(sst_drv_ctx->shim, SST_ISRX, isr.full);
+
+ /* Set IA done bit */
+ clear_ipc.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCD);
+
+ clear_ipc.p.header_high.part.busy = 0;
+ clear_ipc.p.header_high.part.done = 1;
+ clear_ipc.p.header_low_payload = IPC_ACK_SUCCESS;
+ sst_shim_write64(sst_drv_ctx->shim, SST_IPCD, clear_ipc.full);
+ /* un mask busy interrupt */
+ imr.part.busy_interrupt = 0;
+ sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full);
+ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+}
+
+
+/*
+ * process_fw_init - process the FW init msg
+ *
+ * @msg: IPC message mailbox data from FW
+ *
+ * This function processes the FW init msg from FW
+ * marks FW state and prints debug info of loaded FW
+ */
+static void process_fw_init(struct intel_sst_drv *sst_drv_ctx,
+ void *msg)
+{
+ struct ipc_header_fw_init *init =
+ (struct ipc_header_fw_init *)msg;
+ int retval = 0;
+
+ dev_dbg(sst_drv_ctx->dev, "*** FW Init msg came***\n");
+ if (init->result) {
+ sst_set_fw_state_locked(sst_drv_ctx, SST_RESET);
+ dev_err(sst_drv_ctx->dev, "FW Init failed, Error %x\n",
+ init->result);
+ retval = init->result;
+ goto ret;
+ }
+ if (memcmp(&sst_drv_ctx->fw_version, &init->fw_version,
+ sizeof(init->fw_version)))
+ dev_info(sst_drv_ctx->dev, "FW Version %02x.%02x.%02x.%02x\n",
+ init->fw_version.type, init->fw_version.major,
+ init->fw_version.minor, init->fw_version.build);
+ dev_dbg(sst_drv_ctx->dev, "Build date %s Time %s\n",
+ init->build_info.date, init->build_info.time);
+
+ /* Save FW version */
+ sst_drv_ctx->fw_version.type = init->fw_version.type;
+ sst_drv_ctx->fw_version.major = init->fw_version.major;
+ sst_drv_ctx->fw_version.minor = init->fw_version.minor;
+ sst_drv_ctx->fw_version.build = init->fw_version.build;
+
+ret:
+ sst_wake_up_block(sst_drv_ctx, retval, FW_DWNL_ID, 0 , NULL, 0);
+}
+
+static void process_fw_async_msg(struct intel_sst_drv *sst_drv_ctx,
+ struct ipc_post *msg)
+{
+ u32 msg_id;
+ int str_id;
+ u32 data_size, i;
+ void *data_offset;
+ struct stream_info *stream;
+ u32 msg_low, pipe_id;
+
+ msg_low = msg->mrfld_header.p.header_low_payload;
+ msg_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->cmd_id;
+ data_offset = (msg->mailbox_data + sizeof(struct ipc_dsp_hdr));
+ data_size = msg_low - (sizeof(struct ipc_dsp_hdr));
+
+ switch (msg_id) {
+ case IPC_SST_PERIOD_ELAPSED_MRFLD:
+ pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+ str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+ if (str_id > 0) {
+ dev_dbg(sst_drv_ctx->dev,
+ "Period elapsed rcvd for pipe id 0x%x\n",
+ pipe_id);
+ stream = &sst_drv_ctx->streams[str_id];
+ /* If stream is dropped, skip processing this message*/
+ if (stream->status == STREAM_INIT)
+ break;
+ if (stream->period_elapsed)
+ stream->period_elapsed(stream->pcm_substream);
+ if (stream->compr_cb)
+ stream->compr_cb(stream->compr_cb_param);
+ }
+ break;
+
+ case IPC_IA_DRAIN_STREAM_MRFLD:
+ pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+ str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+ if (str_id > 0) {
+ stream = &sst_drv_ctx->streams[str_id];
+ if (stream->drain_notify)
+ stream->drain_notify(stream->drain_cb_param);
+ }
+ break;
+
+ case IPC_IA_FW_ASYNC_ERR_MRFLD:
+ dev_err(sst_drv_ctx->dev, "FW sent async error msg:\n");
+ for (i = 0; i < (data_size/4); i++)
+ print_hex_dump(KERN_DEBUG, NULL, DUMP_PREFIX_NONE,
+ 16, 4, data_offset, data_size, false);
+ break;
+
+ case IPC_IA_FW_INIT_CMPLT_MRFLD:
+ process_fw_init(sst_drv_ctx, data_offset);
+ break;
+
+ case IPC_IA_BUF_UNDER_RUN_MRFLD:
+ pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+ str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+ if (str_id > 0)
+ dev_err(sst_drv_ctx->dev,
+ "Buffer under-run for pipe:%#x str_id:%d\n",
+ pipe_id, str_id);
+ break;
+
+ default:
+ dev_err(sst_drv_ctx->dev,
+ "Unrecognized async msg from FW msg_id %#x\n", msg_id);
+ }
+}
+
+void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ struct ipc_post *msg)
+{
+ unsigned int drv_id;
+ void *data;
+ union ipc_header_high msg_high;
+ u32 msg_low;
+ struct ipc_dsp_hdr *dsp_hdr;
+
+ msg_high = msg->mrfld_header.p.header_high;
+ msg_low = msg->mrfld_header.p.header_low_payload;
+
+ dev_dbg(sst_drv_ctx->dev, "IPC process message header %x payload %x\n",
+ msg->mrfld_header.p.header_high.full,
+ msg->mrfld_header.p.header_low_payload);
+
+ drv_id = msg_high.part.drv_id;
+
+ /* Check for async messages first */
+ if (drv_id == SST_ASYNC_DRV_ID) {
+ /*FW sent async large message*/
+ process_fw_async_msg(sst_drv_ctx, msg);
+ return;
+ }
+
+ /* FW sent short error response for an IPC */
+ if (msg_high.part.result && !msg_high.part.large) {
+ /* 32-bit FW error code in msg_low */
+ dev_err(sst_drv_ctx->dev, "FW sent error response 0x%x", msg_low);
+ sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+ msg_high.part.drv_id,
+ msg_high.part.msg_id, NULL, 0);
+ return;
+ }
+
+ /*
+ * Process all valid responses
+ * if it is a large message, the payload contains the size to
+ * copy from mailbox
+ **/
+ if (msg_high.part.large) {
+ data = kmemdup((void *)msg->mailbox_data, msg_low, GFP_KERNEL);
+ if (!data)
+ return;
+ /* Copy command id so that we can use to put sst to reset */
+ dsp_hdr = (struct ipc_dsp_hdr *)data;
+ dev_dbg(sst_drv_ctx->dev, "cmd_id %d\n", dsp_hdr->cmd_id);
+ if (sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+ msg_high.part.drv_id,
+ msg_high.part.msg_id, data, msg_low))
+ kfree(data);
+ } else {
+ sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+ msg_high.part.drv_id,
+ msg_high.part.msg_id, NULL, 0);
+ }
+
+}
diff --git a/sound/soc/intel/atom/sst/sst_loader.c b/sound/soc/intel/atom/sst/sst_loader.c
new file mode 100644
index 000000000..eea889001
--- /dev/null
+++ b/sound/soc/intel/atom/sst/sst_loader.c
@@ -0,0 +1,454 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * sst_dsp.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corp
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This file contains all dsp controlling functions like firmware download,
+ * setting/resetting dsp cores, etc
+ */
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/dmaengine.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+
+void memcpy32_toio(void __iomem *dst, const void *src, int count)
+{
+ /* __iowrite32_copy uses 32-bit count values so divide by 4 for
+ * right count in words
+ */
+ __iowrite32_copy(dst, src, count / 4);
+}
+
+void memcpy32_fromio(void *dst, const void __iomem *src, int count)
+{
+ /* __ioread32_copy uses 32-bit count values so divide by 4 for
+ * right count in words
+ */
+ __ioread32_copy(dst, src, count / 4);
+}
+
+/**
+ * intel_sst_reset_dsp_mrfld - Resetting SST DSP
+ * @sst_drv_ctx: intel_sst_drv context pointer
+ *
+ * This resets DSP in case of MRFLD platfroms
+ */
+int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+ union config_status_reg_mrfld csr;
+
+ dev_dbg(sst_drv_ctx->dev, "sst: Resetting the DSP in mrfld\n");
+ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+
+ dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+ csr.full |= 0x7;
+ sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+
+ dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+ csr.full &= ~(0x1);
+ sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+ dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+ return 0;
+}
+
+/**
+ * sst_start_mrfld - Start the SST DSP processor
+ * @sst_drv_ctx: intel_sst_drv context pointer
+ *
+ * This starts the DSP in MERRIFIELD platfroms
+ */
+int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+ union config_status_reg_mrfld csr;
+
+ dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP in mrfld LALALALA\n");
+ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+ dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+ csr.full |= 0x7;
+ sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+ dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+ csr.part.xt_snoop = 1;
+ csr.full &= ~(0x5);
+ sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+ dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP_merrifield:%llx\n",
+ csr.full);
+ return 0;
+}
+
+static int sst_validate_fw_image(struct intel_sst_drv *ctx, unsigned long size,
+ struct fw_module_header **module, u32 *num_modules)
+{
+ struct sst_fw_header *header;
+ const void *sst_fw_in_mem = ctx->fw_in_mem;
+
+ dev_dbg(ctx->dev, "Enter\n");
+
+ /* Read the header information from the data pointer */
+ header = (struct sst_fw_header *)sst_fw_in_mem;
+ dev_dbg(ctx->dev,
+ "header sign=%s size=%x modules=%x fmt=%x size=%zx\n",
+ header->signature, header->file_size, header->modules,
+ header->file_format, sizeof(*header));
+
+ /* verify FW */
+ if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) ||
+ (size != header->file_size + sizeof(*header))) {
+ /* Invalid FW signature */
+ dev_err(ctx->dev, "InvalidFW sign/filesize mismatch\n");
+ return -EINVAL;
+ }
+ *num_modules = header->modules;
+ *module = (void *)sst_fw_in_mem + sizeof(*header);
+
+ return 0;
+}
+
+/*
+ * sst_fill_memcpy_list - Fill the memcpy list
+ *
+ * @memcpy_list: List to be filled
+ * @destn: Destination addr to be filled in the list
+ * @src: Source addr to be filled in the list
+ * @size: Size to be filled in the list
+ *
+ * Adds the node to the list after required fields
+ * are populated in the node
+ */
+static int sst_fill_memcpy_list(struct list_head *memcpy_list,
+ void *destn, const void *src, u32 size, bool is_io)
+{
+ struct sst_memcpy_list *listnode;
+
+ listnode = kzalloc(sizeof(*listnode), GFP_KERNEL);
+ if (listnode == NULL)
+ return -ENOMEM;
+ listnode->dstn = destn;
+ listnode->src = src;
+ listnode->size = size;
+ listnode->is_io = is_io;
+ list_add_tail(&listnode->memcpylist, memcpy_list);
+
+ return 0;
+}
+
+/**
+ * sst_parse_module_memcpy - Parse audio FW modules and populate the memcpy list
+ *
+ * @sst_drv_ctx : driver context
+ * @module : FW module header
+ * @memcpy_list : Pointer to the list to be populated
+ * Create the memcpy list as the number of block to be copied
+ * returns error or 0 if module sizes are proper
+ */
+static int sst_parse_module_memcpy(struct intel_sst_drv *sst_drv_ctx,
+ struct fw_module_header *module, struct list_head *memcpy_list)
+{
+ struct fw_block_info *block;
+ u32 count;
+ int ret_val = 0;
+ void __iomem *ram_iomem;
+
+ dev_dbg(sst_drv_ctx->dev, "module sign %s size %x blocks %x type %x\n",
+ module->signature, module->mod_size,
+ module->blocks, module->type);
+ dev_dbg(sst_drv_ctx->dev, "module entrypoint 0x%x\n", module->entry_point);
+
+ block = (void *)module + sizeof(*module);
+
+ for (count = 0; count < module->blocks; count++) {
+ if (block->size <= 0) {
+ dev_err(sst_drv_ctx->dev, "block size invalid\n");
+ return -EINVAL;
+ }
+ switch (block->type) {
+ case SST_IRAM:
+ ram_iomem = sst_drv_ctx->iram;
+ break;
+ case SST_DRAM:
+ ram_iomem = sst_drv_ctx->dram;
+ break;
+ case SST_DDR:
+ ram_iomem = sst_drv_ctx->ddr;
+ break;
+ case SST_CUSTOM_INFO:
+ block = (void *)block + sizeof(*block) + block->size;
+ continue;
+ default:
+ dev_err(sst_drv_ctx->dev, "wrong ram type0x%x in block0x%x\n",
+ block->type, count);
+ return -EINVAL;
+ }
+
+ ret_val = sst_fill_memcpy_list(memcpy_list,
+ ram_iomem + block->ram_offset,
+ (void *)block + sizeof(*block), block->size, 1);
+ if (ret_val)
+ return ret_val;
+
+ block = (void *)block + sizeof(*block) + block->size;
+ }
+ return 0;
+}
+
+/**
+ * sst_parse_fw_memcpy - parse the firmware image & populate the list for memcpy
+ *
+ * @ctx : pointer to drv context
+ * @size : size of the firmware
+ * @fw_list : pointer to list_head to be populated
+ * This function parses the FW image and saves the parsed image in the list
+ * for memcpy
+ */
+static int sst_parse_fw_memcpy(struct intel_sst_drv *ctx, unsigned long size,
+ struct list_head *fw_list)
+{
+ struct fw_module_header *module;
+ u32 count, num_modules;
+ int ret_val;
+
+ ret_val = sst_validate_fw_image(ctx, size, &module, &num_modules);
+ if (ret_val)
+ return ret_val;
+
+ for (count = 0; count < num_modules; count++) {
+ ret_val = sst_parse_module_memcpy(ctx, module, fw_list);
+ if (ret_val)
+ return ret_val;
+ module = (void *)module + sizeof(*module) + module->mod_size;
+ }
+
+ return 0;
+}
+
+/**
+ * sst_do_memcpy - function initiates the memcpy
+ *
+ * @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated
+ *
+ * Triggers the memcpy
+ */
+static void sst_do_memcpy(struct list_head *memcpy_list)
+{
+ struct sst_memcpy_list *listnode;
+
+ list_for_each_entry(listnode, memcpy_list, memcpylist) {
+ if (listnode->is_io)
+ memcpy32_toio((void __iomem *)listnode->dstn,
+ listnode->src, listnode->size);
+ else
+ memcpy(listnode->dstn, listnode->src, listnode->size);
+ }
+}
+
+void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx)
+{
+ struct sst_memcpy_list *listnode, *tmplistnode;
+
+ /* Free the list */
+ list_for_each_entry_safe(listnode, tmplistnode,
+ &sst_drv_ctx->memcpy_list, memcpylist) {
+ list_del(&listnode->memcpylist);
+ kfree(listnode);
+ }
+}
+
+static int sst_cache_and_parse_fw(struct intel_sst_drv *sst,
+ const struct firmware *fw)
+{
+ int retval = 0;
+
+ sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL);
+ if (!sst->fw_in_mem) {
+ retval = -ENOMEM;
+ goto end_release;
+ }
+ dev_dbg(sst->dev, "copied fw to %p", sst->fw_in_mem);
+ dev_dbg(sst->dev, "phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem));
+ memcpy(sst->fw_in_mem, fw->data, fw->size);
+ retval = sst_parse_fw_memcpy(sst, fw->size, &sst->memcpy_list);
+ if (retval) {
+ dev_err(sst->dev, "Failed to parse fw\n");
+ kfree(sst->fw_in_mem);
+ sst->fw_in_mem = NULL;
+ }
+
+end_release:
+ release_firmware(fw);
+ return retval;
+
+}
+
+void sst_firmware_load_cb(const struct firmware *fw, void *context)
+{
+ struct intel_sst_drv *ctx = context;
+
+ dev_dbg(ctx->dev, "Enter\n");
+
+ if (fw == NULL) {
+ dev_err(ctx->dev, "request fw failed\n");
+ return;
+ }
+
+ mutex_lock(&ctx->sst_lock);
+
+ if (ctx->sst_state != SST_RESET ||
+ ctx->fw_in_mem != NULL) {
+ release_firmware(fw);
+ mutex_unlock(&ctx->sst_lock);
+ return;
+ }
+
+ dev_dbg(ctx->dev, "Request Fw completed\n");
+ sst_cache_and_parse_fw(ctx, fw);
+ mutex_unlock(&ctx->sst_lock);
+}
+
+/*
+ * sst_request_fw - requests audio fw from kernel and saves a copy
+ *
+ * This function requests the SST FW from the kernel, parses it and
+ * saves a copy in the driver context
+ */
+static int sst_request_fw(struct intel_sst_drv *sst)
+{
+ int retval = 0;
+ const struct firmware *fw;
+
+ retval = request_firmware(&fw, sst->firmware_name, sst->dev);
+ if (retval) {
+ dev_err(sst->dev, "request fw failed %d\n", retval);
+ return retval;
+ }
+ if (fw == NULL) {
+ dev_err(sst->dev, "fw is returning as null\n");
+ return -EINVAL;
+ }
+ mutex_lock(&sst->sst_lock);
+ retval = sst_cache_and_parse_fw(sst, fw);
+ mutex_unlock(&sst->sst_lock);
+
+ return retval;
+}
+
+/*
+ * Writing the DDR physical base to DCCM offset
+ * so that FW can use it to setup TLB
+ */
+static void sst_dccm_config_write(void __iomem *dram_base,
+ unsigned int ddr_base)
+{
+ void __iomem *addr;
+ u32 bss_reset = 0;
+
+ addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET);
+ memcpy32_toio(addr, (void *)&ddr_base, sizeof(u32));
+ bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT);
+ addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET);
+ memcpy32_toio(addr, &bss_reset, sizeof(u32));
+
+}
+
+void sst_post_download_mrfld(struct intel_sst_drv *ctx)
+{
+ sst_dccm_config_write(ctx->dram, ctx->ddr_base);
+ dev_dbg(ctx->dev, "config written to DCCM\n");
+}
+
+/**
+ * sst_load_fw - function to load FW into DSP
+ * @sst_drv_ctx: intel_sst_drv context pointer
+ *
+ * Transfers the FW to DSP using dma/memcpy
+ */
+int sst_load_fw(struct intel_sst_drv *sst_drv_ctx)
+{
+ int ret_val = 0;
+ struct sst_block *block;
+
+ dev_dbg(sst_drv_ctx->dev, "sst_load_fw\n");
+
+ if (sst_drv_ctx->sst_state != SST_RESET)
+ return -EAGAIN;
+
+ if (!sst_drv_ctx->fw_in_mem) {
+ dev_dbg(sst_drv_ctx->dev, "sst: FW not in memory retry to download\n");
+ ret_val = sst_request_fw(sst_drv_ctx);
+ if (ret_val)
+ return ret_val;
+ }
+
+ block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID);
+ if (block == NULL)
+ return -ENOMEM;
+
+ /* Prevent C-states beyond C6 */
+ cpu_latency_qos_update_request(sst_drv_ctx->qos, 0);
+
+ sst_drv_ctx->sst_state = SST_FW_LOADING;
+
+ ret_val = sst_drv_ctx->ops->reset(sst_drv_ctx);
+ if (ret_val)
+ goto restore;
+
+ sst_do_memcpy(&sst_drv_ctx->memcpy_list);
+
+ /* Write the DRAM/DCCM config before enabling FW */
+ if (sst_drv_ctx->ops->post_download)
+ sst_drv_ctx->ops->post_download(sst_drv_ctx);
+
+ /* bring sst out of reset */
+ ret_val = sst_drv_ctx->ops->start(sst_drv_ctx);
+ if (ret_val)
+ goto restore;
+
+ ret_val = sst_wait_timeout(sst_drv_ctx, block);
+ if (ret_val) {
+ dev_err(sst_drv_ctx->dev, "fw download failed %d\n" , ret_val);
+ /* FW download failed due to timeout */
+ ret_val = -EBUSY;
+
+ }
+
+
+restore:
+ /* Re-enable Deeper C-states beyond C6 */
+ cpu_latency_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE);
+ sst_free_block(sst_drv_ctx, block);
+ dev_dbg(sst_drv_ctx->dev, "fw load successful!!!\n");
+
+ if (sst_drv_ctx->ops->restore_dsp_context)
+ sst_drv_ctx->ops->restore_dsp_context();
+ sst_drv_ctx->sst_state = SST_FW_RUNNING;
+ return ret_val;
+}
+
diff --git a/sound/soc/intel/atom/sst/sst_pci.c b/sound/soc/intel/atom/sst/sst_pci.c
new file mode 100644
index 000000000..5862fe968
--- /dev/null
+++ b/sound/soc/intel/atom/sst/sst_pci.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * sst_pci.c - SST (LPE) driver init file for pci enumeration.
+ *
+ * Copyright (C) 2008-14 Intel Corp
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+
+static int sst_platform_get_resources(struct intel_sst_drv *ctx)
+{
+ int ddr_base, ret = 0;
+ struct pci_dev *pci = ctx->pci;
+
+ ret = pci_request_regions(pci, SST_DRV_NAME);
+ if (ret)
+ return ret;
+
+ /* map registers */
+ /* DDR base */
+ if (ctx->dev_id == SST_MRFLD_PCI_ID) {
+ ctx->ddr_base = pci_resource_start(pci, 0);
+ /* check that the relocated IMR base matches with FW Binary */
+ ddr_base = relocate_imr_addr_mrfld(ctx->ddr_base);
+ if (!ctx->pdata->lib_info) {
+ dev_err(ctx->dev, "lib_info pointer NULL\n");
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ if (ddr_base != ctx->pdata->lib_info->mod_base) {
+ dev_err(ctx->dev,
+ "FW LSP DDR BASE does not match with IFWI\n");
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ ctx->ddr_end = pci_resource_end(pci, 0);
+
+ ctx->ddr = pcim_iomap(pci, 0,
+ pci_resource_len(pci, 0));
+ if (!ctx->ddr) {
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ dev_dbg(ctx->dev, "sst: DDR Ptr %p\n", ctx->ddr);
+ } else {
+ ctx->ddr = NULL;
+ }
+ /* SHIM */
+ ctx->shim_phy_add = pci_resource_start(pci, 1);
+ ctx->shim = pcim_iomap(pci, 1, pci_resource_len(pci, 1));
+ if (!ctx->shim) {
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ dev_dbg(ctx->dev, "SST Shim Ptr %p\n", ctx->shim);
+
+ /* Shared SRAM */
+ ctx->mailbox_add = pci_resource_start(pci, 2);
+ ctx->mailbox = pcim_iomap(pci, 2, pci_resource_len(pci, 2));
+ if (!ctx->mailbox) {
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ dev_dbg(ctx->dev, "SRAM Ptr %p\n", ctx->mailbox);
+
+ /* IRAM */
+ ctx->iram_end = pci_resource_end(pci, 3);
+ ctx->iram_base = pci_resource_start(pci, 3);
+ ctx->iram = pcim_iomap(pci, 3, pci_resource_len(pci, 3));
+ if (!ctx->iram) {
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ dev_dbg(ctx->dev, "IRAM Ptr %p\n", ctx->iram);
+
+ /* DRAM */
+ ctx->dram_end = pci_resource_end(pci, 4);
+ ctx->dram_base = pci_resource_start(pci, 4);
+ ctx->dram = pcim_iomap(pci, 4, pci_resource_len(pci, 4));
+ if (!ctx->dram) {
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ dev_dbg(ctx->dev, "DRAM Ptr %p\n", ctx->dram);
+do_release_regions:
+ pci_release_regions(pci);
+ return ret;
+}
+
+/*
+ * intel_sst_probe - PCI probe function
+ *
+ * @pci: PCI device structure
+ * @pci_id: PCI device ID structure
+ *
+ */
+static int intel_sst_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ int ret = 0;
+ struct intel_sst_drv *sst_drv_ctx;
+ struct sst_platform_info *sst_pdata = pci->dev.platform_data;
+
+ dev_dbg(&pci->dev, "Probe for DID %x\n", pci->device);
+ ret = sst_alloc_drv_context(&sst_drv_ctx, &pci->dev, pci->device);
+ if (ret < 0)
+ return ret;
+
+ sst_drv_ctx->pdata = sst_pdata;
+ sst_drv_ctx->irq_num = pci->irq;
+ snprintf(sst_drv_ctx->firmware_name, sizeof(sst_drv_ctx->firmware_name),
+ "%s%04x%s", "fw_sst_",
+ sst_drv_ctx->dev_id, ".bin");
+
+ ret = sst_context_init(sst_drv_ctx);
+ if (ret < 0)
+ return ret;
+
+ /* Init the device */
+ ret = pcim_enable_device(pci);
+ if (ret) {
+ dev_err(sst_drv_ctx->dev,
+ "device can't be enabled. Returned err: %d\n", ret);
+ goto do_free_drv_ctx;
+ }
+ sst_drv_ctx->pci = pci_dev_get(pci);
+ ret = sst_platform_get_resources(sst_drv_ctx);
+ if (ret < 0)
+ goto do_free_drv_ctx;
+
+ pci_set_drvdata(pci, sst_drv_ctx);
+ sst_configure_runtime_pm(sst_drv_ctx);
+
+ return ret;
+
+do_free_drv_ctx:
+ sst_context_cleanup(sst_drv_ctx);
+ dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret);
+ return ret;
+}
+
+/**
+ * intel_sst_remove - PCI remove function
+ *
+ * @pci: PCI device structure
+ *
+ * This function is called by OS when a device is unloaded
+ * This frees the interrupt etc
+ */
+static void intel_sst_remove(struct pci_dev *pci)
+{
+ struct intel_sst_drv *sst_drv_ctx = pci_get_drvdata(pci);
+
+ sst_context_cleanup(sst_drv_ctx);
+ pci_dev_put(sst_drv_ctx->pci);
+ pci_release_regions(pci);
+ pci_set_drvdata(pci, NULL);
+}
+
+/* PCI Routines */
+static const struct pci_device_id intel_sst_ids[] = {
+ { PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0},
+ { 0, }
+};
+
+static struct pci_driver sst_driver = {
+ .name = SST_DRV_NAME,
+ .id_table = intel_sst_ids,
+ .probe = intel_sst_probe,
+ .remove = intel_sst_remove,
+#ifdef CONFIG_PM
+ .driver = {
+ .pm = &intel_sst_pm,
+ },
+#endif
+};
+
+module_pci_driver(sst_driver);
+
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine PCI Driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_AUTHOR("Dharageswari R <dharageswari.r@intel.com>");
+MODULE_AUTHOR("KP Jeeja <jeeja.kp@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("sst");
diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c
new file mode 100644
index 000000000..e6a5c18a7
--- /dev/null
+++ b/sound/soc/intel/atom/sst/sst_pvt.c
@@ -0,0 +1,406 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * sst_pvt.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corp
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/kobject.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <sound/asound.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+
+int sst_shim_write(void __iomem *addr, int offset, int value)
+{
+ writel(value, addr + offset);
+ return 0;
+}
+
+u32 sst_shim_read(void __iomem *addr, int offset)
+{
+ return readl(addr + offset);
+}
+
+u64 sst_reg_read64(void __iomem *addr, int offset)
+{
+ u64 val = 0;
+
+ memcpy_fromio(&val, addr + offset, sizeof(val));
+
+ return val;
+}
+
+int sst_shim_write64(void __iomem *addr, int offset, u64 value)
+{
+ memcpy_toio(addr + offset, &value, sizeof(value));
+ return 0;
+}
+
+u64 sst_shim_read64(void __iomem *addr, int offset)
+{
+ u64 val = 0;
+
+ memcpy_fromio(&val, addr + offset, sizeof(val));
+ return val;
+}
+
+void sst_set_fw_state_locked(
+ struct intel_sst_drv *sst_drv_ctx, int sst_state)
+{
+ mutex_lock(&sst_drv_ctx->sst_lock);
+ sst_drv_ctx->sst_state = sst_state;
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+}
+
+/*
+ * sst_wait_interruptible - wait on event
+ *
+ * @sst_drv_ctx: Driver context
+ * @block: Driver block to wait on
+ *
+ * This function waits without a timeout (and is interruptable) for a
+ * given block event
+ */
+int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
+ struct sst_block *block)
+{
+ int retval = 0;
+
+ if (!wait_event_interruptible(sst_drv_ctx->wait_queue,
+ block->condition)) {
+ /* event wake */
+ if (block->ret_code < 0) {
+ dev_err(sst_drv_ctx->dev,
+ "stream failed %d\n", block->ret_code);
+ retval = -EBUSY;
+ } else {
+ dev_dbg(sst_drv_ctx->dev, "event up\n");
+ retval = 0;
+ }
+ } else {
+ dev_err(sst_drv_ctx->dev, "signal interrupted\n");
+ retval = -EINTR;
+ }
+ return retval;
+
+}
+
+/*
+ * sst_wait_timeout - wait on event for timeout
+ *
+ * @sst_drv_ctx: Driver context
+ * @block: Driver block to wait on
+ *
+ * This function waits with a timeout value (and is not interruptible) on a
+ * given block event
+ */
+int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, struct sst_block *block)
+{
+ int retval = 0;
+
+ /*
+ * NOTE:
+ * Observed that FW processes the alloc msg and replies even
+ * before the alloc thread has finished execution
+ */
+ dev_dbg(sst_drv_ctx->dev,
+ "waiting for condition %x ipc %d drv_id %d\n",
+ block->condition, block->msg_id, block->drv_id);
+ if (wait_event_timeout(sst_drv_ctx->wait_queue,
+ block->condition,
+ msecs_to_jiffies(SST_BLOCK_TIMEOUT))) {
+ /* event wake */
+ dev_dbg(sst_drv_ctx->dev, "Event wake %x\n",
+ block->condition);
+ dev_dbg(sst_drv_ctx->dev, "message ret: %d\n",
+ block->ret_code);
+ retval = -block->ret_code;
+ } else {
+ block->on = false;
+ dev_err(sst_drv_ctx->dev,
+ "Wait timed-out condition:%#x, msg_id:%#x fw_state %#x\n",
+ block->condition, block->msg_id, sst_drv_ctx->sst_state);
+ sst_drv_ctx->sst_state = SST_RESET;
+
+ retval = -EBUSY;
+ }
+ return retval;
+}
+
+/*
+ * sst_create_ipc_msg - create a IPC message
+ *
+ * @arg: ipc message
+ * @large: large or short message
+ *
+ * this function allocates structures to send a large or short
+ * message to the firmware
+ */
+int sst_create_ipc_msg(struct ipc_post **arg, bool large)
+{
+ struct ipc_post *msg;
+
+ msg = kzalloc(sizeof(*msg), GFP_ATOMIC);
+ if (!msg)
+ return -ENOMEM;
+ if (large) {
+ msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC);
+ if (!msg->mailbox_data) {
+ kfree(msg);
+ return -ENOMEM;
+ }
+ } else {
+ msg->mailbox_data = NULL;
+ }
+ msg->is_large = large;
+ *arg = msg;
+ return 0;
+}
+
+/*
+ * sst_create_block_and_ipc_msg - Creates IPC message and sst block
+ * @arg: passed to sst_create_ipc_message API
+ * @large: large or short message
+ * @sst_drv_ctx: sst driver context
+ * @block: return block allocated
+ * @msg_id: IPC
+ * @drv_id: stream id or private id
+ */
+int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large,
+ struct intel_sst_drv *sst_drv_ctx, struct sst_block **block,
+ u32 msg_id, u32 drv_id)
+{
+ int retval;
+
+ retval = sst_create_ipc_msg(arg, large);
+ if (retval)
+ return retval;
+ *block = sst_create_block(sst_drv_ctx, msg_id, drv_id);
+ if (*block == NULL) {
+ kfree(*arg);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/*
+ * sst_clean_stream - clean the stream context
+ *
+ * @stream: stream structure
+ *
+ * this function resets the stream contexts
+ * should be called in free
+ */
+void sst_clean_stream(struct stream_info *stream)
+{
+ stream->status = STREAM_UN_INIT;
+ stream->prev = STREAM_UN_INIT;
+ mutex_lock(&stream->lock);
+ stream->cumm_bytes = 0;
+ mutex_unlock(&stream->lock);
+}
+
+int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
+ int task_id, int ipc_msg, int cmd_id, int pipe_id,
+ size_t mbox_data_len, const void *mbox_data, void **data,
+ bool large, bool fill_dsp, bool sync, bool response)
+{
+ struct sst_block *block = NULL;
+ struct ipc_post *msg = NULL;
+ struct ipc_dsp_hdr dsp_hdr;
+ int ret = 0, pvt_id;
+
+ pvt_id = sst_assign_pvt_id(sst);
+ if (pvt_id < 0)
+ return pvt_id;
+
+ if (response)
+ ret = sst_create_block_and_ipc_msg(
+ &msg, large, sst, &block, ipc_msg, pvt_id);
+ else
+ ret = sst_create_ipc_msg(&msg, large);
+
+ if (ret < 0) {
+ test_and_clear_bit(pvt_id, &sst->pvt_id);
+ return -ENOMEM;
+ }
+
+ dev_dbg(sst->dev, "pvt_id = %d, pipe id = %d, task = %d ipc_msg: %d\n",
+ pvt_id, pipe_id, task_id, ipc_msg);
+ sst_fill_header_mrfld(&msg->mrfld_header, ipc_msg,
+ task_id, large, pvt_id);
+ msg->mrfld_header.p.header_low_payload = sizeof(dsp_hdr) + mbox_data_len;
+ msg->mrfld_header.p.header_high.part.res_rqd = !sync;
+ dev_dbg(sst->dev, "header:%x\n",
+ msg->mrfld_header.p.header_high.full);
+ dev_dbg(sst->dev, "response rqd: %x",
+ msg->mrfld_header.p.header_high.part.res_rqd);
+ dev_dbg(sst->dev, "msg->mrfld_header.p.header_low_payload:%d",
+ msg->mrfld_header.p.header_low_payload);
+ if (fill_dsp) {
+ sst_fill_header_dsp(&dsp_hdr, cmd_id, pipe_id, mbox_data_len);
+ memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr));
+ if (mbox_data_len) {
+ memcpy(msg->mailbox_data + sizeof(dsp_hdr),
+ mbox_data, mbox_data_len);
+ }
+ }
+
+ if (sync)
+ sst->ops->post_message(sst, msg, true);
+ else
+ sst_add_to_dispatch_list_and_post(sst, msg);
+
+ if (response) {
+ ret = sst_wait_timeout(sst, block);
+ if (ret < 0)
+ goto out;
+
+ if (data && block->data) {
+ *data = kmemdup(block->data, block->size, GFP_KERNEL);
+ if (!*data) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+ }
+out:
+ if (response)
+ sst_free_block(sst, block);
+ test_and_clear_bit(pvt_id, &sst->pvt_id);
+ return ret;
+}
+
+int sst_pm_runtime_put(struct intel_sst_drv *sst_drv)
+{
+ int ret;
+
+ pm_runtime_mark_last_busy(sst_drv->dev);
+ ret = pm_runtime_put_autosuspend(sst_drv->dev);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+void sst_fill_header_mrfld(union ipc_header_mrfld *header,
+ int msg, int task_id, int large, int drv_id)
+{
+ header->full = 0;
+ header->p.header_high.part.msg_id = msg;
+ header->p.header_high.part.task_id = task_id;
+ header->p.header_high.part.large = large;
+ header->p.header_high.part.drv_id = drv_id;
+ header->p.header_high.part.done = 0;
+ header->p.header_high.part.busy = 1;
+ header->p.header_high.part.res_rqd = 1;
+}
+
+void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg,
+ int pipe_id, int len)
+{
+ dsp->cmd_id = msg;
+ dsp->mod_index_id = 0xff;
+ dsp->pipe_id = pipe_id;
+ dsp->length = len;
+ dsp->mod_id = 0;
+}
+
+#define SST_MAX_BLOCKS 15
+/*
+ * sst_assign_pvt_id - assign a pvt id for stream
+ *
+ * @sst_drv_ctx : driver context
+ *
+ * this function assigns a private id for calls that dont have stream
+ * context yet, should be called with lock held
+ * uses bits for the id, and finds first free bits and assigns that
+ */
+int sst_assign_pvt_id(struct intel_sst_drv *drv)
+{
+ int local;
+
+ spin_lock(&drv->block_lock);
+ /* find first zero index from lsb */
+ local = ffz(drv->pvt_id);
+ dev_dbg(drv->dev, "pvt_id assigned --> %d\n", local);
+ if (local >= SST_MAX_BLOCKS){
+ spin_unlock(&drv->block_lock);
+ dev_err(drv->dev, "PVT _ID error: no free id blocks ");
+ return -EINVAL;
+ }
+ /* toggle the index */
+ change_bit(local, &drv->pvt_id);
+ spin_unlock(&drv->block_lock);
+ return local;
+}
+
+int sst_validate_strid(
+ struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ if (str_id <= 0 || str_id > sst_drv_ctx->info.max_streams) {
+ dev_err(sst_drv_ctx->dev,
+ "SST ERR: invalid stream id : %d, max %d\n",
+ str_id, sst_drv_ctx->info.max_streams);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct stream_info *get_stream_info(
+ struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ if (sst_validate_strid(sst_drv_ctx, str_id))
+ return NULL;
+ return &sst_drv_ctx->streams[str_id];
+}
+
+int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ u32 pipe_id)
+{
+ int i;
+
+ for (i = 1; i <= sst_drv_ctx->info.max_streams; i++)
+ if (pipe_id == sst_drv_ctx->streams[i].pipe_id)
+ return i;
+
+ dev_dbg(sst_drv_ctx->dev, "no such pipe_id(%u)", pipe_id);
+ return -1;
+}
+
+u32 relocate_imr_addr_mrfld(u32 base_addr)
+{
+ /* Get the difference from 512MB aligned base addr */
+ /* relocate the base */
+ base_addr = MRFLD_FW_VIRTUAL_BASE + (base_addr % (512 * 1024 * 1024));
+ return base_addr;
+}
+EXPORT_SYMBOL_GPL(relocate_imr_addr_mrfld);
+
+void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst,
+ struct ipc_post *msg)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&sst->ipc_spin_lock, irq_flags);
+ list_add_tail(&msg->node, &sst->ipc_dispatch_list);
+ spin_unlock_irqrestore(&sst->ipc_spin_lock, irq_flags);
+ sst->ops->post_message(sst, NULL, false);
+}
diff --git a/sound/soc/intel/atom/sst/sst_stream.c b/sound/soc/intel/atom/sst/sst_stream.c
new file mode 100644
index 000000000..ea1ef8a61
--- /dev/null
+++ b/sound/soc/intel/atom/sst/sst_stream.c
@@ -0,0 +1,471 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * sst_stream.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corp
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+
+int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params)
+{
+ struct snd_pcm_params *pcm_params;
+ struct snd_sst_params *str_params;
+ struct snd_sst_tstamp fw_tstamp;
+ struct stream_info *str_info;
+ int i, num_ch, str_id;
+
+ dev_dbg(sst_drv_ctx->dev, "Enter\n");
+
+ str_params = (struct snd_sst_params *)params;
+ str_id = str_params->stream_id;
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+
+ memset(&str_info->alloc_param, 0, sizeof(str_info->alloc_param));
+ str_info->alloc_param.operation = str_params->ops;
+ str_info->alloc_param.codec_type = str_params->codec;
+ str_info->alloc_param.sg_count = str_params->aparams.sg_count;
+ str_info->alloc_param.ring_buf_info[0].addr =
+ str_params->aparams.ring_buf_info[0].addr;
+ str_info->alloc_param.ring_buf_info[0].size =
+ str_params->aparams.ring_buf_info[0].size;
+ str_info->alloc_param.frag_size = str_params->aparams.frag_size;
+
+ memcpy(&str_info->alloc_param.codec_params, &str_params->sparams,
+ sizeof(struct snd_sst_stream_params));
+
+ /*
+ * fill channel map params for multichannel support.
+ * Ideally channel map should be received from upper layers
+ * for multichannel support.
+ * Currently hardcoding as per FW reqm.
+ */
+ num_ch = sst_get_num_channel(str_params);
+ pcm_params = &str_info->alloc_param.codec_params.uc.pcm_params;
+ for (i = 0; i < 8; i++) {
+ if (i < num_ch)
+ pcm_params->channel_map[i] = i;
+ else
+ pcm_params->channel_map[i] = 0xff;
+ }
+
+ sst_drv_ctx->streams[str_id].status = STREAM_INIT;
+ sst_drv_ctx->streams[str_id].prev = STREAM_UN_INIT;
+ sst_drv_ctx->streams[str_id].pipe_id = str_params->device_type;
+ sst_drv_ctx->streams[str_id].task_id = str_params->task;
+ sst_drv_ctx->streams[str_id].num_ch = num_ch;
+
+ if (sst_drv_ctx->info.lpe_viewpt_rqd)
+ str_info->alloc_param.ts = sst_drv_ctx->info.mailbox_start +
+ sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
+ else
+ str_info->alloc_param.ts = sst_drv_ctx->mailbox_add +
+ sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
+
+ dev_dbg(sst_drv_ctx->dev, "alloc tstamp location = 0x%x\n",
+ str_info->alloc_param.ts);
+ dev_dbg(sst_drv_ctx->dev, "assigned pipe id 0x%x to task %d\n",
+ str_info->pipe_id, str_info->task_id);
+
+ return sst_realloc_stream(sst_drv_ctx, str_id);
+}
+
+/**
+ * sst_realloc_stream - Send msg for (re-)allocating a stream using the
+ * @sst_drv_ctx: intel_sst_drv context pointer
+ * @str_id: stream ID
+ *
+ * Send a msg for (re-)allocating a stream using the parameters previously
+ * passed to sst_alloc_stream_mrfld() for the same stream ID.
+ * Return: 0 or negative errno value.
+ */
+int sst_realloc_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ struct snd_sst_alloc_response *response;
+ struct stream_info *str_info;
+ void *data = NULL;
+ int ret;
+
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+
+ dev_dbg(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n",
+ str_id, str_info->pipe_id);
+
+ ret = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+ IPC_IA_ALLOC_STREAM_MRFLD, str_info->pipe_id,
+ sizeof(str_info->alloc_param), &str_info->alloc_param,
+ &data, true, true, false, true);
+
+ if (ret < 0) {
+ dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
+ /* alloc failed, so reset the state to uninit */
+ str_info->status = STREAM_UN_INIT;
+ str_id = ret;
+ } else if (data) {
+ response = (struct snd_sst_alloc_response *)data;
+ ret = response->str_type.result;
+ if (!ret)
+ goto out;
+ dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
+ if (ret == SST_ERR_STREAM_IN_USE) {
+ dev_err(sst_drv_ctx->dev,
+ "FW not in clean state, send free for:%d\n", str_id);
+ sst_free_stream(sst_drv_ctx, str_id);
+ }
+ str_id = -ret;
+ }
+out:
+ kfree(data);
+ return str_id;
+}
+
+/**
+ * sst_start_stream - Send msg for a starting stream
+ * @sst_drv_ctx: intel_sst_drv context pointer
+ * @str_id: stream ID
+ *
+ * This function is called by any function which wants to start
+ * a stream.
+ */
+int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+ u16 data = 0;
+
+ dev_dbg(sst_drv_ctx->dev, "sst_start_stream for %d\n", str_id);
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ if (str_info->status != STREAM_RUNNING)
+ return -EBADRQC;
+
+ retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+ IPC_CMD, IPC_IA_START_STREAM_MRFLD, str_info->pipe_id,
+ sizeof(u16), &data, NULL, true, true, true, false);
+
+ return retval;
+}
+
+int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ struct snd_sst_bytes_v2 *bytes)
+{ struct ipc_post *msg = NULL;
+ u32 length;
+ int pvt_id, ret = 0;
+ struct sst_block *block = NULL;
+
+ dev_dbg(sst_drv_ctx->dev,
+ "type:%u ipc_msg:%u block:%u task_id:%u pipe: %#x length:%#x\n",
+ bytes->type, bytes->ipc_msg, bytes->block, bytes->task_id,
+ bytes->pipe_id, bytes->len);
+
+ if (sst_create_ipc_msg(&msg, true))
+ return -ENOMEM;
+
+ pvt_id = sst_assign_pvt_id(sst_drv_ctx);
+ sst_fill_header_mrfld(&msg->mrfld_header, bytes->ipc_msg,
+ bytes->task_id, 1, pvt_id);
+ msg->mrfld_header.p.header_high.part.res_rqd = bytes->block;
+ length = bytes->len;
+ msg->mrfld_header.p.header_low_payload = length;
+ dev_dbg(sst_drv_ctx->dev, "length is %d\n", length);
+ memcpy(msg->mailbox_data, &bytes->bytes, bytes->len);
+ if (bytes->block) {
+ block = sst_create_block(sst_drv_ctx, bytes->ipc_msg, pvt_id);
+ if (block == NULL) {
+ kfree(msg);
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+
+ sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg);
+ dev_dbg(sst_drv_ctx->dev, "msg->mrfld_header.p.header_low_payload:%d",
+ msg->mrfld_header.p.header_low_payload);
+
+ if (bytes->block) {
+ ret = sst_wait_timeout(sst_drv_ctx, block);
+ if (ret) {
+ dev_err(sst_drv_ctx->dev, "fw returned err %d\n", ret);
+ sst_free_block(sst_drv_ctx, block);
+ goto out;
+ }
+ }
+ if (bytes->type == SND_SST_BYTES_GET) {
+ /*
+ * copy the reply and send back
+ * we need to update only sz and payload
+ */
+ if (bytes->block) {
+ unsigned char *r = block->data;
+
+ dev_dbg(sst_drv_ctx->dev, "read back %d bytes",
+ bytes->len);
+ memcpy(bytes->bytes, r, bytes->len);
+ }
+ }
+ if (bytes->block)
+ sst_free_block(sst_drv_ctx, block);
+out:
+ test_and_clear_bit(pvt_id, &sst_drv_ctx->pvt_id);
+ return ret;
+}
+
+/**
+ * sst_pause_stream - Send msg for a pausing stream
+ * @sst_drv_ctx: intel_sst_drv context pointer
+ * @str_id: stream ID
+ *
+ * This function is called by any function which wants to pause
+ * an already running stream.
+ */
+int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_pause_stream for %d\n", str_id);
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ if (str_info->status == STREAM_PAUSED)
+ return 0;
+ if (str_info->status == STREAM_RUNNING ||
+ str_info->status == STREAM_INIT) {
+ if (str_info->prev == STREAM_UN_INIT)
+ return -EBADRQC;
+
+ retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+ IPC_IA_PAUSE_STREAM_MRFLD, str_info->pipe_id,
+ 0, NULL, NULL, true, true, false, true);
+
+ if (retval == 0) {
+ str_info->prev = str_info->status;
+ str_info->status = STREAM_PAUSED;
+ } else if (retval == -SST_ERR_INVALID_STREAM_ID) {
+ retval = -EINVAL;
+ mutex_lock(&sst_drv_ctx->sst_lock);
+ sst_clean_stream(str_info);
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+ }
+ } else {
+ retval = -EBADRQC;
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:BADRQC for stream\n");
+ }
+
+ return retval;
+}
+
+/**
+ * sst_resume_stream - Send msg for resuming stream
+ * @sst_drv_ctx: intel_sst_drv context pointer
+ * @str_id: stream ID
+ *
+ * This function is called by any function which wants to resume
+ * an already paused stream.
+ */
+int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_resume_stream for %d\n", str_id);
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ if (str_info->status == STREAM_RUNNING)
+ return 0;
+
+ if (str_info->resume_status == STREAM_PAUSED &&
+ str_info->resume_prev == STREAM_RUNNING) {
+ /*
+ * Stream was running before suspend and re-created on resume,
+ * start it to get back to running state.
+ */
+ dev_dbg(sst_drv_ctx->dev, "restart recreated stream after resume\n");
+ str_info->status = STREAM_RUNNING;
+ str_info->prev = STREAM_PAUSED;
+ retval = sst_start_stream(sst_drv_ctx, str_id);
+ str_info->resume_status = STREAM_UN_INIT;
+ } else if (str_info->resume_status == STREAM_PAUSED &&
+ str_info->resume_prev == STREAM_INIT) {
+ /*
+ * Stream was idle before suspend and re-created on resume,
+ * keep it as is.
+ */
+ dev_dbg(sst_drv_ctx->dev, "leaving recreated stream idle after resume\n");
+ str_info->status = STREAM_INIT;
+ str_info->prev = STREAM_PAUSED;
+ str_info->resume_status = STREAM_UN_INIT;
+ } else if (str_info->status == STREAM_PAUSED) {
+ retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+ IPC_CMD, IPC_IA_RESUME_STREAM_MRFLD,
+ str_info->pipe_id, 0, NULL, NULL,
+ true, true, false, true);
+
+ if (!retval) {
+ if (str_info->prev == STREAM_RUNNING)
+ str_info->status = STREAM_RUNNING;
+ else
+ str_info->status = STREAM_INIT;
+ str_info->prev = STREAM_PAUSED;
+ } else if (retval == -SST_ERR_INVALID_STREAM_ID) {
+ retval = -EINVAL;
+ mutex_lock(&sst_drv_ctx->sst_lock);
+ sst_clean_stream(str_info);
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+ }
+ } else {
+ retval = -EBADRQC;
+ dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream\n");
+ }
+
+ return retval;
+}
+
+
+/**
+ * sst_drop_stream - Send msg for stopping stream
+ * @sst_drv_ctx: intel_sst_drv context pointer
+ * @str_id: stream ID
+ *
+ * This function is called by any function which wants to stop
+ * a stream.
+ */
+int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drop_stream for %d\n", str_id);
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+
+ if (str_info->status != STREAM_UN_INIT) {
+ str_info->prev = STREAM_UN_INIT;
+ str_info->status = STREAM_INIT;
+ str_info->cumm_bytes = 0;
+ retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+ IPC_CMD, IPC_IA_DROP_STREAM_MRFLD,
+ str_info->pipe_id, 0, NULL, NULL,
+ true, true, true, false);
+ } else {
+ retval = -EBADRQC;
+ dev_dbg(sst_drv_ctx->dev, "BADQRC for stream, state %x\n",
+ str_info->status);
+ }
+ return retval;
+}
+
+/**
+ * sst_drain_stream - Send msg for draining stream
+ * @sst_drv_ctx: intel_sst_drv context pointer
+ * @str_id: stream ID
+ * @partial_drain: boolean indicating if a gapless transition is taking place
+ *
+ * This function is called by any function which wants to drain
+ * a stream.
+ */
+int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
+ int str_id, bool partial_drain)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drain_stream for %d\n", str_id);
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ if (str_info->status != STREAM_RUNNING &&
+ str_info->status != STREAM_INIT &&
+ str_info->status != STREAM_PAUSED) {
+ dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream = %d\n",
+ str_info->status);
+ return -EBADRQC;
+ }
+
+ retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+ IPC_IA_DRAIN_STREAM_MRFLD, str_info->pipe_id,
+ sizeof(u8), &partial_drain, NULL, true, true, false, false);
+ /*
+ * with new non blocked drain implementation in core we dont need to
+ * wait for respsonse, and need to only invoke callback for drain
+ * complete
+ */
+
+ return retval;
+}
+
+/**
+ * sst_free_stream - Frees a stream
+ * @sst_drv_ctx: intel_sst_drv context pointer
+ * @str_id: stream ID
+ *
+ * This function is called by any function which wants to free
+ * a stream.
+ */
+int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_free_stream for %d\n", str_id);
+
+ mutex_lock(&sst_drv_ctx->sst_lock);
+ if (sst_drv_ctx->sst_state == SST_RESET) {
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+ return -ENODEV;
+ }
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+
+ mutex_lock(&str_info->lock);
+ if (str_info->status != STREAM_UN_INIT) {
+ str_info->prev = str_info->status;
+ str_info->status = STREAM_UN_INIT;
+ mutex_unlock(&str_info->lock);
+
+ dev_dbg(sst_drv_ctx->dev, "Free for str %d pipe %#x\n",
+ str_id, str_info->pipe_id);
+ retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+ IPC_IA_FREE_STREAM_MRFLD, str_info->pipe_id, 0,
+ NULL, NULL, true, true, false, true);
+
+ dev_dbg(sst_drv_ctx->dev, "sst: wait for free returned %d\n",
+ retval);
+ mutex_lock(&sst_drv_ctx->sst_lock);
+ sst_clean_stream(str_info);
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:Stream freed\n");
+ } else {
+ mutex_unlock(&str_info->lock);
+ retval = -EBADRQC;
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:BADQRC for stream\n");
+ }
+
+ return retval;
+}