Path: blob/master/sound/soc/intel/atom/sst-atom-controls.c
26493 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* sst-atom-controls.c - Intel MID Platform driver DPCM ALSA controls for Mrfld3*4* Copyright (C) 2013-14 Intel Corp5* Author: Omair Mohammed Abdullah <[email protected]>6* Vinod Koul <[email protected]>7* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~8*9* In the dpcm driver modelling when a particular FE/BE/Mixer/Pipe is active10* we forward the settings and parameters, rest we keep the values in11* driver and forward when DAPM enables them12* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~13*/14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt1516#include <linux/slab.h>17#include <sound/soc.h>18#include <sound/tlv.h>19#include "sst-mfld-platform.h"20#include "sst-atom-controls.h"2122static int sst_fill_byte_control(struct sst_data *drv,23u8 ipc_msg, u8 block,24u8 task_id, u8 pipe_id,25u16 len, void *cmd_data)26{27struct snd_sst_bytes_v2 *byte_data = drv->byte_stream;2829byte_data->type = SST_CMD_BYTES_SET;30byte_data->ipc_msg = ipc_msg;31byte_data->block = block;32byte_data->task_id = task_id;33byte_data->pipe_id = pipe_id;3435if (len > SST_MAX_BIN_BYTES - sizeof(*byte_data)) {36dev_err(&drv->pdev->dev, "command length too big (%u)", len);37return -EINVAL;38}39byte_data->len = len;40memcpy(byte_data->bytes, cmd_data, len);41print_hex_dump_bytes("writing to lpe: ", DUMP_PREFIX_OFFSET,42byte_data, len + sizeof(*byte_data));43return 0;44}4546static int sst_fill_and_send_cmd_unlocked(struct sst_data *drv,47u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id,48void *cmd_data, u16 len)49{50int ret = 0;5152WARN_ON(!mutex_is_locked(&drv->lock));5354ret = sst_fill_byte_control(drv, ipc_msg,55block, task_id, pipe_id, len, cmd_data);56if (ret < 0)57return ret;58return sst->ops->send_byte_stream(sst->dev, drv->byte_stream);59}6061/**62* sst_fill_and_send_cmd - generate the IPC message and send it to the FW63* @drv: sst_data64* @ipc_msg: type of IPC (CMD, SET_PARAMS, GET_PARAMS)65* @block: block index66* @task_id: task index67* @pipe_id: pipe index68* @cmd_data: the IPC payload69* @len: length of data to be sent70*/71static int sst_fill_and_send_cmd(struct sst_data *drv,72u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id,73void *cmd_data, u16 len)74{75int ret;7677mutex_lock(&drv->lock);78ret = sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block,79task_id, pipe_id, cmd_data, len);80mutex_unlock(&drv->lock);8182return ret;83}8485/*86* tx map value is a bitfield where each bit represents a FW channel87*88* 3 2 1 0 # 0 = codec0, 1 = codec189* RLRLRLRL # 3, 4 = reserved90*91* e.g. slot 0 rx map = 00001100b -> data from slot 0 goes into codec_in1 L,R92*/93static u8 sst_ssp_tx_map[SST_MAX_TDM_SLOTS] = {940x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default rx map */95};9697/*98* rx map value is a bitfield where each bit represents a slot99*100* 76543210 # 0 = slot 0, 1 = slot 1101*102* e.g. codec1_0 tx map = 00000101b -> data from codec_out1_0 goes into slot 0, 2103*/104static u8 sst_ssp_rx_map[SST_MAX_TDM_SLOTS] = {1050x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default tx map */106};107108/*109* NOTE: this is invoked with lock held110*/111static int sst_send_slot_map(struct sst_data *drv)112{113struct sst_param_sba_ssp_slot_map cmd;114115SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);116cmd.header.command_id = SBA_SET_SSP_SLOT_MAP;117cmd.header.length = sizeof(struct sst_param_sba_ssp_slot_map)118- sizeof(struct sst_dsp_header);119120cmd.param_id = SBA_SET_SSP_SLOT_MAP;121cmd.param_len = sizeof(cmd.rx_slot_map) + sizeof(cmd.tx_slot_map)122+ sizeof(cmd.ssp_index);123cmd.ssp_index = SSP_CODEC;124125memcpy(cmd.rx_slot_map, &sst_ssp_tx_map[0], sizeof(cmd.rx_slot_map));126memcpy(cmd.tx_slot_map, &sst_ssp_rx_map[0], sizeof(cmd.tx_slot_map));127128return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,129SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,130sizeof(cmd.header) + cmd.header.length);131}132133static int sst_slot_enum_info(struct snd_kcontrol *kcontrol,134struct snd_ctl_elem_info *uinfo)135{136struct sst_enum *e = (struct sst_enum *)kcontrol->private_value;137138uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;139uinfo->count = 1;140uinfo->value.enumerated.items = e->max;141142if (uinfo->value.enumerated.item > e->max - 1)143uinfo->value.enumerated.item = e->max - 1;144strcpy(uinfo->value.enumerated.name,145e->texts[uinfo->value.enumerated.item]);146147return 0;148}149150/**151* sst_slot_get - get the status of the interleaver/deinterleaver control152* @kcontrol: control pointer153* @ucontrol: User data154* Searches the map where the control status is stored, and gets the155* channel/slot which is currently set for this enumerated control. Since it is156* an enumerated control, there is only one possible value.157*/158static int sst_slot_get(struct snd_kcontrol *kcontrol,159struct snd_ctl_elem_value *ucontrol)160{161struct sst_enum *e = (void *)kcontrol->private_value;162struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);163struct sst_data *drv = snd_soc_component_get_drvdata(c);164unsigned int ctl_no = e->reg;165unsigned int is_tx = e->tx;166unsigned int val, mux;167u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;168169mutex_lock(&drv->lock);170val = 1 << ctl_no;171/* search which slot/channel has this bit set - there should be only one */172for (mux = e->max; mux > 0; mux--)173if (map[mux - 1] & val)174break;175176ucontrol->value.enumerated.item[0] = mux;177mutex_unlock(&drv->lock);178179dev_dbg(c->dev, "%s - %s map = %#x\n",180is_tx ? "tx channel" : "rx slot",181e->texts[mux], mux ? map[mux - 1] : -1);182return 0;183}184185/* sst_check_and_send_slot_map - helper for checking power state and sending186* slot map cmd187*188* called with lock held189*/190static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol *kcontrol)191{192struct sst_enum *e = (void *)kcontrol->private_value;193int ret = 0;194195if (e->w && e->w->power)196ret = sst_send_slot_map(drv);197else if (!e->w)198dev_err(&drv->pdev->dev, "Slot control: %s doesn't have DAPM widget!!!\n",199kcontrol->id.name);200return ret;201}202203/**204* sst_slot_put - set the status of interleaver/deinterleaver control205* @kcontrol: control pointer206* @ucontrol: User data207* (de)interleaver controls are defined in opposite sense to be user-friendly208*209* Instead of the enum value being the value written to the register, it is the210* register address; and the kcontrol number (register num) is the value written211* to the register. This is so that there can be only one value for each212* slot/channel since there is only one control for each slot/channel.213*214* This means that whenever an enum is set, we need to clear the bit215* for that kcontrol_no for all the interleaver OR deinterleaver registers216*/217static int sst_slot_put(struct snd_kcontrol *kcontrol,218struct snd_ctl_elem_value *ucontrol)219{220struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);221struct sst_data *drv = snd_soc_component_get_drvdata(c);222struct sst_enum *e = (void *)kcontrol->private_value;223int i, ret = 0;224unsigned int ctl_no = e->reg;225unsigned int is_tx = e->tx;226unsigned int slot_channel_no;227unsigned int val, mux;228u8 *map;229230map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;231232val = 1 << ctl_no;233mux = ucontrol->value.enumerated.item[0];234if (mux > e->max - 1)235return -EINVAL;236237mutex_lock(&drv->lock);238/* first clear all registers of this bit */239for (i = 0; i < e->max; i++)240map[i] &= ~val;241242if (mux == 0) {243/* kctl set to 'none' and we reset the bits so send IPC */244ret = sst_check_and_send_slot_map(drv, kcontrol);245246mutex_unlock(&drv->lock);247return ret;248}249250/* offset by one to take "None" into account */251slot_channel_no = mux - 1;252map[slot_channel_no] |= val;253254dev_dbg(c->dev, "%s %s map = %#x\n",255is_tx ? "tx channel" : "rx slot",256e->texts[mux], map[slot_channel_no]);257258ret = sst_check_and_send_slot_map(drv, kcontrol);259260mutex_unlock(&drv->lock);261return ret;262}263264static int sst_send_algo_cmd(struct sst_data *drv,265struct sst_algo_control *bc)266{267int len, ret = 0;268struct sst_cmd_set_params *cmd;269270/*bc->max includes sizeof algos + length field*/271len = sizeof(cmd->dst) + sizeof(cmd->command_id) + bc->max;272273cmd = kzalloc(len, GFP_KERNEL);274if (cmd == NULL)275return -ENOMEM;276277SST_FILL_DESTINATION(2, cmd->dst, bc->pipe_id, bc->module_id);278cmd->command_id = bc->cmd_id;279memcpy(cmd->params, bc->params, bc->max);280281ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,282SST_FLAG_BLOCKED, bc->task_id, 0, cmd, len);283kfree(cmd);284return ret;285}286287/**288* sst_find_and_send_pipe_algo - send all the algo parameters for a pipe289* @drv: sst_data290* @pipe: string identifier291* @ids: list of algorithms292* The algos which are in each pipeline are sent to the firmware one by one293*294* Called with lock held295*/296static int sst_find_and_send_pipe_algo(struct sst_data *drv,297const char *pipe, struct sst_ids *ids)298{299int ret = 0;300struct sst_algo_control *bc;301struct sst_module *algo;302303dev_dbg(&drv->pdev->dev, "Enter: widget=%s\n", pipe);304305list_for_each_entry(algo, &ids->algo_list, node) {306bc = (void *)algo->kctl->private_value;307308dev_dbg(&drv->pdev->dev, "Found algo control name=%s pipe=%s\n",309algo->kctl->id.name, pipe);310ret = sst_send_algo_cmd(drv, bc);311if (ret)312return ret;313}314return ret;315}316317static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol,318struct snd_ctl_elem_info *uinfo)319{320struct sst_algo_control *bc = (void *)kcontrol->private_value;321322uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;323uinfo->count = bc->max;324325return 0;326}327328static int sst_algo_control_get(struct snd_kcontrol *kcontrol,329struct snd_ctl_elem_value *ucontrol)330{331struct sst_algo_control *bc = (void *)kcontrol->private_value;332struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);333334switch (bc->type) {335case SST_ALGO_PARAMS:336memcpy(ucontrol->value.bytes.data, bc->params, bc->max);337break;338default:339dev_err(component->dev, "Invalid Input- algo type:%d\n",340bc->type);341return -EINVAL;342343}344return 0;345}346347static int sst_algo_control_set(struct snd_kcontrol *kcontrol,348struct snd_ctl_elem_value *ucontrol)349{350int ret = 0;351struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);352struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);353struct sst_algo_control *bc = (void *)kcontrol->private_value;354355dev_dbg(cmpnt->dev, "control_name=%s\n", kcontrol->id.name);356mutex_lock(&drv->lock);357switch (bc->type) {358case SST_ALGO_PARAMS:359memcpy(bc->params, ucontrol->value.bytes.data, bc->max);360break;361default:362mutex_unlock(&drv->lock);363dev_err(cmpnt->dev, "Invalid Input- algo type:%d\n",364bc->type);365return -EINVAL;366}367/*if pipe is enabled, need to send the algo params from here*/368if (bc->w && bc->w->power)369ret = sst_send_algo_cmd(drv, bc);370mutex_unlock(&drv->lock);371372return ret;373}374375static int sst_gain_ctl_info(struct snd_kcontrol *kcontrol,376struct snd_ctl_elem_info *uinfo)377{378struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;379380uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;381uinfo->count = mc->stereo ? 2 : 1;382uinfo->value.integer.min = mc->min;383uinfo->value.integer.max = mc->max;384385return 0;386}387388/**389* sst_send_gain_cmd - send the gain algorithm IPC to the FW390* @drv: sst_data391* @gv:the stored value of gain (also contains rampduration)392* @task_id: task index393* @loc_id: location/position index394* @module_id: module index395* @mute: flag that indicates whether this was called from the396* digital_mute callback or directly. If called from the397* digital_mute callback, module will be muted/unmuted based on this398* flag. The flag is always 0 if called directly.399*400* Called with sst_data.lock held401*402* The user-set gain value is sent only if the user-controllable 'mute' control403* is OFF (indicated by gv->mute). Otherwise, the mute value (MIN value) is404* sent.405*/406static int sst_send_gain_cmd(struct sst_data *drv, struct sst_gain_value *gv,407u16 task_id, u16 loc_id, u16 module_id, int mute)408{409struct sst_cmd_set_gain_dual cmd;410411dev_dbg(&drv->pdev->dev, "Enter\n");412413cmd.header.command_id = MMX_SET_GAIN;414SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);415cmd.gain_cell_num = 1;416417if (mute || gv->mute) {418cmd.cell_gains[0].cell_gain_left = SST_GAIN_MIN_VALUE;419cmd.cell_gains[0].cell_gain_right = SST_GAIN_MIN_VALUE;420} else {421cmd.cell_gains[0].cell_gain_left = gv->l_gain;422cmd.cell_gains[0].cell_gain_right = gv->r_gain;423}424425SST_FILL_DESTINATION(2, cmd.cell_gains[0].dest,426loc_id, module_id);427cmd.cell_gains[0].gain_time_constant = gv->ramp_duration;428429cmd.header.length = sizeof(struct sst_cmd_set_gain_dual)430- sizeof(struct sst_dsp_header);431432/* we are with lock held, so call the unlocked api to send */433return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,434SST_FLAG_BLOCKED, task_id, 0, &cmd,435sizeof(cmd.header) + cmd.header.length);436}437438static int sst_gain_get(struct snd_kcontrol *kcontrol,439struct snd_ctl_elem_value *ucontrol)440{441struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);442struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;443struct sst_gain_value *gv = mc->gain_val;444445switch (mc->type) {446case SST_GAIN_TLV:447ucontrol->value.integer.value[0] = gv->l_gain;448ucontrol->value.integer.value[1] = gv->r_gain;449break;450451case SST_GAIN_MUTE:452ucontrol->value.integer.value[0] = gv->mute ? 0 : 1;453break;454455case SST_GAIN_RAMP_DURATION:456ucontrol->value.integer.value[0] = gv->ramp_duration;457break;458459default:460dev_err(component->dev, "Invalid Input- gain type:%d\n",461mc->type);462return -EINVAL;463}464465return 0;466}467468static int sst_gain_put(struct snd_kcontrol *kcontrol,469struct snd_ctl_elem_value *ucontrol)470{471int ret = 0;472struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);473struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);474struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;475struct sst_gain_value *gv = mc->gain_val;476477mutex_lock(&drv->lock);478479switch (mc->type) {480case SST_GAIN_TLV:481gv->l_gain = ucontrol->value.integer.value[0];482gv->r_gain = ucontrol->value.integer.value[1];483dev_dbg(cmpnt->dev, "%s: Volume %d, %d\n",484mc->pname, gv->l_gain, gv->r_gain);485break;486487case SST_GAIN_MUTE:488gv->mute = !ucontrol->value.integer.value[0];489dev_dbg(cmpnt->dev, "%s: Mute %d\n", mc->pname, gv->mute);490break;491492case SST_GAIN_RAMP_DURATION:493gv->ramp_duration = ucontrol->value.integer.value[0];494dev_dbg(cmpnt->dev, "%s: Ramp Delay%d\n",495mc->pname, gv->ramp_duration);496break;497498default:499mutex_unlock(&drv->lock);500dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n",501mc->type);502return -EINVAL;503}504505if (mc->w && mc->w->power)506ret = sst_send_gain_cmd(drv, gv, mc->task_id,507mc->pipe_id | mc->instance_id, mc->module_id, 0);508mutex_unlock(&drv->lock);509510return ret;511}512513static int sst_set_pipe_gain(struct sst_ids *ids,514struct sst_data *drv, int mute);515516static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w,517struct snd_kcontrol *kcontrol)518{519struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);520struct sst_data *drv = snd_soc_component_get_drvdata(c);521struct sst_ids *ids = w->priv;522523mutex_lock(&drv->lock);524sst_find_and_send_pipe_algo(drv, w->name, ids);525sst_set_pipe_gain(ids, drv, 0);526mutex_unlock(&drv->lock);527528return 0;529}530531static int sst_generic_modules_event(struct snd_soc_dapm_widget *w,532struct snd_kcontrol *k, int event)533{534if (SND_SOC_DAPM_EVENT_ON(event))535return sst_send_pipe_module_params(w, k);536return 0;537}538539static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0);540541/* Look up table to convert MIXER SW bit regs to SWM inputs */542static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = {543[SST_IP_MODEM] = SST_SWM_IN_MODEM,544[SST_IP_CODEC0] = SST_SWM_IN_CODEC0,545[SST_IP_CODEC1] = SST_SWM_IN_CODEC1,546[SST_IP_LOOP0] = SST_SWM_IN_SPROT_LOOP,547[SST_IP_LOOP1] = SST_SWM_IN_MEDIA_LOOP1,548[SST_IP_LOOP2] = SST_SWM_IN_MEDIA_LOOP2,549[SST_IP_PCM0] = SST_SWM_IN_PCM0,550[SST_IP_PCM1] = SST_SWM_IN_PCM1,551[SST_IP_MEDIA0] = SST_SWM_IN_MEDIA0,552[SST_IP_MEDIA1] = SST_SWM_IN_MEDIA1,553[SST_IP_MEDIA2] = SST_SWM_IN_MEDIA2,554[SST_IP_MEDIA3] = SST_SWM_IN_MEDIA3,555};556557/**558* fill_swm_input - fill in the SWM input ids given the register559* @cmpnt: ASoC component560* @swm_input: array of swm_input_ids561* @reg: the register value is a bit-field inicated which mixer inputs are ON.562*563* Use the lookup table to get the input-id and fill it in the564* structure.565*/566static int fill_swm_input(struct snd_soc_component *cmpnt,567struct swm_input_ids *swm_input, unsigned int reg)568{569uint i, is_set, nb_inputs = 0;570u16 input_loc_id;571572dev_dbg(cmpnt->dev, "reg: %#x\n", reg);573for (i = 0; i < SST_SWM_INPUT_COUNT; i++) {574is_set = reg & BIT(i);575if (!is_set)576continue;577578input_loc_id = swm_mixer_input_ids[i];579SST_FILL_DESTINATION(2, swm_input->input_id,580input_loc_id, SST_DEFAULT_MODULE_ID);581nb_inputs++;582swm_input++;583dev_dbg(cmpnt->dev, "input id: %#x, nb_inputs: %d\n",584input_loc_id, nb_inputs);585586if (nb_inputs == SST_CMD_SWM_MAX_INPUTS) {587dev_warn(cmpnt->dev, "SET_SWM cmd max inputs reached");588break;589}590}591return nb_inputs;592}593594595/*596* called with lock held597*/598static int sst_set_pipe_gain(struct sst_ids *ids,599struct sst_data *drv, int mute)600{601int ret = 0;602struct sst_gain_mixer_control *mc;603struct sst_gain_value *gv;604struct sst_module *gain;605606list_for_each_entry(gain, &ids->gain_list, node) {607struct snd_kcontrol *kctl = gain->kctl;608609dev_dbg(&drv->pdev->dev, "control name=%s\n", kctl->id.name);610mc = (void *)kctl->private_value;611gv = mc->gain_val;612613ret = sst_send_gain_cmd(drv, gv, mc->task_id,614mc->pipe_id | mc->instance_id, mc->module_id, mute);615if (ret)616return ret;617}618return ret;619}620621static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,622struct snd_kcontrol *k, int event)623{624struct sst_cmd_set_swm cmd;625struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);626struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);627struct sst_ids *ids = w->priv;628bool set_mixer = false;629struct soc_mixer_control *mc;630int val = 0;631int i = 0;632633dev_dbg(cmpnt->dev, "widget = %s\n", w->name);634/*635* Identify which mixer input is on and send the bitmap of the636* inputs as an IPC to the DSP.637*/638for (i = 0; i < w->num_kcontrols; i++) {639if (dapm_kcontrol_get_value(w->kcontrols[i])) {640mc = (struct soc_mixer_control *)(w->kcontrols[i])->private_value;641val |= 1 << mc->shift;642}643}644dev_dbg(cmpnt->dev, "val = %#x\n", val);645646switch (event) {647case SND_SOC_DAPM_PRE_PMU:648case SND_SOC_DAPM_POST_PMD:649set_mixer = true;650break;651case SND_SOC_DAPM_POST_REG:652if (w->power)653set_mixer = true;654break;655default:656set_mixer = false;657}658659if (!set_mixer)660return 0;661662if (SND_SOC_DAPM_EVENT_ON(event) ||663event == SND_SOC_DAPM_POST_REG)664cmd.switch_state = SST_SWM_ON;665else666cmd.switch_state = SST_SWM_OFF;667668SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);669/* MMX_SET_SWM == SBA_SET_SWM */670cmd.header.command_id = SBA_SET_SWM;671672SST_FILL_DESTINATION(2, cmd.output_id,673ids->location_id, SST_DEFAULT_MODULE_ID);674cmd.nb_inputs = fill_swm_input(cmpnt, &cmd.input[0], val);675cmd.header.length = offsetof(struct sst_cmd_set_swm, input)676- sizeof(struct sst_dsp_header)677+ (cmd.nb_inputs * sizeof(cmd.input[0]));678679return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,680ids->task_id, 0, &cmd,681sizeof(cmd.header) + cmd.header.length);682}683684/* SBA mixers - 16 inputs */685#define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name) \686static const struct snd_kcontrol_new kctl_name[] = { \687SOC_DAPM_SINGLE("modem_in Switch", SND_SOC_NOPM, SST_IP_MODEM, 1, 0), \688SOC_DAPM_SINGLE("codec_in0 Switch", SND_SOC_NOPM, SST_IP_CODEC0, 1, 0), \689SOC_DAPM_SINGLE("codec_in1 Switch", SND_SOC_NOPM, SST_IP_CODEC1, 1, 0), \690SOC_DAPM_SINGLE("sprot_loop_in Switch", SND_SOC_NOPM, SST_IP_LOOP0, 1, 0), \691SOC_DAPM_SINGLE("media_loop1_in Switch", SND_SOC_NOPM, SST_IP_LOOP1, 1, 0), \692SOC_DAPM_SINGLE("media_loop2_in Switch", SND_SOC_NOPM, SST_IP_LOOP2, 1, 0), \693SOC_DAPM_SINGLE("pcm0_in Switch", SND_SOC_NOPM, SST_IP_PCM0, 1, 0), \694SOC_DAPM_SINGLE("pcm1_in Switch", SND_SOC_NOPM, SST_IP_PCM1, 1, 0), \695}696697#define SST_SBA_MIXER_GRAPH_MAP(mix_name) \698{ mix_name, "modem_in Switch", "modem_in" }, \699{ mix_name, "codec_in0 Switch", "codec_in0" }, \700{ mix_name, "codec_in1 Switch", "codec_in1" }, \701{ mix_name, "sprot_loop_in Switch", "sprot_loop_in" }, \702{ mix_name, "media_loop1_in Switch", "media_loop1_in" }, \703{ mix_name, "media_loop2_in Switch", "media_loop2_in" }, \704{ mix_name, "pcm0_in Switch", "pcm0_in" }, \705{ mix_name, "pcm1_in Switch", "pcm1_in" }706707#define SST_MMX_DECLARE_MIX_CONTROLS(kctl_name) \708static const struct snd_kcontrol_new kctl_name[] = { \709SOC_DAPM_SINGLE("media0_in Switch", SND_SOC_NOPM, SST_IP_MEDIA0, 1, 0), \710SOC_DAPM_SINGLE("media1_in Switch", SND_SOC_NOPM, SST_IP_MEDIA1, 1, 0), \711SOC_DAPM_SINGLE("media2_in Switch", SND_SOC_NOPM, SST_IP_MEDIA2, 1, 0), \712SOC_DAPM_SINGLE("media3_in Switch", SND_SOC_NOPM, SST_IP_MEDIA3, 1, 0), \713}714715SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media0_controls);716SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media1_controls);717718/* 18 SBA mixers */719SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm0_controls);720SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm1_controls);721SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm2_controls);722SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_sprot_l0_controls);723SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l1_controls);724SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls);725SST_SBA_DECLARE_MIX_CONTROLS(__maybe_unused sst_mix_voip_controls);726SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls);727SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls);728SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_modem_controls);729730/*731* sst_handle_vb_timer - Start/Stop the DSP scheduler732*733* The DSP expects first cmd to be SBA_VB_START, so at first startup send734* that.735* DSP expects last cmd to be SBA_VB_IDLE, so at last shutdown send that.736*737* Do refcount internally so that we send command only at first start738* and last end. Since SST driver does its own ref count, invoke sst's739* power ops always!740*/741int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable)742{743int ret = 0;744struct sst_cmd_generic cmd;745struct sst_data *drv = snd_soc_dai_get_drvdata(dai);746static int timer_usage;747748if (enable)749cmd.header.command_id = SBA_VB_START;750else751cmd.header.command_id = SBA_IDLE;752dev_dbg(dai->dev, "enable=%u, usage=%d\n", enable, timer_usage);753754SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);755cmd.header.length = 0;756757if (enable) {758ret = sst->ops->power(sst->dev, true);759if (ret < 0)760return ret;761}762763mutex_lock(&drv->lock);764if (enable)765timer_usage++;766else767timer_usage--;768769/*770* Send the command only if this call is the first enable or last771* disable772*/773if ((enable && (timer_usage == 1)) ||774(!enable && (timer_usage == 0))) {775ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD,776SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,777sizeof(cmd.header) + cmd.header.length);778if (ret && enable) {779timer_usage--;780enable = false;781}782}783mutex_unlock(&drv->lock);784785if (!enable)786sst->ops->power(sst->dev, false);787return ret;788}789790int sst_fill_ssp_slot(struct snd_soc_dai *dai, unsigned int tx_mask,791unsigned int rx_mask, int slots, int slot_width)792{793struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);794795ctx->ssp_cmd.nb_slots = slots;796ctx->ssp_cmd.active_tx_slot_map = tx_mask;797ctx->ssp_cmd.active_rx_slot_map = rx_mask;798ctx->ssp_cmd.nb_bits_per_slots = slot_width;799800return 0;801}802803static int sst_get_frame_sync_polarity(struct snd_soc_dai *dai,804unsigned int fmt)805{806int format;807808format = fmt & SND_SOC_DAIFMT_INV_MASK;809dev_dbg(dai->dev, "Enter:%s, format=%x\n", __func__, format);810811switch (format) {812case SND_SOC_DAIFMT_NB_NF:813case SND_SOC_DAIFMT_IB_NF:814return SSP_FS_ACTIVE_HIGH;815case SND_SOC_DAIFMT_NB_IF:816case SND_SOC_DAIFMT_IB_IF:817return SSP_FS_ACTIVE_LOW;818default:819dev_err(dai->dev, "Invalid frame sync polarity %d\n", format);820}821822return -EINVAL;823}824825static int sst_get_ssp_mode(struct snd_soc_dai *dai, unsigned int fmt)826{827int format;828829format = (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK);830dev_dbg(dai->dev, "Enter:%s, format=%x\n", __func__, format);831832switch (format) {833case SND_SOC_DAIFMT_BP_FP:834return SSP_MODE_PROVIDER;835case SND_SOC_DAIFMT_BC_FC:836return SSP_MODE_CONSUMER;837default:838dev_err(dai->dev, "Invalid ssp protocol: %d\n", format);839}840841return -EINVAL;842}843844845int sst_fill_ssp_config(struct snd_soc_dai *dai, unsigned int fmt)846{847unsigned int mode;848int fs_polarity;849struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);850851mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;852853switch (mode) {854case SND_SOC_DAIFMT_DSP_B:855ctx->ssp_cmd.ssp_protocol = SSP_MODE_PCM;856ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NETWORK << 1);857ctx->ssp_cmd.start_delay = 0;858ctx->ssp_cmd.data_polarity = 1;859ctx->ssp_cmd.frame_sync_width = 1;860break;861862case SND_SOC_DAIFMT_DSP_A:863ctx->ssp_cmd.ssp_protocol = SSP_MODE_PCM;864ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NETWORK << 1);865ctx->ssp_cmd.start_delay = 1;866ctx->ssp_cmd.data_polarity = 1;867ctx->ssp_cmd.frame_sync_width = 1;868break;869870case SND_SOC_DAIFMT_I2S:871ctx->ssp_cmd.ssp_protocol = SSP_MODE_I2S;872ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NORMAL << 1);873ctx->ssp_cmd.start_delay = 1;874ctx->ssp_cmd.data_polarity = 0;875ctx->ssp_cmd.frame_sync_width = ctx->ssp_cmd.nb_bits_per_slots;876break;877878case SND_SOC_DAIFMT_LEFT_J:879ctx->ssp_cmd.ssp_protocol = SSP_MODE_I2S;880ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NORMAL << 1);881ctx->ssp_cmd.start_delay = 0;882ctx->ssp_cmd.data_polarity = 0;883ctx->ssp_cmd.frame_sync_width = ctx->ssp_cmd.nb_bits_per_slots;884break;885886default:887dev_dbg(dai->dev, "using default ssp configs\n");888}889890fs_polarity = sst_get_frame_sync_polarity(dai, fmt);891if (fs_polarity < 0)892return fs_polarity;893894ctx->ssp_cmd.frame_sync_polarity = fs_polarity;895896return 0;897}898899/*900* sst_ssp_config - contains SSP configuration for media UC901* this can be overwritten by set_dai_xxx APIs902*/903static const struct sst_ssp_config sst_ssp_configs = {904.ssp_id = SSP_CODEC,905.bits_per_slot = 24,906.slots = 4,907.ssp_mode = SSP_MODE_PROVIDER,908.pcm_mode = SSP_PCM_MODE_NETWORK,909.duplex = SSP_DUPLEX,910.ssp_protocol = SSP_MODE_PCM,911.fs_width = 1,912.fs_frequency = SSP_FS_48_KHZ,913.active_slot_map = 0xF,914.start_delay = 0,915.frame_sync_polarity = SSP_FS_ACTIVE_HIGH,916.data_polarity = 1,917};918919void sst_fill_ssp_defaults(struct snd_soc_dai *dai)920{921const struct sst_ssp_config *config;922struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);923924config = &sst_ssp_configs;925926ctx->ssp_cmd.selection = config->ssp_id;927ctx->ssp_cmd.nb_bits_per_slots = config->bits_per_slot;928ctx->ssp_cmd.nb_slots = config->slots;929ctx->ssp_cmd.mode = config->ssp_mode | (config->pcm_mode << 1);930ctx->ssp_cmd.duplex = config->duplex;931ctx->ssp_cmd.active_tx_slot_map = config->active_slot_map;932ctx->ssp_cmd.active_rx_slot_map = config->active_slot_map;933ctx->ssp_cmd.frame_sync_frequency = config->fs_frequency;934ctx->ssp_cmd.frame_sync_polarity = config->frame_sync_polarity;935ctx->ssp_cmd.data_polarity = config->data_polarity;936ctx->ssp_cmd.frame_sync_width = config->fs_width;937ctx->ssp_cmd.ssp_protocol = config->ssp_protocol;938ctx->ssp_cmd.start_delay = config->start_delay;939ctx->ssp_cmd.reserved1 = ctx->ssp_cmd.reserved2 = 0xFF;940}941942int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable)943{944struct sst_data *drv = snd_soc_dai_get_drvdata(dai);945int ssp_id;946947dev_dbg(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id);948949if (strcmp(id, "ssp0-port") == 0)950ssp_id = SSP_MODEM;951else if (strcmp(id, "ssp2-port") == 0)952ssp_id = SSP_CODEC;953else {954dev_dbg(dai->dev, "port %s is not supported\n", id);955return -1;956}957958SST_FILL_DEFAULT_DESTINATION(drv->ssp_cmd.header.dst);959drv->ssp_cmd.header.command_id = SBA_HW_SET_SSP;960drv->ssp_cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp)961- sizeof(struct sst_dsp_header);962963drv->ssp_cmd.selection = ssp_id;964dev_dbg(dai->dev, "ssp_id: %u\n", ssp_id);965966if (enable)967drv->ssp_cmd.switch_state = SST_SWITCH_ON;968else969drv->ssp_cmd.switch_state = SST_SWITCH_OFF;970971return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,972SST_TASK_SBA, 0, &drv->ssp_cmd,973sizeof(drv->ssp_cmd.header) + drv->ssp_cmd.header.length);974}975976static int sst_set_be_modules(struct snd_soc_dapm_widget *w,977struct snd_kcontrol *k, int event)978{979int ret = 0;980struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);981struct sst_data *drv = snd_soc_component_get_drvdata(c);982983dev_dbg(c->dev, "Enter: widget=%s\n", w->name);984985if (SND_SOC_DAPM_EVENT_ON(event)) {986mutex_lock(&drv->lock);987ret = sst_send_slot_map(drv);988mutex_unlock(&drv->lock);989if (ret)990return ret;991ret = sst_send_pipe_module_params(w, k);992}993return ret;994}995996static int sst_set_media_path(struct snd_soc_dapm_widget *w,997struct snd_kcontrol *k, int event)998{999int ret = 0;1000struct sst_cmd_set_media_path cmd;1001struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);1002struct sst_data *drv = snd_soc_component_get_drvdata(c);1003struct sst_ids *ids = w->priv;10041005dev_dbg(c->dev, "widget=%s\n", w->name);1006dev_dbg(c->dev, "task=%u, location=%#x\n",1007ids->task_id, ids->location_id);10081009if (SND_SOC_DAPM_EVENT_ON(event))1010cmd.switch_state = SST_PATH_ON;1011else1012cmd.switch_state = SST_PATH_OFF;10131014SST_FILL_DESTINATION(2, cmd.header.dst,1015ids->location_id, SST_DEFAULT_MODULE_ID);10161017/* MMX_SET_MEDIA_PATH == SBA_SET_MEDIA_PATH */1018cmd.header.command_id = MMX_SET_MEDIA_PATH;1019cmd.header.length = sizeof(struct sst_cmd_set_media_path)1020- sizeof(struct sst_dsp_header);10211022ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,1023ids->task_id, 0, &cmd,1024sizeof(cmd.header) + cmd.header.length);1025if (ret)1026return ret;10271028if (SND_SOC_DAPM_EVENT_ON(event))1029ret = sst_send_pipe_module_params(w, k);1030return ret;1031}10321033static int sst_set_media_loop(struct snd_soc_dapm_widget *w,1034struct snd_kcontrol *k, int event)1035{1036int ret = 0;1037struct sst_cmd_sba_set_media_loop_map cmd;1038struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);1039struct sst_data *drv = snd_soc_component_get_drvdata(c);1040struct sst_ids *ids = w->priv;10411042dev_dbg(c->dev, "Enter:widget=%s\n", w->name);1043if (SND_SOC_DAPM_EVENT_ON(event))1044cmd.switch_state = SST_SWITCH_ON;1045else1046cmd.switch_state = SST_SWITCH_OFF;10471048SST_FILL_DESTINATION(2, cmd.header.dst,1049ids->location_id, SST_DEFAULT_MODULE_ID);10501051cmd.header.command_id = SBA_SET_MEDIA_LOOP_MAP;1052cmd.header.length = sizeof(struct sst_cmd_sba_set_media_loop_map)1053- sizeof(struct sst_dsp_header);1054cmd.param.part.cfg.rate = 2; /* 48khz */10551056cmd.param.part.cfg.format = ids->format; /* stereo/Mono */1057cmd.param.part.cfg.s_length = 1; /* 24bit left justified */1058cmd.map = 0; /* Algo sequence: Gain - DRP - FIR - IIR */10591060ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,1061SST_TASK_SBA, 0, &cmd,1062sizeof(cmd.header) + cmd.header.length);1063if (ret)1064return ret;10651066if (SND_SOC_DAPM_EVENT_ON(event))1067ret = sst_send_pipe_module_params(w, k);1068return ret;1069}10701071static const struct snd_soc_dapm_widget sst_dapm_widgets[] = {1072SST_AIF_IN("modem_in", sst_set_be_modules),1073SST_AIF_IN("codec_in0", sst_set_be_modules),1074SST_AIF_IN("codec_in1", sst_set_be_modules),1075SST_AIF_OUT("modem_out", sst_set_be_modules),1076SST_AIF_OUT("codec_out0", sst_set_be_modules),1077SST_AIF_OUT("codec_out1", sst_set_be_modules),10781079/* Media Paths */1080/* MediaX IN paths are set via ALLOC, so no SET_MEDIA_PATH command */1081SST_PATH_INPUT("media0_in", SST_TASK_MMX, SST_SWM_IN_MEDIA0, sst_generic_modules_event),1082SST_PATH_INPUT("media1_in", SST_TASK_MMX, SST_SWM_IN_MEDIA1, NULL),1083SST_PATH_INPUT("media2_in", SST_TASK_MMX, SST_SWM_IN_MEDIA2, sst_set_media_path),1084SST_PATH_INPUT("media3_in", SST_TASK_MMX, SST_SWM_IN_MEDIA3, NULL),1085SST_PATH_OUTPUT("media0_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA0, sst_set_media_path),1086SST_PATH_OUTPUT("media1_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA1, sst_set_media_path),10871088/* SBA PCM Paths */1089SST_PATH_INPUT("pcm0_in", SST_TASK_SBA, SST_SWM_IN_PCM0, sst_set_media_path),1090SST_PATH_INPUT("pcm1_in", SST_TASK_SBA, SST_SWM_IN_PCM1, sst_set_media_path),1091SST_PATH_OUTPUT("pcm0_out", SST_TASK_SBA, SST_SWM_OUT_PCM0, sst_set_media_path),1092SST_PATH_OUTPUT("pcm1_out", SST_TASK_SBA, SST_SWM_OUT_PCM1, sst_set_media_path),1093SST_PATH_OUTPUT("pcm2_out", SST_TASK_SBA, SST_SWM_OUT_PCM2, sst_set_media_path),10941095/* SBA Loops */1096SST_PATH_INPUT("sprot_loop_in", SST_TASK_SBA, SST_SWM_IN_SPROT_LOOP, NULL),1097SST_PATH_INPUT("media_loop1_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP1, NULL),1098SST_PATH_INPUT("media_loop2_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP2, NULL),1099SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_STEREO, sst_set_media_loop),1100SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_STEREO, sst_set_media_loop),1101SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop),11021103/* Media Mixers */1104SST_SWM_MIXER("media0_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA0,1105sst_mix_media0_controls, sst_swm_mixer_event),1106SST_SWM_MIXER("media1_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA1,1107sst_mix_media1_controls, sst_swm_mixer_event),11081109/* SBA PCM mixers */1110SST_SWM_MIXER("pcm0_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM0,1111sst_mix_pcm0_controls, sst_swm_mixer_event),1112SST_SWM_MIXER("pcm1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM1,1113sst_mix_pcm1_controls, sst_swm_mixer_event),1114SST_SWM_MIXER("pcm2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM2,1115sst_mix_pcm2_controls, sst_swm_mixer_event),11161117/* SBA Loop mixers */1118SST_SWM_MIXER("sprot_loop_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP,1119sst_mix_sprot_l0_controls, sst_swm_mixer_event),1120SST_SWM_MIXER("media_loop1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1,1121sst_mix_media_l1_controls, sst_swm_mixer_event),1122SST_SWM_MIXER("media_loop2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2,1123sst_mix_media_l2_controls, sst_swm_mixer_event),11241125/* SBA Backend mixers */1126SST_SWM_MIXER("codec_out0 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC0,1127sst_mix_codec0_controls, sst_swm_mixer_event),1128SST_SWM_MIXER("codec_out1 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC1,1129sst_mix_codec1_controls, sst_swm_mixer_event),1130SST_SWM_MIXER("modem_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MODEM,1131sst_mix_modem_controls, sst_swm_mixer_event),11321133};11341135static const struct snd_soc_dapm_route intercon[] = {1136{"media0_in", NULL, "Compress Playback"},1137{"media1_in", NULL, "Headset Playback"},1138{"media2_in", NULL, "pcm0_out"},1139{"media3_in", NULL, "Deepbuffer Playback"},11401141{"media0_out mix 0", "media0_in Switch", "media0_in"},1142{"media0_out mix 0", "media1_in Switch", "media1_in"},1143{"media0_out mix 0", "media2_in Switch", "media2_in"},1144{"media0_out mix 0", "media3_in Switch", "media3_in"},1145{"media1_out mix 0", "media0_in Switch", "media0_in"},1146{"media1_out mix 0", "media1_in Switch", "media1_in"},1147{"media1_out mix 0", "media2_in Switch", "media2_in"},1148{"media1_out mix 0", "media3_in Switch", "media3_in"},11491150{"media0_out", NULL, "media0_out mix 0"},1151{"media1_out", NULL, "media1_out mix 0"},1152{"pcm0_in", NULL, "media0_out"},1153{"pcm1_in", NULL, "media1_out"},11541155{"Headset Capture", NULL, "pcm1_out"},1156{"Headset Capture", NULL, "pcm2_out"},1157{"pcm0_out", NULL, "pcm0_out mix 0"},1158SST_SBA_MIXER_GRAPH_MAP("pcm0_out mix 0"),1159{"pcm1_out", NULL, "pcm1_out mix 0"},1160SST_SBA_MIXER_GRAPH_MAP("pcm1_out mix 0"),1161{"pcm2_out", NULL, "pcm2_out mix 0"},1162SST_SBA_MIXER_GRAPH_MAP("pcm2_out mix 0"),11631164{"media_loop1_in", NULL, "media_loop1_out"},1165{"media_loop1_out", NULL, "media_loop1_out mix 0"},1166SST_SBA_MIXER_GRAPH_MAP("media_loop1_out mix 0"),1167{"media_loop2_in", NULL, "media_loop2_out"},1168{"media_loop2_out", NULL, "media_loop2_out mix 0"},1169SST_SBA_MIXER_GRAPH_MAP("media_loop2_out mix 0"),1170{"sprot_loop_in", NULL, "sprot_loop_out"},1171{"sprot_loop_out", NULL, "sprot_loop_out mix 0"},1172SST_SBA_MIXER_GRAPH_MAP("sprot_loop_out mix 0"),11731174{"codec_out0", NULL, "codec_out0 mix 0"},1175SST_SBA_MIXER_GRAPH_MAP("codec_out0 mix 0"),1176{"codec_out1", NULL, "codec_out1 mix 0"},1177SST_SBA_MIXER_GRAPH_MAP("codec_out1 mix 0"),1178{"modem_out", NULL, "modem_out mix 0"},1179SST_SBA_MIXER_GRAPH_MAP("modem_out mix 0"),118011811182};1183static const char * const slot_names[] = {1184"none",1185"slot 0", "slot 1", "slot 2", "slot 3",1186"slot 4", "slot 5", "slot 6", "slot 7", /* not supported by FW */1187};11881189static const char * const channel_names[] = {1190"none",1191"codec_out0_0", "codec_out0_1", "codec_out1_0", "codec_out1_1",1192"codec_out2_0", "codec_out2_1", "codec_out3_0", "codec_out3_1", /* not supported by FW */1193};11941195#define SST_INTERLEAVER(xpname, slot_name, slotno) \1196SST_SSP_SLOT_CTL(xpname, "tx interleaver", slot_name, slotno, true, \1197channel_names, sst_slot_get, sst_slot_put)11981199#define SST_DEINTERLEAVER(xpname, channel_name, channel_no) \1200SST_SSP_SLOT_CTL(xpname, "rx deinterleaver", channel_name, channel_no, false, \1201slot_names, sst_slot_get, sst_slot_put)12021203static const struct snd_kcontrol_new sst_slot_controls[] = {1204SST_INTERLEAVER("codec_out", "slot 0", 0),1205SST_INTERLEAVER("codec_out", "slot 1", 1),1206SST_INTERLEAVER("codec_out", "slot 2", 2),1207SST_INTERLEAVER("codec_out", "slot 3", 3),1208SST_DEINTERLEAVER("codec_in", "codec_in0_0", 0),1209SST_DEINTERLEAVER("codec_in", "codec_in0_1", 1),1210SST_DEINTERLEAVER("codec_in", "codec_in1_0", 2),1211SST_DEINTERLEAVER("codec_in", "codec_in1_1", 3),1212};12131214/* Gain helper with min/max set */1215#define SST_GAIN(name, path_id, task_id, instance, gain_var) \1216SST_GAIN_KCONTROLS(name, "Gain", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \1217SST_GAIN_TC_MIN, SST_GAIN_TC_MAX, \1218sst_gain_get, sst_gain_put, \1219SST_MODULE_ID_GAIN_CELL, path_id, instance, task_id, \1220sst_gain_tlv_common, gain_var)12211222#define SST_VOLUME(name, path_id, task_id, instance, gain_var) \1223SST_GAIN_KCONTROLS(name, "Volume", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \1224SST_GAIN_TC_MIN, SST_GAIN_TC_MAX, \1225sst_gain_get, sst_gain_put, \1226SST_MODULE_ID_VOLUME, path_id, instance, task_id, \1227sst_gain_tlv_common, gain_var)12281229static struct sst_gain_value sst_gains[];12301231static const struct snd_kcontrol_new sst_gain_controls[] = {1232SST_GAIN("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[0]),1233SST_GAIN("media1_in", SST_PATH_INDEX_MEDIA1_IN, SST_TASK_MMX, 0, &sst_gains[1]),1234SST_GAIN("media2_in", SST_PATH_INDEX_MEDIA2_IN, SST_TASK_MMX, 0, &sst_gains[2]),1235SST_GAIN("media3_in", SST_PATH_INDEX_MEDIA3_IN, SST_TASK_MMX, 0, &sst_gains[3]),12361237SST_GAIN("pcm0_in", SST_PATH_INDEX_PCM0_IN, SST_TASK_SBA, 0, &sst_gains[4]),1238SST_GAIN("pcm1_in", SST_PATH_INDEX_PCM1_IN, SST_TASK_SBA, 0, &sst_gains[5]),1239SST_GAIN("pcm1_out", SST_PATH_INDEX_PCM1_OUT, SST_TASK_SBA, 0, &sst_gains[6]),1240SST_GAIN("pcm2_out", SST_PATH_INDEX_PCM2_OUT, SST_TASK_SBA, 0, &sst_gains[7]),12411242SST_GAIN("codec_in0", SST_PATH_INDEX_CODEC_IN0, SST_TASK_SBA, 0, &sst_gains[8]),1243SST_GAIN("codec_in1", SST_PATH_INDEX_CODEC_IN1, SST_TASK_SBA, 0, &sst_gains[9]),1244SST_GAIN("codec_out0", SST_PATH_INDEX_CODEC_OUT0, SST_TASK_SBA, 0, &sst_gains[10]),1245SST_GAIN("codec_out1", SST_PATH_INDEX_CODEC_OUT1, SST_TASK_SBA, 0, &sst_gains[11]),1246SST_GAIN("media_loop1_out", SST_PATH_INDEX_MEDIA_LOOP1_OUT, SST_TASK_SBA, 0, &sst_gains[12]),1247SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[13]),1248SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[14]),1249SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[15]),1250SST_GAIN("modem_in", SST_PATH_INDEX_MODEM_IN, SST_TASK_SBA, 0, &sst_gains[16]),1251SST_GAIN("modem_out", SST_PATH_INDEX_MODEM_OUT, SST_TASK_SBA, 0, &sst_gains[17]),12521253};12541255#define SST_GAIN_NUM_CONTROLS 31256/* the SST_GAIN macro above will create three alsa controls for each1257* instance invoked, gain, mute and ramp duration, which use the same gain1258* cell sst_gain to keep track of data1259* To calculate number of gain cell instances we need to device by 3 in1260* below caulcation for gain cell memory.1261* This gets rid of static number and issues while adding new controls1262*/1263static struct sst_gain_value sst_gains[ARRAY_SIZE(sst_gain_controls)/SST_GAIN_NUM_CONTROLS];12641265static const struct snd_kcontrol_new sst_algo_controls[] = {1266SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 272, SST_MODULE_ID_FIR_24,1267SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR),1268SST_ALGO_KCONTROL_BYTES("media_loop1_out", "iir", 300, SST_MODULE_ID_IIR_24,1269SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR),1270SST_ALGO_KCONTROL_BYTES("media_loop1_out", "mdrp", 286, SST_MODULE_ID_MDRP,1271SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP),1272SST_ALGO_KCONTROL_BYTES("media_loop2_out", "fir", 272, SST_MODULE_ID_FIR_24,1273SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR),1274SST_ALGO_KCONTROL_BYTES("media_loop2_out", "iir", 300, SST_MODULE_ID_IIR_24,1275SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR),1276SST_ALGO_KCONTROL_BYTES("media_loop2_out", "mdrp", 286, SST_MODULE_ID_MDRP,1277SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP),1278SST_ALGO_KCONTROL_BYTES("sprot_loop_out", "lpro", 192, SST_MODULE_ID_SPROT,1279SST_PATH_INDEX_SPROT_LOOP_OUT, 0, SST_TASK_SBA, SBA_VB_LPRO),1280SST_ALGO_KCONTROL_BYTES("codec_in0", "dcr", 52, SST_MODULE_ID_FILT_DCR,1281SST_PATH_INDEX_CODEC_IN0, 0, SST_TASK_SBA, SBA_VB_SET_IIR),1282SST_ALGO_KCONTROL_BYTES("codec_in1", "dcr", 52, SST_MODULE_ID_FILT_DCR,1283SST_PATH_INDEX_CODEC_IN1, 0, SST_TASK_SBA, SBA_VB_SET_IIR),12841285};12861287static int sst_algo_control_init(struct device *dev)1288{1289int i = 0;1290struct sst_algo_control *bc;1291/*allocate space to cache the algo parameters in the driver*/1292for (i = 0; i < ARRAY_SIZE(sst_algo_controls); i++) {1293bc = (struct sst_algo_control *)sst_algo_controls[i].private_value;1294bc->params = devm_kzalloc(dev, bc->max, GFP_KERNEL);1295if (bc->params == NULL)1296return -ENOMEM;1297}1298return 0;1299}13001301static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w)1302{1303switch (w->id) {1304case snd_soc_dapm_pga:1305case snd_soc_dapm_aif_in:1306case snd_soc_dapm_aif_out:1307case snd_soc_dapm_input:1308case snd_soc_dapm_output:1309case snd_soc_dapm_mixer:1310return true;1311default:1312return false;1313}1314}13151316/**1317* sst_send_pipe_gains - send gains for the front-end DAIs1318* @dai: front-end dai1319* @stream: direction1320* @mute: boolean indicating mute status1321*1322* The gains in the pipes connected to the front-ends are muted/unmuted1323* automatically via the digital_mute() DAPM callback. This function sends the1324* gains for the front-end pipes.1325*/1326int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)1327{1328struct sst_data *drv = snd_soc_dai_get_drvdata(dai);1329struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, stream);1330struct snd_soc_dapm_path *p;13311332dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream);1333dev_dbg(dai->dev, "Stream name=%s\n", w->name);13341335if (stream == SNDRV_PCM_STREAM_PLAYBACK) {1336snd_soc_dapm_widget_for_each_sink_path(w, p) {1337if (p->connected && !p->connected(w, p->sink))1338continue;13391340if (p->connect && p->sink->power &&1341is_sst_dapm_widget(p->sink)) {1342struct sst_ids *ids = p->sink->priv;13431344dev_dbg(dai->dev, "send gains for widget=%s\n",1345p->sink->name);1346mutex_lock(&drv->lock);1347sst_set_pipe_gain(ids, drv, mute);1348mutex_unlock(&drv->lock);1349}1350}1351} else {1352snd_soc_dapm_widget_for_each_source_path(w, p) {1353if (p->connected && !p->connected(w, p->source))1354continue;13551356if (p->connect && p->source->power &&1357is_sst_dapm_widget(p->source)) {1358struct sst_ids *ids = p->source->priv;13591360dev_dbg(dai->dev, "send gain for widget=%s\n",1361p->source->name);1362mutex_lock(&drv->lock);1363sst_set_pipe_gain(ids, drv, mute);1364mutex_unlock(&drv->lock);1365}1366}1367}1368return 0;1369}13701371/**1372* sst_fill_module_list - populate the list of modules/gains for a pipe1373* @kctl: kcontrol pointer1374* @w: dapm widget1375* @type: widget type1376*1377* Fills the widget pointer in the kcontrol private data, and also fills the1378* kcontrol pointer in the widget private data.1379*1380* Widget pointer is used to send the algo/gain in the .put() handler if the1381* widget is powerd on.1382*1383* Kcontrol pointer is used to send the algo/gain in the widget power ON/OFF1384* event handler. Each widget (pipe) has multiple algos stored in the algo_list.1385*/1386static int sst_fill_module_list(struct snd_kcontrol *kctl,1387struct snd_soc_dapm_widget *w, int type)1388{1389struct sst_module *module;1390struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);1391struct sst_ids *ids = w->priv;1392int ret = 0;13931394module = devm_kzalloc(c->dev, sizeof(*module), GFP_KERNEL);1395if (!module)1396return -ENOMEM;13971398if (type == SST_MODULE_GAIN) {1399struct sst_gain_mixer_control *mc = (void *)kctl->private_value;14001401mc->w = w;1402module->kctl = kctl;1403list_add_tail(&module->node, &ids->gain_list);1404} else if (type == SST_MODULE_ALGO) {1405struct sst_algo_control *bc = (void *)kctl->private_value;14061407bc->w = w;1408module->kctl = kctl;1409list_add_tail(&module->node, &ids->algo_list);1410} else {1411dev_err(c->dev, "invoked for unknown type %d module %s",1412type, kctl->id.name);1413ret = -EINVAL;1414}14151416return ret;1417}14181419/**1420* sst_fill_widget_module_info - fill list of gains/algos for the pipe1421* @w: pipe modeled as a DAPM widget1422* @component: ASoC component1423*1424* Fill the list of gains/algos for the widget by looking at all the card1425* controls and comparing the name of the widget with the first part of control1426* name. First part of control name contains the pipe name (widget name).1427*/1428static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w,1429struct snd_soc_component *component)1430{1431struct snd_kcontrol *kctl;1432int index, ret = 0;1433struct snd_card *card = component->card->snd_card;1434char *idx;14351436down_read(&card->controls_rwsem);14371438list_for_each_entry(kctl, &card->controls, list) {1439idx = strchr(kctl->id.name, ' ');1440if (idx == NULL)1441continue;1442index = idx - (char*)kctl->id.name;1443if (strncmp(kctl->id.name, w->name, index))1444continue;14451446if (strstr(kctl->id.name, "Volume"))1447ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN);14481449else if (strstr(kctl->id.name, "params"))1450ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO);14511452else if (strstr(kctl->id.name, "Switch") &&1453strstr(kctl->id.name, "Gain")) {1454struct sst_gain_mixer_control *mc =1455(void *)kctl->private_value;14561457mc->w = w;14581459} else if (strstr(kctl->id.name, "interleaver")) {1460struct sst_enum *e = (void *)kctl->private_value;14611462e->w = w;14631464} else if (strstr(kctl->id.name, "deinterleaver")) {1465struct sst_enum *e = (void *)kctl->private_value;14661467e->w = w;1468}14691470if (ret < 0) {1471up_read(&card->controls_rwsem);1472return ret;1473}1474}14751476up_read(&card->controls_rwsem);1477return 0;1478}14791480/**1481* sst_fill_linked_widgets - fill the parent pointer for the linked widget1482* @component: ASoC component1483* @ids: sst_ids array1484*/1485static void sst_fill_linked_widgets(struct snd_soc_component *component,1486struct sst_ids *ids)1487{1488struct snd_soc_dapm_widget *w;1489unsigned int len = strlen(ids->parent_wname);14901491list_for_each_entry(w, &component->card->widgets, list) {1492if (!strncmp(ids->parent_wname, w->name, len)) {1493ids->parent_w = w;1494break;1495}1496}1497}14981499/**1500* sst_map_modules_to_pipe - fill algo/gains list for all pipes1501* @component: ASoC component1502*/1503static int sst_map_modules_to_pipe(struct snd_soc_component *component)1504{1505struct snd_soc_dapm_widget *w;1506int ret = 0;15071508list_for_each_entry(w, &component->card->widgets, list) {1509if (is_sst_dapm_widget(w) && (w->priv)) {1510struct sst_ids *ids = w->priv;15111512dev_dbg(component->dev, "widget type=%d name=%s\n",1513w->id, w->name);1514INIT_LIST_HEAD(&ids->algo_list);1515INIT_LIST_HEAD(&ids->gain_list);1516ret = sst_fill_widget_module_info(w, component);15171518if (ret < 0)1519return ret;15201521/* fill linked widgets */1522if (ids->parent_wname != NULL)1523sst_fill_linked_widgets(component, ids);1524}1525}1526return 0;1527}15281529int sst_dsp_init_v2_dpcm(struct snd_soc_component *component)1530{1531int i, ret = 0;1532struct snd_soc_dapm_context *dapm =1533snd_soc_component_get_dapm(component);1534struct sst_data *drv = snd_soc_component_get_drvdata(component);1535unsigned int gains = ARRAY_SIZE(sst_gain_controls)/3;15361537drv->byte_stream = devm_kzalloc(component->dev,1538SST_MAX_BIN_BYTES, GFP_KERNEL);1539if (!drv->byte_stream)1540return -ENOMEM;15411542snd_soc_dapm_new_controls(dapm, sst_dapm_widgets,1543ARRAY_SIZE(sst_dapm_widgets));1544snd_soc_dapm_add_routes(dapm, intercon,1545ARRAY_SIZE(intercon));1546snd_soc_dapm_new_widgets(dapm->card);15471548for (i = 0; i < gains; i++) {1549sst_gains[i].mute = SST_GAIN_MUTE_DEFAULT;1550sst_gains[i].l_gain = SST_GAIN_VOLUME_DEFAULT;1551sst_gains[i].r_gain = SST_GAIN_VOLUME_DEFAULT;1552sst_gains[i].ramp_duration = SST_GAIN_RAMP_DURATION_DEFAULT;1553}15541555ret = snd_soc_add_component_controls(component, sst_gain_controls,1556ARRAY_SIZE(sst_gain_controls));1557if (ret)1558return ret;15591560/* Initialize algo control params */1561ret = sst_algo_control_init(component->dev);1562if (ret)1563return ret;1564ret = snd_soc_add_component_controls(component, sst_algo_controls,1565ARRAY_SIZE(sst_algo_controls));1566if (ret)1567return ret;15681569ret = snd_soc_add_component_controls(component, sst_slot_controls,1570ARRAY_SIZE(sst_slot_controls));1571if (ret)1572return ret;15731574ret = sst_map_modules_to_pipe(component);15751576return ret;1577}157815791580