brintos

brintos / linux-shallow public Read only

0
0
Text · 48.9 KiB · 38116c7 Raw
1579 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2 /*3 *  sst-atom-controls.c - Intel MID Platform driver DPCM ALSA controls for Mrfld4 *5 *  Copyright (C) 2013-14 Intel Corp6 *  Author: Omair Mohammed Abdullah <omair.m.abdullah@intel.com>7 *	Vinod Koul <vinod.koul@intel.com>8 *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~9 *10 *  In the dpcm driver modelling when a particular FE/BE/Mixer/Pipe is active11 *  we forward the settings and parameters, rest we keep the values  in12 *  driver and forward when DAPM enables them13 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~14 */15#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt16 17#include <linux/slab.h>18#include <sound/soc.h>19#include <sound/tlv.h>20#include "sst-mfld-platform.h"21#include "sst-atom-controls.h"22 23static int sst_fill_byte_control(struct sst_data *drv,24					 u8 ipc_msg, u8 block,25					 u8 task_id, u8 pipe_id,26					 u16 len, void *cmd_data)27{28	struct snd_sst_bytes_v2 *byte_data = drv->byte_stream;29 30	byte_data->type = SST_CMD_BYTES_SET;31	byte_data->ipc_msg = ipc_msg;32	byte_data->block = block;33	byte_data->task_id = task_id;34	byte_data->pipe_id = pipe_id;35 36	if (len > SST_MAX_BIN_BYTES - sizeof(*byte_data)) {37		dev_err(&drv->pdev->dev, "command length too big (%u)", len);38		return -EINVAL;39	}40	byte_data->len = len;41	memcpy(byte_data->bytes, cmd_data, len);42	print_hex_dump_bytes("writing to lpe: ", DUMP_PREFIX_OFFSET,43			     byte_data, len + sizeof(*byte_data));44	return 0;45}46 47static int sst_fill_and_send_cmd_unlocked(struct sst_data *drv,48				 u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id,49				 void *cmd_data, u16 len)50{51	int ret = 0;52 53	WARN_ON(!mutex_is_locked(&drv->lock));54 55	ret = sst_fill_byte_control(drv, ipc_msg,56				block, task_id, pipe_id, len, cmd_data);57	if (ret < 0)58		return ret;59	return sst->ops->send_byte_stream(sst->dev, drv->byte_stream);60}61 62/**63 * sst_fill_and_send_cmd - generate the IPC message and send it to the FW64 * @drv: sst_data65 * @ipc_msg: type of IPC (CMD, SET_PARAMS, GET_PARAMS)66 * @block: block index67 * @task_id: task index68 * @pipe_id: pipe index69 * @cmd_data: the IPC payload70 * @len: length of data to be sent71 */72static int sst_fill_and_send_cmd(struct sst_data *drv,73				 u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id,74				 void *cmd_data, u16 len)75{76	int ret;77 78	mutex_lock(&drv->lock);79	ret = sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block,80					task_id, pipe_id, cmd_data, len);81	mutex_unlock(&drv->lock);82 83	return ret;84}85 86/*87 * tx map value is a bitfield where each bit represents a FW channel88 *89 *			3 2 1 0		# 0 = codec0, 1 = codec190 *			RLRLRLRL	# 3, 4 = reserved91 *92 * e.g. slot 0 rx map =	00001100b -> data from slot 0 goes into codec_in1 L,R93 */94static u8 sst_ssp_tx_map[SST_MAX_TDM_SLOTS] = {95	0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default rx map */96};97 98/*99 * rx map value is a bitfield where each bit represents a slot100 *101 *			  76543210	# 0 = slot 0, 1 = slot 1102 *103 * e.g. codec1_0 tx map = 00000101b -> data from codec_out1_0 goes into slot 0, 2104 */105static u8 sst_ssp_rx_map[SST_MAX_TDM_SLOTS] = {106	0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default tx map */107};108 109/*110 * NOTE: this is invoked with lock held111 */112static int sst_send_slot_map(struct sst_data *drv)113{114	struct sst_param_sba_ssp_slot_map cmd;115 116	SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);117	cmd.header.command_id = SBA_SET_SSP_SLOT_MAP;118	cmd.header.length = sizeof(struct sst_param_sba_ssp_slot_map)119				- sizeof(struct sst_dsp_header);120 121	cmd.param_id = SBA_SET_SSP_SLOT_MAP;122	cmd.param_len = sizeof(cmd.rx_slot_map) + sizeof(cmd.tx_slot_map)123					+ sizeof(cmd.ssp_index);124	cmd.ssp_index = SSP_CODEC;125 126	memcpy(cmd.rx_slot_map, &sst_ssp_tx_map[0], sizeof(cmd.rx_slot_map));127	memcpy(cmd.tx_slot_map, &sst_ssp_rx_map[0], sizeof(cmd.tx_slot_map));128 129	return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,130			SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,131			      sizeof(cmd.header) + cmd.header.length);132}133 134static int sst_slot_enum_info(struct snd_kcontrol *kcontrol,135		       struct snd_ctl_elem_info *uinfo)136{137	struct sst_enum *e = (struct sst_enum *)kcontrol->private_value;138 139	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;140	uinfo->count = 1;141	uinfo->value.enumerated.items = e->max;142 143	if (uinfo->value.enumerated.item > e->max - 1)144		uinfo->value.enumerated.item = e->max - 1;145	strcpy(uinfo->value.enumerated.name,146		e->texts[uinfo->value.enumerated.item]);147 148	return 0;149}150 151/**152 * sst_slot_get - get the status of the interleaver/deinterleaver control153 * @kcontrol: control pointer154 * @ucontrol: User data155 * Searches the map where the control status is stored, and gets the156 * channel/slot which is currently set for this enumerated control. Since it is157 * an enumerated control, there is only one possible value.158 */159static int sst_slot_get(struct snd_kcontrol *kcontrol,160			struct snd_ctl_elem_value *ucontrol)161{162	struct sst_enum *e = (void *)kcontrol->private_value;163	struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);164	struct sst_data *drv = snd_soc_component_get_drvdata(c);165	unsigned int ctl_no = e->reg;166	unsigned int is_tx = e->tx;167	unsigned int val, mux;168	u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;169 170	mutex_lock(&drv->lock);171	val = 1 << ctl_no;172	/* search which slot/channel has this bit set - there should be only one */173	for (mux = e->max; mux > 0;  mux--)174		if (map[mux - 1] & val)175			break;176 177	ucontrol->value.enumerated.item[0] = mux;178	mutex_unlock(&drv->lock);179 180	dev_dbg(c->dev, "%s - %s map = %#x\n",181			is_tx ? "tx channel" : "rx slot",182			 e->texts[mux], mux ? map[mux - 1] : -1);183	return 0;184}185 186/* sst_check_and_send_slot_map - helper for checking power state and sending187 * slot map cmd188 *189 * called with lock held190 */191static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol *kcontrol)192{193	struct sst_enum *e = (void *)kcontrol->private_value;194	int ret = 0;195 196	if (e->w && e->w->power)197		ret = sst_send_slot_map(drv);198	else if (!e->w)199		dev_err(&drv->pdev->dev, "Slot control: %s doesn't have DAPM widget!!!\n",200				kcontrol->id.name);201	return ret;202}203 204/**205 * sst_slot_put - set the status of interleaver/deinterleaver control206 * @kcontrol: control pointer207 * @ucontrol: User data208 * (de)interleaver controls are defined in opposite sense to be user-friendly209 *210 * Instead of the enum value being the value written to the register, it is the211 * register address; and the kcontrol number (register num) is the value written212 * to the register. This is so that there can be only one value for each213 * slot/channel since there is only one control for each slot/channel.214 *215 * This means that whenever an enum is set, we need to clear the bit216 * for that kcontrol_no for all the interleaver OR deinterleaver registers217 */218static int sst_slot_put(struct snd_kcontrol *kcontrol,219			struct snd_ctl_elem_value *ucontrol)220{221	struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);222	struct sst_data *drv = snd_soc_component_get_drvdata(c);223	struct sst_enum *e = (void *)kcontrol->private_value;224	int i, ret = 0;225	unsigned int ctl_no = e->reg;226	unsigned int is_tx = e->tx;227	unsigned int slot_channel_no;228	unsigned int val, mux;229	u8 *map;230 231	map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;232 233	val = 1 << ctl_no;234	mux = ucontrol->value.enumerated.item[0];235	if (mux > e->max - 1)236		return -EINVAL;237 238	mutex_lock(&drv->lock);239	/* first clear all registers of this bit */240	for (i = 0; i < e->max; i++)241		map[i] &= ~val;242 243	if (mux == 0) {244		/* kctl set to 'none' and we reset the bits so send IPC */245		ret = sst_check_and_send_slot_map(drv, kcontrol);246 247		mutex_unlock(&drv->lock);248		return ret;249	}250 251	/* offset by one to take "None" into account */252	slot_channel_no = mux - 1;253	map[slot_channel_no] |= val;254 255	dev_dbg(c->dev, "%s %s map = %#x\n",256			is_tx ? "tx channel" : "rx slot",257			e->texts[mux], map[slot_channel_no]);258 259	ret = sst_check_and_send_slot_map(drv, kcontrol);260 261	mutex_unlock(&drv->lock);262	return ret;263}264 265static int sst_send_algo_cmd(struct sst_data *drv,266			      struct sst_algo_control *bc)267{268	int len, ret = 0;269	struct sst_cmd_set_params *cmd;270 271	/*bc->max includes sizeof algos + length field*/272	len = sizeof(cmd->dst) + sizeof(cmd->command_id) + bc->max;273 274	cmd = kzalloc(len, GFP_KERNEL);275	if (cmd == NULL)276		return -ENOMEM;277 278	SST_FILL_DESTINATION(2, cmd->dst, bc->pipe_id, bc->module_id);279	cmd->command_id = bc->cmd_id;280	memcpy(cmd->params, bc->params, bc->max);281 282	ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,283				SST_FLAG_BLOCKED, bc->task_id, 0, cmd, len);284	kfree(cmd);285	return ret;286}287 288/**289 * sst_find_and_send_pipe_algo - send all the algo parameters for a pipe290 * @drv: sst_data291 * @pipe: string identifier292 * @ids: list of algorithms293 * The algos which are in each pipeline are sent to the firmware one by one294 *295 * Called with lock held296 */297static int sst_find_and_send_pipe_algo(struct sst_data *drv,298					const char *pipe, struct sst_ids *ids)299{300	int ret = 0;301	struct sst_algo_control *bc;302	struct sst_module *algo;303 304	dev_dbg(&drv->pdev->dev, "Enter: widget=%s\n", pipe);305 306	list_for_each_entry(algo, &ids->algo_list, node) {307		bc = (void *)algo->kctl->private_value;308 309		dev_dbg(&drv->pdev->dev, "Found algo control name=%s pipe=%s\n",310				algo->kctl->id.name, pipe);311		ret = sst_send_algo_cmd(drv, bc);312		if (ret)313			return ret;314	}315	return ret;316}317 318static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol,319			    struct snd_ctl_elem_info *uinfo)320{321	struct sst_algo_control *bc = (void *)kcontrol->private_value;322 323	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;324	uinfo->count = bc->max;325 326	return 0;327}328 329static int sst_algo_control_get(struct snd_kcontrol *kcontrol,330				struct snd_ctl_elem_value *ucontrol)331{332	struct sst_algo_control *bc = (void *)kcontrol->private_value;333	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);334 335	switch (bc->type) {336	case SST_ALGO_PARAMS:337		memcpy(ucontrol->value.bytes.data, bc->params, bc->max);338		break;339	default:340		dev_err(component->dev, "Invalid Input- algo type:%d\n",341				bc->type);342		return -EINVAL;343 344	}345	return 0;346}347 348static int sst_algo_control_set(struct snd_kcontrol *kcontrol,349				struct snd_ctl_elem_value *ucontrol)350{351	int ret = 0;352	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);353	struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);354	struct sst_algo_control *bc = (void *)kcontrol->private_value;355 356	dev_dbg(cmpnt->dev, "control_name=%s\n", kcontrol->id.name);357	mutex_lock(&drv->lock);358	switch (bc->type) {359	case SST_ALGO_PARAMS:360		memcpy(bc->params, ucontrol->value.bytes.data, bc->max);361		break;362	default:363		mutex_unlock(&drv->lock);364		dev_err(cmpnt->dev, "Invalid Input- algo type:%d\n",365				bc->type);366		return -EINVAL;367	}368	/*if pipe is enabled, need to send the algo params from here*/369	if (bc->w && bc->w->power)370		ret = sst_send_algo_cmd(drv, bc);371	mutex_unlock(&drv->lock);372 373	return ret;374}375 376static int sst_gain_ctl_info(struct snd_kcontrol *kcontrol,377	struct snd_ctl_elem_info *uinfo)378{379	struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;380 381	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;382	uinfo->count = mc->stereo ? 2 : 1;383	uinfo->value.integer.min = mc->min;384	uinfo->value.integer.max = mc->max;385 386	return 0;387}388 389/**390 * sst_send_gain_cmd - send the gain algorithm IPC to the FW391 * @drv: sst_data392 * @gv:the stored value of gain (also contains rampduration)393 * @task_id: task index394 * @loc_id: location/position index395 * @module_id: module index396 * @mute: flag that indicates whether this was called from the397 *  digital_mute callback or directly. If called from the398 *  digital_mute callback, module will be muted/unmuted based on this399 *  flag. The flag is always 0 if called directly.400 *401 * Called with sst_data.lock held402 *403 * The user-set gain value is sent only if the user-controllable 'mute' control404 * is OFF (indicated by gv->mute). Otherwise, the mute value (MIN value) is405 * sent.406 */407static int sst_send_gain_cmd(struct sst_data *drv, struct sst_gain_value *gv,408			      u16 task_id, u16 loc_id, u16 module_id, int mute)409{410	struct sst_cmd_set_gain_dual cmd;411 412	dev_dbg(&drv->pdev->dev, "Enter\n");413 414	cmd.header.command_id = MMX_SET_GAIN;415	SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);416	cmd.gain_cell_num = 1;417 418	if (mute || gv->mute) {419		cmd.cell_gains[0].cell_gain_left = SST_GAIN_MIN_VALUE;420		cmd.cell_gains[0].cell_gain_right = SST_GAIN_MIN_VALUE;421	} else {422		cmd.cell_gains[0].cell_gain_left = gv->l_gain;423		cmd.cell_gains[0].cell_gain_right = gv->r_gain;424	}425 426	SST_FILL_DESTINATION(2, cmd.cell_gains[0].dest,427			     loc_id, module_id);428	cmd.cell_gains[0].gain_time_constant = gv->ramp_duration;429 430	cmd.header.length = sizeof(struct sst_cmd_set_gain_dual)431				- sizeof(struct sst_dsp_header);432 433	/* we are with lock held, so call the unlocked api  to send */434	return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,435				SST_FLAG_BLOCKED, task_id, 0, &cmd,436			      sizeof(cmd.header) + cmd.header.length);437}438 439static int sst_gain_get(struct snd_kcontrol *kcontrol,440			struct snd_ctl_elem_value *ucontrol)441{442	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);443	struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;444	struct sst_gain_value *gv = mc->gain_val;445 446	switch (mc->type) {447	case SST_GAIN_TLV:448		ucontrol->value.integer.value[0] = gv->l_gain;449		ucontrol->value.integer.value[1] = gv->r_gain;450		break;451 452	case SST_GAIN_MUTE:453		ucontrol->value.integer.value[0] = gv->mute ? 0 : 1;454		break;455 456	case SST_GAIN_RAMP_DURATION:457		ucontrol->value.integer.value[0] = gv->ramp_duration;458		break;459 460	default:461		dev_err(component->dev, "Invalid Input- gain type:%d\n",462				mc->type);463		return -EINVAL;464	}465 466	return 0;467}468 469static int sst_gain_put(struct snd_kcontrol *kcontrol,470			struct snd_ctl_elem_value *ucontrol)471{472	int ret = 0;473	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);474	struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);475	struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;476	struct sst_gain_value *gv = mc->gain_val;477 478	mutex_lock(&drv->lock);479 480	switch (mc->type) {481	case SST_GAIN_TLV:482		gv->l_gain = ucontrol->value.integer.value[0];483		gv->r_gain = ucontrol->value.integer.value[1];484		dev_dbg(cmpnt->dev, "%s: Volume %d, %d\n",485				mc->pname, gv->l_gain, gv->r_gain);486		break;487 488	case SST_GAIN_MUTE:489		gv->mute = !ucontrol->value.integer.value[0];490		dev_dbg(cmpnt->dev, "%s: Mute %d\n", mc->pname, gv->mute);491		break;492 493	case SST_GAIN_RAMP_DURATION:494		gv->ramp_duration = ucontrol->value.integer.value[0];495		dev_dbg(cmpnt->dev, "%s: Ramp Delay%d\n",496					mc->pname, gv->ramp_duration);497		break;498 499	default:500		mutex_unlock(&drv->lock);501		dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n",502				mc->type);503		return -EINVAL;504	}505 506	if (mc->w && mc->w->power)507		ret = sst_send_gain_cmd(drv, gv, mc->task_id,508			mc->pipe_id | mc->instance_id, mc->module_id, 0);509	mutex_unlock(&drv->lock);510 511	return ret;512}513 514static int sst_set_pipe_gain(struct sst_ids *ids,515				struct sst_data *drv, int mute);516 517static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w,518		struct snd_kcontrol *kcontrol)519{520	struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);521	struct sst_data *drv = snd_soc_component_get_drvdata(c);522	struct sst_ids *ids = w->priv;523 524	mutex_lock(&drv->lock);525	sst_find_and_send_pipe_algo(drv, w->name, ids);526	sst_set_pipe_gain(ids, drv, 0);527	mutex_unlock(&drv->lock);528 529	return 0;530}531 532static int sst_generic_modules_event(struct snd_soc_dapm_widget *w,533				     struct snd_kcontrol *k, int event)534{535	if (SND_SOC_DAPM_EVENT_ON(event))536		return sst_send_pipe_module_params(w, k);537	return 0;538}539 540static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0);541 542/* Look up table to convert MIXER SW bit regs to SWM inputs */543static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = {544	[SST_IP_MODEM]		= SST_SWM_IN_MODEM,545	[SST_IP_CODEC0]		= SST_SWM_IN_CODEC0,546	[SST_IP_CODEC1]		= SST_SWM_IN_CODEC1,547	[SST_IP_LOOP0]		= SST_SWM_IN_SPROT_LOOP,548	[SST_IP_LOOP1]		= SST_SWM_IN_MEDIA_LOOP1,549	[SST_IP_LOOP2]		= SST_SWM_IN_MEDIA_LOOP2,550	[SST_IP_PCM0]		= SST_SWM_IN_PCM0,551	[SST_IP_PCM1]		= SST_SWM_IN_PCM1,552	[SST_IP_MEDIA0]		= SST_SWM_IN_MEDIA0,553	[SST_IP_MEDIA1]		= SST_SWM_IN_MEDIA1,554	[SST_IP_MEDIA2]		= SST_SWM_IN_MEDIA2,555	[SST_IP_MEDIA3]		= SST_SWM_IN_MEDIA3,556};557 558/**559 * fill_swm_input - fill in the SWM input ids given the register560 * @cmpnt: ASoC component561 * @swm_input: array of swm_input_ids562 * @reg: the register value is a bit-field inicated which mixer inputs are ON.563 *564 * Use the lookup table to get the input-id and fill it in the565 * structure.566 */567static int fill_swm_input(struct snd_soc_component *cmpnt,568		struct swm_input_ids *swm_input, unsigned int reg)569{570	uint i, is_set, nb_inputs = 0;571	u16 input_loc_id;572 573	dev_dbg(cmpnt->dev, "reg: %#x\n", reg);574	for (i = 0; i < SST_SWM_INPUT_COUNT; i++) {575		is_set = reg & BIT(i);576		if (!is_set)577			continue;578 579		input_loc_id = swm_mixer_input_ids[i];580		SST_FILL_DESTINATION(2, swm_input->input_id,581				     input_loc_id, SST_DEFAULT_MODULE_ID);582		nb_inputs++;583		swm_input++;584		dev_dbg(cmpnt->dev, "input id: %#x, nb_inputs: %d\n",585				input_loc_id, nb_inputs);586 587		if (nb_inputs == SST_CMD_SWM_MAX_INPUTS) {588			dev_warn(cmpnt->dev, "SET_SWM cmd max inputs reached");589			break;590		}591	}592	return nb_inputs;593}594 595 596/*597 * called with lock held598 */599static int sst_set_pipe_gain(struct sst_ids *ids,600			struct sst_data *drv, int mute)601{602	int ret = 0;603	struct sst_gain_mixer_control *mc;604	struct sst_gain_value *gv;605	struct sst_module *gain;606 607	list_for_each_entry(gain, &ids->gain_list, node) {608		struct snd_kcontrol *kctl = gain->kctl;609 610		dev_dbg(&drv->pdev->dev, "control name=%s\n", kctl->id.name);611		mc = (void *)kctl->private_value;612		gv = mc->gain_val;613 614		ret = sst_send_gain_cmd(drv, gv, mc->task_id,615			mc->pipe_id | mc->instance_id, mc->module_id, mute);616		if (ret)617			return ret;618	}619	return ret;620}621 622static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,623			struct snd_kcontrol *k, int event)624{625	struct sst_cmd_set_swm cmd;626	struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);627	struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);628	struct sst_ids *ids = w->priv;629	bool set_mixer = false;630	struct soc_mixer_control *mc;631	int val = 0;632	int i = 0;633 634	dev_dbg(cmpnt->dev, "widget = %s\n", w->name);635	/*636	 * Identify which mixer input is on and send the bitmap of the637	 * inputs as an IPC to the DSP.638	 */639	for (i = 0; i < w->num_kcontrols; i++) {640		if (dapm_kcontrol_get_value(w->kcontrols[i])) {641			mc = (struct soc_mixer_control *)(w->kcontrols[i])->private_value;642			val |= 1 << mc->shift;643		}644	}645	dev_dbg(cmpnt->dev, "val = %#x\n", val);646 647	switch (event) {648	case SND_SOC_DAPM_PRE_PMU:649	case SND_SOC_DAPM_POST_PMD:650		set_mixer = true;651		break;652	case SND_SOC_DAPM_POST_REG:653		if (w->power)654			set_mixer = true;655		break;656	default:657		set_mixer = false;658	}659 660	if (!set_mixer)661		return 0;662 663	if (SND_SOC_DAPM_EVENT_ON(event) ||664	    event == SND_SOC_DAPM_POST_REG)665		cmd.switch_state = SST_SWM_ON;666	else667		cmd.switch_state = SST_SWM_OFF;668 669	SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);670	/* MMX_SET_SWM == SBA_SET_SWM */671	cmd.header.command_id = SBA_SET_SWM;672 673	SST_FILL_DESTINATION(2, cmd.output_id,674			     ids->location_id, SST_DEFAULT_MODULE_ID);675	cmd.nb_inputs =	fill_swm_input(cmpnt, &cmd.input[0], val);676	cmd.header.length = offsetof(struct sst_cmd_set_swm, input)677				- sizeof(struct sst_dsp_header)678				+ (cmd.nb_inputs * sizeof(cmd.input[0]));679 680	return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,681			      ids->task_id, 0, &cmd,682			      sizeof(cmd.header) + cmd.header.length);683}684 685/* SBA mixers - 16 inputs */686#define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name)							\687	static const struct snd_kcontrol_new kctl_name[] = {					\688		SOC_DAPM_SINGLE("modem_in Switch", SND_SOC_NOPM, SST_IP_MODEM, 1, 0),		\689		SOC_DAPM_SINGLE("codec_in0 Switch", SND_SOC_NOPM, SST_IP_CODEC0, 1, 0),		\690		SOC_DAPM_SINGLE("codec_in1 Switch", SND_SOC_NOPM, SST_IP_CODEC1, 1, 0),		\691		SOC_DAPM_SINGLE("sprot_loop_in Switch", SND_SOC_NOPM, SST_IP_LOOP0, 1, 0),	\692		SOC_DAPM_SINGLE("media_loop1_in Switch", SND_SOC_NOPM, SST_IP_LOOP1, 1, 0),	\693		SOC_DAPM_SINGLE("media_loop2_in Switch", SND_SOC_NOPM, SST_IP_LOOP2, 1, 0),	\694		SOC_DAPM_SINGLE("pcm0_in Switch", SND_SOC_NOPM, SST_IP_PCM0, 1, 0),		\695		SOC_DAPM_SINGLE("pcm1_in Switch", SND_SOC_NOPM, SST_IP_PCM1, 1, 0),		\696	}697 698#define SST_SBA_MIXER_GRAPH_MAP(mix_name)			\699	{ mix_name, "modem_in Switch",	"modem_in" },		\700	{ mix_name, "codec_in0 Switch",	"codec_in0" },		\701	{ mix_name, "codec_in1 Switch",	"codec_in1" },		\702	{ mix_name, "sprot_loop_in Switch",	"sprot_loop_in" },	\703	{ mix_name, "media_loop1_in Switch",	"media_loop1_in" },	\704	{ mix_name, "media_loop2_in Switch",	"media_loop2_in" },	\705	{ mix_name, "pcm0_in Switch",		"pcm0_in" },		\706	{ mix_name, "pcm1_in Switch",		"pcm1_in" }707 708#define SST_MMX_DECLARE_MIX_CONTROLS(kctl_name)						\709	static const struct snd_kcontrol_new kctl_name[] = {				\710		SOC_DAPM_SINGLE("media0_in Switch", SND_SOC_NOPM, SST_IP_MEDIA0, 1, 0),	\711		SOC_DAPM_SINGLE("media1_in Switch", SND_SOC_NOPM, SST_IP_MEDIA1, 1, 0),	\712		SOC_DAPM_SINGLE("media2_in Switch", SND_SOC_NOPM, SST_IP_MEDIA2, 1, 0),	\713		SOC_DAPM_SINGLE("media3_in Switch", SND_SOC_NOPM, SST_IP_MEDIA3, 1, 0),	\714	}715 716SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media0_controls);717SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media1_controls);718 719/* 18 SBA mixers */720SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm0_controls);721SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm1_controls);722SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm2_controls);723SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_sprot_l0_controls);724SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l1_controls);725SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls);726SST_SBA_DECLARE_MIX_CONTROLS(__maybe_unused sst_mix_voip_controls);727SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls);728SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls);729SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_modem_controls);730 731/*732 * sst_handle_vb_timer - Start/Stop the DSP scheduler733 *734 * The DSP expects first cmd to be SBA_VB_START, so at first startup send735 * that.736 * DSP expects last cmd to be SBA_VB_IDLE, so at last shutdown send that.737 *738 * Do refcount internally so that we send command only at first start739 * and last end. Since SST driver does its own ref count, invoke sst's740 * power ops always!741 */742int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable)743{744	int ret = 0;745	struct sst_cmd_generic cmd;746	struct sst_data *drv = snd_soc_dai_get_drvdata(dai);747	static int timer_usage;748 749	if (enable)750		cmd.header.command_id = SBA_VB_START;751	else752		cmd.header.command_id = SBA_IDLE;753	dev_dbg(dai->dev, "enable=%u, usage=%d\n", enable, timer_usage);754 755	SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);756	cmd.header.length = 0;757 758	if (enable) {759		ret = sst->ops->power(sst->dev, true);760		if (ret < 0)761			return ret;762	}763 764	mutex_lock(&drv->lock);765	if (enable)766		timer_usage++;767	else768		timer_usage--;769 770	/*771	 * Send the command only if this call is the first enable or last772	 * disable773	 */774	if ((enable && (timer_usage == 1)) ||775	    (!enable && (timer_usage == 0))) {776		ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD,777				SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,778				sizeof(cmd.header) + cmd.header.length);779		if (ret && enable) {780			timer_usage--;781			enable  = false;782		}783	}784	mutex_unlock(&drv->lock);785 786	if (!enable)787		sst->ops->power(sst->dev, false);788	return ret;789}790 791int sst_fill_ssp_slot(struct snd_soc_dai *dai, unsigned int tx_mask,792		unsigned int rx_mask, int slots, int slot_width)793{794	struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);795 796	ctx->ssp_cmd.nb_slots = slots;797	ctx->ssp_cmd.active_tx_slot_map = tx_mask;798	ctx->ssp_cmd.active_rx_slot_map = rx_mask;799	ctx->ssp_cmd.nb_bits_per_slots = slot_width;800 801	return 0;802}803 804static int sst_get_frame_sync_polarity(struct snd_soc_dai *dai,805		unsigned int fmt)806{807	int format;808 809	format = fmt & SND_SOC_DAIFMT_INV_MASK;810	dev_dbg(dai->dev, "Enter:%s, format=%x\n", __func__, format);811 812	switch (format) {813	case SND_SOC_DAIFMT_NB_NF:814	case SND_SOC_DAIFMT_IB_NF:815		return SSP_FS_ACTIVE_HIGH;816	case SND_SOC_DAIFMT_NB_IF:817	case SND_SOC_DAIFMT_IB_IF:818		return SSP_FS_ACTIVE_LOW;819	default:820		dev_err(dai->dev, "Invalid frame sync polarity %d\n", format);821	}822 823	return -EINVAL;824}825 826static int sst_get_ssp_mode(struct snd_soc_dai *dai, unsigned int fmt)827{828	int format;829 830	format = (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK);831	dev_dbg(dai->dev, "Enter:%s, format=%x\n", __func__, format);832 833	switch (format) {834	case SND_SOC_DAIFMT_BP_FP:835		return SSP_MODE_PROVIDER;836	case SND_SOC_DAIFMT_BC_FC:837		return SSP_MODE_CONSUMER;838	default:839		dev_err(dai->dev, "Invalid ssp protocol: %d\n", format);840	}841 842	return -EINVAL;843}844 845 846int sst_fill_ssp_config(struct snd_soc_dai *dai, unsigned int fmt)847{848	unsigned int mode;849	int fs_polarity;850	struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);851 852	mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;853 854	switch (mode) {855	case SND_SOC_DAIFMT_DSP_B:856		ctx->ssp_cmd.ssp_protocol = SSP_MODE_PCM;857		ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NETWORK << 1);858		ctx->ssp_cmd.start_delay = 0;859		ctx->ssp_cmd.data_polarity = 1;860		ctx->ssp_cmd.frame_sync_width = 1;861		break;862 863	case SND_SOC_DAIFMT_DSP_A:864		ctx->ssp_cmd.ssp_protocol = SSP_MODE_PCM;865		ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NETWORK << 1);866		ctx->ssp_cmd.start_delay = 1;867		ctx->ssp_cmd.data_polarity = 1;868		ctx->ssp_cmd.frame_sync_width = 1;869		break;870 871	case SND_SOC_DAIFMT_I2S:872		ctx->ssp_cmd.ssp_protocol = SSP_MODE_I2S;873		ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NORMAL << 1);874		ctx->ssp_cmd.start_delay = 1;875		ctx->ssp_cmd.data_polarity = 0;876		ctx->ssp_cmd.frame_sync_width = ctx->ssp_cmd.nb_bits_per_slots;877		break;878 879	case SND_SOC_DAIFMT_LEFT_J:880		ctx->ssp_cmd.ssp_protocol = SSP_MODE_I2S;881		ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NORMAL << 1);882		ctx->ssp_cmd.start_delay = 0;883		ctx->ssp_cmd.data_polarity = 0;884		ctx->ssp_cmd.frame_sync_width = ctx->ssp_cmd.nb_bits_per_slots;885		break;886 887	default:888		dev_dbg(dai->dev, "using default ssp configs\n");889	}890 891	fs_polarity = sst_get_frame_sync_polarity(dai, fmt);892	if (fs_polarity < 0)893		return fs_polarity;894 895	ctx->ssp_cmd.frame_sync_polarity = fs_polarity;896 897	return 0;898}899 900/*901 * sst_ssp_config - contains SSP configuration for media UC902 * this can be overwritten by set_dai_xxx APIs903 */904static const struct sst_ssp_config sst_ssp_configs = {905	.ssp_id = SSP_CODEC,906	.bits_per_slot = 24,907	.slots = 4,908	.ssp_mode = SSP_MODE_PROVIDER,909	.pcm_mode = SSP_PCM_MODE_NETWORK,910	.duplex = SSP_DUPLEX,911	.ssp_protocol = SSP_MODE_PCM,912	.fs_width = 1,913	.fs_frequency = SSP_FS_48_KHZ,914	.active_slot_map = 0xF,915	.start_delay = 0,916	.frame_sync_polarity = SSP_FS_ACTIVE_HIGH,917	.data_polarity = 1,918};919 920void sst_fill_ssp_defaults(struct snd_soc_dai *dai)921{922	const struct sst_ssp_config *config;923	struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);924 925	config = &sst_ssp_configs;926 927	ctx->ssp_cmd.selection = config->ssp_id;928	ctx->ssp_cmd.nb_bits_per_slots = config->bits_per_slot;929	ctx->ssp_cmd.nb_slots = config->slots;930	ctx->ssp_cmd.mode = config->ssp_mode | (config->pcm_mode << 1);931	ctx->ssp_cmd.duplex = config->duplex;932	ctx->ssp_cmd.active_tx_slot_map = config->active_slot_map;933	ctx->ssp_cmd.active_rx_slot_map = config->active_slot_map;934	ctx->ssp_cmd.frame_sync_frequency = config->fs_frequency;935	ctx->ssp_cmd.frame_sync_polarity = config->frame_sync_polarity;936	ctx->ssp_cmd.data_polarity = config->data_polarity;937	ctx->ssp_cmd.frame_sync_width = config->fs_width;938	ctx->ssp_cmd.ssp_protocol = config->ssp_protocol;939	ctx->ssp_cmd.start_delay = config->start_delay;940	ctx->ssp_cmd.reserved1 = ctx->ssp_cmd.reserved2 = 0xFF;941}942 943int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable)944{945	struct sst_data *drv = snd_soc_dai_get_drvdata(dai);946	int ssp_id;947 948	dev_dbg(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id);949 950	if (strcmp(id, "ssp0-port") == 0)951		ssp_id = SSP_MODEM;952	else if (strcmp(id, "ssp2-port") == 0)953		ssp_id = SSP_CODEC;954	else {955		dev_dbg(dai->dev, "port %s is not supported\n", id);956		return -1;957	}958 959	SST_FILL_DEFAULT_DESTINATION(drv->ssp_cmd.header.dst);960	drv->ssp_cmd.header.command_id = SBA_HW_SET_SSP;961	drv->ssp_cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp)962				- sizeof(struct sst_dsp_header);963 964	drv->ssp_cmd.selection = ssp_id;965	dev_dbg(dai->dev, "ssp_id: %u\n", ssp_id);966 967	if (enable)968		drv->ssp_cmd.switch_state = SST_SWITCH_ON;969	else970		drv->ssp_cmd.switch_state = SST_SWITCH_OFF;971 972	return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,973				SST_TASK_SBA, 0, &drv->ssp_cmd,974				sizeof(drv->ssp_cmd.header) + drv->ssp_cmd.header.length);975}976 977static int sst_set_be_modules(struct snd_soc_dapm_widget *w,978			 struct snd_kcontrol *k, int event)979{980	int ret = 0;981	struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);982	struct sst_data *drv = snd_soc_component_get_drvdata(c);983 984	dev_dbg(c->dev, "Enter: widget=%s\n", w->name);985 986	if (SND_SOC_DAPM_EVENT_ON(event)) {987		mutex_lock(&drv->lock);988		ret = sst_send_slot_map(drv);989		mutex_unlock(&drv->lock);990		if (ret)991			return ret;992		ret = sst_send_pipe_module_params(w, k);993	}994	return ret;995}996 997static int sst_set_media_path(struct snd_soc_dapm_widget *w,998			      struct snd_kcontrol *k, int event)999{1000	int ret = 0;1001	struct sst_cmd_set_media_path cmd;1002	struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);1003	struct sst_data *drv = snd_soc_component_get_drvdata(c);1004	struct sst_ids *ids = w->priv;1005 1006	dev_dbg(c->dev, "widget=%s\n", w->name);1007	dev_dbg(c->dev, "task=%u, location=%#x\n",1008				ids->task_id, ids->location_id);1009 1010	if (SND_SOC_DAPM_EVENT_ON(event))1011		cmd.switch_state = SST_PATH_ON;1012	else1013		cmd.switch_state = SST_PATH_OFF;1014 1015	SST_FILL_DESTINATION(2, cmd.header.dst,1016			     ids->location_id, SST_DEFAULT_MODULE_ID);1017 1018	/* MMX_SET_MEDIA_PATH == SBA_SET_MEDIA_PATH */1019	cmd.header.command_id = MMX_SET_MEDIA_PATH;1020	cmd.header.length = sizeof(struct sst_cmd_set_media_path)1021				- sizeof(struct sst_dsp_header);1022 1023	ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,1024			      ids->task_id, 0, &cmd,1025			      sizeof(cmd.header) + cmd.header.length);1026	if (ret)1027		return ret;1028 1029	if (SND_SOC_DAPM_EVENT_ON(event))1030		ret = sst_send_pipe_module_params(w, k);1031	return ret;1032}1033 1034static int sst_set_media_loop(struct snd_soc_dapm_widget *w,1035			struct snd_kcontrol *k, int event)1036{1037	int ret = 0;1038	struct sst_cmd_sba_set_media_loop_map cmd;1039	struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);1040	struct sst_data *drv = snd_soc_component_get_drvdata(c);1041	struct sst_ids *ids = w->priv;1042 1043	dev_dbg(c->dev, "Enter:widget=%s\n", w->name);1044	if (SND_SOC_DAPM_EVENT_ON(event))1045		cmd.switch_state = SST_SWITCH_ON;1046	else1047		cmd.switch_state = SST_SWITCH_OFF;1048 1049	SST_FILL_DESTINATION(2, cmd.header.dst,1050			     ids->location_id, SST_DEFAULT_MODULE_ID);1051 1052	cmd.header.command_id = SBA_SET_MEDIA_LOOP_MAP;1053	cmd.header.length = sizeof(struct sst_cmd_sba_set_media_loop_map)1054				 - sizeof(struct sst_dsp_header);1055	cmd.param.part.cfg.rate = 2; /* 48khz */1056 1057	cmd.param.part.cfg.format = ids->format; /* stereo/Mono */1058	cmd.param.part.cfg.s_length = 1; /* 24bit left justified */1059	cmd.map = 0; /* Algo sequence: Gain - DRP - FIR - IIR */1060 1061	ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,1062			      SST_TASK_SBA, 0, &cmd,1063			      sizeof(cmd.header) + cmd.header.length);1064	if (ret)1065		return ret;1066 1067	if (SND_SOC_DAPM_EVENT_ON(event))1068		ret = sst_send_pipe_module_params(w, k);1069	return ret;1070}1071 1072static const struct snd_soc_dapm_widget sst_dapm_widgets[] = {1073	SST_AIF_IN("modem_in", sst_set_be_modules),1074	SST_AIF_IN("codec_in0", sst_set_be_modules),1075	SST_AIF_IN("codec_in1", sst_set_be_modules),1076	SST_AIF_OUT("modem_out", sst_set_be_modules),1077	SST_AIF_OUT("codec_out0", sst_set_be_modules),1078	SST_AIF_OUT("codec_out1", sst_set_be_modules),1079 1080	/* Media Paths */1081	/* MediaX IN paths are set via ALLOC, so no SET_MEDIA_PATH command */1082	SST_PATH_INPUT("media0_in", SST_TASK_MMX, SST_SWM_IN_MEDIA0, sst_generic_modules_event),1083	SST_PATH_INPUT("media1_in", SST_TASK_MMX, SST_SWM_IN_MEDIA1, NULL),1084	SST_PATH_INPUT("media2_in", SST_TASK_MMX, SST_SWM_IN_MEDIA2, sst_set_media_path),1085	SST_PATH_INPUT("media3_in", SST_TASK_MMX, SST_SWM_IN_MEDIA3, NULL),1086	SST_PATH_OUTPUT("media0_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA0, sst_set_media_path),1087	SST_PATH_OUTPUT("media1_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA1, sst_set_media_path),1088 1089	/* SBA PCM Paths */1090	SST_PATH_INPUT("pcm0_in", SST_TASK_SBA, SST_SWM_IN_PCM0, sst_set_media_path),1091	SST_PATH_INPUT("pcm1_in", SST_TASK_SBA, SST_SWM_IN_PCM1, sst_set_media_path),1092	SST_PATH_OUTPUT("pcm0_out", SST_TASK_SBA, SST_SWM_OUT_PCM0, sst_set_media_path),1093	SST_PATH_OUTPUT("pcm1_out", SST_TASK_SBA, SST_SWM_OUT_PCM1, sst_set_media_path),1094	SST_PATH_OUTPUT("pcm2_out", SST_TASK_SBA, SST_SWM_OUT_PCM2, sst_set_media_path),1095 1096	/* SBA Loops */1097	SST_PATH_INPUT("sprot_loop_in", SST_TASK_SBA, SST_SWM_IN_SPROT_LOOP, NULL),1098	SST_PATH_INPUT("media_loop1_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP1, NULL),1099	SST_PATH_INPUT("media_loop2_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP2, NULL),1100	SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_STEREO, sst_set_media_loop),1101	SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_STEREO, sst_set_media_loop),1102	SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop),1103 1104	/* Media Mixers */1105	SST_SWM_MIXER("media0_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA0,1106		      sst_mix_media0_controls, sst_swm_mixer_event),1107	SST_SWM_MIXER("media1_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA1,1108		      sst_mix_media1_controls, sst_swm_mixer_event),1109 1110	/* SBA PCM mixers */1111	SST_SWM_MIXER("pcm0_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM0,1112		      sst_mix_pcm0_controls, sst_swm_mixer_event),1113	SST_SWM_MIXER("pcm1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM1,1114		      sst_mix_pcm1_controls, sst_swm_mixer_event),1115	SST_SWM_MIXER("pcm2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM2,1116		      sst_mix_pcm2_controls, sst_swm_mixer_event),1117 1118	/* SBA Loop mixers */1119	SST_SWM_MIXER("sprot_loop_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP,1120		      sst_mix_sprot_l0_controls, sst_swm_mixer_event),1121	SST_SWM_MIXER("media_loop1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1,1122		      sst_mix_media_l1_controls, sst_swm_mixer_event),1123	SST_SWM_MIXER("media_loop2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2,1124		      sst_mix_media_l2_controls, sst_swm_mixer_event),1125 1126	/* SBA Backend mixers */1127	SST_SWM_MIXER("codec_out0 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC0,1128		      sst_mix_codec0_controls, sst_swm_mixer_event),1129	SST_SWM_MIXER("codec_out1 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC1,1130		      sst_mix_codec1_controls, sst_swm_mixer_event),1131	SST_SWM_MIXER("modem_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MODEM,1132		      sst_mix_modem_controls, sst_swm_mixer_event),1133 1134};1135 1136static const struct snd_soc_dapm_route intercon[] = {1137	{"media0_in", NULL, "Compress Playback"},1138	{"media1_in", NULL, "Headset Playback"},1139	{"media2_in", NULL, "pcm0_out"},1140	{"media3_in", NULL, "Deepbuffer Playback"},1141 1142	{"media0_out mix 0", "media0_in Switch", "media0_in"},1143	{"media0_out mix 0", "media1_in Switch", "media1_in"},1144	{"media0_out mix 0", "media2_in Switch", "media2_in"},1145	{"media0_out mix 0", "media3_in Switch", "media3_in"},1146	{"media1_out mix 0", "media0_in Switch", "media0_in"},1147	{"media1_out mix 0", "media1_in Switch", "media1_in"},1148	{"media1_out mix 0", "media2_in Switch", "media2_in"},1149	{"media1_out mix 0", "media3_in Switch", "media3_in"},1150 1151	{"media0_out", NULL, "media0_out mix 0"},1152	{"media1_out", NULL, "media1_out mix 0"},1153	{"pcm0_in", NULL, "media0_out"},1154	{"pcm1_in", NULL, "media1_out"},1155 1156	{"Headset Capture", NULL, "pcm1_out"},1157	{"Headset Capture", NULL, "pcm2_out"},1158	{"pcm0_out", NULL, "pcm0_out mix 0"},1159	SST_SBA_MIXER_GRAPH_MAP("pcm0_out mix 0"),1160	{"pcm1_out", NULL, "pcm1_out mix 0"},1161	SST_SBA_MIXER_GRAPH_MAP("pcm1_out mix 0"),1162	{"pcm2_out", NULL, "pcm2_out mix 0"},1163	SST_SBA_MIXER_GRAPH_MAP("pcm2_out mix 0"),1164 1165	{"media_loop1_in", NULL, "media_loop1_out"},1166	{"media_loop1_out", NULL, "media_loop1_out mix 0"},1167	SST_SBA_MIXER_GRAPH_MAP("media_loop1_out mix 0"),1168	{"media_loop2_in", NULL, "media_loop2_out"},1169	{"media_loop2_out", NULL, "media_loop2_out mix 0"},1170	SST_SBA_MIXER_GRAPH_MAP("media_loop2_out mix 0"),1171	{"sprot_loop_in", NULL, "sprot_loop_out"},1172	{"sprot_loop_out", NULL, "sprot_loop_out mix 0"},1173	SST_SBA_MIXER_GRAPH_MAP("sprot_loop_out mix 0"),1174 1175	{"codec_out0", NULL, "codec_out0 mix 0"},1176	SST_SBA_MIXER_GRAPH_MAP("codec_out0 mix 0"),1177	{"codec_out1", NULL, "codec_out1 mix 0"},1178	SST_SBA_MIXER_GRAPH_MAP("codec_out1 mix 0"),1179	{"modem_out", NULL, "modem_out mix 0"},1180	SST_SBA_MIXER_GRAPH_MAP("modem_out mix 0"),1181 1182 1183};1184static const char * const slot_names[] = {1185	"none",1186	"slot 0", "slot 1", "slot 2", "slot 3",1187	"slot 4", "slot 5", "slot 6", "slot 7", /* not supported by FW */1188};1189 1190static const char * const channel_names[] = {1191	"none",1192	"codec_out0_0", "codec_out0_1", "codec_out1_0", "codec_out1_1",1193	"codec_out2_0", "codec_out2_1", "codec_out3_0", "codec_out3_1", /* not supported by FW */1194};1195 1196#define SST_INTERLEAVER(xpname, slot_name, slotno) \1197	SST_SSP_SLOT_CTL(xpname, "tx interleaver", slot_name, slotno, true, \1198			 channel_names, sst_slot_get, sst_slot_put)1199 1200#define SST_DEINTERLEAVER(xpname, channel_name, channel_no) \1201	SST_SSP_SLOT_CTL(xpname, "rx deinterleaver", channel_name, channel_no, false, \1202			 slot_names, sst_slot_get, sst_slot_put)1203 1204static const struct snd_kcontrol_new sst_slot_controls[] = {1205	SST_INTERLEAVER("codec_out", "slot 0", 0),1206	SST_INTERLEAVER("codec_out", "slot 1", 1),1207	SST_INTERLEAVER("codec_out", "slot 2", 2),1208	SST_INTERLEAVER("codec_out", "slot 3", 3),1209	SST_DEINTERLEAVER("codec_in", "codec_in0_0", 0),1210	SST_DEINTERLEAVER("codec_in", "codec_in0_1", 1),1211	SST_DEINTERLEAVER("codec_in", "codec_in1_0", 2),1212	SST_DEINTERLEAVER("codec_in", "codec_in1_1", 3),1213};1214 1215/* Gain helper with min/max set */1216#define SST_GAIN(name, path_id, task_id, instance, gain_var)				\1217	SST_GAIN_KCONTROLS(name, "Gain", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE,	\1218		SST_GAIN_TC_MIN, SST_GAIN_TC_MAX,					\1219		sst_gain_get, sst_gain_put,						\1220		SST_MODULE_ID_GAIN_CELL, path_id, instance, task_id,			\1221		sst_gain_tlv_common, gain_var)1222 1223#define SST_VOLUME(name, path_id, task_id, instance, gain_var)				\1224	SST_GAIN_KCONTROLS(name, "Volume", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE,	\1225		SST_GAIN_TC_MIN, SST_GAIN_TC_MAX,					\1226		sst_gain_get, sst_gain_put,						\1227		SST_MODULE_ID_VOLUME, path_id, instance, task_id,			\1228		sst_gain_tlv_common, gain_var)1229 1230static struct sst_gain_value sst_gains[];1231 1232static const struct snd_kcontrol_new sst_gain_controls[] = {1233	SST_GAIN("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[0]),1234	SST_GAIN("media1_in", SST_PATH_INDEX_MEDIA1_IN, SST_TASK_MMX, 0, &sst_gains[1]),1235	SST_GAIN("media2_in", SST_PATH_INDEX_MEDIA2_IN, SST_TASK_MMX, 0, &sst_gains[2]),1236	SST_GAIN("media3_in", SST_PATH_INDEX_MEDIA3_IN, SST_TASK_MMX, 0, &sst_gains[3]),1237 1238	SST_GAIN("pcm0_in", SST_PATH_INDEX_PCM0_IN, SST_TASK_SBA, 0, &sst_gains[4]),1239	SST_GAIN("pcm1_in", SST_PATH_INDEX_PCM1_IN, SST_TASK_SBA, 0, &sst_gains[5]),1240	SST_GAIN("pcm1_out", SST_PATH_INDEX_PCM1_OUT, SST_TASK_SBA, 0, &sst_gains[6]),1241	SST_GAIN("pcm2_out", SST_PATH_INDEX_PCM2_OUT, SST_TASK_SBA, 0, &sst_gains[7]),1242 1243	SST_GAIN("codec_in0", SST_PATH_INDEX_CODEC_IN0, SST_TASK_SBA, 0, &sst_gains[8]),1244	SST_GAIN("codec_in1", SST_PATH_INDEX_CODEC_IN1, SST_TASK_SBA, 0, &sst_gains[9]),1245	SST_GAIN("codec_out0", SST_PATH_INDEX_CODEC_OUT0, SST_TASK_SBA, 0, &sst_gains[10]),1246	SST_GAIN("codec_out1", SST_PATH_INDEX_CODEC_OUT1, SST_TASK_SBA, 0, &sst_gains[11]),1247	SST_GAIN("media_loop1_out", SST_PATH_INDEX_MEDIA_LOOP1_OUT, SST_TASK_SBA, 0, &sst_gains[12]),1248	SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[13]),1249	SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[14]),1250	SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[15]),1251	SST_GAIN("modem_in", SST_PATH_INDEX_MODEM_IN, SST_TASK_SBA, 0, &sst_gains[16]),1252	SST_GAIN("modem_out", SST_PATH_INDEX_MODEM_OUT, SST_TASK_SBA, 0, &sst_gains[17]),1253 1254};1255 1256#define SST_GAIN_NUM_CONTROLS 31257/* the SST_GAIN macro above will create three alsa controls for each1258 * instance invoked, gain, mute and ramp duration, which use the same gain1259 * cell sst_gain to keep track of data1260 * To calculate number of gain cell instances we need to device by 3 in1261 * below caulcation for gain cell memory.1262 * This gets rid of static number and issues while adding new controls1263 */1264static struct sst_gain_value sst_gains[ARRAY_SIZE(sst_gain_controls)/SST_GAIN_NUM_CONTROLS];1265 1266static const struct snd_kcontrol_new sst_algo_controls[] = {1267	SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 272, SST_MODULE_ID_FIR_24,1268		 SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR),1269	SST_ALGO_KCONTROL_BYTES("media_loop1_out", "iir", 300, SST_MODULE_ID_IIR_24,1270		SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR),1271	SST_ALGO_KCONTROL_BYTES("media_loop1_out", "mdrp", 286, SST_MODULE_ID_MDRP,1272		SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP),1273	SST_ALGO_KCONTROL_BYTES("media_loop2_out", "fir", 272, SST_MODULE_ID_FIR_24,1274		SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR),1275	SST_ALGO_KCONTROL_BYTES("media_loop2_out", "iir", 300, SST_MODULE_ID_IIR_24,1276		SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR),1277	SST_ALGO_KCONTROL_BYTES("media_loop2_out", "mdrp", 286, SST_MODULE_ID_MDRP,1278		SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP),1279	SST_ALGO_KCONTROL_BYTES("sprot_loop_out", "lpro", 192, SST_MODULE_ID_SPROT,1280		SST_PATH_INDEX_SPROT_LOOP_OUT, 0, SST_TASK_SBA, SBA_VB_LPRO),1281	SST_ALGO_KCONTROL_BYTES("codec_in0", "dcr", 52, SST_MODULE_ID_FILT_DCR,1282		SST_PATH_INDEX_CODEC_IN0, 0, SST_TASK_SBA, SBA_VB_SET_IIR),1283	SST_ALGO_KCONTROL_BYTES("codec_in1", "dcr", 52, SST_MODULE_ID_FILT_DCR,1284		SST_PATH_INDEX_CODEC_IN1, 0, SST_TASK_SBA, SBA_VB_SET_IIR),1285 1286};1287 1288static int sst_algo_control_init(struct device *dev)1289{1290	int i = 0;1291	struct sst_algo_control *bc;1292	/*allocate space to cache the algo parameters in the driver*/1293	for (i = 0; i < ARRAY_SIZE(sst_algo_controls); i++) {1294		bc = (struct sst_algo_control *)sst_algo_controls[i].private_value;1295		bc->params = devm_kzalloc(dev, bc->max, GFP_KERNEL);1296		if (bc->params == NULL)1297			return -ENOMEM;1298	}1299	return 0;1300}1301 1302static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w)1303{1304	switch (w->id) {1305	case snd_soc_dapm_pga:1306	case snd_soc_dapm_aif_in:1307	case snd_soc_dapm_aif_out:1308	case snd_soc_dapm_input:1309	case snd_soc_dapm_output:1310	case snd_soc_dapm_mixer:1311		return true;1312	default:1313		return false;1314	}1315}1316 1317/**1318 * sst_send_pipe_gains - send gains for the front-end DAIs1319 * @dai: front-end dai1320 * @stream: direction1321 * @mute: boolean indicating mute status1322 *1323 * The gains in the pipes connected to the front-ends are muted/unmuted1324 * automatically via the digital_mute() DAPM callback. This function sends the1325 * gains for the front-end pipes.1326 */1327int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)1328{1329	struct sst_data *drv = snd_soc_dai_get_drvdata(dai);1330	struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, stream);1331	struct snd_soc_dapm_path *p;1332 1333	dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream);1334	dev_dbg(dai->dev, "Stream name=%s\n", w->name);1335 1336	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {1337		snd_soc_dapm_widget_for_each_sink_path(w, p) {1338			if (p->connected && !p->connected(w, p->sink))1339				continue;1340 1341			if (p->connect && p->sink->power &&1342					is_sst_dapm_widget(p->sink)) {1343				struct sst_ids *ids = p->sink->priv;1344 1345				dev_dbg(dai->dev, "send gains for widget=%s\n",1346						p->sink->name);1347				mutex_lock(&drv->lock);1348				sst_set_pipe_gain(ids, drv, mute);1349				mutex_unlock(&drv->lock);1350			}1351		}1352	} else {1353		snd_soc_dapm_widget_for_each_source_path(w, p) {1354			if (p->connected && !p->connected(w, p->source))1355				continue;1356 1357			if (p->connect &&  p->source->power &&1358					is_sst_dapm_widget(p->source)) {1359				struct sst_ids *ids = p->source->priv;1360 1361				dev_dbg(dai->dev, "send gain for widget=%s\n",1362						p->source->name);1363				mutex_lock(&drv->lock);1364				sst_set_pipe_gain(ids, drv, mute);1365				mutex_unlock(&drv->lock);1366			}1367		}1368	}1369	return 0;1370}1371 1372/**1373 * sst_fill_module_list - populate the list of modules/gains for a pipe1374 * @kctl: kcontrol pointer1375 * @w: dapm widget1376 * @type: widget type1377 *1378 * Fills the widget pointer in the kcontrol private data, and also fills the1379 * kcontrol pointer in the widget private data.1380 *1381 * Widget pointer is used to send the algo/gain in the .put() handler if the1382 * widget is powerd on.1383 *1384 * Kcontrol pointer is used to send the algo/gain in the widget power ON/OFF1385 * event handler. Each widget (pipe) has multiple algos stored in the algo_list.1386 */1387static int sst_fill_module_list(struct snd_kcontrol *kctl,1388	 struct snd_soc_dapm_widget *w, int type)1389{1390	struct sst_module *module;1391	struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);1392	struct sst_ids *ids = w->priv;1393	int ret = 0;1394 1395	module = devm_kzalloc(c->dev, sizeof(*module), GFP_KERNEL);1396	if (!module)1397		return -ENOMEM;1398 1399	if (type == SST_MODULE_GAIN) {1400		struct sst_gain_mixer_control *mc = (void *)kctl->private_value;1401 1402		mc->w = w;1403		module->kctl = kctl;1404		list_add_tail(&module->node, &ids->gain_list);1405	} else if (type == SST_MODULE_ALGO) {1406		struct sst_algo_control *bc = (void *)kctl->private_value;1407 1408		bc->w = w;1409		module->kctl = kctl;1410		list_add_tail(&module->node, &ids->algo_list);1411	} else {1412		dev_err(c->dev, "invoked for unknown type %d module %s",1413				type, kctl->id.name);1414		ret = -EINVAL;1415	}1416 1417	return ret;1418}1419 1420/**1421 * sst_fill_widget_module_info - fill list of gains/algos for the pipe1422 * @w: pipe modeled as a DAPM widget1423 * @component: ASoC component1424 *1425 * Fill the list of gains/algos for the widget by looking at all the card1426 * controls and comparing the name of the widget with the first part of control1427 * name. First part of control name contains the pipe name (widget name).1428 */1429static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w,1430	struct snd_soc_component *component)1431{1432	struct snd_kcontrol *kctl;1433	int index, ret = 0;1434	struct snd_card *card = component->card->snd_card;1435	char *idx;1436 1437	down_read(&card->controls_rwsem);1438 1439	list_for_each_entry(kctl, &card->controls, list) {1440		idx = strchr(kctl->id.name, ' ');1441		if (idx == NULL)1442			continue;1443		index = idx - (char*)kctl->id.name;1444		if (strncmp(kctl->id.name, w->name, index))1445			continue;1446 1447		if (strstr(kctl->id.name, "Volume"))1448			ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN);1449 1450		else if (strstr(kctl->id.name, "params"))1451			ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO);1452 1453		else if (strstr(kctl->id.name, "Switch") &&1454			 strstr(kctl->id.name, "Gain")) {1455			struct sst_gain_mixer_control *mc =1456						(void *)kctl->private_value;1457 1458			mc->w = w;1459 1460		} else if (strstr(kctl->id.name, "interleaver")) {1461			struct sst_enum *e = (void *)kctl->private_value;1462 1463			e->w = w;1464 1465		} else if (strstr(kctl->id.name, "deinterleaver")) {1466			struct sst_enum *e = (void *)kctl->private_value;1467 1468			e->w = w;1469		}1470 1471		if (ret < 0) {1472			up_read(&card->controls_rwsem);1473			return ret;1474		}1475	}1476 1477	up_read(&card->controls_rwsem);1478	return 0;1479}1480 1481/**1482 * sst_fill_linked_widgets - fill the parent pointer for the linked widget1483 * @component: ASoC component1484 * @ids: sst_ids array1485 */1486static void sst_fill_linked_widgets(struct snd_soc_component *component,1487						struct sst_ids *ids)1488{1489	struct snd_soc_dapm_widget *w;1490	unsigned int len = strlen(ids->parent_wname);1491 1492	list_for_each_entry(w, &component->card->widgets, list) {1493		if (!strncmp(ids->parent_wname, w->name, len)) {1494			ids->parent_w = w;1495			break;1496		}1497	}1498}1499 1500/**1501 * sst_map_modules_to_pipe - fill algo/gains list for all pipes1502 * @component: ASoC component1503 */1504static int sst_map_modules_to_pipe(struct snd_soc_component *component)1505{1506	struct snd_soc_dapm_widget *w;1507	int ret = 0;1508 1509	list_for_each_entry(w, &component->card->widgets, list) {1510		if (is_sst_dapm_widget(w) && (w->priv)) {1511			struct sst_ids *ids = w->priv;1512 1513			dev_dbg(component->dev, "widget type=%d name=%s\n",1514					w->id, w->name);1515			INIT_LIST_HEAD(&ids->algo_list);1516			INIT_LIST_HEAD(&ids->gain_list);1517			ret = sst_fill_widget_module_info(w, component);1518 1519			if (ret < 0)1520				return ret;1521 1522			/* fill linked widgets */1523			if (ids->parent_wname !=  NULL)1524				sst_fill_linked_widgets(component, ids);1525		}1526	}1527	return 0;1528}1529 1530int sst_dsp_init_v2_dpcm(struct snd_soc_component *component)1531{1532	int i, ret = 0;1533	struct snd_soc_dapm_context *dapm =1534			snd_soc_component_get_dapm(component);1535	struct sst_data *drv = snd_soc_component_get_drvdata(component);1536	unsigned int gains = ARRAY_SIZE(sst_gain_controls)/3;1537 1538	drv->byte_stream = devm_kzalloc(component->dev,1539					SST_MAX_BIN_BYTES, GFP_KERNEL);1540	if (!drv->byte_stream)1541		return -ENOMEM;1542 1543	snd_soc_dapm_new_controls(dapm, sst_dapm_widgets,1544			ARRAY_SIZE(sst_dapm_widgets));1545	snd_soc_dapm_add_routes(dapm, intercon,1546			ARRAY_SIZE(intercon));1547	snd_soc_dapm_new_widgets(dapm->card);1548 1549	for (i = 0; i < gains; i++) {1550		sst_gains[i].mute = SST_GAIN_MUTE_DEFAULT;1551		sst_gains[i].l_gain = SST_GAIN_VOLUME_DEFAULT;1552		sst_gains[i].r_gain = SST_GAIN_VOLUME_DEFAULT;1553		sst_gains[i].ramp_duration = SST_GAIN_RAMP_DURATION_DEFAULT;1554	}1555 1556	ret = snd_soc_add_component_controls(component, sst_gain_controls,1557			ARRAY_SIZE(sst_gain_controls));1558	if (ret)1559		return ret;1560 1561	/* Initialize algo control params */1562	ret = sst_algo_control_init(component->dev);1563	if (ret)1564		return ret;1565	ret = snd_soc_add_component_controls(component, sst_algo_controls,1566			ARRAY_SIZE(sst_algo_controls));1567	if (ret)1568		return ret;1569 1570	ret = snd_soc_add_component_controls(component, sst_slot_controls,1571			ARRAY_SIZE(sst_slot_controls));1572	if (ret)1573		return ret;1574 1575	ret = sst_map_modules_to_pipe(component);1576 1577	return ret;1578}1579