// SPDX-License-Identifier: GPL-2.0-only /* * sst-atom-controls.c - Intel MID Platform driver DPCM ALSA controls for Mrfld * * Copyright (C) 2013-14 Intel Corp * Author: Omair Mohammed Abdullah <[email protected]> * Vinod Koul <[email protected]> * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * In the dpcm driver modelling when a particular FE/BE/Mixer/Pipe is active * we forward the settings and parameters, rest we keep the values in * driver and forward when DAPM enables them * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #define pr_fmt(fmt) … #include <linux/slab.h> #include <sound/soc.h> #include <sound/tlv.h> #include "sst-mfld-platform.h" #include "sst-atom-controls.h" static int sst_fill_byte_control(struct sst_data *drv, u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, u16 len, void *cmd_data) { … } static int sst_fill_and_send_cmd_unlocked(struct sst_data *drv, u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, void *cmd_data, u16 len) { … } /** * sst_fill_and_send_cmd - generate the IPC message and send it to the FW * @drv: sst_data * @ipc_msg: type of IPC (CMD, SET_PARAMS, GET_PARAMS) * @block: block index * @task_id: task index * @pipe_id: pipe index * @cmd_data: the IPC payload * @len: length of data to be sent */ static int sst_fill_and_send_cmd(struct sst_data *drv, u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, void *cmd_data, u16 len) { … } /* * tx map value is a bitfield where each bit represents a FW channel * * 3 2 1 0 # 0 = codec0, 1 = codec1 * RLRLRLRL # 3, 4 = reserved * * e.g. slot 0 rx map = 00001100b -> data from slot 0 goes into codec_in1 L,R */ static u8 sst_ssp_tx_map[SST_MAX_TDM_SLOTS] = …; /* * rx map value is a bitfield where each bit represents a slot * * 76543210 # 0 = slot 0, 1 = slot 1 * * e.g. codec1_0 tx map = 00000101b -> data from codec_out1_0 goes into slot 0, 2 */ static u8 sst_ssp_rx_map[SST_MAX_TDM_SLOTS] = …; /* * NOTE: this is invoked with lock held */ static int sst_send_slot_map(struct sst_data *drv) { … } static int sst_slot_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { … } /** * sst_slot_get - get the status of the interleaver/deinterleaver control * @kcontrol: control pointer * @ucontrol: User data * Searches the map where the control status is stored, and gets the * channel/slot which is currently set for this enumerated control. Since it is * an enumerated control, there is only one possible value. */ static int sst_slot_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { … } /* sst_check_and_send_slot_map - helper for checking power state and sending * slot map cmd * * called with lock held */ static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol *kcontrol) { … } /** * sst_slot_put - set the status of interleaver/deinterleaver control * @kcontrol: control pointer * @ucontrol: User data * (de)interleaver controls are defined in opposite sense to be user-friendly * * Instead of the enum value being the value written to the register, it is the * register address; and the kcontrol number (register num) is the value written * to the register. This is so that there can be only one value for each * slot/channel since there is only one control for each slot/channel. * * This means that whenever an enum is set, we need to clear the bit * for that kcontrol_no for all the interleaver OR deinterleaver registers */ static int sst_slot_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { … } static int sst_send_algo_cmd(struct sst_data *drv, struct sst_algo_control *bc) { … } /** * sst_find_and_send_pipe_algo - send all the algo parameters for a pipe * @drv: sst_data * @pipe: string identifier * @ids: list of algorithms * The algos which are in each pipeline are sent to the firmware one by one * * Called with lock held */ static int sst_find_and_send_pipe_algo(struct sst_data *drv, const char *pipe, struct sst_ids *ids) { … } static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { … } static int sst_algo_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { … } static int sst_algo_control_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { … } static int sst_gain_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { … } /** * sst_send_gain_cmd - send the gain algorithm IPC to the FW * @drv: sst_data * @gv:the stored value of gain (also contains rampduration) * @task_id: task index * @loc_id: location/position index * @module_id: module index * @mute: flag that indicates whether this was called from the * digital_mute callback or directly. If called from the * digital_mute callback, module will be muted/unmuted based on this * flag. The flag is always 0 if called directly. * * Called with sst_data.lock held * * The user-set gain value is sent only if the user-controllable 'mute' control * is OFF (indicated by gv->mute). Otherwise, the mute value (MIN value) is * sent. */ static int sst_send_gain_cmd(struct sst_data *drv, struct sst_gain_value *gv, u16 task_id, u16 loc_id, u16 module_id, int mute) { … } static int sst_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { … } static int sst_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { … } static int sst_set_pipe_gain(struct sst_ids *ids, struct sst_data *drv, int mute); static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol) { … } static int sst_generic_modules_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { … } static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0); /* Look up table to convert MIXER SW bit regs to SWM inputs */ static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = …; /** * fill_swm_input - fill in the SWM input ids given the register * @cmpnt: ASoC component * @swm_input: array of swm_input_ids * @reg: the register value is a bit-field inicated which mixer inputs are ON. * * Use the lookup table to get the input-id and fill it in the * structure. */ static int fill_swm_input(struct snd_soc_component *cmpnt, struct swm_input_ids *swm_input, unsigned int reg) { … } /* * called with lock held */ static int sst_set_pipe_gain(struct sst_ids *ids, struct sst_data *drv, int mute) { … } static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { … } /* SBA mixers - 16 inputs */ #define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name) … #define SST_SBA_MIXER_GRAPH_MAP(mix_name) … #define SST_MMX_DECLARE_MIX_CONTROLS(kctl_name) … SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media0_controls); SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media1_controls); /* 18 SBA mixers */ SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm0_controls); SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm1_controls); SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm2_controls); SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_sprot_l0_controls); SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l1_controls); SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls); SST_SBA_DECLARE_MIX_CONTROLS(__maybe_unused sst_mix_voip_controls); SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls); SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls); SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_modem_controls); /* * sst_handle_vb_timer - Start/Stop the DSP scheduler * * The DSP expects first cmd to be SBA_VB_START, so at first startup send * that. * DSP expects last cmd to be SBA_VB_IDLE, so at last shutdown send that. * * Do refcount internally so that we send command only at first start * and last end. Since SST driver does its own ref count, invoke sst's * power ops always! */ int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable) { … } int sst_fill_ssp_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) { … } static int sst_get_frame_sync_polarity(struct snd_soc_dai *dai, unsigned int fmt) { … } static int sst_get_ssp_mode(struct snd_soc_dai *dai, unsigned int fmt) { … } int sst_fill_ssp_config(struct snd_soc_dai *dai, unsigned int fmt) { … } /* * sst_ssp_config - contains SSP configuration for media UC * this can be overwritten by set_dai_xxx APIs */ static const struct sst_ssp_config sst_ssp_configs = …; void sst_fill_ssp_defaults(struct snd_soc_dai *dai) { … } int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable) { … } static int sst_set_be_modules(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { … } static int sst_set_media_path(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { … } static int sst_set_media_loop(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { … } static const struct snd_soc_dapm_widget sst_dapm_widgets[] = …; static const struct snd_soc_dapm_route intercon[] = …; static const char * const slot_names[] = …; static const char * const channel_names[] = …; #define SST_INTERLEAVER(xpname, slot_name, slotno) … #define SST_DEINTERLEAVER(xpname, channel_name, channel_no) … static const struct snd_kcontrol_new sst_slot_controls[] = …; /* Gain helper with min/max set */ #define SST_GAIN(name, path_id, task_id, instance, gain_var) … #define SST_VOLUME(name, path_id, task_id, instance, gain_var) … static struct sst_gain_value sst_gains[]; static const struct snd_kcontrol_new sst_gain_controls[] = …; #define SST_GAIN_NUM_CONTROLS … /* the SST_GAIN macro above will create three alsa controls for each * instance invoked, gain, mute and ramp duration, which use the same gain * cell sst_gain to keep track of data * To calculate number of gain cell instances we need to device by 3 in * below caulcation for gain cell memory. * This gets rid of static number and issues while adding new controls */ static struct sst_gain_value sst_gains[ARRAY_SIZE(sst_gain_controls)/SST_GAIN_NUM_CONTROLS]; static const struct snd_kcontrol_new sst_algo_controls[] = …; static int sst_algo_control_init(struct device *dev) { … } static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w) { … } /** * sst_send_pipe_gains - send gains for the front-end DAIs * @dai: front-end dai * @stream: direction * @mute: boolean indicating mute status * * The gains in the pipes connected to the front-ends are muted/unmuted * automatically via the digital_mute() DAPM callback. This function sends the * gains for the front-end pipes. */ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) { … } /** * sst_fill_module_list - populate the list of modules/gains for a pipe * @kctl: kcontrol pointer * @w: dapm widget * @type: widget type * * Fills the widget pointer in the kcontrol private data, and also fills the * kcontrol pointer in the widget private data. * * Widget pointer is used to send the algo/gain in the .put() handler if the * widget is powerd on. * * Kcontrol pointer is used to send the algo/gain in the widget power ON/OFF * event handler. Each widget (pipe) has multiple algos stored in the algo_list. */ static int sst_fill_module_list(struct snd_kcontrol *kctl, struct snd_soc_dapm_widget *w, int type) { … } /** * sst_fill_widget_module_info - fill list of gains/algos for the pipe * @w: pipe modeled as a DAPM widget * @component: ASoC component * * Fill the list of gains/algos for the widget by looking at all the card * controls and comparing the name of the widget with the first part of control * name. First part of control name contains the pipe name (widget name). */ static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w, struct snd_soc_component *component) { … } /** * sst_fill_linked_widgets - fill the parent pointer for the linked widget * @component: ASoC component * @ids: sst_ids array */ static void sst_fill_linked_widgets(struct snd_soc_component *component, struct sst_ids *ids) { … } /** * sst_map_modules_to_pipe - fill algo/gains list for all pipes * @component: ASoC component */ static int sst_map_modules_to_pipe(struct snd_soc_component *component) { … } int sst_dsp_init_v2_dpcm(struct snd_soc_component *component) { … }