From: Jie Yang Date: Thu, 2 Apr 2015 07:37:04 +0000 (+0800) Subject: ASoC: Intel: create atom folder and move atom platform files in X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=b97169da06992ef04081e66ed22bbdb23dbf6610;p=GitHub%2Fmoto-9609%2Fandroid_kernel_motorola_exynos9610.git ASoC: Intel: create atom folder and move atom platform files in Restructure the sound/soc/intel/ directory: create atom folder, and move sst atom platform files here. Signed-off-by: Jie Yang Acked-by: Vinod Koul Signed-off-by: Mark Brown --- diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index 62de82af6703..cd9aee9871a3 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -1,19 +1,10 @@ # Core support obj-$(CONFIG_SND_SOC_INTEL_SST) += common/ -snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \ - sst-mfld-platform-compress.o sst-atom-controls.o -snd-soc-mfld-machine-objs := mfld_machine.o - -obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o -obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o - # Platform Support obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/ obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += baytrail/ +obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += atom/ # Machine support obj-$(CONFIG_SND_SOC_INTEL_SST) += boards/ - -# DSP driver -obj-$(CONFIG_SND_SST_IPC) += sst/ diff --git a/sound/soc/intel/atom/Makefile b/sound/soc/intel/atom/Makefile new file mode 100644 index 000000000000..ce8074fa6d66 --- /dev/null +++ b/sound/soc/intel/atom/Makefile @@ -0,0 +1,7 @@ +snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \ + sst-mfld-platform-compress.o sst-atom-controls.o + +obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o + +# DSP driver +obj-$(CONFIG_SND_SST_IPC) += 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 000000000000..90aa5c0476f3 --- /dev/null +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -0,0 +1,1422 @@ +/* + * sst-atom-controls.c - Intel MID Platform driver DPCM ALSA controls for Mrfld + * + * Copyright (C) 2013-14 Intel Corp + * Author: Omair Mohammed Abdullah + * Vinod Koul + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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 +#include +#include +#include "sst-mfld-platform.h" +#include "sst-atom-controls.h" + +static int sst_fill_byte_control(struct sst_data *drv, + u8 ipc_msg, u8 block, + u8 task_id, u8 pipe_id, + u16 len, void *cmd_data) +{ + struct snd_sst_bytes_v2 *byte_data = drv->byte_stream; + + byte_data->type = SST_CMD_BYTES_SET; + byte_data->ipc_msg = ipc_msg; + byte_data->block = block; + byte_data->task_id = task_id; + byte_data->pipe_id = pipe_id; + + if (len > SST_MAX_BIN_BYTES - sizeof(*byte_data)) { + dev_err(&drv->pdev->dev, "command length too big (%u)", len); + return -EINVAL; + } + byte_data->len = len; + memcpy(byte_data->bytes, cmd_data, len); + print_hex_dump_bytes("writing to lpe: ", DUMP_PREFIX_OFFSET, + byte_data, len + sizeof(*byte_data)); + return 0; +} + +static int sst_fill_and_send_cmd_unlocked(struct sst_data *drv, + u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, + void *cmd_data, u16 len) +{ + int ret = 0; + + ret = sst_fill_byte_control(drv, ipc_msg, + block, task_id, pipe_id, len, cmd_data); + if (ret < 0) + return ret; + return sst->ops->send_byte_stream(sst->dev, drv->byte_stream); +} + +/** + * sst_fill_and_send_cmd - generate the IPC message and send it to the FW + * @ipc_msg: type of IPC (CMD, SET_PARAMS, GET_PARAMS) + * @cmd_data: the IPC payload + */ +static int sst_fill_and_send_cmd(struct sst_data *drv, + u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, + void *cmd_data, u16 len) +{ + int ret; + + mutex_lock(&drv->lock); + ret = sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block, + task_id, pipe_id, cmd_data, len); + mutex_unlock(&drv->lock); + + return ret; +} + +/** + * 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); +} + +int sst_slot_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct sst_enum *e = (struct sst_enum *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = e->max; + + if (uinfo->value.enumerated.item > e->max - 1) + uinfo->value.enumerated.item = e->max - 1; + strcpy(uinfo->value.enumerated.name, + e->texts[uinfo->value.enumerated.item]); + + return 0; +} + +/** + * sst_slot_get - get the status of the interleaver/deinterleaver control + * + * Searches the map where the control status is stored, and gets the + * channel/slot which is currently set for this enumerated control. Since it is + * an enumerated control, there is only one possible value. + */ +static int sst_slot_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sst_enum *e = (void *)kcontrol->private_value; + struct snd_soc_component *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 + 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 + * + * (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 + * + * 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 = NULL; + + dev_dbg(&drv->pdev->dev, "Enter: widget=%s\n", pipe); + + list_for_each_entry(algo, &ids->algo_list, node) { + bc = (void *)algo->kctl->private_value; + + dev_dbg(&drv->pdev->dev, "Found algo control name=%s pipe=%s\n", + algo->kctl->id.name, pipe); + 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 + * @gv: the stored value of gain (also contains rampduration) + * @mute: flag that indicates whether this was called from the + * digital_mute callback or directly. If called from the + * digital_mute callback, module will be muted/unmuted based on this + * flag. The flag is always 0 if called directly. + * + * 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 ? 1 : 0; + break; + + case SST_GAIN_RAMP_DURATION: + ucontrol->value.integer.value[0] = gv->ramp_duration; + break; + + default: + dev_err(component->dev, "Invalid Input- gain type:%d\n", + mc->type); + return -EINVAL; + } + + return 0; +} + +static int sst_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + 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_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 + * + * 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 = NULL; + + list_for_each_entry(gain, &ids->gain_list, node) { + struct snd_kcontrol *kctl = gain->kctl; + + dev_dbg(&drv->pdev->dev, "control name=%s\n", kctl->id.name); + mc = (void *)kctl->private_value; + gv = mc->gain_val; + + 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 == false) + return 0; + + if (SND_SOC_DAPM_EVENT_ON(event) || + event == SND_SOC_DAPM_POST_REG) + cmd.switch_state = SST_SWM_ON; + else + cmd.switch_state = SST_SWM_OFF; + + SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); + /* MMX_SET_SWM == SBA_SET_SWM */ + cmd.header.command_id = SBA_SET_SWM; + + SST_FILL_DESTINATION(2, cmd.output_id, + ids->location_id, SST_DEFAULT_MODULE_ID); + cmd.nb_inputs = fill_swm_input(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("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, "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(sst_mix_voip_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_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; +} + +/** + * sst_ssp_config - contains SSP configuration for media UC + */ +static const struct sst_ssp_config sst_ssp_configs = { + .ssp_id = SSP_CODEC, + .bits_per_slot = 24, + .slots = 4, + .ssp_mode = SSP_MODE_MASTER, + .pcm_mode = SSP_PCM_MODE_NETWORK, + .duplex = SSP_DUPLEX, + .ssp_protocol = SSP_MODE_PCM, + .fs_width = 1, + .fs_frequency = SSP_FS_48_KHZ, + .active_slot_map = 0xF, + .start_delay = 0, +}; + +int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable) +{ + struct sst_cmd_sba_hw_set_ssp cmd; + struct sst_data *drv = snd_soc_dai_get_drvdata(dai); + const struct sst_ssp_config *config; + + dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id); + + SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); + cmd.header.command_id = SBA_HW_SET_SSP; + cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp) + - sizeof(struct sst_dsp_header); + + config = &sst_ssp_configs; + dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id); + + if (enable) + cmd.switch_state = SST_SWITCH_ON; + else + cmd.switch_state = SST_SWITCH_OFF; + + cmd.selection = config->ssp_id; + cmd.nb_bits_per_slots = config->bits_per_slot; + cmd.nb_slots = config->slots; + cmd.mode = config->ssp_mode | (config->pcm_mode << 1); + cmd.duplex = config->duplex; + cmd.active_tx_slot_map = config->active_slot_map; + cmd.active_rx_slot_map = config->active_slot_map; + cmd.frame_sync_frequency = config->fs_frequency; + cmd.frame_sync_polarity = SSP_FS_ACTIVE_HIGH; + cmd.data_polarity = 1; + cmd.frame_sync_width = config->fs_width; + cmd.ssp_protocol = config->ssp_protocol; + cmd.start_delay = config->start_delay; + cmd.reserved1 = cmd.reserved2 = 0xFF; + + return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, + SST_TASK_SBA, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); +} + +static int sst_set_be_modules(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + 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)) { + ret = sst_send_slot_map(drv); + 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("codec_in0", sst_set_be_modules), + SST_AIF_IN("codec_in1", sst_set_be_modules), + SST_AIF_OUT("codec_out0", sst_set_be_modules), + SST_AIF_OUT("codec_out1", sst_set_be_modules), + + /* Media Paths */ + /* MediaX IN paths are set via ALLOC, so no SET_MEDIA_PATH command */ + SST_PATH_INPUT("media0_in", SST_TASK_MMX, SST_SWM_IN_MEDIA0, sst_generic_modules_event), + SST_PATH_INPUT("media1_in", SST_TASK_MMX, SST_SWM_IN_MEDIA1, NULL), + SST_PATH_INPUT("media2_in", SST_TASK_MMX, SST_SWM_IN_MEDIA2, sst_set_media_path), + SST_PATH_INPUT("media3_in", SST_TASK_MMX, SST_SWM_IN_MEDIA3, NULL), + SST_PATH_OUTPUT("media0_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA0, sst_set_media_path), + SST_PATH_OUTPUT("media1_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA1, sst_set_media_path), + + /* SBA PCM Paths */ + SST_PATH_INPUT("pcm0_in", SST_TASK_SBA, SST_SWM_IN_PCM0, sst_set_media_path), + SST_PATH_INPUT("pcm1_in", SST_TASK_SBA, SST_SWM_IN_PCM1, sst_set_media_path), + SST_PATH_OUTPUT("pcm0_out", SST_TASK_SBA, SST_SWM_OUT_PCM0, sst_set_media_path), + SST_PATH_OUTPUT("pcm1_out", SST_TASK_SBA, SST_SWM_OUT_PCM1, sst_set_media_path), + SST_PATH_OUTPUT("pcm2_out", SST_TASK_SBA, SST_SWM_OUT_PCM2, sst_set_media_path), + + /* SBA Loops */ + SST_PATH_INPUT("sprot_loop_in", SST_TASK_SBA, SST_SWM_IN_SPROT_LOOP, NULL), + SST_PATH_INPUT("media_loop1_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP1, NULL), + SST_PATH_INPUT("media_loop2_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP2, NULL), + SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_MONO, sst_set_media_loop), + SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_MONO, sst_set_media_loop), + SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop), + + /* Media Mixers */ + SST_SWM_MIXER("media0_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA0, + sst_mix_media0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("media1_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA1, + sst_mix_media1_controls, sst_swm_mixer_event), + + /* SBA PCM mixers */ + SST_SWM_MIXER("pcm0_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM0, + sst_mix_pcm0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("pcm1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM1, + sst_mix_pcm1_controls, sst_swm_mixer_event), + SST_SWM_MIXER("pcm2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM2, + sst_mix_pcm2_controls, sst_swm_mixer_event), + + /* SBA Loop mixers */ + SST_SWM_MIXER("sprot_loop_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, + sst_mix_sprot_l0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("media_loop1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, + sst_mix_media_l1_controls, sst_swm_mixer_event), + SST_SWM_MIXER("media_loop2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, + sst_mix_media_l2_controls, sst_swm_mixer_event), + + /* SBA Backend mixers */ + SST_SWM_MIXER("codec_out0 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC0, + sst_mix_codec0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("codec_out1 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC1, + sst_mix_codec1_controls, sst_swm_mixer_event), +}; + +static const struct snd_soc_dapm_route intercon[] = { + {"media0_in", NULL, "Compress Playback"}, + {"media1_in", NULL, "Headset Playback"}, + {"media2_in", NULL, "pcm0_out"}, + + {"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"), + +}; +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]), +}; + +#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 + * + * The gains in the pipes connected to the front-ends are muted/unmuted + * automatically via the digital_mute() DAPM callback. This function sends the + * gains for the front-end pipes. + */ +int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) +{ + struct sst_data *drv = snd_soc_dai_get_drvdata(dai); + struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_path *p = NULL; + + dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + dev_dbg(dai->dev, "Stream name=%s\n", + dai->playback_widget->name); + w = dai->playback_widget; + list_for_each_entry(p, &w->sinks, list_source) { + if (p->connected && !p->connected(w, p->sink)) + continue; + + if (p->connect && p->sink->power && + is_sst_dapm_widget(p->sink)) { + struct sst_ids *ids = p->sink->priv; + + dev_dbg(dai->dev, "send gains for widget=%s\n", + p->sink->name); + 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; + list_for_each_entry(p, &w->sources, list_sink) { + if (p->connected && !p->connected(w, p->sink)) + continue; + + if (p->connect && p->source->power && + is_sst_dapm_widget(p->source)) { + struct sst_ids *ids = p->source->priv; + + dev_dbg(dai->dev, "send gain for widget=%s\n", + p->source->name); + 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 + * + * + * Fills the widget pointer in the kcontrol private data, and also fills the + * kcontrol pointer in the widget private data. + * + * Widget pointer is used to send the algo/gain in the .put() handler if the + * widget is powerd on. + * + * Kcontrol pointer is used to send the algo/gain in the widget power ON/OFF + * event handler. Each widget (pipe) has multiple algos stored in the algo_list. + */ +static int sst_fill_module_list(struct snd_kcontrol *kctl, + struct snd_soc_dapm_widget *w, int type) +{ + struct sst_module *module = NULL; + struct snd_soc_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 + * @widget: pipe modelled as a DAPM widget + * + * Fill the list of gains/algos for the widget by looking at all the card + * controls and comparing the name of the widget with the first part of control + * name. First part of control name contains the pipe name (widget name). + */ +static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w, + struct snd_soc_platform *platform) +{ + struct snd_kcontrol *kctl; + int index, ret = 0; + struct snd_card *card = platform->component.card->snd_card; + char *idx; + + down_read(&card->controls_rwsem); + + list_for_each_entry(kctl, &card->controls, list) { + idx = strstr(kctl->id.name, " "); + if (idx == NULL) + continue; + index = strlen(kctl->id.name) - strlen(idx); + + if (strstr(kctl->id.name, "Volume") && + !strncmp(kctl->id.name, w->name, index)) + ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN); + + else if (strstr(kctl->id.name, "params") && + !strncmp(kctl->id.name, w->name, index)) + ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO); + + else if (strstr(kctl->id.name, "Switch") && + !strncmp(kctl->id.name, w->name, index) && + 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") && + !strncmp(kctl->id.name, w->name, index)) { + struct sst_enum *e = (void *)kctl->private_value; + + e->w = w; + + } else if (strstr(kctl->id.name, "deinterleaver") && + !strncmp(kctl->id.name, w->name, index)) { + + struct sst_enum *e = (void *)kctl->private_value; + + e->w = w; + } + + if (ret < 0) { + up_read(&card->controls_rwsem); + return ret; + } + } + + up_read(&card->controls_rwsem); + return 0; +} + +/** + * sst_fill_linked_widgets - fill the parent pointer for the linked widget + */ +static void sst_fill_linked_widgets(struct snd_soc_platform *platform, + struct sst_ids *ids) +{ + struct snd_soc_dapm_widget *w; + unsigned int len = strlen(ids->parent_wname); + + list_for_each_entry(w, &platform->component.card->widgets, list) { + if (!strncmp(ids->parent_wname, w->name, len)) { + ids->parent_w = w; + break; + } + } +} + +/** + * sst_map_modules_to_pipe - fill algo/gains list for all pipes + */ +static int sst_map_modules_to_pipe(struct snd_soc_platform *platform) +{ + struct snd_soc_dapm_widget *w; + int ret = 0; + + list_for_each_entry(w, &platform->component.card->widgets, list) { + if (is_sst_dapm_widget(w) && (w->priv)) { + struct sst_ids *ids = w->priv; + + dev_dbg(platform->dev, "widget type=%d name=%s\n", + w->id, w->name); + INIT_LIST_HEAD(&ids->algo_list); + INIT_LIST_HEAD(&ids->gain_list); + ret = sst_fill_widget_module_info(w, platform); + + if (ret < 0) + return ret; + + /* fill linked widgets */ + if (ids->parent_wname != NULL) + sst_fill_linked_widgets(platform, ids); + } + } + return 0; +} + +int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) +{ + int i, ret = 0; + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(&platform->component); + struct sst_data *drv = snd_soc_platform_get_drvdata(platform); + unsigned int gains = ARRAY_SIZE(sst_gain_controls)/3; + + drv->byte_stream = devm_kzalloc(platform->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_platform_controls(platform, sst_gain_controls, + ARRAY_SIZE(sst_gain_controls)); + if (ret) + return ret; + + /* Initialize algo control params */ + ret = sst_algo_control_init(platform->dev); + if (ret) + return ret; + ret = snd_soc_add_platform_controls(platform, sst_algo_controls, + ARRAY_SIZE(sst_algo_controls)); + if (ret) + return ret; + + ret = snd_soc_add_platform_controls(platform, sst_slot_controls, + ARRAY_SIZE(sst_slot_controls)); + if (ret) + return ret; + + ret = sst_map_modules_to_pipe(platform); + + 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 000000000000..daecc58f28af --- /dev/null +++ b/sound/soc/intel/atom/sst-atom-controls.h @@ -0,0 +1,870 @@ +/* + * sst-atom-controls.h - Intel MID Platform driver header file + * + * Copyright (C) 2013-14 Intel Corp + * Author: Ramesh Babu + * Omair M Abdullah + * Samreen Nilofer + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#ifndef __SST_ATOM_CONTROLS_H__ +#define __SST_ATOM_CONTROLS_H__ + +#include +#include + +enum { + MERR_DPCM_AUDIO = 0, + MERR_DPCM_COMPR, +}; + +/* define a bit for each mixer input */ +#define SST_MIX_IP(x) (x) + +#define SST_IP_CODEC0 SST_MIX_IP(2) +#define SST_IP_CODEC1 SST_MIX_IP(3) +#define SST_IP_LOOP0 SST_MIX_IP(4) +#define SST_IP_LOOP1 SST_MIX_IP(5) +#define SST_IP_LOOP2 SST_MIX_IP(6) +#define SST_IP_PROBE SST_MIX_IP(7) +#define SST_IP_VOIP SST_MIX_IP(12) +#define SST_IP_PCM0 SST_MIX_IP(13) +#define SST_IP_PCM1 SST_MIX_IP(14) +#define SST_IP_MEDIA0 SST_MIX_IP(17) +#define SST_IP_MEDIA1 SST_MIX_IP(18) +#define SST_IP_MEDIA2 SST_MIX_IP(19) +#define SST_IP_MEDIA3 SST_MIX_IP(20) + +#define SST_IP_LAST SST_IP_MEDIA3 + +#define SST_SWM_INPUT_COUNT (SST_IP_LAST + 1) +#define SST_CMD_SWM_MAX_INPUTS 6 + +#define SST_PATH_ID_SHIFT 8 +#define SST_DEFAULT_LOCATION_ID 0xFFFF +#define SST_DEFAULT_CELL_NBR 0xFF +#define SST_DEFAULT_MODULE_ID 0xFFFF + +/* + * Audio DSP Path Ids. Specified by the audio DSP FW + */ +enum sst_path_index { + SST_PATH_INDEX_CODEC_OUT0 = (0x02 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_CODEC_OUT1 = (0x03 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_SPROT_LOOP_OUT = (0x04 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA_LOOP1_OUT = (0x05 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA_LOOP2_OUT = (0x06 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_VOIP_OUT = (0x0C << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PCM0_OUT = (0x0D << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PCM1_OUT = (0x0E << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PCM2_OUT = (0x0F << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_MEDIA0_OUT = (0x12 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA1_OUT = (0x13 << SST_PATH_ID_SHIFT), + + + /* Start of input paths */ + SST_PATH_INDEX_CODEC_IN0 = (0x82 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_CODEC_IN1 = (0x83 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_SPROT_LOOP_IN = (0x84 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA_LOOP1_IN = (0x85 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA_LOOP2_IN = (0x86 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_VOIP_IN = (0x8C << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_PCM0_IN = (0x8D << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PCM1_IN = (0x8E << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_MEDIA0_IN = (0x8F << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA1_IN = (0x90 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA2_IN = (0x91 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_MEDIA3_IN = (0x9C << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_RESERVED = (0xFF << SST_PATH_ID_SHIFT), +}; + +/* + * path IDs + */ +enum sst_swm_inputs { + SST_SWM_IN_CODEC0 = (SST_PATH_INDEX_CODEC_IN0 | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_CODEC1 = (SST_PATH_INDEX_CODEC_IN1 | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_VOIP = (SST_PATH_INDEX_VOIP_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_PCM0 = (SST_PATH_INDEX_PCM0_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_PCM1 = (SST_PATH_INDEX_PCM1_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_MEDIA0 = (SST_PATH_INDEX_MEDIA0_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_IN_MEDIA1 = (SST_PATH_INDEX_MEDIA1_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_IN_MEDIA2 = (SST_PATH_INDEX_MEDIA2_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_IN_MEDIA3 = (SST_PATH_INDEX_MEDIA3_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_IN_END = (SST_PATH_INDEX_RESERVED | SST_DEFAULT_CELL_NBR) +}; + +/* + * path IDs + */ +enum sst_swm_outputs { + SST_SWM_OUT_CODEC0 = (SST_PATH_INDEX_CODEC_OUT0 | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_CODEC1 = (SST_PATH_INDEX_CODEC_OUT1 | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_VOIP = (SST_PATH_INDEX_VOIP_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_PCM0 = (SST_PATH_INDEX_PCM0_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_PCM1 = (SST_PATH_INDEX_PCM1_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_PCM2 = (SST_PATH_INDEX_PCM2_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_MEDIA0 = (SST_PATH_INDEX_MEDIA0_OUT | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_OUT_MEDIA1 = (SST_PATH_INDEX_MEDIA1_OUT | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_OUT_END = (SST_PATH_INDEX_RESERVED | SST_DEFAULT_CELL_NBR), +}; + +enum sst_ipc_msg { + SST_IPC_IA_CMD = 1, + SST_IPC_IA_SET_PARAMS, + SST_IPC_IA_GET_PARAMS, +}; + +enum sst_cmd_type { + SST_CMD_BYTES_SET = 1, + SST_CMD_BYTES_GET = 2, +}; + +enum sst_task { + SST_TASK_SBA = 1, + SST_TASK_MMX = 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[0]; +} __packed; + + +struct sst_cmd_sba_vb_start { + struct sst_dsp_header header; +} __packed; + +union sba_media_loop_params { + struct { + u16 rsvd:8; + struct pcm_cfg cfg; + } part; + u16 full; +} __packed; + +struct sst_cmd_sba_set_media_loop_map { + struct sst_dsp_header header; + u16 switch_state; + union sba_media_loop_params param; + u16 map; +} __packed; + +struct sst_cmd_tone_stop { + struct sst_dsp_header header; + u16 switch_state; +} __packed; + +enum sst_ssp_mode { + SSP_MODE_MASTER = 0, + SSP_MODE_SLAVE = 1, +}; + +enum sst_ssp_pcm_mode { + SSP_PCM_MODE_NORMAL = 0, + SSP_PCM_MODE_NETWORK = 1, +}; + +enum sst_ssp_duplex { + SSP_DUPLEX = 0, + SSP_RX = 1, + SSP_TX = 2, +}; + +enum sst_ssp_fs_frequency { + SSP_FS_8_KHZ = 0, + SSP_FS_16_KHZ = 1, + SSP_FS_44_1_KHZ = 2, + SSP_FS_48_KHZ = 3, +}; + +enum sst_ssp_fs_polarity { + SSP_FS_ACTIVE_LOW = 0, + SSP_FS_ACTIVE_HIGH = 1, +}; + +enum sst_ssp_protocol { + SSP_MODE_PCM = 0, + SSP_MODE_I2S = 1, +}; + +enum sst_ssp_port_id { + SSP_MODEM = 0, + SSP_BT = 1, + SSP_FM = 2, + SSP_CODEC = 3, +}; + +struct sst_cmd_sba_hw_set_ssp { + struct sst_dsp_header header; + u16 selection; /* 0:SSP0(def), 1:SSP1, 2:SSP2 */ + + u16 switch_state; + + u16 nb_bits_per_slots:6; /* 0-32 bits, 24 (def) */ + u16 nb_slots:4; /* 0-8: slots per frame */ + u16 mode:3; /* 0:Master, 1: Slave */ + u16 duplex:3; + + u16 active_tx_slot_map:8; /* Bit map, 0:off, 1:on */ + u16 reserved1:8; + + u16 active_rx_slot_map:8; /* Bit map 0: Off, 1:On */ + u16 reserved2:8; + + u16 frame_sync_frequency; + + u16 frame_sync_polarity:8; + u16 data_polarity:8; + + u16 frame_sync_width; /* 1 to N clocks */ + u16 ssp_protocol:8; + u16 start_delay:8; /* Start delay in terms of clock ticks */ +} __packed; + +#define SST_MAX_TDM_SLOTS 8 + +struct sst_param_sba_ssp_slot_map { + struct sst_dsp_header header; + + u16 param_id; + u16 param_len; + u16 ssp_index; + + u8 rx_slot_map[SST_MAX_TDM_SLOTS]; + u8 tx_slot_map[SST_MAX_TDM_SLOTS]; +} __packed; + +enum { + SST_PROBE_EXTRACTOR = 0, + SST_PROBE_INJECTOR = 1, +}; + +/**** widget defines *****/ + +#define SST_MODULE_GAIN 1 +#define SST_MODULE_ALGO 2 + +#define SST_FMT_MONO 0 +#define SST_FMT_STEREO 3 + +/* physical SSP numbers */ +enum { + SST_SSP0 = 0, + SST_SSP1, + SST_SSP2, + SST_SSP_LAST = SST_SSP2, +}; + +#define SST_NUM_SSPS (SST_SSP_LAST + 1) /* physical SSPs */ +#define SST_MAX_SSP_MUX 2 /* single SSP muxed between pipes */ +#define SST_MAX_SSP_DOMAINS 2 /* domains present in each pipe */ + +struct sst_module { + struct snd_kcontrol *kctl; + struct list_head node; +}; + +struct sst_ssp_config { + u8 ssp_id; + u8 bits_per_slot; + u8 slots; + u8 ssp_mode; + u8 pcm_mode; + u8 duplex; + u8 ssp_protocol; + u8 fs_frequency; + u8 active_slot_map; + u8 start_delay; + u16 fs_width; +}; + +struct sst_ssp_cfg { + const u8 ssp_number; + const int *mux_shift; + const int (*domain_shift)[SST_MAX_SSP_MUX]; + const struct sst_ssp_config (*ssp_config)[SST_MAX_SSP_MUX][SST_MAX_SSP_DOMAINS]; +}; + +struct sst_ids { + u16 location_id; + u16 module_id; + 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[44]; + struct snd_soc_dapm_widget *w; +}; + +struct sst_gain_value { + u16 ramp_duration; + s16 l_gain; + s16 r_gain; + bool mute; +}; +#define SST_GAIN_VOLUME_DEFAULT (-1440) +#define SST_GAIN_RAMP_DURATION_DEFAULT 5 /* timeconstant */ +#define SST_GAIN_MUTE_DEFAULT true + +#define SST_GAIN_KCONTROL_TLV(xname, xhandler_get, xhandler_put, \ + xmod, xpipe, xinstance, xtask, tlv_array, xgain_val, \ + xmin, xmax, xpname) \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = sst_gain_ctl_info,\ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct sst_gain_mixer_control) \ + { .stereo = true, .max = xmax, .min = xmin, .type = SST_GAIN_TLV, \ + .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\ + .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname} + +#define SST_GAIN_KCONTROL_INT(xname, xhandler_get, xhandler_put, \ + xmod, xpipe, xinstance, xtask, xtype, xgain_val, \ + xmin, xmax, xpname) \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = sst_gain_ctl_info, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct sst_gain_mixer_control) \ + { .stereo = false, .max = xmax, .min = xmin, .type = xtype, \ + .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\ + .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname} + +#define SST_GAIN_KCONTROL_BOOL(xname, xhandler_get, xhandler_put,\ + xmod, xpipe, xinstance, xtask, xgain_val, xpname) \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_bool_ext, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct sst_gain_mixer_control) \ + { .stereo = false, .type = SST_GAIN_MUTE, \ + .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\ + .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname} +#define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \ + xpname " " xmname " " #xinstance " " xtype + +#define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \ + xpname " " xmname " " #xinstance " " xtype " " xsubmodule + +/* + * 3 Controls for each Gain module + * e.g. - pcm0_in Gain 0 Volume + * - pcm0_in Gain 0 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)) + +#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 000000000000..4257263157cd --- /dev/null +++ b/sound/soc/intel/atom/sst-mfld-dsp.h @@ -0,0 +1,533 @@ +#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 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#define 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, /*! + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#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_compr_stream *cstream) +{ + + int ret_val = 0; + 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_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_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 snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_platform *platform = rtd->platform; + struct sst_data *ctx = snd_soc_platform_get_drvdata(platform); + + stream = cstream->runtime->private_data; + /* construct fw structure for this*/ + memset(&str_params, 0, sizeof(str_params)); + + /* 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_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); + case SNDRV_PCM_TRIGGER_STOP: + if (stream->compr_ops->stream_drop) + return stream->compr_ops->stream_drop(sst->dev, stream->id); + case SND_COMPR_TRIGGER_DRAIN: + if (stream->compr_ops->stream_drain) + return stream->compr_ops->stream_drain(sst->dev, stream->id); + case SND_COMPR_TRIGGER_PARTIAL_DRAIN: + if (stream->compr_ops->stream_partial_drain) + return stream->compr_ops->stream_partial_drain(sst->dev, stream->id); + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (stream->compr_ops->stream_pause) + return stream->compr_ops->stream_pause(sst->dev, stream->id); + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (stream->compr_ops->stream_pause_release) + return stream->compr_ops->stream_pause_release(sst->dev, stream->id); + default: + return -EINVAL; + } +} + +static int sst_platform_compr_pointer(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_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_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_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_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); +} + +struct snd_compr_ops sst_platform_compr_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 000000000000..2fbaf2c75d17 --- /dev/null +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -0,0 +1,804 @@ +/* + * sst_mfld_platform.c - Intel MID Platform driver + * + * Copyright (C) 2010-2014 Intel Corp + * Author: Vinod Koul + * Author: Harsha Priya + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sst-mfld-platform.h" +#include "sst-atom-controls.h" + +struct sst_device *sst; +static DEFINE_MUTEX(sst_lock); +extern struct snd_compr_ops sst_platform_compr_ops; + +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 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}, +}; + +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 = virt_to_phys(substream->dma_buffer.area); + + channels = substream->runtime->channels; + period_size = substream->runtime->period_size; + periodbytes = samples_to_bytes(substream->runtime, period_size); + alloc_param->ring_buf_info[0].addr = buffer_addr; + alloc_param->ring_buf_info[0].size = buffer_bytes; + alloc_param->sg_count = 1; + alloc_param->reserved = 0; + alloc_param->frag_size = periodbytes * channels; + +} +static void sst_fill_pcm_params(struct snd_pcm_substream *substream, + struct 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 == true) + cstream = (struct snd_compr_stream *)substream; + else + pstream = (struct snd_pcm_substream *)substream; + + str_params->stream_type = SST_STREAM_TYPE_MUSIC; + + /* For pcm streams */ + if (pstream) { + 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, ¶m); + sst_fill_alloc_params(substream, &alloc_params); + substream->runtime->dma_area = substream->dma_buffer.area; + 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 = substream->private_data; + 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) + return ret_val; + + /* Make sure, that the period size is always even */ + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIODS, 2); + + return snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); +out_ops: + kfree(stream); + mutex_unlock(&sst_lock); + return ret_val; +} + +static void sst_media_close(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sst_runtime_stream *stream; + int ret_val = 0, str_id; + + stream = substream->runtime->private_data; + power_down_sst(stream); + + str_id = stream->stream_info.str_id; + if (str_id) + ret_val = stream->ops->close(sst->dev, str_id); + module_put(sst->dev->driver->owner); + kfree(stream); +} + +static inline unsigned int get_current_pipe_id(struct snd_soc_dai *dai, + struct snd_pcm_substream *substream) +{ + struct sst_data *sst = snd_soc_dai_get_drvdata(dai); + struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map; + struct sst_runtime_stream *stream = + substream->runtime->private_data; + u32 str_id = stream->stream_info.str_id; + unsigned int pipe_id; + + pipe_id = map[str_id].device_id; + + dev_dbg(dai->dev, "got pipe_id = %#x for str_id = %d\n", + pipe_id, str_id); + return pipe_id; +} + +static int sst_media_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sst_runtime_stream *stream; + int ret_val = 0, str_id; + + 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 ret_val; +} + +static int sst_media_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); + return 0; +} + +static int sst_media_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int sst_enable_ssp(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret = 0; + + if (!dai->active) { + ret = sst_handle_vb_timer(dai, true); + if (ret) + return ret; + ret = send_ssp_cmd(dai, dai->name, 1); + } + return ret; +} + +static void sst_disable_ssp(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (!dai->active) { + send_ssp_cmd(dai, dai->name, 0); + sst_handle_vb_timer(dai, false); + } +} + +static struct snd_soc_dai_ops sst_media_dai_ops = { + .startup = sst_media_open, + .shutdown = sst_media_close, + .prepare = sst_media_prepare, + .hw_params = sst_media_hw_params, + .hw_free = sst_media_hw_free, + .mute_stream = sst_media_digital_mute, +}; + +static struct snd_soc_dai_ops sst_compr_dai_ops = { + .mute_stream = sst_media_digital_mute, +}; + +static struct snd_soc_dai_ops sst_be_dai_ops = { + .startup = sst_enable_ssp, + .shutdown = sst_disable_ssp, +}; + +static struct snd_soc_dai_driver sst_platform_dai[] = { +{ + .name = "media-cpu-dai", + .ops = &sst_media_dai_ops, + .playback = { + .stream_name = "Headset Playback", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Headset Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = "compress-cpu-dai", + .compress_dai = 1, + .ops = &sst_compr_dai_ops, + .playback = { + .stream_name = "Compress Playback", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +/* BE CPU Dais */ +{ + .name = "ssp0-port", + .ops = &sst_be_dai_ops, + .playback = { + .stream_name = "ssp0 Tx", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp0 Rx", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = "ssp1-port", + .ops = &sst_be_dai_ops, + .playback = { + .stream_name = "ssp1 Tx", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp1 Rx", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = "ssp2-port", + .ops = &sst_be_dai_ops, + .playback = { + .stream_name = "ssp2 Tx", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp2 Rx", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +}; + +static int sst_platform_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime; + + if (substream->pcm->internal) + return 0; + + runtime = substream->runtime; + runtime->hw = sst_platform_pcm_hw; + return 0; +} + +static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + int ret_val = 0, str_id; + struct sst_runtime_stream *stream; + int status; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + dev_dbg(rtd->dev, "sst_platform_pcm_trigger called\n"); + if (substream->pcm->internal) + return 0; + stream = substream->runtime->private_data; + str_id = stream->stream_info.str_id; + switch (cmd) { + 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_platform_pcm_pointer + (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 = substream->private_data; + + 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; + } + substream->runtime->delay = str_info->pcm_delay; + return str_info->buffer_ptr; +} + +static struct snd_pcm_ops sst_platform_ops = { + .open = sst_platform_open, + .ioctl = snd_pcm_lib_ioctl, + .trigger = sst_platform_pcm_trigger, + .pointer = sst_platform_pcm_pointer, +}; + +static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; + int retval = 0; + + if (dai->driver->playback.channels_min || + dai->driver->capture.channels_min) { + retval = snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_DMA), + SST_MIN_BUFFER, SST_MAX_BUFFER); + if (retval) { + dev_err(rtd->dev, "dma buffer allocationf fail\n"); + return retval; + } + } + return retval; +} + +static int sst_soc_probe(struct snd_soc_platform *platform) +{ + struct sst_data *drv = dev_get_drvdata(platform->dev); + + drv->soc_card = platform->component.card; + return sst_dsp_init_v2_dpcm(platform); +} + +static struct snd_soc_platform_driver sst_soc_platform_drv = { + .probe = sst_soc_probe, + .ops = &sst_platform_ops, + .compr_ops = &sst_platform_compr_ops, + .pcm_new = sst_pcm_new, +}; + +static const struct snd_soc_component_driver sst_component = { + .name = "sst", +}; + + +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 = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv); + if (ret) { + dev_err(&pdev->dev, "registering soc platform failed\n"); + return ret; + } + + ret = snd_soc_register_component(&pdev->dev, &sst_component, + sst_platform_dai, ARRAY_SIZE(sst_platform_dai)); + if (ret) { + dev_err(&pdev->dev, "registering cpu dais failed\n"); + snd_soc_unregister_platform(&pdev->dev); + } + return ret; +} + +static int sst_platform_remove(struct platform_device *pdev) +{ + + snd_soc_unregister_component(&pdev->dev); + snd_soc_unregister_platform(&pdev->dev); + 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); + int i; + + /* suspend all pcms first */ + snd_soc_suspend(drv->soc_card->dev); + snd_soc_poweroff(drv->soc_card->dev); + + /* set the SSPs to idle */ + for (i = 0; i < drv->soc_card->num_rtd; i++) { + struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai; + + if (dai->active) { + 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); + int i; + + /* restart SSPs */ + for (i = 0; i < drv->soc_card->num_rtd; i++) { + struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai; + + if (dai->active) { + 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 "); +MODULE_AUTHOR("Harsha Priya "); +MODULE_LICENSE("GPL v2"); +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 000000000000..9094314be2b0 --- /dev/null +++ b/sound/soc/intel/atom/sst-mfld-platform.h @@ -0,0 +1,181 @@ +/* + * sst_mfld_platform.h - Intel MID Platform driver header file + * + * Copyright (C) 2010 Intel Corp + * Author: Vinod Koul + * Author: Harsha Priya + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#ifndef __SST_PLATFORMDRV_H__ +#define __SST_PLATFORMDRV_H__ + +#include "sst-mfld-dsp.h" + +extern struct sst_device *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_platform *platform); +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; +}; +int sst_register_dsp(struct sst_device *sst); +int sst_unregister_dsp(struct sst_device *sst); +#endif diff --git a/sound/soc/intel/atom/sst/Makefile b/sound/soc/intel/atom/sst/Makefile new file mode 100644 index 000000000000..fd21726361b5 --- /dev/null +++ b/sound/soc/intel/atom/sst/Makefile @@ -0,0 +1,7 @@ +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_IPC) += snd-intel-sst-core.o +obj-$(CONFIG_SND_SST_IPC_PCI) += snd-intel-sst-pci.o +obj-$(CONFIG_SND_SST_IPC_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 000000000000..96c2e420cce6 --- /dev/null +++ b/sound/soc/intel/atom/sst/sst.c @@ -0,0 +1,557 @@ +/* + * sst.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-14 Intel Corp + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst-mfld-platform.h" +#include "sst.h" +#include "../../common/sst-dsp.h" + +MODULE_AUTHOR("Vinod Koul "); +MODULE_AUTHOR("Harsha Priya "); +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 = 0; + 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 = NULL; + 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 capablities 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); + +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; + } + pm_qos_add_request(ctx->qos, PM_QOS_CPU_DMA_LATENCY, + 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; + } + sst_register(ctx->dev); + return 0; + +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); + flush_scheduled_work(); + destroy_workqueue(ctx->post_msg_wq); + pm_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); + ctx = NULL; +} +EXPORT_SYMBOL_GPL(sst_context_cleanup); + +static inline void sst_save_shim64(struct intel_sst_drv *ctx, + void __iomem *shim, + struct sst_shim_regs64 *shim_regs) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags); + + shim_regs->imrx = sst_shim_read64(shim, SST_IMRX); + shim_regs->csr = sst_shim_read64(shim, SST_CSR); + + + spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags); +} + +static inline void sst_restore_shim64(struct intel_sst_drv *ctx, + void __iomem *shim, + struct sst_shim_regs64 *shim_regs) +{ + unsigned long irq_flags; + + /* + * we only need to restore IMRX for this case, rest will be + * initialize by FW or driver when firmware is loaded + */ + spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags); + sst_shim_write64(shim, SST_IMRX, shim_regs->imrx), + sst_shim_write64(shim, SST_CSR, shim_regs->csr), + spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags); +} + +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); + + sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64); +} +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); + /* save the shim registers because PMC doesn't save state */ + sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64); + + 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 = 0; + + /* 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, cant susupend, abort\n", i); + return -EBUSY; + } + } + 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 = kzalloc(ctx->iram_end - ctx->iram_base, GFP_KERNEL); + if (!fw_save->iram) { + ret = -ENOMEM; + goto iram; + } + fw_save->dram = kzalloc(ctx->dram_end - ctx->dram_base, GFP_KERNEL); + if (!fw_save->dram) { + ret = -ENOMEM; + goto dram; + } + fw_save->sram = kzalloc(SST_MAILBOX_SIZE, GFP_KERNEL); + if (!fw_save->sram) { + ret = -ENOMEM; + goto sram; + } + + fw_save->ddr = kzalloc(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: + kfree(fw_save->sram); +sram: + kfree(fw_save->dram); +dram: + kfree(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; + int ret = 0; + struct sst_block *block; + + 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); + + kfree(fw_save->sram); + kfree(fw_save->dram); + kfree(fw_save->iram); + kfree(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); + } + + 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 000000000000..3f493862e98d --- /dev/null +++ b/sound/soc/intel/atom/sst/sst.h @@ -0,0 +1,559 @@ +/* + * sst.h - Intel SST Driver for audio engine + * + * Copyright (C) 2008-14 Intel Corporation + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Common private declarations for SST + */ +#ifndef __SST_H__ +#define __SST_H__ + +#include + +/* 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 + +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_DECODE = 3, /* stream is in decoding only state */ + STREAM_INIT = 4, /* stream init, waiting for data */ + STREAM_RESET = 5, /* force reset on recovery */ +}; + +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 + * @ops : stream operation pb/cp/drm... + * @bufs: stream buffer list + * @lock : stream mutex for protecting state + * @pcm_substream : PCM substream + * @period_elapsed : PCM period elapsed callback + * @sfreq : stream sampling freq + * @str_type : stream type + * @cumm_bytes : cummulative bytes decoded + * @str_type : stream type + * @src : stream source + */ +struct stream_info { + unsigned int status; + unsigned int prev; + unsigned int ops; + struct mutex lock; + + 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 str_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_shim_regs64 { + u64 csr; + u64 pisr; + u64 pimr; + u64 isrx; + u64 isrd; + u64 imrx; + u64 imrd; + u64 ipcx; + u64 ipcd; + u64 isrsc; + u64 isrlpesc; + u64 imrsc; + u64 imrlpesc; + u64 ipcsc; + u64 ipclpesc; + u64 clkctl; + u64 csr2; +}; + +struct sst_fw_save { + void *iram; + void *dram; + void *sram; + void *ddr; +}; + +/** + * 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 + * @shim_regs64: Struct to save shim registers + * @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 sst_shim_regs64 *shim_regs64; + 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 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_pause_stream(struct intel_sst_drv *sst_drv_ctx, int id); +int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int id); +int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int id); +int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int 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 *ctx, + struct snd_sst_bytes_v2 *sbytes); +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 *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 *ctx, + struct ipc_post *msg, bool sync); +void sst_process_reply_mrfld(struct intel_sst_drv *ctx, struct ipc_post *msg); +int sst_start_mrfld(struct intel_sst_drv *ctx); +int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *ctx); +void intel_sst_clear_intr_mrfld(struct intel_sst_drv *ctx); + +int sst_load_fw(struct intel_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 *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 *sst_drv_ctx); +void sst_init_stream(struct stream_info *stream, + int codec, int sst_id, int ops, u8 slot); +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 000000000000..678f36ed97a5 --- /dev/null +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -0,0 +1,384 @@ +/* + * sst_acpi.c - SST (LPE) driver init file for ACPI enumeration. + * + * Copyright (c) 2013, Intel Corporation. + * + * Authors: Ramesh Babu K V + * Authors: Omair Mohammed Abdullah + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst-mfld-platform.h" +#include "../../common/sst-dsp.h" +#include "sst.h" + +struct sst_machines { + char *codec_id; + char board[32]; + char machine[32]; + void (*machine_quirk)(void); + char firmware[FW_NAME_SIZE]; + struct sst_platform_info *pdata; + +}; + +/* 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, +}; + +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", +}; + +/* Cherryview (Cherrytrail and Braswell) uses same mrfld dpcm fw as Baytrail, + * so pdata is same as Baytrail. + */ +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"); + 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_nocache(ctx->dev, ctx->iram_base, + ctx->pdata->res_info->iram_size); + if (!ctx->iram) { + dev_err(ctx->dev, "unable to map IRAM"); + 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_nocache(ctx->dev, ctx->dram_base, + ctx->pdata->res_info->dram_size); + if (!ctx->dram) { + dev_err(ctx->dev, "unable to map DRAM"); + 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_nocache(ctx->dev, ctx->shim_phy_add, + ctx->pdata->res_info->shim_size); + if (!ctx->shim) { + dev_err(ctx->dev, "unable to map SHIM"); + 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_nocache(ctx->dev, ctx->mailbox_add, + ctx->pdata->res_info->mbox_size); + if (!ctx->mailbox) { + dev_err(ctx->dev, "unable to map mailbox"); + 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"); + 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_nocache(ctx->dev, ctx->ddr_base, + resource_size(rsrc)); + if (!ctx->ddr) { + dev_err(ctx->dev, "unable to map DDR"); + return -EIO; + } + + /* Find the IRQ */ + ctx->irq_num = platform_get_irq(pdev, + ctx->pdata->res_info->acpi_ipc_irq_index); + return 0; +} + +static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, + void *context, void **ret) +{ + *(bool *)context = true; + return AE_OK; +} + +static struct sst_machines *sst_acpi_find_machine( + struct sst_machines *machines) +{ + struct sst_machines *mach; + bool found = false; + + for (mach = machines; mach->codec_id; mach++) + if (ACPI_SUCCESS(acpi_get_devices(mach->codec_id, + sst_acpi_mach_match, + &found, NULL)) && found) + return mach; + + return NULL; +} + +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 sst_machines *mach; + struct platform_device *mdev; + struct platform_device *plat_dev; + unsigned int dev_id; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + dev_dbg(dev, "for %s", id->id); + + mach = (struct sst_machines *)id->driver_data; + mach = sst_acpi_find_machine(mach); + if (mach == NULL) { + dev_err(dev, "No matching machine driver found\n"); + return -ENODEV; + } + + 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); + + plat_dev = platform_device_register_data(dev, mach->pdata->platform, -1, NULL, 0); + if (IS_ERR(plat_dev)) { + dev_err(dev, "Failed to create machine device: %s\n", mach->pdata->platform); + return PTR_ERR(plat_dev); + } + + /* Create platform device for sst machine driver */ + mdev = platform_device_register_data(dev, mach->machine, -1, NULL, 0); + if (IS_ERR(mdev)) { + dev_err(dev, "Failed to create machine device: %s\n", mach->machine); + return PTR_ERR(mdev); + } + + ret = sst_alloc_drv_context(&ctx, dev, dev_id); + if (ret < 0) + return ret; + + /* Fill sst platform data */ + ctx->pdata = mach->pdata; + strcpy(ctx->firmware_name, mach->firmware); + + ret = sst_platform_get_resources(ctx); + if (ret) + return ret; + + ret = sst_context_init(ctx); + if (ret < 0) + return ret; + + /* need to save shim registers in BYT */ + ctx->shim_regs64 = devm_kzalloc(ctx->dev, sizeof(*ctx->shim_regs64), + GFP_KERNEL); + if (!ctx->shim_regs64) { + return -ENOMEM; + goto do_sst_cleanup; + } + + sst_configure_runtime_pm(ctx); + platform_set_drvdata(pdev, ctx); + return ret; + +do_sst_cleanup: + sst_context_cleanup(ctx); + platform_set_drvdata(pdev, NULL); + dev_err(ctx->dev, "failed with %d\n", ret); + return ret; +} + +/** +* intel_sst_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 struct sst_machines sst_acpi_bytcr[] = { + {"10EC5640", "T100", "bytt100_rt5640", NULL, "intel/fw_sst_0f28.bin", + &byt_rvp_platform_data }, + {}, +}; + +/* Cherryview-based platforms: CherryTrail and Braswell */ +static struct sst_machines sst_acpi_chv[] = { + {"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "intel/fw_sst_22a8.bin", + &chv_platform_data }, + {"10EC5645", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin", + &chv_platform_data }, + {}, +}; + +static const struct acpi_device_id sst_acpi_ids[] = { + { "80860F28", (unsigned long)&sst_acpi_bytcr}, + { "808622A8", (unsigned long) &sst_acpi_chv}, + { }, +}; + +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 000000000000..718838b3fc24 --- /dev/null +++ b/sound/soc/intel/atom/sst/sst_drv_interface.c @@ -0,0 +1,741 @@ +/* + * sst_drv_interface.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-14 Intel Corp + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst-mfld-platform.h" +#include "sst.h" +#include "../../common/sst-dsp.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) + +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; + +#ifdef CONFIG_PM + usage_count = atomic_read(&dev->power.usage_count); +#else + usage_count = 1; +#endif + + if (state == true) { + ret = pm_runtime_get_sync(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 { + 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_get_sync(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; + } + + if (stream->status == STREAM_RESET) { + dev_dbg(dev, "stream in reset state...\n"); + stream->status = STREAM_UN_INIT; + + retval = 0; + goto put; + } + + retval = sst_free_stream(ctx, str_id); +put: + 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); + + memcpy_fromio(&fw_tstamp, + ((void *)(ctx->mailbox + ctx->tstamp) + +(str_id * sizeof(fw_tstamp))), + 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); + + addr = ((void *)(ctx->mailbox + ctx->tstamp)) + + (str_id * sizeof(fw_tstamp)); + 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); + + memcpy_fromio(&fw_tstamp, + ((void *)(ctx->mailbox + ctx->tstamp) + +(str_id * sizeof(fw_tstamp))), + 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 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 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; + } + + if (stream->status == STREAM_RESET) { + /* silently fail here as we have cleaned the stream earlier */ + dev_dbg(ctx->dev, "stream in reset state...\n"); + + retval = 0; + goto put; + } + + retval = free_stream_context(ctx, str_id); +put: + 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 / substream->runtime->channels; + 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); + + 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; + + memcpy_fromio(&fw_tstamp, + ((void *)(ctx->mailbox + ctx->tstamp) + + (str_id * sizeof(fw_tstamp))), + 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_get_sync(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 000000000000..5a278618466c --- /dev/null +++ b/sound/soc/intel/atom/sst/sst_ipc.c @@ -0,0 +1,373 @@ +/* + * sst_ipc.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-14 Intel Corporation + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst-mfld-platform.h" +#include "sst.h" +#include "../../common/sst-dsp.h" + +struct sst_block *sst_create_block(struct intel_sst_drv *ctx, + u32 msg_id, u32 drv_id) +{ + struct sst_block *msg = NULL; + + 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 = NULL; + + 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 = NULL, *__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, cant 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; + } + +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; + union ipc_header_high msg_high; + u32 msg_low, pipe_id; + + msg_high = msg->mrfld_header.p.header_high; + 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->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; + unsigned int cmd_id; + + 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 && drv_id && !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 = kzalloc(msg_low, GFP_KERNEL); + if (!data) + return; + memcpy(data, (void *) msg->mailbox_data, msg_low); + /* Copy command id so that we can use to put sst to reset */ + dsp_hdr = (struct ipc_dsp_hdr *)data; + cmd_id = dsp_hdr->cmd_id; + 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 000000000000..33917146d9c4 --- /dev/null +++ b/sound/soc/intel/atom/sst/sst_loader.c @@ -0,0 +1,463 @@ +/* + * sst_dsp.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-14 Intel Corp + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This file contains all dsp controlling functions like firmware download, + * setting/resetting dsp cores, etc + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst-mfld-platform.h" +#include "sst.h" +#include "../../common/sst-dsp.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) +{ + /* __iowrite32_copy uses 32-bit count values so divide by 4 for + * right count in words + */ + __iowrite32_copy(dst, src, count/4); +} + +/** + * intel_sst_reset_dsp_mrfld - Resetting SST DSP + * + * 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_merrifield - Start the SST DSP processor + * + * 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 == true) + 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 */ + if (!list_empty(&sst_drv_ctx->memcpy_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 (fw == NULL) { + dev_err(sst->dev, "fw is returning as null\n"); + return -EINVAL; + } + if (retval) { + dev_err(sst->dev, "request fw failed %d\n", retval); + return retval; + } + 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 + * 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 || + sst_drv_ctx->sst_state == SST_SHUTDOWN) + 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; + } + + BUG_ON(!sst_drv_ctx->fw_in_mem); + block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID); + if (block == NULL) + return -ENOMEM; + + /* Prevent C-states beyond C6 */ + pm_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 */ + pm_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 000000000000..3a0b3bf0af97 --- /dev/null +++ b/sound/soc/intel/atom/sst/sst_pci.c @@ -0,0 +1,209 @@ +/* + * sst_pci.c - SST (LPE) driver init file for pci enumeration. + * + * Copyright (C) 2008-14 Intel Corp + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#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 0; +} + +/* + * 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 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 "); +MODULE_AUTHOR("Harsha Priya "); +MODULE_AUTHOR("Dharageswari R "); +MODULE_AUTHOR("KP Jeeja "); +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 000000000000..3c178444638b --- /dev/null +++ b/sound/soc/intel/atom/sst/sst_pvt.c @@ -0,0 +1,449 @@ +/* + * sst_pvt.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-14 Intel Corp + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst-mfld-platform.h" +#include "sst.h" +#include "../../common/sst-dsp.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; + +} + +unsigned long long read_shim_data(struct intel_sst_drv *sst, int addr) +{ + unsigned long long val = 0; + + switch (sst->dev_id) { + case SST_MRFLD_PCI_ID: + case SST_BYT_ACPI_ID: + val = sst_shim_read64(sst->shim, addr); + break; + } + return val; +} + +void write_shim_data(struct intel_sst_drv *sst, int addr, + unsigned long long data) +{ + switch (sst->dev_id) { + case SST_MRFLD_PCI_ID: + case SST_BYT_ACPI_ID: + sst_shim_write64(sst->shim, addr, (u64) data); + break; + } +} + +/* + * 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(struct ipc_post), 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 = 0; + + 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 retval; +} + +/* + * 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 ipc_post *msg = NULL; + struct ipc_dsp_hdr dsp_hdr; + struct sst_block *block; + 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; + } else if(block->data) { + if (!data) + goto out; + *data = kzalloc(block->size, GFP_KERNEL); + if (!(*data)) { + ret = -ENOMEM; + goto out; + } else + memcpy(data, (void *) block->data, block->size); + } + } +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; +} + +void sst_init_stream(struct stream_info *stream, + int codec, int sst_id, int ops, u8 slot) +{ + stream->status = STREAM_INIT; + stream->prev = STREAM_UN_INIT; + stream->ops = ops; +} + +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 000000000000..a74c64c7053c --- /dev/null +++ b/sound/soc/intel/atom/sst/sst_stream.c @@ -0,0 +1,437 @@ +/* + * sst_stream.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-14 Intel Corp + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst-mfld-platform.h" +#include "sst.h" +#include "../../common/sst-dsp.h" + +int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params) +{ + struct snd_sst_alloc_mrfld alloc_param; + struct snd_sst_params *str_params; + struct snd_sst_tstamp fw_tstamp; + struct stream_info *str_info; + struct snd_sst_alloc_response *response; + unsigned int str_id, pipe_id, task_id; + int i, num_ch, ret = 0; + void *data = NULL; + + dev_dbg(sst_drv_ctx->dev, "Enter\n"); + BUG_ON(!params); + + str_params = (struct snd_sst_params *)params; + memset(&alloc_param, 0, sizeof(alloc_param)); + alloc_param.operation = str_params->ops; + alloc_param.codec_type = str_params->codec; + alloc_param.sg_count = str_params->aparams.sg_count; + alloc_param.ring_buf_info[0].addr = + str_params->aparams.ring_buf_info[0].addr; + alloc_param.ring_buf_info[0].size = + str_params->aparams.ring_buf_info[0].size; + alloc_param.frag_size = str_params->aparams.frag_size; + + memcpy(&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); + for (i = 0; i < 8; i++) { + if (i < num_ch) + alloc_param.codec_params.uc.pcm_params.channel_map[i] = i; + else + alloc_param.codec_params.uc.pcm_params.channel_map[i] = 0xFF; + } + + str_id = str_params->stream_id; + str_info = get_stream_info(sst_drv_ctx, str_id); + if (str_info == NULL) { + dev_err(sst_drv_ctx->dev, "get stream info returned null\n"); + return -EINVAL; + } + + pipe_id = str_params->device_type; + task_id = str_params->task; + sst_drv_ctx->streams[str_id].pipe_id = pipe_id; + sst_drv_ctx->streams[str_id].task_id = task_id; + sst_drv_ctx->streams[str_id].num_ch = num_ch; + + if (sst_drv_ctx->info.lpe_viewpt_rqd) + alloc_param.ts = sst_drv_ctx->info.mailbox_start + + sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp)); + else + 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", + alloc_param.ts); + dev_dbg(sst_drv_ctx->dev, "assigned pipe id 0x%x to task %d\n", + pipe_id, task_id); + + /* allocate device type context */ + sst_init_stream(&sst_drv_ctx->streams[str_id], alloc_param.codec_type, + str_id, alloc_param.operation, 0); + + dev_info(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n", + str_id, pipe_id); + ret = sst_prepare_and_post_msg(sst_drv_ctx, task_id, IPC_CMD, + IPC_IA_ALLOC_STREAM_MRFLD, pipe_id, sizeof(alloc_param), + &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 +* @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 0; +} + +/* + * sst_pause_stream - Send msg for a pausing stream + * @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 + * @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->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 + * @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 +* @str_id: stream ID +* +* 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 + * @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; + struct intel_sst_ops *ops; + + 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; + ops = sst_drv_ctx->ops; + + 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_info(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; +} diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 5c2d8fabb5ed..7f55d59024a8 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -27,7 +27,7 @@ #include #include #include "../../codecs/rt5640.h" -#include "../sst-atom-controls.h" +#include "../atom/sst-atom-controls.h" static const struct snd_soc_dapm_widget byt_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 93bb6711ba3d..20a28b22e30f 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -28,7 +28,7 @@ #include #include #include "../../codecs/rt5645.h" -#include "../sst-atom-controls.h" +#include "../atom/sst-atom-controls.h" #define CHT_PLAT_CLK_3_HZ 19200000 #define CHT_CODEC_DAI "rt5645-aif1" diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index 2cea002a31bb..2c9cc5be439e 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -24,7 +24,7 @@ #include #include #include "../../codecs/rt5670.h" -#include "../sst-atom-controls.h" +#include "../atom/sst-atom-controls.h" /* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */ #define CHT_PLAT_CLK_3_HZ 19200000 diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c deleted file mode 100644 index 90aa5c0476f3..000000000000 --- a/sound/soc/intel/sst-atom-controls.c +++ /dev/null @@ -1,1422 +0,0 @@ -/* - * sst-atom-controls.c - Intel MID Platform driver DPCM ALSA controls for Mrfld - * - * Copyright (C) 2013-14 Intel Corp - * Author: Omair Mohammed Abdullah - * Vinod Koul - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * 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 -#include -#include -#include "sst-mfld-platform.h" -#include "sst-atom-controls.h" - -static int sst_fill_byte_control(struct sst_data *drv, - u8 ipc_msg, u8 block, - u8 task_id, u8 pipe_id, - u16 len, void *cmd_data) -{ - struct snd_sst_bytes_v2 *byte_data = drv->byte_stream; - - byte_data->type = SST_CMD_BYTES_SET; - byte_data->ipc_msg = ipc_msg; - byte_data->block = block; - byte_data->task_id = task_id; - byte_data->pipe_id = pipe_id; - - if (len > SST_MAX_BIN_BYTES - sizeof(*byte_data)) { - dev_err(&drv->pdev->dev, "command length too big (%u)", len); - return -EINVAL; - } - byte_data->len = len; - memcpy(byte_data->bytes, cmd_data, len); - print_hex_dump_bytes("writing to lpe: ", DUMP_PREFIX_OFFSET, - byte_data, len + sizeof(*byte_data)); - return 0; -} - -static int sst_fill_and_send_cmd_unlocked(struct sst_data *drv, - u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, - void *cmd_data, u16 len) -{ - int ret = 0; - - ret = sst_fill_byte_control(drv, ipc_msg, - block, task_id, pipe_id, len, cmd_data); - if (ret < 0) - return ret; - return sst->ops->send_byte_stream(sst->dev, drv->byte_stream); -} - -/** - * sst_fill_and_send_cmd - generate the IPC message and send it to the FW - * @ipc_msg: type of IPC (CMD, SET_PARAMS, GET_PARAMS) - * @cmd_data: the IPC payload - */ -static int sst_fill_and_send_cmd(struct sst_data *drv, - u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, - void *cmd_data, u16 len) -{ - int ret; - - mutex_lock(&drv->lock); - ret = sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block, - task_id, pipe_id, cmd_data, len); - mutex_unlock(&drv->lock); - - return ret; -} - -/** - * 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); -} - -int sst_slot_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct sst_enum *e = (struct sst_enum *)kcontrol->private_value; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = e->max; - - if (uinfo->value.enumerated.item > e->max - 1) - uinfo->value.enumerated.item = e->max - 1; - strcpy(uinfo->value.enumerated.name, - e->texts[uinfo->value.enumerated.item]); - - return 0; -} - -/** - * sst_slot_get - get the status of the interleaver/deinterleaver control - * - * Searches the map where the control status is stored, and gets the - * channel/slot which is currently set for this enumerated control. Since it is - * an enumerated control, there is only one possible value. - */ -static int sst_slot_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct sst_enum *e = (void *)kcontrol->private_value; - struct snd_soc_component *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 - 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 - * - * (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 - * - * 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 = NULL; - - dev_dbg(&drv->pdev->dev, "Enter: widget=%s\n", pipe); - - list_for_each_entry(algo, &ids->algo_list, node) { - bc = (void *)algo->kctl->private_value; - - dev_dbg(&drv->pdev->dev, "Found algo control name=%s pipe=%s\n", - algo->kctl->id.name, pipe); - 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 - * @gv: the stored value of gain (also contains rampduration) - * @mute: flag that indicates whether this was called from the - * digital_mute callback or directly. If called from the - * digital_mute callback, module will be muted/unmuted based on this - * flag. The flag is always 0 if called directly. - * - * 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 ? 1 : 0; - break; - - case SST_GAIN_RAMP_DURATION: - ucontrol->value.integer.value[0] = gv->ramp_duration; - break; - - default: - dev_err(component->dev, "Invalid Input- gain type:%d\n", - mc->type); - return -EINVAL; - } - - return 0; -} - -static int sst_gain_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - 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_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 - * - * 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 = NULL; - - list_for_each_entry(gain, &ids->gain_list, node) { - struct snd_kcontrol *kctl = gain->kctl; - - dev_dbg(&drv->pdev->dev, "control name=%s\n", kctl->id.name); - mc = (void *)kctl->private_value; - gv = mc->gain_val; - - 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 == false) - return 0; - - if (SND_SOC_DAPM_EVENT_ON(event) || - event == SND_SOC_DAPM_POST_REG) - cmd.switch_state = SST_SWM_ON; - else - cmd.switch_state = SST_SWM_OFF; - - SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); - /* MMX_SET_SWM == SBA_SET_SWM */ - cmd.header.command_id = SBA_SET_SWM; - - SST_FILL_DESTINATION(2, cmd.output_id, - ids->location_id, SST_DEFAULT_MODULE_ID); - cmd.nb_inputs = fill_swm_input(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("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, "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(sst_mix_voip_controls); -SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls); -SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_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; -} - -/** - * sst_ssp_config - contains SSP configuration for media UC - */ -static const struct sst_ssp_config sst_ssp_configs = { - .ssp_id = SSP_CODEC, - .bits_per_slot = 24, - .slots = 4, - .ssp_mode = SSP_MODE_MASTER, - .pcm_mode = SSP_PCM_MODE_NETWORK, - .duplex = SSP_DUPLEX, - .ssp_protocol = SSP_MODE_PCM, - .fs_width = 1, - .fs_frequency = SSP_FS_48_KHZ, - .active_slot_map = 0xF, - .start_delay = 0, -}; - -int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable) -{ - struct sst_cmd_sba_hw_set_ssp cmd; - struct sst_data *drv = snd_soc_dai_get_drvdata(dai); - const struct sst_ssp_config *config; - - dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id); - - SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); - cmd.header.command_id = SBA_HW_SET_SSP; - cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp) - - sizeof(struct sst_dsp_header); - - config = &sst_ssp_configs; - dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id); - - if (enable) - cmd.switch_state = SST_SWITCH_ON; - else - cmd.switch_state = SST_SWITCH_OFF; - - cmd.selection = config->ssp_id; - cmd.nb_bits_per_slots = config->bits_per_slot; - cmd.nb_slots = config->slots; - cmd.mode = config->ssp_mode | (config->pcm_mode << 1); - cmd.duplex = config->duplex; - cmd.active_tx_slot_map = config->active_slot_map; - cmd.active_rx_slot_map = config->active_slot_map; - cmd.frame_sync_frequency = config->fs_frequency; - cmd.frame_sync_polarity = SSP_FS_ACTIVE_HIGH; - cmd.data_polarity = 1; - cmd.frame_sync_width = config->fs_width; - cmd.ssp_protocol = config->ssp_protocol; - cmd.start_delay = config->start_delay; - cmd.reserved1 = cmd.reserved2 = 0xFF; - - return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, - SST_TASK_SBA, 0, &cmd, - sizeof(cmd.header) + cmd.header.length); -} - -static int sst_set_be_modules(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - 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)) { - ret = sst_send_slot_map(drv); - 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("codec_in0", sst_set_be_modules), - SST_AIF_IN("codec_in1", sst_set_be_modules), - SST_AIF_OUT("codec_out0", sst_set_be_modules), - SST_AIF_OUT("codec_out1", sst_set_be_modules), - - /* Media Paths */ - /* MediaX IN paths are set via ALLOC, so no SET_MEDIA_PATH command */ - SST_PATH_INPUT("media0_in", SST_TASK_MMX, SST_SWM_IN_MEDIA0, sst_generic_modules_event), - SST_PATH_INPUT("media1_in", SST_TASK_MMX, SST_SWM_IN_MEDIA1, NULL), - SST_PATH_INPUT("media2_in", SST_TASK_MMX, SST_SWM_IN_MEDIA2, sst_set_media_path), - SST_PATH_INPUT("media3_in", SST_TASK_MMX, SST_SWM_IN_MEDIA3, NULL), - SST_PATH_OUTPUT("media0_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA0, sst_set_media_path), - SST_PATH_OUTPUT("media1_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA1, sst_set_media_path), - - /* SBA PCM Paths */ - SST_PATH_INPUT("pcm0_in", SST_TASK_SBA, SST_SWM_IN_PCM0, sst_set_media_path), - SST_PATH_INPUT("pcm1_in", SST_TASK_SBA, SST_SWM_IN_PCM1, sst_set_media_path), - SST_PATH_OUTPUT("pcm0_out", SST_TASK_SBA, SST_SWM_OUT_PCM0, sst_set_media_path), - SST_PATH_OUTPUT("pcm1_out", SST_TASK_SBA, SST_SWM_OUT_PCM1, sst_set_media_path), - SST_PATH_OUTPUT("pcm2_out", SST_TASK_SBA, SST_SWM_OUT_PCM2, sst_set_media_path), - - /* SBA Loops */ - SST_PATH_INPUT("sprot_loop_in", SST_TASK_SBA, SST_SWM_IN_SPROT_LOOP, NULL), - SST_PATH_INPUT("media_loop1_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP1, NULL), - SST_PATH_INPUT("media_loop2_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP2, NULL), - SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_MONO, sst_set_media_loop), - SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_MONO, sst_set_media_loop), - SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop), - - /* Media Mixers */ - SST_SWM_MIXER("media0_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA0, - sst_mix_media0_controls, sst_swm_mixer_event), - SST_SWM_MIXER("media1_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA1, - sst_mix_media1_controls, sst_swm_mixer_event), - - /* SBA PCM mixers */ - SST_SWM_MIXER("pcm0_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM0, - sst_mix_pcm0_controls, sst_swm_mixer_event), - SST_SWM_MIXER("pcm1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM1, - sst_mix_pcm1_controls, sst_swm_mixer_event), - SST_SWM_MIXER("pcm2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM2, - sst_mix_pcm2_controls, sst_swm_mixer_event), - - /* SBA Loop mixers */ - SST_SWM_MIXER("sprot_loop_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, - sst_mix_sprot_l0_controls, sst_swm_mixer_event), - SST_SWM_MIXER("media_loop1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, - sst_mix_media_l1_controls, sst_swm_mixer_event), - SST_SWM_MIXER("media_loop2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, - sst_mix_media_l2_controls, sst_swm_mixer_event), - - /* SBA Backend mixers */ - SST_SWM_MIXER("codec_out0 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC0, - sst_mix_codec0_controls, sst_swm_mixer_event), - SST_SWM_MIXER("codec_out1 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC1, - sst_mix_codec1_controls, sst_swm_mixer_event), -}; - -static const struct snd_soc_dapm_route intercon[] = { - {"media0_in", NULL, "Compress Playback"}, - {"media1_in", NULL, "Headset Playback"}, - {"media2_in", NULL, "pcm0_out"}, - - {"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"), - -}; -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]), -}; - -#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 - * - * The gains in the pipes connected to the front-ends are muted/unmuted - * automatically via the digital_mute() DAPM callback. This function sends the - * gains for the front-end pipes. - */ -int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) -{ - struct sst_data *drv = snd_soc_dai_get_drvdata(dai); - struct snd_soc_dapm_widget *w; - struct snd_soc_dapm_path *p = NULL; - - dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream); - - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - dev_dbg(dai->dev, "Stream name=%s\n", - dai->playback_widget->name); - w = dai->playback_widget; - list_for_each_entry(p, &w->sinks, list_source) { - if (p->connected && !p->connected(w, p->sink)) - continue; - - if (p->connect && p->sink->power && - is_sst_dapm_widget(p->sink)) { - struct sst_ids *ids = p->sink->priv; - - dev_dbg(dai->dev, "send gains for widget=%s\n", - p->sink->name); - 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; - list_for_each_entry(p, &w->sources, list_sink) { - if (p->connected && !p->connected(w, p->sink)) - continue; - - if (p->connect && p->source->power && - is_sst_dapm_widget(p->source)) { - struct sst_ids *ids = p->source->priv; - - dev_dbg(dai->dev, "send gain for widget=%s\n", - p->source->name); - 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 - * - * - * Fills the widget pointer in the kcontrol private data, and also fills the - * kcontrol pointer in the widget private data. - * - * Widget pointer is used to send the algo/gain in the .put() handler if the - * widget is powerd on. - * - * Kcontrol pointer is used to send the algo/gain in the widget power ON/OFF - * event handler. Each widget (pipe) has multiple algos stored in the algo_list. - */ -static int sst_fill_module_list(struct snd_kcontrol *kctl, - struct snd_soc_dapm_widget *w, int type) -{ - struct sst_module *module = NULL; - struct snd_soc_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 - * @widget: pipe modelled as a DAPM widget - * - * Fill the list of gains/algos for the widget by looking at all the card - * controls and comparing the name of the widget with the first part of control - * name. First part of control name contains the pipe name (widget name). - */ -static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w, - struct snd_soc_platform *platform) -{ - struct snd_kcontrol *kctl; - int index, ret = 0; - struct snd_card *card = platform->component.card->snd_card; - char *idx; - - down_read(&card->controls_rwsem); - - list_for_each_entry(kctl, &card->controls, list) { - idx = strstr(kctl->id.name, " "); - if (idx == NULL) - continue; - index = strlen(kctl->id.name) - strlen(idx); - - if (strstr(kctl->id.name, "Volume") && - !strncmp(kctl->id.name, w->name, index)) - ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN); - - else if (strstr(kctl->id.name, "params") && - !strncmp(kctl->id.name, w->name, index)) - ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO); - - else if (strstr(kctl->id.name, "Switch") && - !strncmp(kctl->id.name, w->name, index) && - 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") && - !strncmp(kctl->id.name, w->name, index)) { - struct sst_enum *e = (void *)kctl->private_value; - - e->w = w; - - } else if (strstr(kctl->id.name, "deinterleaver") && - !strncmp(kctl->id.name, w->name, index)) { - - struct sst_enum *e = (void *)kctl->private_value; - - e->w = w; - } - - if (ret < 0) { - up_read(&card->controls_rwsem); - return ret; - } - } - - up_read(&card->controls_rwsem); - return 0; -} - -/** - * sst_fill_linked_widgets - fill the parent pointer for the linked widget - */ -static void sst_fill_linked_widgets(struct snd_soc_platform *platform, - struct sst_ids *ids) -{ - struct snd_soc_dapm_widget *w; - unsigned int len = strlen(ids->parent_wname); - - list_for_each_entry(w, &platform->component.card->widgets, list) { - if (!strncmp(ids->parent_wname, w->name, len)) { - ids->parent_w = w; - break; - } - } -} - -/** - * sst_map_modules_to_pipe - fill algo/gains list for all pipes - */ -static int sst_map_modules_to_pipe(struct snd_soc_platform *platform) -{ - struct snd_soc_dapm_widget *w; - int ret = 0; - - list_for_each_entry(w, &platform->component.card->widgets, list) { - if (is_sst_dapm_widget(w) && (w->priv)) { - struct sst_ids *ids = w->priv; - - dev_dbg(platform->dev, "widget type=%d name=%s\n", - w->id, w->name); - INIT_LIST_HEAD(&ids->algo_list); - INIT_LIST_HEAD(&ids->gain_list); - ret = sst_fill_widget_module_info(w, platform); - - if (ret < 0) - return ret; - - /* fill linked widgets */ - if (ids->parent_wname != NULL) - sst_fill_linked_widgets(platform, ids); - } - } - return 0; -} - -int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) -{ - int i, ret = 0; - struct snd_soc_dapm_context *dapm = - snd_soc_component_get_dapm(&platform->component); - struct sst_data *drv = snd_soc_platform_get_drvdata(platform); - unsigned int gains = ARRAY_SIZE(sst_gain_controls)/3; - - drv->byte_stream = devm_kzalloc(platform->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_platform_controls(platform, sst_gain_controls, - ARRAY_SIZE(sst_gain_controls)); - if (ret) - return ret; - - /* Initialize algo control params */ - ret = sst_algo_control_init(platform->dev); - if (ret) - return ret; - ret = snd_soc_add_platform_controls(platform, sst_algo_controls, - ARRAY_SIZE(sst_algo_controls)); - if (ret) - return ret; - - ret = snd_soc_add_platform_controls(platform, sst_slot_controls, - ARRAY_SIZE(sst_slot_controls)); - if (ret) - return ret; - - ret = sst_map_modules_to_pipe(platform); - - return ret; -} diff --git a/sound/soc/intel/sst-atom-controls.h b/sound/soc/intel/sst-atom-controls.h deleted file mode 100644 index daecc58f28af..000000000000 --- a/sound/soc/intel/sst-atom-controls.h +++ /dev/null @@ -1,870 +0,0 @@ -/* - * sst-atom-controls.h - Intel MID Platform driver header file - * - * Copyright (C) 2013-14 Intel Corp - * Author: Ramesh Babu - * Omair M Abdullah - * Samreen Nilofer - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - */ - -#ifndef __SST_ATOM_CONTROLS_H__ -#define __SST_ATOM_CONTROLS_H__ - -#include -#include - -enum { - MERR_DPCM_AUDIO = 0, - MERR_DPCM_COMPR, -}; - -/* define a bit for each mixer input */ -#define SST_MIX_IP(x) (x) - -#define SST_IP_CODEC0 SST_MIX_IP(2) -#define SST_IP_CODEC1 SST_MIX_IP(3) -#define SST_IP_LOOP0 SST_MIX_IP(4) -#define SST_IP_LOOP1 SST_MIX_IP(5) -#define SST_IP_LOOP2 SST_MIX_IP(6) -#define SST_IP_PROBE SST_MIX_IP(7) -#define SST_IP_VOIP SST_MIX_IP(12) -#define SST_IP_PCM0 SST_MIX_IP(13) -#define SST_IP_PCM1 SST_MIX_IP(14) -#define SST_IP_MEDIA0 SST_MIX_IP(17) -#define SST_IP_MEDIA1 SST_MIX_IP(18) -#define SST_IP_MEDIA2 SST_MIX_IP(19) -#define SST_IP_MEDIA3 SST_MIX_IP(20) - -#define SST_IP_LAST SST_IP_MEDIA3 - -#define SST_SWM_INPUT_COUNT (SST_IP_LAST + 1) -#define SST_CMD_SWM_MAX_INPUTS 6 - -#define SST_PATH_ID_SHIFT 8 -#define SST_DEFAULT_LOCATION_ID 0xFFFF -#define SST_DEFAULT_CELL_NBR 0xFF -#define SST_DEFAULT_MODULE_ID 0xFFFF - -/* - * Audio DSP Path Ids. Specified by the audio DSP FW - */ -enum sst_path_index { - SST_PATH_INDEX_CODEC_OUT0 = (0x02 << SST_PATH_ID_SHIFT), - SST_PATH_INDEX_CODEC_OUT1 = (0x03 << SST_PATH_ID_SHIFT), - - SST_PATH_INDEX_SPROT_LOOP_OUT = (0x04 << SST_PATH_ID_SHIFT), - SST_PATH_INDEX_MEDIA_LOOP1_OUT = (0x05 << SST_PATH_ID_SHIFT), - SST_PATH_INDEX_MEDIA_LOOP2_OUT = (0x06 << SST_PATH_ID_SHIFT), - - SST_PATH_INDEX_VOIP_OUT = (0x0C << SST_PATH_ID_SHIFT), - SST_PATH_INDEX_PCM0_OUT = (0x0D << SST_PATH_ID_SHIFT), - SST_PATH_INDEX_PCM1_OUT = (0x0E << SST_PATH_ID_SHIFT), - SST_PATH_INDEX_PCM2_OUT = (0x0F << SST_PATH_ID_SHIFT), - - SST_PATH_INDEX_MEDIA0_OUT = (0x12 << SST_PATH_ID_SHIFT), - SST_PATH_INDEX_MEDIA1_OUT = (0x13 << SST_PATH_ID_SHIFT), - - - /* Start of input paths */ - SST_PATH_INDEX_CODEC_IN0 = (0x82 << SST_PATH_ID_SHIFT), - SST_PATH_INDEX_CODEC_IN1 = (0x83 << SST_PATH_ID_SHIFT), - - SST_PATH_INDEX_SPROT_LOOP_IN = (0x84 << SST_PATH_ID_SHIFT), - SST_PATH_INDEX_MEDIA_LOOP1_IN = (0x85 << SST_PATH_ID_SHIFT), - SST_PATH_INDEX_MEDIA_LOOP2_IN = (0x86 << SST_PATH_ID_SHIFT), - - SST_PATH_INDEX_VOIP_IN = (0x8C << SST_PATH_ID_SHIFT), - - SST_PATH_INDEX_PCM0_IN = (0x8D << SST_PATH_ID_SHIFT), - SST_PATH_INDEX_PCM1_IN = (0x8E << SST_PATH_ID_SHIFT), - - SST_PATH_INDEX_MEDIA0_IN = (0x8F << SST_PATH_ID_SHIFT), - SST_PATH_INDEX_MEDIA1_IN = (0x90 << SST_PATH_ID_SHIFT), - SST_PATH_INDEX_MEDIA2_IN = (0x91 << SST_PATH_ID_SHIFT), - - SST_PATH_INDEX_MEDIA3_IN = (0x9C << SST_PATH_ID_SHIFT), - - SST_PATH_INDEX_RESERVED = (0xFF << SST_PATH_ID_SHIFT), -}; - -/* - * path IDs - */ -enum sst_swm_inputs { - SST_SWM_IN_CODEC0 = (SST_PATH_INDEX_CODEC_IN0 | SST_DEFAULT_CELL_NBR), - SST_SWM_IN_CODEC1 = (SST_PATH_INDEX_CODEC_IN1 | SST_DEFAULT_CELL_NBR), - SST_SWM_IN_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_IN | SST_DEFAULT_CELL_NBR), - SST_SWM_IN_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_IN | SST_DEFAULT_CELL_NBR), - SST_SWM_IN_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_IN | SST_DEFAULT_CELL_NBR), - SST_SWM_IN_VOIP = (SST_PATH_INDEX_VOIP_IN | SST_DEFAULT_CELL_NBR), - SST_SWM_IN_PCM0 = (SST_PATH_INDEX_PCM0_IN | SST_DEFAULT_CELL_NBR), - SST_SWM_IN_PCM1 = (SST_PATH_INDEX_PCM1_IN | SST_DEFAULT_CELL_NBR), - SST_SWM_IN_MEDIA0 = (SST_PATH_INDEX_MEDIA0_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ - SST_SWM_IN_MEDIA1 = (SST_PATH_INDEX_MEDIA1_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ - SST_SWM_IN_MEDIA2 = (SST_PATH_INDEX_MEDIA2_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ - SST_SWM_IN_MEDIA3 = (SST_PATH_INDEX_MEDIA3_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ - SST_SWM_IN_END = (SST_PATH_INDEX_RESERVED | SST_DEFAULT_CELL_NBR) -}; - -/* - * path IDs - */ -enum sst_swm_outputs { - SST_SWM_OUT_CODEC0 = (SST_PATH_INDEX_CODEC_OUT0 | SST_DEFAULT_CELL_NBR), - SST_SWM_OUT_CODEC1 = (SST_PATH_INDEX_CODEC_OUT1 | SST_DEFAULT_CELL_NBR), - SST_SWM_OUT_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_OUT | SST_DEFAULT_CELL_NBR), - SST_SWM_OUT_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_OUT | SST_DEFAULT_CELL_NBR), - SST_SWM_OUT_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_OUT | SST_DEFAULT_CELL_NBR), - SST_SWM_OUT_VOIP = (SST_PATH_INDEX_VOIP_OUT | SST_DEFAULT_CELL_NBR), - SST_SWM_OUT_PCM0 = (SST_PATH_INDEX_PCM0_OUT | SST_DEFAULT_CELL_NBR), - SST_SWM_OUT_PCM1 = (SST_PATH_INDEX_PCM1_OUT | SST_DEFAULT_CELL_NBR), - SST_SWM_OUT_PCM2 = (SST_PATH_INDEX_PCM2_OUT | SST_DEFAULT_CELL_NBR), - SST_SWM_OUT_MEDIA0 = (SST_PATH_INDEX_MEDIA0_OUT | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ - SST_SWM_OUT_MEDIA1 = (SST_PATH_INDEX_MEDIA1_OUT | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ - SST_SWM_OUT_END = (SST_PATH_INDEX_RESERVED | SST_DEFAULT_CELL_NBR), -}; - -enum sst_ipc_msg { - SST_IPC_IA_CMD = 1, - SST_IPC_IA_SET_PARAMS, - SST_IPC_IA_GET_PARAMS, -}; - -enum sst_cmd_type { - SST_CMD_BYTES_SET = 1, - SST_CMD_BYTES_GET = 2, -}; - -enum sst_task { - SST_TASK_SBA = 1, - SST_TASK_MMX = 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[0]; -} __packed; - - -struct sst_cmd_sba_vb_start { - struct sst_dsp_header header; -} __packed; - -union sba_media_loop_params { - struct { - u16 rsvd:8; - struct pcm_cfg cfg; - } part; - u16 full; -} __packed; - -struct sst_cmd_sba_set_media_loop_map { - struct sst_dsp_header header; - u16 switch_state; - union sba_media_loop_params param; - u16 map; -} __packed; - -struct sst_cmd_tone_stop { - struct sst_dsp_header header; - u16 switch_state; -} __packed; - -enum sst_ssp_mode { - SSP_MODE_MASTER = 0, - SSP_MODE_SLAVE = 1, -}; - -enum sst_ssp_pcm_mode { - SSP_PCM_MODE_NORMAL = 0, - SSP_PCM_MODE_NETWORK = 1, -}; - -enum sst_ssp_duplex { - SSP_DUPLEX = 0, - SSP_RX = 1, - SSP_TX = 2, -}; - -enum sst_ssp_fs_frequency { - SSP_FS_8_KHZ = 0, - SSP_FS_16_KHZ = 1, - SSP_FS_44_1_KHZ = 2, - SSP_FS_48_KHZ = 3, -}; - -enum sst_ssp_fs_polarity { - SSP_FS_ACTIVE_LOW = 0, - SSP_FS_ACTIVE_HIGH = 1, -}; - -enum sst_ssp_protocol { - SSP_MODE_PCM = 0, - SSP_MODE_I2S = 1, -}; - -enum sst_ssp_port_id { - SSP_MODEM = 0, - SSP_BT = 1, - SSP_FM = 2, - SSP_CODEC = 3, -}; - -struct sst_cmd_sba_hw_set_ssp { - struct sst_dsp_header header; - u16 selection; /* 0:SSP0(def), 1:SSP1, 2:SSP2 */ - - u16 switch_state; - - u16 nb_bits_per_slots:6; /* 0-32 bits, 24 (def) */ - u16 nb_slots:4; /* 0-8: slots per frame */ - u16 mode:3; /* 0:Master, 1: Slave */ - u16 duplex:3; - - u16 active_tx_slot_map:8; /* Bit map, 0:off, 1:on */ - u16 reserved1:8; - - u16 active_rx_slot_map:8; /* Bit map 0: Off, 1:On */ - u16 reserved2:8; - - u16 frame_sync_frequency; - - u16 frame_sync_polarity:8; - u16 data_polarity:8; - - u16 frame_sync_width; /* 1 to N clocks */ - u16 ssp_protocol:8; - u16 start_delay:8; /* Start delay in terms of clock ticks */ -} __packed; - -#define SST_MAX_TDM_SLOTS 8 - -struct sst_param_sba_ssp_slot_map { - struct sst_dsp_header header; - - u16 param_id; - u16 param_len; - u16 ssp_index; - - u8 rx_slot_map[SST_MAX_TDM_SLOTS]; - u8 tx_slot_map[SST_MAX_TDM_SLOTS]; -} __packed; - -enum { - SST_PROBE_EXTRACTOR = 0, - SST_PROBE_INJECTOR = 1, -}; - -/**** widget defines *****/ - -#define SST_MODULE_GAIN 1 -#define SST_MODULE_ALGO 2 - -#define SST_FMT_MONO 0 -#define SST_FMT_STEREO 3 - -/* physical SSP numbers */ -enum { - SST_SSP0 = 0, - SST_SSP1, - SST_SSP2, - SST_SSP_LAST = SST_SSP2, -}; - -#define SST_NUM_SSPS (SST_SSP_LAST + 1) /* physical SSPs */ -#define SST_MAX_SSP_MUX 2 /* single SSP muxed between pipes */ -#define SST_MAX_SSP_DOMAINS 2 /* domains present in each pipe */ - -struct sst_module { - struct snd_kcontrol *kctl; - struct list_head node; -}; - -struct sst_ssp_config { - u8 ssp_id; - u8 bits_per_slot; - u8 slots; - u8 ssp_mode; - u8 pcm_mode; - u8 duplex; - u8 ssp_protocol; - u8 fs_frequency; - u8 active_slot_map; - u8 start_delay; - u16 fs_width; -}; - -struct sst_ssp_cfg { - const u8 ssp_number; - const int *mux_shift; - const int (*domain_shift)[SST_MAX_SSP_MUX]; - const struct sst_ssp_config (*ssp_config)[SST_MAX_SSP_MUX][SST_MAX_SSP_DOMAINS]; -}; - -struct sst_ids { - u16 location_id; - u16 module_id; - 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[44]; - struct snd_soc_dapm_widget *w; -}; - -struct sst_gain_value { - u16 ramp_duration; - s16 l_gain; - s16 r_gain; - bool mute; -}; -#define SST_GAIN_VOLUME_DEFAULT (-1440) -#define SST_GAIN_RAMP_DURATION_DEFAULT 5 /* timeconstant */ -#define SST_GAIN_MUTE_DEFAULT true - -#define SST_GAIN_KCONTROL_TLV(xname, xhandler_get, xhandler_put, \ - xmod, xpipe, xinstance, xtask, tlv_array, xgain_val, \ - xmin, xmax, xpname) \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ - SNDRV_CTL_ELEM_ACCESS_READWRITE, \ - .tlv.p = (tlv_array), \ - .info = sst_gain_ctl_info,\ - .get = xhandler_get, .put = xhandler_put, \ - .private_value = (unsigned long)&(struct sst_gain_mixer_control) \ - { .stereo = true, .max = xmax, .min = xmin, .type = SST_GAIN_TLV, \ - .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\ - .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname} - -#define SST_GAIN_KCONTROL_INT(xname, xhandler_get, xhandler_put, \ - xmod, xpipe, xinstance, xtask, xtype, xgain_val, \ - xmin, xmax, xpname) \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = sst_gain_ctl_info, \ - .get = xhandler_get, .put = xhandler_put, \ - .private_value = (unsigned long)&(struct sst_gain_mixer_control) \ - { .stereo = false, .max = xmax, .min = xmin, .type = xtype, \ - .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\ - .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname} - -#define SST_GAIN_KCONTROL_BOOL(xname, xhandler_get, xhandler_put,\ - xmod, xpipe, xinstance, xtask, xgain_val, xpname) \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = snd_soc_info_bool_ext, \ - .get = xhandler_get, .put = xhandler_put, \ - .private_value = (unsigned long)&(struct sst_gain_mixer_control) \ - { .stereo = false, .type = SST_GAIN_MUTE, \ - .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\ - .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname} -#define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \ - xpname " " xmname " " #xinstance " " xtype - -#define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \ - xpname " " xmname " " #xinstance " " xtype " " xsubmodule - -/* - * 3 Controls for each Gain module - * e.g. - pcm0_in Gain 0 Volume - * - pcm0_in Gain 0 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)) - -#endif diff --git a/sound/soc/intel/sst-mfld-dsp.h b/sound/soc/intel/sst-mfld-dsp.h deleted file mode 100644 index 4257263157cd..000000000000 --- a/sound/soc/intel/sst-mfld-dsp.h +++ /dev/null @@ -1,533 +0,0 @@ -#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 - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -#define 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, /*! - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#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_compr_stream *cstream) -{ - - int ret_val = 0; - 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_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_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 snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_platform *platform = rtd->platform; - struct sst_data *ctx = snd_soc_platform_get_drvdata(platform); - - stream = cstream->runtime->private_data; - /* construct fw structure for this*/ - memset(&str_params, 0, sizeof(str_params)); - - /* 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_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); - case SNDRV_PCM_TRIGGER_STOP: - if (stream->compr_ops->stream_drop) - return stream->compr_ops->stream_drop(sst->dev, stream->id); - case SND_COMPR_TRIGGER_DRAIN: - if (stream->compr_ops->stream_drain) - return stream->compr_ops->stream_drain(sst->dev, stream->id); - case SND_COMPR_TRIGGER_PARTIAL_DRAIN: - if (stream->compr_ops->stream_partial_drain) - return stream->compr_ops->stream_partial_drain(sst->dev, stream->id); - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (stream->compr_ops->stream_pause) - return stream->compr_ops->stream_pause(sst->dev, stream->id); - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (stream->compr_ops->stream_pause_release) - return stream->compr_ops->stream_pause_release(sst->dev, stream->id); - default: - return -EINVAL; - } -} - -static int sst_platform_compr_pointer(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_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_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_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_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); -} - -struct snd_compr_ops sst_platform_compr_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/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c deleted file mode 100644 index 2fbaf2c75d17..000000000000 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ /dev/null @@ -1,804 +0,0 @@ -/* - * sst_mfld_platform.c - Intel MID Platform driver - * - * Copyright (C) 2010-2014 Intel Corp - * Author: Vinod Koul - * Author: Harsha Priya - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sst-mfld-platform.h" -#include "sst-atom-controls.h" - -struct sst_device *sst; -static DEFINE_MUTEX(sst_lock); -extern struct snd_compr_ops sst_platform_compr_ops; - -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 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}, -}; - -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 = virt_to_phys(substream->dma_buffer.area); - - channels = substream->runtime->channels; - period_size = substream->runtime->period_size; - periodbytes = samples_to_bytes(substream->runtime, period_size); - alloc_param->ring_buf_info[0].addr = buffer_addr; - alloc_param->ring_buf_info[0].size = buffer_bytes; - alloc_param->sg_count = 1; - alloc_param->reserved = 0; - alloc_param->frag_size = periodbytes * channels; - -} -static void sst_fill_pcm_params(struct snd_pcm_substream *substream, - struct 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 == true) - cstream = (struct snd_compr_stream *)substream; - else - pstream = (struct snd_pcm_substream *)substream; - - str_params->stream_type = SST_STREAM_TYPE_MUSIC; - - /* For pcm streams */ - if (pstream) { - 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, ¶m); - sst_fill_alloc_params(substream, &alloc_params); - substream->runtime->dma_area = substream->dma_buffer.area; - 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 = substream->private_data; - 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) - return ret_val; - - /* Make sure, that the period size is always even */ - snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_PERIODS, 2); - - return snd_pcm_hw_constraint_integer(runtime, - SNDRV_PCM_HW_PARAM_PERIODS); -out_ops: - kfree(stream); - mutex_unlock(&sst_lock); - return ret_val; -} - -static void sst_media_close(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct sst_runtime_stream *stream; - int ret_val = 0, str_id; - - stream = substream->runtime->private_data; - power_down_sst(stream); - - str_id = stream->stream_info.str_id; - if (str_id) - ret_val = stream->ops->close(sst->dev, str_id); - module_put(sst->dev->driver->owner); - kfree(stream); -} - -static inline unsigned int get_current_pipe_id(struct snd_soc_dai *dai, - struct snd_pcm_substream *substream) -{ - struct sst_data *sst = snd_soc_dai_get_drvdata(dai); - struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map; - struct sst_runtime_stream *stream = - substream->runtime->private_data; - u32 str_id = stream->stream_info.str_id; - unsigned int pipe_id; - - pipe_id = map[str_id].device_id; - - dev_dbg(dai->dev, "got pipe_id = %#x for str_id = %d\n", - pipe_id, str_id); - return pipe_id; -} - -static int sst_media_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct sst_runtime_stream *stream; - int ret_val = 0, str_id; - - 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 ret_val; -} - -static int sst_media_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); - memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); - return 0; -} - -static int sst_media_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - return snd_pcm_lib_free_pages(substream); -} - -static int sst_enable_ssp(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - int ret = 0; - - if (!dai->active) { - ret = sst_handle_vb_timer(dai, true); - if (ret) - return ret; - ret = send_ssp_cmd(dai, dai->name, 1); - } - return ret; -} - -static void sst_disable_ssp(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - if (!dai->active) { - send_ssp_cmd(dai, dai->name, 0); - sst_handle_vb_timer(dai, false); - } -} - -static struct snd_soc_dai_ops sst_media_dai_ops = { - .startup = sst_media_open, - .shutdown = sst_media_close, - .prepare = sst_media_prepare, - .hw_params = sst_media_hw_params, - .hw_free = sst_media_hw_free, - .mute_stream = sst_media_digital_mute, -}; - -static struct snd_soc_dai_ops sst_compr_dai_ops = { - .mute_stream = sst_media_digital_mute, -}; - -static struct snd_soc_dai_ops sst_be_dai_ops = { - .startup = sst_enable_ssp, - .shutdown = sst_disable_ssp, -}; - -static struct snd_soc_dai_driver sst_platform_dai[] = { -{ - .name = "media-cpu-dai", - .ops = &sst_media_dai_ops, - .playback = { - .stream_name = "Headset Playback", - .channels_min = SST_STEREO, - .channels_max = SST_STEREO, - .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .capture = { - .stream_name = "Headset Capture", - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, -}, -{ - .name = "compress-cpu-dai", - .compress_dai = 1, - .ops = &sst_compr_dai_ops, - .playback = { - .stream_name = "Compress Playback", - .channels_min = SST_STEREO, - .channels_max = SST_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, -}, -/* BE CPU Dais */ -{ - .name = "ssp0-port", - .ops = &sst_be_dai_ops, - .playback = { - .stream_name = "ssp0 Tx", - .channels_min = SST_STEREO, - .channels_max = SST_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .capture = { - .stream_name = "ssp0 Rx", - .channels_min = SST_STEREO, - .channels_max = SST_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, -}, -{ - .name = "ssp1-port", - .ops = &sst_be_dai_ops, - .playback = { - .stream_name = "ssp1 Tx", - .channels_min = SST_STEREO, - .channels_max = SST_STEREO, - .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .capture = { - .stream_name = "ssp1 Rx", - .channels_min = SST_STEREO, - .channels_max = SST_STEREO, - .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, -}, -{ - .name = "ssp2-port", - .ops = &sst_be_dai_ops, - .playback = { - .stream_name = "ssp2 Tx", - .channels_min = SST_STEREO, - .channels_max = SST_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .capture = { - .stream_name = "ssp2 Rx", - .channels_min = SST_STEREO, - .channels_max = SST_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, -}, -}; - -static int sst_platform_open(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime; - - if (substream->pcm->internal) - return 0; - - runtime = substream->runtime; - runtime->hw = sst_platform_pcm_hw; - return 0; -} - -static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, - int cmd) -{ - int ret_val = 0, str_id; - struct sst_runtime_stream *stream; - int status; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - - dev_dbg(rtd->dev, "sst_platform_pcm_trigger called\n"); - if (substream->pcm->internal) - return 0; - stream = substream->runtime->private_data; - str_id = stream->stream_info.str_id; - switch (cmd) { - 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_platform_pcm_pointer - (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 = substream->private_data; - - 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; - } - substream->runtime->delay = str_info->pcm_delay; - return str_info->buffer_ptr; -} - -static struct snd_pcm_ops sst_platform_ops = { - .open = sst_platform_open, - .ioctl = snd_pcm_lib_ioctl, - .trigger = sst_platform_pcm_trigger, - .pointer = sst_platform_pcm_pointer, -}; - -static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_dai *dai = rtd->cpu_dai; - struct snd_pcm *pcm = rtd->pcm; - int retval = 0; - - if (dai->driver->playback.channels_min || - dai->driver->capture.channels_min) { - retval = snd_pcm_lib_preallocate_pages_for_all(pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_DMA), - SST_MIN_BUFFER, SST_MAX_BUFFER); - if (retval) { - dev_err(rtd->dev, "dma buffer allocationf fail\n"); - return retval; - } - } - return retval; -} - -static int sst_soc_probe(struct snd_soc_platform *platform) -{ - struct sst_data *drv = dev_get_drvdata(platform->dev); - - drv->soc_card = platform->component.card; - return sst_dsp_init_v2_dpcm(platform); -} - -static struct snd_soc_platform_driver sst_soc_platform_drv = { - .probe = sst_soc_probe, - .ops = &sst_platform_ops, - .compr_ops = &sst_platform_compr_ops, - .pcm_new = sst_pcm_new, -}; - -static const struct snd_soc_component_driver sst_component = { - .name = "sst", -}; - - -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 = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv); - if (ret) { - dev_err(&pdev->dev, "registering soc platform failed\n"); - return ret; - } - - ret = snd_soc_register_component(&pdev->dev, &sst_component, - sst_platform_dai, ARRAY_SIZE(sst_platform_dai)); - if (ret) { - dev_err(&pdev->dev, "registering cpu dais failed\n"); - snd_soc_unregister_platform(&pdev->dev); - } - return ret; -} - -static int sst_platform_remove(struct platform_device *pdev) -{ - - snd_soc_unregister_component(&pdev->dev); - snd_soc_unregister_platform(&pdev->dev); - 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); - int i; - - /* suspend all pcms first */ - snd_soc_suspend(drv->soc_card->dev); - snd_soc_poweroff(drv->soc_card->dev); - - /* set the SSPs to idle */ - for (i = 0; i < drv->soc_card->num_rtd; i++) { - struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai; - - if (dai->active) { - 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); - int i; - - /* restart SSPs */ - for (i = 0; i < drv->soc_card->num_rtd; i++) { - struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai; - - if (dai->active) { - 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 "); -MODULE_AUTHOR("Harsha Priya "); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:sst-mfld-platform"); diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h deleted file mode 100644 index 9094314be2b0..000000000000 --- a/sound/soc/intel/sst-mfld-platform.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * sst_mfld_platform.h - Intel MID Platform driver header file - * - * Copyright (C) 2010 Intel Corp - * Author: Vinod Koul - * Author: Harsha Priya - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -#ifndef __SST_PLATFORMDRV_H__ -#define __SST_PLATFORMDRV_H__ - -#include "sst-mfld-dsp.h" - -extern struct sst_device *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_platform *platform); -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; -}; -int sst_register_dsp(struct sst_device *sst); -int sst_unregister_dsp(struct sst_device *sst); -#endif diff --git a/sound/soc/intel/sst/Makefile b/sound/soc/intel/sst/Makefile deleted file mode 100644 index fd21726361b5..000000000000 --- a/sound/soc/intel/sst/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -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_IPC) += snd-intel-sst-core.o -obj-$(CONFIG_SND_SST_IPC_PCI) += snd-intel-sst-pci.o -obj-$(CONFIG_SND_SST_IPC_ACPI) += snd-intel-sst-acpi.o diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c deleted file mode 100644 index 26b1e31c5003..000000000000 --- a/sound/soc/intel/sst/sst.c +++ /dev/null @@ -1,557 +0,0 @@ -/* - * sst.c - Intel SST Driver for audio engine - * - * Copyright (C) 2008-14 Intel Corp - * Authors: Vinod Koul - * Harsha Priya - * Dharageswari R - * KP Jeeja - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../sst-mfld-platform.h" -#include "sst.h" -#include "../common/sst-dsp.h" - -MODULE_AUTHOR("Vinod Koul "); -MODULE_AUTHOR("Harsha Priya "); -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 = 0; - 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 = NULL; - 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 capablities 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); - -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; - } - pm_qos_add_request(ctx->qos, PM_QOS_CPU_DMA_LATENCY, - 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; - } - sst_register(ctx->dev); - return 0; - -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); - flush_scheduled_work(); - destroy_workqueue(ctx->post_msg_wq); - pm_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); - ctx = NULL; -} -EXPORT_SYMBOL_GPL(sst_context_cleanup); - -static inline void sst_save_shim64(struct intel_sst_drv *ctx, - void __iomem *shim, - struct sst_shim_regs64 *shim_regs) -{ - unsigned long irq_flags; - - spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags); - - shim_regs->imrx = sst_shim_read64(shim, SST_IMRX); - shim_regs->csr = sst_shim_read64(shim, SST_CSR); - - - spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags); -} - -static inline void sst_restore_shim64(struct intel_sst_drv *ctx, - void __iomem *shim, - struct sst_shim_regs64 *shim_regs) -{ - unsigned long irq_flags; - - /* - * we only need to restore IMRX for this case, rest will be - * initialize by FW or driver when firmware is loaded - */ - spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags); - sst_shim_write64(shim, SST_IMRX, shim_regs->imrx), - sst_shim_write64(shim, SST_CSR, shim_regs->csr), - spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags); -} - -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); - - sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64); -} -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); - /* save the shim registers because PMC doesn't save state */ - sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64); - - 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 = 0; - - /* 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, cant susupend, abort\n", i); - return -EBUSY; - } - } - 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 = kzalloc(ctx->iram_end - ctx->iram_base, GFP_KERNEL); - if (!fw_save->iram) { - ret = -ENOMEM; - goto iram; - } - fw_save->dram = kzalloc(ctx->dram_end - ctx->dram_base, GFP_KERNEL); - if (!fw_save->dram) { - ret = -ENOMEM; - goto dram; - } - fw_save->sram = kzalloc(SST_MAILBOX_SIZE, GFP_KERNEL); - if (!fw_save->sram) { - ret = -ENOMEM; - goto sram; - } - - fw_save->ddr = kzalloc(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: - kfree(fw_save->sram); -sram: - kfree(fw_save->dram); -dram: - kfree(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; - int ret = 0; - struct sst_block *block; - - 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); - - kfree(fw_save->sram); - kfree(fw_save->dram); - kfree(fw_save->iram); - kfree(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); - } - - 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/sst/sst.h b/sound/soc/intel/sst/sst.h deleted file mode 100644 index 3f493862e98d..000000000000 --- a/sound/soc/intel/sst/sst.h +++ /dev/null @@ -1,559 +0,0 @@ -/* - * sst.h - Intel SST Driver for audio engine - * - * Copyright (C) 2008-14 Intel Corporation - * Authors: Vinod Koul - * Harsha Priya - * Dharageswari R - * KP Jeeja - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Common private declarations for SST - */ -#ifndef __SST_H__ -#define __SST_H__ - -#include - -/* 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 - -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_DECODE = 3, /* stream is in decoding only state */ - STREAM_INIT = 4, /* stream init, waiting for data */ - STREAM_RESET = 5, /* force reset on recovery */ -}; - -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 - * @ops : stream operation pb/cp/drm... - * @bufs: stream buffer list - * @lock : stream mutex for protecting state - * @pcm_substream : PCM substream - * @period_elapsed : PCM period elapsed callback - * @sfreq : stream sampling freq - * @str_type : stream type - * @cumm_bytes : cummulative bytes decoded - * @str_type : stream type - * @src : stream source - */ -struct stream_info { - unsigned int status; - unsigned int prev; - unsigned int ops; - struct mutex lock; - - 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 str_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_shim_regs64 { - u64 csr; - u64 pisr; - u64 pimr; - u64 isrx; - u64 isrd; - u64 imrx; - u64 imrd; - u64 ipcx; - u64 ipcd; - u64 isrsc; - u64 isrlpesc; - u64 imrsc; - u64 imrlpesc; - u64 ipcsc; - u64 ipclpesc; - u64 clkctl; - u64 csr2; -}; - -struct sst_fw_save { - void *iram; - void *dram; - void *sram; - void *ddr; -}; - -/** - * 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 - * @shim_regs64: Struct to save shim registers - * @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 sst_shim_regs64 *shim_regs64; - 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 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_pause_stream(struct intel_sst_drv *sst_drv_ctx, int id); -int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int id); -int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int id); -int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int 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 *ctx, - struct snd_sst_bytes_v2 *sbytes); -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 *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 *ctx, - struct ipc_post *msg, bool sync); -void sst_process_reply_mrfld(struct intel_sst_drv *ctx, struct ipc_post *msg); -int sst_start_mrfld(struct intel_sst_drv *ctx); -int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *ctx); -void intel_sst_clear_intr_mrfld(struct intel_sst_drv *ctx); - -int sst_load_fw(struct intel_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 *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 *sst_drv_ctx); -void sst_init_stream(struct stream_info *stream, - int codec, int sst_id, int ops, u8 slot); -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/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c deleted file mode 100644 index 2a19cbcac811..000000000000 --- a/sound/soc/intel/sst/sst_acpi.c +++ /dev/null @@ -1,384 +0,0 @@ -/* - * sst_acpi.c - SST (LPE) driver init file for ACPI enumeration. - * - * Copyright (c) 2013, Intel Corporation. - * - * Authors: Ramesh Babu K V - * Authors: Omair Mohammed Abdullah - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../sst-mfld-platform.h" -#include "../common/sst-dsp.h" -#include "sst.h" - -struct sst_machines { - char *codec_id; - char board[32]; - char machine[32]; - void (*machine_quirk)(void); - char firmware[FW_NAME_SIZE]; - struct sst_platform_info *pdata; - -}; - -/* 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, -}; - -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", -}; - -/* Cherryview (Cherrytrail and Braswell) uses same mrfld dpcm fw as Baytrail, - * so pdata is same as Baytrail. - */ -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"); - 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_nocache(ctx->dev, ctx->iram_base, - ctx->pdata->res_info->iram_size); - if (!ctx->iram) { - dev_err(ctx->dev, "unable to map IRAM"); - 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_nocache(ctx->dev, ctx->dram_base, - ctx->pdata->res_info->dram_size); - if (!ctx->dram) { - dev_err(ctx->dev, "unable to map DRAM"); - 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_nocache(ctx->dev, ctx->shim_phy_add, - ctx->pdata->res_info->shim_size); - if (!ctx->shim) { - dev_err(ctx->dev, "unable to map SHIM"); - 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_nocache(ctx->dev, ctx->mailbox_add, - ctx->pdata->res_info->mbox_size); - if (!ctx->mailbox) { - dev_err(ctx->dev, "unable to map mailbox"); - 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"); - 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_nocache(ctx->dev, ctx->ddr_base, - resource_size(rsrc)); - if (!ctx->ddr) { - dev_err(ctx->dev, "unable to map DDR"); - return -EIO; - } - - /* Find the IRQ */ - ctx->irq_num = platform_get_irq(pdev, - ctx->pdata->res_info->acpi_ipc_irq_index); - return 0; -} - -static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, - void *context, void **ret) -{ - *(bool *)context = true; - return AE_OK; -} - -static struct sst_machines *sst_acpi_find_machine( - struct sst_machines *machines) -{ - struct sst_machines *mach; - bool found = false; - - for (mach = machines; mach->codec_id; mach++) - if (ACPI_SUCCESS(acpi_get_devices(mach->codec_id, - sst_acpi_mach_match, - &found, NULL)) && found) - return mach; - - return NULL; -} - -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 sst_machines *mach; - struct platform_device *mdev; - struct platform_device *plat_dev; - unsigned int dev_id; - - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return -ENODEV; - dev_dbg(dev, "for %s", id->id); - - mach = (struct sst_machines *)id->driver_data; - mach = sst_acpi_find_machine(mach); - if (mach == NULL) { - dev_err(dev, "No matching machine driver found\n"); - return -ENODEV; - } - - 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); - - plat_dev = platform_device_register_data(dev, mach->pdata->platform, -1, NULL, 0); - if (IS_ERR(plat_dev)) { - dev_err(dev, "Failed to create machine device: %s\n", mach->pdata->platform); - return PTR_ERR(plat_dev); - } - - /* Create platform device for sst machine driver */ - mdev = platform_device_register_data(dev, mach->machine, -1, NULL, 0); - if (IS_ERR(mdev)) { - dev_err(dev, "Failed to create machine device: %s\n", mach->machine); - return PTR_ERR(mdev); - } - - ret = sst_alloc_drv_context(&ctx, dev, dev_id); - if (ret < 0) - return ret; - - /* Fill sst platform data */ - ctx->pdata = mach->pdata; - strcpy(ctx->firmware_name, mach->firmware); - - ret = sst_platform_get_resources(ctx); - if (ret) - return ret; - - ret = sst_context_init(ctx); - if (ret < 0) - return ret; - - /* need to save shim registers in BYT */ - ctx->shim_regs64 = devm_kzalloc(ctx->dev, sizeof(*ctx->shim_regs64), - GFP_KERNEL); - if (!ctx->shim_regs64) { - return -ENOMEM; - goto do_sst_cleanup; - } - - sst_configure_runtime_pm(ctx); - platform_set_drvdata(pdev, ctx); - return ret; - -do_sst_cleanup: - sst_context_cleanup(ctx); - platform_set_drvdata(pdev, NULL); - dev_err(ctx->dev, "failed with %d\n", ret); - return ret; -} - -/** -* intel_sst_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 struct sst_machines sst_acpi_bytcr[] = { - {"10EC5640", "T100", "bytt100_rt5640", NULL, "intel/fw_sst_0f28.bin", - &byt_rvp_platform_data }, - {}, -}; - -/* Cherryview-based platforms: CherryTrail and Braswell */ -static struct sst_machines sst_acpi_chv[] = { - {"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "intel/fw_sst_22a8.bin", - &chv_platform_data }, - {"10EC5645", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin", - &chv_platform_data }, - {}, -}; - -static const struct acpi_device_id sst_acpi_ids[] = { - { "80860F28", (unsigned long)&sst_acpi_bytcr}, - { "808622A8", (unsigned long) &sst_acpi_chv}, - { }, -}; - -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/sst/sst_drv_interface.c b/sound/soc/intel/sst/sst_drv_interface.c deleted file mode 100644 index 36d68b8dfd28..000000000000 --- a/sound/soc/intel/sst/sst_drv_interface.c +++ /dev/null @@ -1,741 +0,0 @@ -/* - * sst_drv_interface.c - Intel SST Driver for audio engine - * - * Copyright (C) 2008-14 Intel Corp - * Authors: Vinod Koul - * Harsha Priya - * Dharageswari R -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../sst-mfld-platform.h" -#include "sst.h" -#include "../common/sst-dsp.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) - -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; - -#ifdef CONFIG_PM - usage_count = atomic_read(&dev->power.usage_count); -#else - usage_count = 1; -#endif - - if (state == true) { - ret = pm_runtime_get_sync(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 { - 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_get_sync(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; - } - - if (stream->status == STREAM_RESET) { - dev_dbg(dev, "stream in reset state...\n"); - stream->status = STREAM_UN_INIT; - - retval = 0; - goto put; - } - - retval = sst_free_stream(ctx, str_id); -put: - 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); - - memcpy_fromio(&fw_tstamp, - ((void *)(ctx->mailbox + ctx->tstamp) - +(str_id * sizeof(fw_tstamp))), - 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); - - addr = ((void *)(ctx->mailbox + ctx->tstamp)) + - (str_id * sizeof(fw_tstamp)); - 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); - - memcpy_fromio(&fw_tstamp, - ((void *)(ctx->mailbox + ctx->tstamp) - +(str_id * sizeof(fw_tstamp))), - 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 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 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; - } - - if (stream->status == STREAM_RESET) { - /* silently fail here as we have cleaned the stream earlier */ - dev_dbg(ctx->dev, "stream in reset state...\n"); - - retval = 0; - goto put; - } - - retval = free_stream_context(ctx, str_id); -put: - 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 / substream->runtime->channels; - 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); - - 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; - - memcpy_fromio(&fw_tstamp, - ((void *)(ctx->mailbox + ctx->tstamp) - + (str_id * sizeof(fw_tstamp))), - 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_get_sync(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/sst/sst_ipc.c b/sound/soc/intel/sst/sst_ipc.c deleted file mode 100644 index 3943ae856c31..000000000000 --- a/sound/soc/intel/sst/sst_ipc.c +++ /dev/null @@ -1,373 +0,0 @@ -/* - * sst_ipc.c - Intel SST Driver for audio engine - * - * Copyright (C) 2008-14 Intel Corporation - * Authors: Vinod Koul - * Harsha Priya - * Dharageswari R - * KP Jeeja - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../sst-mfld-platform.h" -#include "sst.h" -#include "../common/sst-dsp.h" - -struct sst_block *sst_create_block(struct intel_sst_drv *ctx, - u32 msg_id, u32 drv_id) -{ - struct sst_block *msg = NULL; - - 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 = NULL; - - 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 = NULL, *__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, cant 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; - } - -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; - union ipc_header_high msg_high; - u32 msg_low, pipe_id; - - msg_high = msg->mrfld_header.p.header_high; - 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->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; - unsigned int cmd_id; - - 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 && drv_id && !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 = kzalloc(msg_low, GFP_KERNEL); - if (!data) - return; - memcpy(data, (void *) msg->mailbox_data, msg_low); - /* Copy command id so that we can use to put sst to reset */ - dsp_hdr = (struct ipc_dsp_hdr *)data; - cmd_id = dsp_hdr->cmd_id; - 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/sst/sst_loader.c b/sound/soc/intel/sst/sst_loader.c deleted file mode 100644 index 6622e66e1796..000000000000 --- a/sound/soc/intel/sst/sst_loader.c +++ /dev/null @@ -1,463 +0,0 @@ -/* - * sst_dsp.c - Intel SST Driver for audio engine - * - * Copyright (C) 2008-14 Intel Corp - * Authors: Vinod Koul - * Harsha Priya - * Dharageswari R - * KP Jeeja - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This file contains all dsp controlling functions like firmware download, - * setting/resetting dsp cores, etc - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../sst-mfld-platform.h" -#include "sst.h" -#include "../common/sst-dsp.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) -{ - /* __iowrite32_copy uses 32-bit count values so divide by 4 for - * right count in words - */ - __iowrite32_copy(dst, src, count/4); -} - -/** - * intel_sst_reset_dsp_mrfld - Resetting SST DSP - * - * 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_merrifield - Start the SST DSP processor - * - * 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 == true) - 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 */ - if (!list_empty(&sst_drv_ctx->memcpy_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 (fw == NULL) { - dev_err(sst->dev, "fw is returning as null\n"); - return -EINVAL; - } - if (retval) { - dev_err(sst->dev, "request fw failed %d\n", retval); - return retval; - } - 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 - * 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 || - sst_drv_ctx->sst_state == SST_SHUTDOWN) - 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; - } - - BUG_ON(!sst_drv_ctx->fw_in_mem); - block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID); - if (block == NULL) - return -ENOMEM; - - /* Prevent C-states beyond C6 */ - pm_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 */ - pm_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/sst/sst_pci.c b/sound/soc/intel/sst/sst_pci.c deleted file mode 100644 index 3a0b3bf0af97..000000000000 --- a/sound/soc/intel/sst/sst_pci.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * sst_pci.c - SST (LPE) driver init file for pci enumeration. - * - * Copyright (C) 2008-14 Intel Corp - * Authors: Vinod Koul - * Harsha Priya - * Dharageswari R - * KP Jeeja - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ -#include -#include -#include -#include -#include -#include -#include -#include -#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 0; -} - -/* - * 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 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 "); -MODULE_AUTHOR("Harsha Priya "); -MODULE_AUTHOR("Dharageswari R "); -MODULE_AUTHOR("KP Jeeja "); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("sst"); diff --git a/sound/soc/intel/sst/sst_pvt.c b/sound/soc/intel/sst/sst_pvt.c deleted file mode 100644 index 2bb0e9e0677d..000000000000 --- a/sound/soc/intel/sst/sst_pvt.c +++ /dev/null @@ -1,449 +0,0 @@ -/* - * sst_pvt.c - Intel SST Driver for audio engine - * - * Copyright (C) 2008-14 Intel Corp - * Authors: Vinod Koul - * Harsha Priya - * Dharageswari R - * KP Jeeja - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../sst-mfld-platform.h" -#include "sst.h" -#include "../common/sst-dsp.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; - -} - -unsigned long long read_shim_data(struct intel_sst_drv *sst, int addr) -{ - unsigned long long val = 0; - - switch (sst->dev_id) { - case SST_MRFLD_PCI_ID: - case SST_BYT_ACPI_ID: - val = sst_shim_read64(sst->shim, addr); - break; - } - return val; -} - -void write_shim_data(struct intel_sst_drv *sst, int addr, - unsigned long long data) -{ - switch (sst->dev_id) { - case SST_MRFLD_PCI_ID: - case SST_BYT_ACPI_ID: - sst_shim_write64(sst->shim, addr, (u64) data); - break; - } -} - -/* - * 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(struct ipc_post), 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 = 0; - - 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 retval; -} - -/* - * 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 ipc_post *msg = NULL; - struct ipc_dsp_hdr dsp_hdr; - struct sst_block *block; - 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; - } else if(block->data) { - if (!data) - goto out; - *data = kzalloc(block->size, GFP_KERNEL); - if (!(*data)) { - ret = -ENOMEM; - goto out; - } else - memcpy(data, (void *) block->data, block->size); - } - } -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; -} - -void sst_init_stream(struct stream_info *stream, - int codec, int sst_id, int ops, u8 slot) -{ - stream->status = STREAM_INIT; - stream->prev = STREAM_UN_INIT; - stream->ops = ops; -} - -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/sst/sst_stream.c b/sound/soc/intel/sst/sst_stream.c deleted file mode 100644 index 7638fca02de0..000000000000 --- a/sound/soc/intel/sst/sst_stream.c +++ /dev/null @@ -1,437 +0,0 @@ -/* - * sst_stream.c - Intel SST Driver for audio engine - * - * Copyright (C) 2008-14 Intel Corp - * Authors: Vinod Koul - * Harsha Priya - * Dharageswari R - * KP Jeeja - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../sst-mfld-platform.h" -#include "sst.h" -#include "../common/sst-dsp.h" - -int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params) -{ - struct snd_sst_alloc_mrfld alloc_param; - struct snd_sst_params *str_params; - struct snd_sst_tstamp fw_tstamp; - struct stream_info *str_info; - struct snd_sst_alloc_response *response; - unsigned int str_id, pipe_id, task_id; - int i, num_ch, ret = 0; - void *data = NULL; - - dev_dbg(sst_drv_ctx->dev, "Enter\n"); - BUG_ON(!params); - - str_params = (struct snd_sst_params *)params; - memset(&alloc_param, 0, sizeof(alloc_param)); - alloc_param.operation = str_params->ops; - alloc_param.codec_type = str_params->codec; - alloc_param.sg_count = str_params->aparams.sg_count; - alloc_param.ring_buf_info[0].addr = - str_params->aparams.ring_buf_info[0].addr; - alloc_param.ring_buf_info[0].size = - str_params->aparams.ring_buf_info[0].size; - alloc_param.frag_size = str_params->aparams.frag_size; - - memcpy(&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); - for (i = 0; i < 8; i++) { - if (i < num_ch) - alloc_param.codec_params.uc.pcm_params.channel_map[i] = i; - else - alloc_param.codec_params.uc.pcm_params.channel_map[i] = 0xFF; - } - - str_id = str_params->stream_id; - str_info = get_stream_info(sst_drv_ctx, str_id); - if (str_info == NULL) { - dev_err(sst_drv_ctx->dev, "get stream info returned null\n"); - return -EINVAL; - } - - pipe_id = str_params->device_type; - task_id = str_params->task; - sst_drv_ctx->streams[str_id].pipe_id = pipe_id; - sst_drv_ctx->streams[str_id].task_id = task_id; - sst_drv_ctx->streams[str_id].num_ch = num_ch; - - if (sst_drv_ctx->info.lpe_viewpt_rqd) - alloc_param.ts = sst_drv_ctx->info.mailbox_start + - sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp)); - else - 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", - alloc_param.ts); - dev_dbg(sst_drv_ctx->dev, "assigned pipe id 0x%x to task %d\n", - pipe_id, task_id); - - /* allocate device type context */ - sst_init_stream(&sst_drv_ctx->streams[str_id], alloc_param.codec_type, - str_id, alloc_param.operation, 0); - - dev_info(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n", - str_id, pipe_id); - ret = sst_prepare_and_post_msg(sst_drv_ctx, task_id, IPC_CMD, - IPC_IA_ALLOC_STREAM_MRFLD, pipe_id, sizeof(alloc_param), - &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 -* @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 0; -} - -/* - * sst_pause_stream - Send msg for a pausing stream - * @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 - * @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->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 - * @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 -* @str_id: stream ID -* -* 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 - * @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; - struct intel_sst_ops *ops; - - 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; - ops = sst_drv_ctx->ops; - - 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_info(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; -}