Commit c8220e87 authored by Mark Brown's avatar Mark Brown

ASoC: SOF: Miscellaneous preparatory patches for IPC4

Merge series from Ranjani Sridharan <ranjani.sridharan@linux.intel.com>:

This series includes last few remaining miscellaneous patches to prepare
for the introduction of new IPC version, IPC4, in the SOF driver. The changes
include new IPC ops for topology parsing to set up the volume table, prepare
the widgets for set up and free the routes. The remaining patches introduce
new fields in the existing data structures for use in IPC4 and align the flows
for widget/route set up so that they are common for both IPC3 and IPC4.
parents 756a6150 66344c6d
......@@ -90,26 +90,6 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
return iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set);
}
static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size)
{
if (value >= size)
return volume_map[size - 1];
return volume_map[value];
}
static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size)
{
int i;
for (i = 0; i < size; i++) {
if (volume_map[i] >= value)
return i;
}
return i - 1;
}
static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
{
struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
......@@ -694,6 +674,23 @@ static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev,
return 0;
}
static int
sof_ipc3_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS], int size)
{
int i;
/* init the volume table */
scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL);
if (!scontrol->volume_table)
return -ENOMEM;
/* populate the volume table */
for (i = 0; i < size ; i++)
scontrol->volume_table[i] = vol_compute_gain(i, tlv);
return 0;
}
const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = {
.volume_put = sof_ipc3_volume_put,
.volume_get = sof_ipc3_volume_get,
......@@ -708,4 +705,5 @@ const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = {
.bytes_ext_volatile_get = sof_ipc3_bytes_ext_volatile_get,
.update = sof_ipc3_control_update,
.widget_kcontrol_setup = sof_ipc3_widget_kcontrol_setup,
.set_up_volume_table = sof_ipc3_set_up_volume_table,
};
......@@ -82,8 +82,10 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL(snd_sof_pcm_period_elapsed);
int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd,
struct snd_sof_pcm *spcm, int dir)
static int
sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd,
struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *params,
struct snd_sof_platform_stream_params *platform_params, int dir)
{
struct snd_soc_dai *dai;
int ret, j;
......@@ -102,7 +104,7 @@ int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm
spcm->stream[dir].list = list;
ret = sof_widget_list_setup(sdev, spcm, dir);
ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir);
if (ret < 0) {
dev_err(sdev->dev, "error: failed widget list set up for pcm %d dir %d\n",
spcm->pcm.pcm_id, dir);
......@@ -150,9 +152,16 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n",
spcm->pcm.pcm_id, substream->stream);
ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params);
if (ret < 0) {
dev_err(component->dev, "platform hw params failed\n");
return ret;
}
/* if this is a repeated hw_params without hw_free, skip setting up widgets */
if (!spcm->stream[substream->stream].list) {
ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, substream->stream);
ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, &platform_params,
substream->stream);
if (ret < 0)
return ret;
}
......@@ -166,12 +175,6 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
return ret;
}
ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params);
if (ret < 0) {
dev_err(component->dev, "platform hw params failed\n");
return ret;
}
if (pcm_ops->hw_params) {
ret = pcm_ops->hw_params(component, substream, params, &platform_params);
if (ret < 0)
......
......@@ -14,11 +14,16 @@
static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget)
{
const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
struct snd_sof_route *sroute;
list_for_each_entry(sroute, &sdev->route_list, list)
if (sroute->src_widget == widget || sroute->sink_widget == widget)
if (sroute->src_widget == widget || sroute->sink_widget == widget) {
if (sroute->setup && tplg_ops->route_free)
tplg_ops->route_free(sdev, sroute);
sroute->setup = false;
}
}
int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
......@@ -34,6 +39,9 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
if (--swidget->use_count)
return 0;
/* reset route setup status for all routes that contain this widget */
sof_reset_route_setup_status(sdev, swidget);
/* continue to disable core even if IPC fails */
if (tplg_ops->widget_free)
err = tplg_ops->widget_free(sdev, swidget);
......@@ -50,10 +58,6 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
err = ret;
}
/* reset route setup status for all routes that contain this widget */
sof_reset_route_setup_status(sdev, swidget);
swidget->complete = 0;
/*
* free the scheduler widget (same as pipe_widget) associated with the current swidget.
* skip for static pipelines
......@@ -62,6 +66,7 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
ret = sof_widget_free(sdev, swidget->pipe_widget);
if (ret < 0 && !err)
err = ret;
swidget->pipe_widget->complete = 0;
}
if (!err)
......@@ -252,28 +257,249 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
return 0;
}
int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
static void
sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget)
{
const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
struct snd_sof_widget *swidget = widget->dobj.private;
struct snd_soc_dapm_path *p;
if (!widget_ops[widget->id].ipc_unprepare || !swidget->prepared)
goto sink_unprepare;
/* unprepare the source widget */
widget_ops[widget->id].ipc_unprepare(swidget);
swidget->prepared = false;
sink_unprepare:
/* unprepare all widgets in the sink paths */
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
if (!p->walking && p->sink->dobj.private) {
p->walking = true;
sof_unprepare_widgets_in_path(sdev, p->sink);
p->walking = false;
}
}
}
static int
sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params,
struct snd_pcm_hw_params *pipeline_params, int dir)
{
const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
struct snd_sof_widget *swidget = widget->dobj.private;
struct snd_soc_dapm_path *p;
int ret;
if (!widget_ops[widget->id].ipc_prepare || swidget->prepared)
goto sink_prepare;
/* prepare the source widget */
ret = widget_ops[widget->id].ipc_prepare(swidget, fe_params, platform_params,
pipeline_params, dir);
if (ret < 0) {
dev_err(sdev->dev, "failed to prepare widget %s\n", widget->name);
return ret;
}
swidget->prepared = true;
sink_prepare:
/* prepare all widgets in the sink paths */
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
if (!p->walking && p->sink->dobj.private) {
p->walking = true;
ret = sof_prepare_widgets_in_path(sdev, p->sink, fe_params,
platform_params, pipeline_params, dir);
p->walking = false;
if (ret < 0) {
/* unprepare the source widget */
if (!widget_ops[widget->id].ipc_unprepare && swidget->prepared) {
widget_ops[widget->id].ipc_unprepare(swidget);
swidget->prepared = false;
}
return ret;
}
}
}
return 0;
}
/*
* free all widgets in the sink path starting from the source widget
* (DAI type for capture, AIF type for playback)
*/
static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
int dir)
{
struct snd_soc_dapm_path *p;
int err;
int ret = 0;
/* free all widgets even in case of error to keep use counts balanced */
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
if (!p->walking && p->sink->dobj.private && widget->dobj.private) {
p->walking = true;
if (WIDGET_IS_AIF_OR_DAI(widget->id)) {
err = sof_widget_free(sdev, widget->dobj.private);
if (err < 0)
ret = err;
}
err = sof_widget_free(sdev, p->sink->dobj.private);
if (err < 0)
ret = err;
err = sof_free_widgets_in_path(sdev, p->sink, dir);
if (err < 0)
ret = err;
p->walking = false;
}
}
return ret;
}
/*
* set up all widgets in the sink path starting from the source widget
* (DAI type for capture, AIF type for playback).
* The error path in this function ensures that all successfully set up widgets getting freed.
*/
static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
int dir)
{
struct snd_soc_dapm_path *p;
int ret;
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
if (!p->walking && p->sink->dobj.private && widget->dobj.private) {
p->walking = true;
if (WIDGET_IS_AIF_OR_DAI(widget->id)) {
ret = sof_widget_setup(sdev, widget->dobj.private);
if (ret < 0)
goto out;
}
ret = sof_widget_setup(sdev, p->sink->dobj.private);
if (ret < 0) {
if (WIDGET_IS_AIF_OR_DAI(widget->id))
sof_widget_free(sdev, widget->dobj.private);
goto out;
}
ret = sof_set_up_widgets_in_path(sdev, p->sink, dir);
if (ret < 0) {
if (WIDGET_IS_AIF_OR_DAI(widget->id))
sof_widget_free(sdev, widget->dobj.private);
sof_widget_free(sdev, p->sink->dobj.private);
}
out:
p->walking = false;
if (ret < 0)
return ret;
}
}
return 0;
}
static int
sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params, int dir,
enum sof_widget_op op)
{
struct snd_soc_dapm_widget *widget;
char *str;
int ret = 0;
int i;
for_each_dapm_widgets(list, i, widget) {
/* starting widget for playback is AIF type */
if (dir == SNDRV_PCM_STREAM_PLAYBACK && !WIDGET_IS_AIF(widget->id))
continue;
/* starting widget for capture is DAI type */
if (dir == SNDRV_PCM_STREAM_CAPTURE && !WIDGET_IS_DAI(widget->id))
continue;
switch (op) {
case SOF_WIDGET_SETUP:
ret = sof_set_up_widgets_in_path(sdev, widget, dir);
str = "set up";
break;
case SOF_WIDGET_FREE:
ret = sof_free_widgets_in_path(sdev, widget, dir);
str = "free";
break;
case SOF_WIDGET_PREPARE:
{
struct snd_pcm_hw_params pipeline_params;
str = "prepare";
/*
* When walking the list of connected widgets, the pipeline_params for each
* widget is modified by the source widget in the path. Use a local
* copy of the runtime params as the pipeline_params so that the runtime
* params does not get overwritten.
*/
memcpy(&pipeline_params, fe_params, sizeof(*fe_params));
ret = sof_prepare_widgets_in_path(sdev, widget, fe_params,
platform_params, &pipeline_params, dir);
break;
}
case SOF_WIDGET_UNPREPARE:
sof_unprepare_widgets_in_path(sdev, widget);
break;
default:
dev_err(sdev->dev, "Invalid widget op %d\n", op);
return -EINVAL;
}
if (ret < 0) {
dev_err(sdev->dev, "Failed to %s connected widgets\n", str);
return ret;
}
}
return 0;
}
int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params,
int dir)
{
const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
struct snd_soc_dapm_widget *widget;
int i, ret, num_widgets;
int i, ret;
/* nothing to set up */
if (!list)
return 0;
/* set up widgets in the list */
for_each_dapm_widgets(list, num_widgets, widget) {
struct snd_sof_widget *swidget = widget->dobj.private;
if (!swidget)
continue;
/*
* Prepare widgets for set up. The prepare step is used to allocate memory, assign
* instance ID and pick the widget configuration based on the runtime PCM params.
*/
ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
dir, SOF_WIDGET_PREPARE);
if (ret < 0)
return ret;
/* set up the widget */
ret = sof_widget_setup(sdev, swidget);
if (ret < 0)
goto widget_free;
/* Set up is used to send the IPC to the DSP to create the widget */
ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
dir, SOF_WIDGET_SETUP);
if (ret < 0) {
ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
dir, SOF_WIDGET_UNPREPARE);
return ret;
}
/*
......@@ -315,18 +541,9 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in
return 0;
widget_free:
/* free all widgets that have been set up successfully */
for_each_dapm_widgets(list, i, widget) {
struct snd_sof_widget *swidget = widget->dobj.private;
if (!swidget)
continue;
if (!num_widgets--)
break;
sof_widget_free(sdev, swidget);
}
sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, dir,
SOF_WIDGET_FREE);
sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
return ret;
}
......@@ -334,37 +551,22 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in
int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
{
struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
struct snd_soc_dapm_widget *widget;
int i, ret;
int ret1 = 0;
int ret;
/* nothing to free */
if (!list)
return 0;
/*
* Free widgets in the list. This can fail but continue freeing other widgets to keep
* use_counts balanced.
*/
for_each_dapm_widgets(list, i, widget) {
struct snd_sof_widget *swidget = widget->dobj.private;
if (!swidget)
continue;
/* send IPC to free widget in the DSP */
ret = sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_FREE);
/*
* free widget and its pipe_widget. Either of these can fail, but free as many as
* possible before freeing the list and returning the error.
*/
ret = sof_widget_free(sdev, swidget);
if (ret < 0)
ret1 = ret;
}
/* unprepare the widget */
sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
snd_soc_dapm_dai_free_widgets(&list);
spcm->stream[dir].list = NULL;
return ret1;
return ret;
}
/*
......
......@@ -29,16 +29,47 @@
#define DMA_CHAN_INVALID 0xFFFFFFFF
#define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out)
#define WIDGET_IS_AIF(id) ((id) == snd_soc_dapm_aif_in || (id) == snd_soc_dapm_aif_out)
#define WIDGET_IS_AIF_OR_DAI(id) (WIDGET_IS_DAI(id) || WIDGET_IS_AIF(id))
#define SOF_DAI_CLK_INTEL_SSP_MCLK 0
#define SOF_DAI_CLK_INTEL_SSP_BCLK 1
enum sof_widget_op {
SOF_WIDGET_PREPARE,
SOF_WIDGET_SETUP,
SOF_WIDGET_FREE,
SOF_WIDGET_UNPREPARE,
};
/*
* Volume fractional word length define to 16 sets
* the volume linear gain value to use Qx.16 format
*/
#define VOLUME_FWL 16
#define SOF_TLV_ITEMS 3
static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size)
{
if (value >= size)
return volume_map[size - 1];
return volume_map[value];
}
static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size)
{
int i;
for (i = 0; i < size; i++) {
if (volume_map[i] >= value)
return i;
}
return i - 1;
}
struct snd_sof_widget;
struct snd_sof_route;
struct snd_sof_control;
......@@ -88,6 +119,9 @@ struct sof_ipc_tplg_control_ops {
void (*update)(struct snd_sof_dev *sdev, void *ipc_control_message);
/* Optional callback to setup kcontrols associated with an swidget */
int (*widget_kcontrol_setup)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
/* mandatory callback to set up volume table for volume kcontrols */
int (*set_up_volume_table)(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS],
int size);
};
/**
......@@ -97,6 +131,8 @@ struct sof_ipc_tplg_control_ops {
* @token_list: List of token ID's that should be parsed for the widget
* @token_list_size: number of elements in token_list
* @bind_event: Function pointer for binding events to the widget
* @ipc_prepare: Optional op for preparing a widget for set up
* @ipc_unprepare: Optional op for unpreparing a widget
*/
struct sof_ipc_tplg_widget_ops {
int (*ipc_setup)(struct snd_sof_widget *swidget);
......@@ -105,6 +141,11 @@ struct sof_ipc_tplg_widget_ops {
int token_list_size;
int (*bind_event)(struct snd_soc_component *scomp, struct snd_sof_widget *swidget,
u16 event_type);
int (*ipc_prepare)(struct snd_sof_widget *swidget,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params,
struct snd_pcm_hw_params *source_params, int dir);
void (*ipc_unprepare)(struct snd_sof_widget *swidget);
};
/**
......@@ -114,6 +155,7 @@ struct sof_ipc_tplg_widget_ops {
* initialized to 0.
* @control: Pointer to the IPC-specific ops for topology kcontrol IO
* @route_setup: Function pointer for setting up pipeline connections
* @route_free: Optional op for freeing pipeline connections.
* @token_list: List of all tokens supported by the IPC version. The size of the token_list
* array should be SOF_TOKEN_COUNT. The unused elements in the array will be
* initialized to 0.
......@@ -131,6 +173,7 @@ struct sof_ipc_tplg_ops {
const struct sof_ipc_tplg_widget_ops *widget;
const struct sof_ipc_tplg_control_ops *control;
int (*route_setup)(struct snd_sof_dev *sdev, struct snd_sof_route *sroute);
int (*route_free)(struct snd_sof_dev *sdev, struct snd_sof_route *sroute);
const struct sof_token_info *token_list;
int (*control_setup)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol);
int (*control_free)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol);
......@@ -293,10 +336,24 @@ struct snd_sof_widget {
struct snd_soc_component *scomp;
int comp_id;
int pipeline_id;
/*
* complete flag is used to indicate that pipeline set up is complete for scheduler type
* widgets. It is unused for all other widget types.
*/
int complete;
/*
* the prepared flag is used to indicate that a widget has been prepared for getting set
* up in the DSP.
*/
bool prepared;
int use_count; /* use_count will be protected by the PCM mutex held by the core */
int core;
int id;
int id; /* id is the DAPM widget type */
/*
* Instance ID is set dynamically when the widget gets set up in the FW. It should be
* unique for each module type across all pipelines. This will not be used in SOF_IPC.
*/
int instance_id;
/*
* Flag indicating if the widget should be set up dynamically when a PCM is opened.
......@@ -311,6 +368,7 @@ struct snd_sof_widget {
struct snd_soc_dapm_widget *widget;
struct list_head list; /* list in sdev widget list */
struct snd_sof_widget *pipe_widget;
void *module_info;
const guid_t uuid;
......@@ -447,7 +505,10 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc
struct snd_soc_dapm_widget *wsink);
/* PCM */
int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params,
int dir);
int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev,
struct snd_sof_pcm *spcm);
......@@ -461,6 +522,5 @@ int get_token_uuid(void *elem, void *object, u32 offset);
int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum sof_tokens token_id,
struct snd_sof_tuple *tuples, int num_tuples,
size_t object_size, int token_instance_num);
int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd,
struct snd_sof_pcm *spcm, int dir);
u32 vol_compute_gain(u32 value, int *tlv);
#endif
......@@ -32,7 +32,6 @@
#define VOL_HALF_DB_STEP 50
/* TLV data items */
#define TLV_ITEMS 3
#define TLV_MIN 0
#define TLV_STEP 1
#define TLV_MUTE 2
......@@ -134,7 +133,7 @@ int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum so
return 0;
}
static inline int get_tlv_data(const int *p, int tlv[TLV_ITEMS])
static inline int get_tlv_data(const int *p, int tlv[SOF_TLV_ITEMS])
{
/* we only support dB scale TLV type at the moment */
if ((int)p[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE)
......@@ -224,7 +223,7 @@ static u32 vol_pow32(u32 a, int exp, u32 fwl)
* Function to calculate volume gain from TLV data.
* This function can only handle gain steps that are multiples of 0.5 dB
*/
static u32 vol_compute_gain(u32 value, int *tlv)
u32 vol_compute_gain(u32 value, int *tlv)
{
int dB_gain;
u32 linear_gain;
......@@ -263,20 +262,17 @@ static u32 vol_compute_gain(u32 value, int *tlv)
* "size" specifies the number of entries in the table
*/
static int set_up_volume_table(struct snd_sof_control *scontrol,
int tlv[TLV_ITEMS], int size)
int tlv[SOF_TLV_ITEMS], int size)
{
int j;
/* init the volume table */
scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL);
if (!scontrol->volume_table)
return -ENOMEM;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
/* populate the volume table */
for (j = 0; j < size ; j++)
scontrol->volume_table[j] = vol_compute_gain(j, tlv);
if (tplg_ops->control->set_up_volume_table)
return tplg_ops->control->set_up_volume_table(scontrol, tlv, size);
return 0;
dev_err(scomp->dev, "Mandatory op %s not set\n", __func__);
return -EINVAL;
}
struct sof_dai_types {
......@@ -772,7 +768,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_mixer_control *mc =
container_of(hdr, struct snd_soc_tplg_mixer_control, hdr);
int tlv[TLV_ITEMS];
int tlv[SOF_TLV_ITEMS];
unsigned int mask;
int ret;
......@@ -1725,14 +1721,16 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
return -ENOMEM;
}
/* parse one set of DAI link tokens */
ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size),
SOF_DAI_LINK_TOKENS, 1, slink->tuples,
num_tuples, &slink->num_tuples);
if (ret < 0) {
dev_err(scomp->dev, "failed to parse %s for dai link %s\n",
token_list[SOF_DAI_LINK_TOKENS].name, link->name);
goto err;
if (token_list[SOF_DAI_LINK_TOKENS].tokens) {
/* parse one set of DAI link tokens */
ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size),
SOF_DAI_LINK_TOKENS, 1, slink->tuples,
num_tuples, &slink->num_tuples);
if (ret < 0) {
dev_err(scomp->dev, "failed to parse %s for dai link %s\n",
token_list[SOF_DAI_LINK_TOKENS].name, link->name);
goto err;
}
}
/* nothing more to do if there are no DAI type-specific tokens defined */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment