Contributors: 2
Author Tokens Token Proportion Commits Commit Proportion
Charles Keepax 6204 98.01% 5 83.33%
Pierre-Louis Bossart 126 1.99% 1 16.67%
Total 6330 6


// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2025 Cirrus Logic, Inc. and
//                    Cirrus Logic International Semiconductor Ltd.

/*
 * The MIPI SDCA specification is available for public downloads at
 * https://www.mipi.org/mipi-sdca-v1-0-download
 */

#include <linux/bitmap.h>
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/device.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/overflow.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/string_helpers.h>
#include <sound/control.h>
#include <sound/sdca.h>
#include <sound/sdca_asoc.h>
#include <sound/sdca_function.h>
#include <sound/soc.h>
#include <sound/soc-component.h>
#include <sound/soc-dai.h>
#include <sound/soc-dapm.h>
#include <sound/tlv.h>

static struct sdca_control *selector_find_control(struct device *dev,
						  struct sdca_entity *entity,
						  const int sel)
{
	int i;

	for (i = 0; i < entity->num_controls; i++) {
		struct sdca_control *control = &entity->controls[i];

		if (control->sel == sel)
			return control;
	}

	dev_err(dev, "%s: control %#x: missing\n", entity->label, sel);
	return NULL;
}

static struct sdca_control_range *control_find_range(struct device *dev,
						     struct sdca_entity *entity,
						     struct sdca_control *control,
						     int cols, int rows)
{
	struct sdca_control_range *range = &control->range;

	if ((cols && range->cols != cols) || (rows && range->rows != rows) ||
	    !range->data) {
		dev_err(dev, "%s: control %#x: ranges invalid (%d,%d)\n",
			entity->label, control->sel, range->cols, range->rows);
		return NULL;
	}

	return range;
}

static struct sdca_control_range *selector_find_range(struct device *dev,
						      struct sdca_entity *entity,
						      int sel, int cols, int rows)
{
	struct sdca_control *control;

	control = selector_find_control(dev, entity, sel);
	if (!control)
		return NULL;

	return control_find_range(dev, entity, control, cols, rows);
}

static bool exported_control(struct sdca_entity *entity, struct sdca_control *control)
{
	switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
	case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
		return true;
	default:
		break;
	}

	return control->layers & (SDCA_ACCESS_LAYER_USER |
				  SDCA_ACCESS_LAYER_APPLICATION);
}

static bool readonly_control(struct sdca_control *control)
{
	return control->has_fixed || control->mode == SDCA_ACCESS_MODE_RO;
}

/**
 * sdca_asoc_count_component - count the various component parts
 * @function: Pointer to the Function information.
 * @num_widgets: Output integer pointer, will be filled with the
 * required number of DAPM widgets for the Function.
 * @num_routes: Output integer pointer, will be filled with the
 * required number of DAPM routes for the Function.
 * @num_controls: Output integer pointer, will be filled with the
 * required number of ALSA controls for the Function.
 * @num_dais: Output integer pointer, will be filled with the
 * required number of ASoC DAIs for the Function.
 *
 * This function counts various things within the SDCA Function such
 * that the calling driver can allocate appropriate space before
 * calling the appropriate population functions.
 *
 * Return: Returns zero on success, and a negative error code on failure.
 */
int sdca_asoc_count_component(struct device *dev, struct sdca_function_data *function,
			      int *num_widgets, int *num_routes, int *num_controls,
			      int *num_dais)
{
	int i, j;

	*num_widgets = function->num_entities - 1;
	*num_routes = 0;
	*num_controls = 0;
	*num_dais = 0;

	for (i = 0; i < function->num_entities - 1; i++) {
		struct sdca_entity *entity = &function->entities[i];

		/* Add supply/DAI widget connections */
		switch (entity->type) {
		case SDCA_ENTITY_TYPE_IT:
		case SDCA_ENTITY_TYPE_OT:
			*num_routes += !!entity->iot.clock;
			*num_routes += !!entity->iot.is_dataport;
			*num_controls += !entity->iot.is_dataport;
			*num_dais += !!entity->iot.is_dataport;
			break;
		case SDCA_ENTITY_TYPE_PDE:
			*num_routes += entity->pde.num_managed;
			break;
		default:
			break;
		}

		if (entity->group)
			(*num_routes)++;

		/* Add primary entity connections from DisCo */
		*num_routes += entity->num_sources;

		for (j = 0; j < entity->num_controls; j++) {
			if (exported_control(entity, &entity->controls[j]))
				(*num_controls)++;
		}
	}

	return 0;
}
EXPORT_SYMBOL_NS(sdca_asoc_count_component, "SND_SOC_SDCA");

static const char *get_terminal_name(enum sdca_terminal_type type)
{
	switch (type) {
	case SDCA_TERM_TYPE_LINEIN_STEREO:
		return SDCA_TERM_TYPE_LINEIN_STEREO_NAME;
	case SDCA_TERM_TYPE_LINEIN_FRONT_LR:
		return SDCA_TERM_TYPE_LINEIN_FRONT_LR_NAME;
	case SDCA_TERM_TYPE_LINEIN_CENTER_LFE:
		return SDCA_TERM_TYPE_LINEIN_CENTER_LFE_NAME;
	case SDCA_TERM_TYPE_LINEIN_SURROUND_LR:
		return SDCA_TERM_TYPE_LINEIN_SURROUND_LR_NAME;
	case SDCA_TERM_TYPE_LINEIN_REAR_LR:
		return SDCA_TERM_TYPE_LINEIN_REAR_LR_NAME;
	case SDCA_TERM_TYPE_LINEOUT_STEREO:
		return SDCA_TERM_TYPE_LINEOUT_STEREO_NAME;
	case SDCA_TERM_TYPE_LINEOUT_FRONT_LR:
		return SDCA_TERM_TYPE_LINEOUT_FRONT_LR_NAME;
	case SDCA_TERM_TYPE_LINEOUT_CENTER_LFE:
		return SDCA_TERM_TYPE_LINEOUT_CENTER_LFE_NAME;
	case SDCA_TERM_TYPE_LINEOUT_SURROUND_LR:
		return SDCA_TERM_TYPE_LINEOUT_SURROUND_LR_NAME;
	case SDCA_TERM_TYPE_LINEOUT_REAR_LR:
		return SDCA_TERM_TYPE_LINEOUT_REAR_LR_NAME;
	case SDCA_TERM_TYPE_MIC_JACK:
		return SDCA_TERM_TYPE_MIC_JACK_NAME;
	case SDCA_TERM_TYPE_STEREO_JACK:
		return SDCA_TERM_TYPE_STEREO_JACK_NAME;
	case SDCA_TERM_TYPE_FRONT_LR_JACK:
		return SDCA_TERM_TYPE_FRONT_LR_JACK_NAME;
	case SDCA_TERM_TYPE_CENTER_LFE_JACK:
		return SDCA_TERM_TYPE_CENTER_LFE_JACK_NAME;
	case SDCA_TERM_TYPE_SURROUND_LR_JACK:
		return SDCA_TERM_TYPE_SURROUND_LR_JACK_NAME;
	case SDCA_TERM_TYPE_REAR_LR_JACK:
		return SDCA_TERM_TYPE_REAR_LR_JACK_NAME;
	case SDCA_TERM_TYPE_HEADPHONE_JACK:
		return SDCA_TERM_TYPE_HEADPHONE_JACK_NAME;
	case SDCA_TERM_TYPE_HEADSET_JACK:
		return SDCA_TERM_TYPE_HEADSET_JACK_NAME;
	default:
		return NULL;
	}
}

static int entity_early_parse_ge(struct device *dev,
				 struct sdca_function_data *function,
				 struct sdca_entity *entity)
{
	struct sdca_control_range *range;
	struct sdca_control *control;
	struct snd_kcontrol_new *kctl;
	struct soc_enum *soc_enum;
	const char *control_name;
	unsigned int *values;
	const char **texts;
	int i;

	control = selector_find_control(dev, entity, SDCA_CTL_GE_SELECTED_MODE);
	if (!control)
		return -EINVAL;

	if (control->layers != SDCA_ACCESS_LAYER_CLASS)
		dev_warn(dev, "%s: unexpected access layer: %x\n",
			 entity->label, control->layers);

	range = control_find_range(dev, entity, control, SDCA_SELECTED_MODE_NCOLS, 0);
	if (!range)
		return -EINVAL;

	control_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s",
				      entity->label, control->label);
	if (!control_name)
		return -ENOMEM;

	kctl = devm_kmalloc(dev, sizeof(*kctl), GFP_KERNEL);
	if (!kctl)
		return -ENOMEM;

	soc_enum = devm_kmalloc(dev, sizeof(*soc_enum), GFP_KERNEL);
	if (!soc_enum)
		return -ENOMEM;

	texts = devm_kcalloc(dev, range->rows + 3, sizeof(*texts), GFP_KERNEL);
	if (!texts)
		return -ENOMEM;

	values = devm_kcalloc(dev, range->rows + 3, sizeof(*values), GFP_KERNEL);
	if (!values)
		return -ENOMEM;

	texts[0] = "No Jack";
	texts[1] = "Jack Unknown";
	texts[2] = "Detection in Progress";
	values[0] = 0;
	values[1] = 1;
	values[2] = 2;
	for (i = 0; i < range->rows; i++) {
		enum sdca_terminal_type type;

		type = sdca_range(range, SDCA_SELECTED_MODE_TERM_TYPE, i);

		values[i + 3] = sdca_range(range, SDCA_SELECTED_MODE_INDEX, i);
		texts[i + 3] = get_terminal_name(type);
		if (!texts[i + 3]) {
			dev_err(dev, "%s: unrecognised terminal type: %#x\n",
				entity->label, type);
			return -EINVAL;
		}
	}

	soc_enum->reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, 0);
	soc_enum->items = range->rows + 3;
	soc_enum->mask = roundup_pow_of_two(soc_enum->items) - 1;
	soc_enum->texts = texts;
	soc_enum->values = values;

	kctl->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
	kctl->name = control_name;
	kctl->info = snd_soc_info_enum_double;
	kctl->get = snd_soc_dapm_get_enum_double;
	kctl->put = snd_soc_dapm_put_enum_double;
	kctl->private_value = (unsigned long)soc_enum;

	entity->ge.kctl = kctl;

	return 0;
}

static void add_route(struct snd_soc_dapm_route **route, const char *sink,
		      const char *control, const char *source)
{
	(*route)->sink = sink;
	(*route)->control = control;
	(*route)->source = source;
	(*route)++;
}

static int entity_parse_simple(struct device *dev,
			       struct sdca_function_data *function,
			       struct sdca_entity *entity,
			       struct snd_soc_dapm_widget **widget,
			       struct snd_soc_dapm_route **route,
			       enum snd_soc_dapm_type id)
{
	int i;

	(*widget)->id = id;
	(*widget)++;

	for (i = 0; i < entity->num_sources; i++)
		add_route(route, entity->label, NULL, entity->sources[i]->label);

	return 0;
}

static int entity_parse_it(struct device *dev,
			   struct sdca_function_data *function,
			   struct sdca_entity *entity,
			   struct snd_soc_dapm_widget **widget,
			   struct snd_soc_dapm_route **route)
{
	int i;

	if (entity->iot.is_dataport) {
		const char *aif_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s",
						      entity->label, "Playback");
		if (!aif_name)
			return -ENOMEM;

		(*widget)->id = snd_soc_dapm_aif_in;

		add_route(route, entity->label, NULL, aif_name);
	} else {
		(*widget)->id = snd_soc_dapm_mic;
	}

	if (entity->iot.clock)
		add_route(route, entity->label, NULL, entity->iot.clock->label);

	for (i = 0; i < entity->num_sources; i++)
		add_route(route, entity->label, NULL, entity->sources[i]->label);

	(*widget)++;

	return 0;
}

static int entity_parse_ot(struct device *dev,
			   struct sdca_function_data *function,
			   struct sdca_entity *entity,
			   struct snd_soc_dapm_widget **widget,
			   struct snd_soc_dapm_route **route)
{
	int i;

	if (entity->iot.is_dataport) {
		const char *aif_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s",
						      entity->label, "Capture");
		if (!aif_name)
			return -ENOMEM;

		(*widget)->id = snd_soc_dapm_aif_out;

		add_route(route, aif_name, NULL, entity->label);
	} else {
		(*widget)->id = snd_soc_dapm_spk;
	}

	if (entity->iot.clock)
		add_route(route, entity->label, NULL, entity->iot.clock->label);

	for (i = 0; i < entity->num_sources; i++)
		add_route(route, entity->label, NULL, entity->sources[i]->label);

	(*widget)++;

	return 0;
}

static int entity_pde_event(struct snd_soc_dapm_widget *widget,
			    struct snd_kcontrol *kctl, int event)
{
	struct snd_soc_component *component = widget->dapm->component;
	struct sdca_entity *entity = widget->priv;
	static const int polls = 100;
	unsigned int reg, val;
	int from, to, i;
	int poll_us;
	int ret;

	if (!component)
		return -EIO;

	switch (event) {
	case SND_SOC_DAPM_POST_PMD:
		from = widget->on_val;
		to = widget->off_val;
		break;
	case SND_SOC_DAPM_POST_PMU:
		from = widget->off_val;
		to = widget->on_val;
		break;
	}

	for (i = 0; i < entity->pde.num_max_delay; i++) {
		struct sdca_pde_delay *delay = &entity->pde.max_delay[i];

		if (delay->from_ps == from && delay->to_ps == to) {
			poll_us = delay->us / polls;
			break;
		}
	}

	reg = SDW_SDCA_CTL(SDW_SDCA_CTL_FUNC(widget->reg),
			   SDW_SDCA_CTL_ENT(widget->reg),
			   SDCA_CTL_PDE_ACTUAL_PS, 0);

	for (i = 0; i < polls; i++) {
		if (i)
			fsleep(poll_us);

		ret = regmap_read(component->regmap, reg, &val);
		if (ret)
			return ret;
		else if (val == to)
			return 0;
	}

	dev_err(component->dev, "%s: power transition failed: %x\n",
		entity->label, val);
	return -ETIMEDOUT;
}

static int entity_parse_pde(struct device *dev,
			    struct sdca_function_data *function,
			    struct sdca_entity *entity,
			    struct snd_soc_dapm_widget **widget,
			    struct snd_soc_dapm_route **route)
{
	unsigned int target = (1 << SDCA_PDE_PS0) | (1 << SDCA_PDE_PS3);
	struct sdca_control_range *range;
	struct sdca_control *control;
	unsigned int mask = 0;
	int i;

	control = selector_find_control(dev, entity, SDCA_CTL_PDE_REQUESTED_PS);
	if (!control)
		return -EINVAL;

	/* Power should only be controlled by the driver */
	if (control->layers != SDCA_ACCESS_LAYER_CLASS)
		dev_warn(dev, "%s: unexpected access layer: %x\n",
			 entity->label, control->layers);

	range = control_find_range(dev, entity, control, SDCA_REQUESTED_PS_NCOLS, 0);
	if (!range)
		return -EINVAL;

	for (i = 0; i < range->rows; i++)
		mask |= 1 << sdca_range(range, SDCA_REQUESTED_PS_STATE, i);

	if ((mask & target) != target) {
		dev_err(dev, "%s: power control missing states\n", entity->label);
		return -EINVAL;
	}

	(*widget)->id = snd_soc_dapm_supply;
	(*widget)->reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, 0);
	(*widget)->mask = GENMASK(control->nbits - 1, 0);
	(*widget)->on_val = SDCA_PDE_PS0;
	(*widget)->off_val = SDCA_PDE_PS3;
	(*widget)->event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD;
	(*widget)->event = entity_pde_event;
	(*widget)->priv = entity;
	(*widget)++;

	for (i = 0; i < entity->pde.num_managed; i++)
		add_route(route, entity->pde.managed[i]->label, NULL, entity->label);

	for (i = 0; i < entity->num_sources; i++)
		add_route(route, entity->label, NULL, entity->sources[i]->label);

	return 0;
}

/* Device selector units are controlled through a group entity */
static int entity_parse_su_device(struct device *dev,
				  struct sdca_function_data *function,
				  struct sdca_entity *entity,
				  struct snd_soc_dapm_widget **widget,
				  struct snd_soc_dapm_route **route)
{
	struct sdca_control_range *range;
	int num_routes = 0;
	int i, j;

	if (!entity->group) {
		dev_err(dev, "%s: device selector unit missing group\n", entity->label);
		return -EINVAL;
	}

	range = selector_find_range(dev, entity->group, SDCA_CTL_GE_SELECTED_MODE,
				    SDCA_SELECTED_MODE_NCOLS, 0);
	if (!range)
		return -EINVAL;

	(*widget)->id = snd_soc_dapm_mux;
	(*widget)->kcontrol_news = entity->group->ge.kctl;
	(*widget)->num_kcontrols = 1;
	(*widget)++;

	for (i = 0; i < entity->group->ge.num_modes; i++) {
		struct sdca_ge_mode *mode = &entity->group->ge.modes[i];

		for (j = 0; j < mode->num_controls; j++) {
			struct sdca_ge_control *affected = &mode->controls[j];
			int term;

			if (affected->id != entity->id ||
			    affected->sel != SDCA_CTL_SU_SELECTOR ||
			    !affected->val)
				continue;

			if (affected->val - 1 >= entity->num_sources) {
				dev_err(dev, "%s: bad control value: %#x\n",
					entity->label, affected->val);
				return -EINVAL;
			}

			if (++num_routes > entity->num_sources) {
				dev_err(dev, "%s: too many input routes\n", entity->label);
				return -EINVAL;
			}

			term = sdca_range_search(range, SDCA_SELECTED_MODE_INDEX,
						 mode->val, SDCA_SELECTED_MODE_TERM_TYPE);
			if (!term) {
				dev_err(dev, "%s: mode not found: %#x\n",
					entity->label, mode->val);
				return -EINVAL;
			}

			add_route(route, entity->label, get_terminal_name(term),
				  entity->sources[affected->val - 1]->label);
		}
	}

	return 0;
}

/* Class selector units will be exported as an ALSA control */
static int entity_parse_su_class(struct device *dev,
				 struct sdca_function_data *function,
				 struct sdca_entity *entity,
				 struct sdca_control *control,
				 struct snd_soc_dapm_widget **widget,
				 struct snd_soc_dapm_route **route)
{
	struct snd_kcontrol_new *kctl;
	struct soc_enum *soc_enum;
	const char **texts;
	int i;

	kctl = devm_kmalloc(dev, sizeof(*kctl), GFP_KERNEL);
	if (!kctl)
		return -ENOMEM;

	soc_enum = devm_kmalloc(dev, sizeof(*soc_enum), GFP_KERNEL);
	if (!soc_enum)
		return -ENOMEM;

	texts = devm_kcalloc(dev, entity->num_sources + 1, sizeof(*texts), GFP_KERNEL);
	if (!texts)
		return -ENOMEM;

	texts[0] = "No Signal";
	for (i = 0; i < entity->num_sources; i++)
		texts[i + 1] = entity->sources[i]->label;

	soc_enum->reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, 0);
	soc_enum->items = entity->num_sources + 1;
	soc_enum->mask = roundup_pow_of_two(soc_enum->items) - 1;
	soc_enum->texts = texts;

	kctl->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
	kctl->name = "Route";
	kctl->info = snd_soc_info_enum_double;
	kctl->get = snd_soc_dapm_get_enum_double;
	kctl->put = snd_soc_dapm_put_enum_double;
	kctl->private_value = (unsigned long)soc_enum;

	(*widget)->id = snd_soc_dapm_mux;
	(*widget)->kcontrol_news = kctl;
	(*widget)->num_kcontrols = 1;
	(*widget)++;

	for (i = 0; i < entity->num_sources; i++)
		add_route(route, entity->label, texts[i + 1], entity->sources[i]->label);

	return 0;
}

static int entity_parse_su(struct device *dev,
			   struct sdca_function_data *function,
			   struct sdca_entity *entity,
			   struct snd_soc_dapm_widget **widget,
			   struct snd_soc_dapm_route **route)
{
	struct sdca_control *control;

	if (!entity->num_sources) {
		dev_err(dev, "%s: selector with no inputs\n", entity->label);
		return -EINVAL;
	}

	control = selector_find_control(dev, entity, SDCA_CTL_SU_SELECTOR);
	if (!control)
		return -EINVAL;

	if (control->layers == SDCA_ACCESS_LAYER_DEVICE)
		return entity_parse_su_device(dev, function, entity, widget, route);

	if (control->layers != SDCA_ACCESS_LAYER_CLASS)
		dev_warn(dev, "%s: unexpected access layer: %x\n",
			 entity->label, control->layers);

	return entity_parse_su_class(dev, function, entity, control, widget, route);
}

static int entity_parse_mu(struct device *dev,
			   struct sdca_function_data *function,
			   struct sdca_entity *entity,
			   struct snd_soc_dapm_widget **widget,
			   struct snd_soc_dapm_route **route)
{
	struct sdca_control *control;
	struct snd_kcontrol_new *kctl;
	int cn;
	int i;

	if (!entity->num_sources) {
		dev_err(dev, "%s: selector 1 or more inputs\n", entity->label);
		return -EINVAL;
	}

	control = selector_find_control(dev, entity, SDCA_CTL_MU_MIXER);
	if (!control)
		return -EINVAL;

	/* MU control should be through DAPM */
	if (control->layers != SDCA_ACCESS_LAYER_CLASS)
		dev_warn(dev, "%s: unexpected access layer: %x\n",
			 entity->label, control->layers);

	if (entity->num_sources != hweight64(control->cn_list)) {
		dev_err(dev, "%s: mismatched control and sources\n", entity->label);
		return -EINVAL;
	}

	kctl = devm_kcalloc(dev, entity->num_sources, sizeof(*kctl), GFP_KERNEL);
	if (!kctl)
		return -ENOMEM;

	i = 0;
	for_each_set_bit(cn, (unsigned long *)&control->cn_list,
			 BITS_PER_TYPE(control->cn_list)) {
		const char *control_name;
		struct soc_mixer_control *mc;

		control_name = devm_kasprintf(dev, GFP_KERNEL, "%s %d",
					      control->label, i + 1);
		if (!control_name)
			return -ENOMEM;

		mc = devm_kmalloc(dev, sizeof(*mc), GFP_KERNEL);
		if (!mc)
			return -ENOMEM;

		mc->reg = SND_SOC_NOPM;
		mc->rreg = SND_SOC_NOPM;
		mc->invert = 1; // Ensure default is connected
		mc->min = 0;
		mc->max = 1;

		kctl[i].name = control_name;
		kctl[i].private_value = (unsigned long)mc;
		kctl[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
		kctl[i].info = snd_soc_info_volsw;
		kctl[i].get = snd_soc_dapm_get_volsw;
		kctl[i].put = snd_soc_dapm_put_volsw;
		i++;
	}

	(*widget)->id = snd_soc_dapm_mixer;
	(*widget)->kcontrol_news = kctl;
	(*widget)->num_kcontrols = entity->num_sources;
	(*widget)++;

	for (i = 0; i < entity->num_sources; i++)
		add_route(route, entity->label, kctl[i].name, entity->sources[i]->label);

	return 0;
}

static int entity_cs_event(struct snd_soc_dapm_widget *widget,
			   struct snd_kcontrol *kctl, int event)
{
	struct snd_soc_component *component = widget->dapm->component;
	struct sdca_entity *entity = widget->priv;

	if (!component)
		return -EIO;

	if (entity->cs.max_delay)
		fsleep(entity->cs.max_delay);

	return 0;
}

static int entity_parse_cs(struct device *dev,
			   struct sdca_function_data *function,
			   struct sdca_entity *entity,
			   struct snd_soc_dapm_widget **widget,
			   struct snd_soc_dapm_route **route)
{
	int i;

	(*widget)->id = snd_soc_dapm_supply;
	(*widget)->subseq = 1; /* Ensure these run after PDEs */
	(*widget)->event_flags = SND_SOC_DAPM_POST_PMU;
	(*widget)->event = entity_cs_event;
	(*widget)->priv = entity;
	(*widget)++;

	for (i = 0; i < entity->num_sources; i++)
		add_route(route, entity->label, NULL, entity->sources[i]->label);

	return 0;
}

/**
 * sdca_asoc_populate_dapm - fill in arrays of DAPM widgets and routes
 * @dev: Pointer to the device against which allocations will be done.
 * @function: Pointer to the Function information.
 * @widget: Array of DAPM widgets to be populated.
 * @route: Array of DAPM routes to be populated.
 *
 * This function populates arrays of DAPM widgets and routes from the
 * DisCo information for a particular SDCA Function. Typically,
 * snd_soc_asoc_count_component will be used to allocate appropriately
 * sized arrays before calling this function.
 *
 * Return: Returns zero on success, and a negative error code on failure.
 */
int sdca_asoc_populate_dapm(struct device *dev, struct sdca_function_data *function,
			    struct snd_soc_dapm_widget *widget,
			    struct snd_soc_dapm_route *route)
{
	int ret;
	int i;

	for (i = 0; i < function->num_entities - 1; i++) {
		struct sdca_entity *entity = &function->entities[i];

		/*
		 * Some entities need to add controls "early" as they are
		 * referenced by other entities.
		 */
		switch (entity->type) {
		case SDCA_ENTITY_TYPE_GE:
			ret = entity_early_parse_ge(dev, function, entity);
			if (ret)
				return ret;
			break;
		default:
			break;
		}
	}

	for (i = 0; i < function->num_entities - 1; i++) {
		struct sdca_entity *entity = &function->entities[i];

		widget->name = entity->label;
		widget->reg = SND_SOC_NOPM;

		switch (entity->type) {
		case SDCA_ENTITY_TYPE_IT:
			ret = entity_parse_it(dev, function, entity, &widget, &route);
			break;
		case SDCA_ENTITY_TYPE_OT:
			ret = entity_parse_ot(dev, function, entity, &widget, &route);
			break;
		case SDCA_ENTITY_TYPE_PDE:
			ret = entity_parse_pde(dev, function, entity, &widget, &route);
			break;
		case SDCA_ENTITY_TYPE_SU:
			ret = entity_parse_su(dev, function, entity, &widget, &route);
			break;
		case SDCA_ENTITY_TYPE_MU:
			ret = entity_parse_mu(dev, function, entity, &widget, &route);
			break;
		case SDCA_ENTITY_TYPE_CS:
			ret = entity_parse_cs(dev, function, entity, &widget, &route);
			break;
		case SDCA_ENTITY_TYPE_CX:
			/*
			 * FIXME: For now we will just treat these as a supply,
			 * meaning all options are enabled.
			 */
			dev_warn(dev, "%s: clock selectors not fully supported yet\n",
				 entity->label);
			ret = entity_parse_simple(dev, function, entity, &widget,
						  &route, snd_soc_dapm_supply);
			break;
		case SDCA_ENTITY_TYPE_TG:
			ret = entity_parse_simple(dev, function, entity, &widget,
						  &route, snd_soc_dapm_siggen);
			break;
		case SDCA_ENTITY_TYPE_GE:
			ret = entity_parse_simple(dev, function, entity, &widget,
						  &route, snd_soc_dapm_supply);
			break;
		default:
			ret = entity_parse_simple(dev, function, entity, &widget,
						  &route, snd_soc_dapm_pga);
			break;
		}
		if (ret)
			return ret;

		if (entity->group)
			add_route(&route, entity->label, NULL, entity->group->label);
	}

	return 0;
}
EXPORT_SYMBOL_NS(sdca_asoc_populate_dapm, "SND_SOC_SDCA");

static int control_limit_kctl(struct device *dev,
			      struct sdca_entity *entity,
			      struct sdca_control *control,
			      struct snd_kcontrol_new *kctl)
{
	struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value;
	struct sdca_control_range *range;
	int min, max, step;
	unsigned int *tlv;
	int shift;

	if (control->type != SDCA_CTL_DATATYPE_Q7P8DB)
		return 0;

	/*
	 * FIXME: For now only handle the simple case of a single linear range
	 */
	range = control_find_range(dev, entity, control, SDCA_VOLUME_LINEAR_NCOLS, 1);
	if (!range)
		return -EINVAL;

	min = sdca_range(range, SDCA_VOLUME_LINEAR_MIN, 0);
	max = sdca_range(range, SDCA_VOLUME_LINEAR_MAX, 0);
	step = sdca_range(range, SDCA_VOLUME_LINEAR_STEP, 0);

	min = sign_extend32(min, control->nbits - 1);
	max = sign_extend32(max, control->nbits - 1);

	/*
	 * FIXME: Only support power of 2 step sizes as this can be supported
	 * by a simple shift.
	 */
	if (hweight32(step) != 1) {
		dev_err(dev, "%s: %s: currently unsupported step size\n",
			entity->label, control->label);
		return -EINVAL;
	}

	/*
	 * The SDCA volumes are in steps of 1/256th of a dB, a step down of
	 * 64 (shift of 6) gives 1/4dB. 1/4dB is the smallest unit that is also
	 * representable in the ALSA TLVs which are in 1/100ths of a dB.
	 */
	shift = max(ffs(step) - 1, 6);

	tlv = devm_kcalloc(dev, 4, sizeof(*tlv), GFP_KERNEL);
	if (!tlv)
		return -ENOMEM;

	tlv[0] = SNDRV_CTL_TLVT_DB_SCALE;
	tlv[1] = 2 * sizeof(*tlv);
	tlv[2] = (min * 100) >> 8;
	tlv[3] = ((1 << shift) * 100) >> 8;

	mc->min = min >> shift;
	mc->max = max >> shift;
	mc->shift = shift;
	mc->rshift = shift;
	mc->sign_bit = 15 - shift;

	kctl->tlv.p = tlv;
	kctl->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;

	return 0;
}

static int populate_control(struct device *dev,
			    struct sdca_function_data *function,
			    struct sdca_entity *entity,
			    struct sdca_control *control,
			    struct snd_kcontrol_new **kctl)
{
	const char *control_suffix = "";
	const char *control_name;
	struct soc_mixer_control *mc;
	int index = 0;
	int ret;
	int cn;

	if (!exported_control(entity, control))
		return 0;

	if (control->type == SDCA_CTL_DATATYPE_ONEBIT)
		control_suffix = " Switch";

	control_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s%s", entity->label,
				      control->label, control_suffix);
	if (!control_name)
		return -ENOMEM;

	mc = devm_kmalloc(dev, sizeof(*mc), GFP_KERNEL);
	if (!mc)
		return -ENOMEM;

	for_each_set_bit(cn, (unsigned long *)&control->cn_list,
			 BITS_PER_TYPE(control->cn_list)) {
		switch (index++) {
		case 0:
			mc->reg = SDW_SDCA_CTL(function->desc->adr, entity->id,
					       control->sel, cn);
			mc->rreg = mc->reg;
			break;
		case 1:
			mc->rreg = SDW_SDCA_CTL(function->desc->adr, entity->id,
						control->sel, cn);
			break;
		default:
			dev_err(dev, "%s: %s: only mono/stereo controls supported\n",
				entity->label, control->label);
			return -EINVAL;
		}
	}

	mc->min = 0;
	mc->max = clamp((0x1ull << control->nbits) - 1, 0, type_max(mc->max));

	(*kctl)->name = control_name;
	(*kctl)->private_value = (unsigned long)mc;
	(*kctl)->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
	(*kctl)->info = snd_soc_info_volsw;
	(*kctl)->get = snd_soc_get_volsw;
	(*kctl)->put = snd_soc_put_volsw;

	if (readonly_control(control))
		(*kctl)->access = SNDRV_CTL_ELEM_ACCESS_READ;
	else
		(*kctl)->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;

	ret = control_limit_kctl(dev, entity, control, *kctl);
	if (ret)
		return ret;

	(*kctl)++;

	return 0;
}

static int populate_pin_switch(struct device *dev,
			       struct sdca_entity *entity,
			       struct snd_kcontrol_new **kctl)
{
	const char *control_name;

	control_name = devm_kasprintf(dev, GFP_KERNEL, "%s Switch", entity->label);
	if (!control_name)
		return -ENOMEM;

	(*kctl)->name = control_name;
	(*kctl)->private_value = (unsigned long)entity->label;
	(*kctl)->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
	(*kctl)->info = snd_soc_dapm_info_pin_switch;
	(*kctl)->get = snd_soc_dapm_get_component_pin_switch;
	(*kctl)->put = snd_soc_dapm_put_component_pin_switch;
	(*kctl)++;

	return 0;
}

/**
 * sdca_asoc_populate_controls - fill in an array of ALSA controls for a Function
 * @dev: Pointer to the device against which allocations will be done.
 * @function: Pointer to the Function information.
 * @route: Array of ALSA controls to be populated.
 *
 * This function populates an array of ALSA controls from the DisCo
 * information for a particular SDCA Function. Typically,
 * snd_soc_asoc_count_component will be used to allocate an
 * appropriately sized array before calling this function.
 *
 * Return: Returns zero on success, and a negative error code on failure.
 */
int sdca_asoc_populate_controls(struct device *dev,
				struct sdca_function_data *function,
				struct snd_kcontrol_new *kctl)
{
	int i, j;
	int ret;

	for (i = 0; i < function->num_entities; i++) {
		struct sdca_entity *entity = &function->entities[i];

		switch (entity->type) {
		case SDCA_ENTITY_TYPE_IT:
		case SDCA_ENTITY_TYPE_OT:
			if (!entity->iot.is_dataport) {
				ret = populate_pin_switch(dev, entity, &kctl);
				if (ret)
					return ret;
			}
			break;
		default:
			break;
		}

		for (j = 0; j < entity->num_controls; j++) {
			ret = populate_control(dev, function, entity,
					       &entity->controls[j], &kctl);
			if (ret)
				return ret;
		}
	}

	return 0;
}
EXPORT_SYMBOL_NS(sdca_asoc_populate_controls, "SND_SOC_SDCA");

static unsigned int rate_find_mask(unsigned int rate)
{
	switch (rate) {
	case 0:
		return SNDRV_PCM_RATE_8000_768000;
	case 5512:
		return SNDRV_PCM_RATE_5512;
	case 8000:
		return SNDRV_PCM_RATE_8000;
	case 11025:
		return SNDRV_PCM_RATE_11025;
	case 16000:
		return SNDRV_PCM_RATE_16000;
	case 22050:
		return SNDRV_PCM_RATE_22050;
	case 32000:
		return SNDRV_PCM_RATE_32000;
	case 44100:
		return SNDRV_PCM_RATE_44100;
	case 48000:
		return SNDRV_PCM_RATE_48000;
	case 64000:
		return SNDRV_PCM_RATE_64000;
	case 88200:
		return SNDRV_PCM_RATE_88200;
	case 96000:
		return SNDRV_PCM_RATE_96000;
	case 176400:
		return SNDRV_PCM_RATE_176400;
	case 192000:
		return SNDRV_PCM_RATE_192000;
	case 352800:
		return SNDRV_PCM_RATE_352800;
	case 384000:
		return SNDRV_PCM_RATE_384000;
	case 705600:
		return SNDRV_PCM_RATE_705600;
	case 768000:
		return SNDRV_PCM_RATE_768000;
	case 12000:
		return SNDRV_PCM_RATE_12000;
	case 24000:
		return SNDRV_PCM_RATE_24000;
	case 128000:
		return SNDRV_PCM_RATE_128000;
	default:
		return 0;
	}
}

static u64 width_find_mask(unsigned int bits)
{
	switch (bits) {
	case 0:
		return SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |
		       SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE |
		       SNDRV_PCM_FMTBIT_S32_LE;
	case 8:
		return SNDRV_PCM_FMTBIT_S8;
	case 16:
		return SNDRV_PCM_FMTBIT_S16_LE;
	case 20:
		return SNDRV_PCM_FMTBIT_S20_LE;
	case 24:
		return SNDRV_PCM_FMTBIT_S24_LE;
	case 32:
		return SNDRV_PCM_FMTBIT_S32_LE;
	default:
		return 0;
	}
}

static int populate_rate_format(struct device *dev,
				struct sdca_function_data *function,
				struct sdca_entity *entity,
				struct snd_soc_pcm_stream *stream)
{
	struct sdca_control_range *range;
	unsigned int sample_rate, sample_width;
	unsigned int clock_rates = 0;
	unsigned int rates = 0;
	u64 formats = 0;
	int sel, i;

	switch (entity->type) {
	case SDCA_ENTITY_TYPE_IT:
		sel = SDCA_CTL_IT_USAGE;
		break;
	case SDCA_ENTITY_TYPE_OT:
		sel = SDCA_CTL_OT_USAGE;
		break;
	default:
		dev_err(dev, "%s: entity type has no usage control\n",
			entity->label);
		return -EINVAL;
	}

	if (entity->iot.clock) {
		range = selector_find_range(dev, entity->iot.clock,
					    SDCA_CTL_CS_SAMPLERATEINDEX,
					    SDCA_SAMPLERATEINDEX_NCOLS, 0);
		if (!range)
			return -EINVAL;

		for (i = 0; i < range->rows; i++) {
			sample_rate = sdca_range(range, SDCA_SAMPLERATEINDEX_RATE, i);
			clock_rates |= rate_find_mask(sample_rate);
		}
	} else {
		clock_rates = UINT_MAX;
	}

	range = selector_find_range(dev, entity, sel, SDCA_USAGE_NCOLS, 0);
	if (!range)
		return -EINVAL;

	for (i = 0; i < range->rows; i++) {
		sample_rate = sdca_range(range, SDCA_USAGE_SAMPLE_RATE, i);
		sample_rate = rate_find_mask(sample_rate);

		if (sample_rate & clock_rates) {
			rates |= sample_rate;

			sample_width = sdca_range(range, SDCA_USAGE_SAMPLE_WIDTH, i);
			formats |= width_find_mask(sample_width);
		}
	}

	stream->formats = formats;
	stream->rates = rates;

	return 0;
}

/**
 * sdca_asoc_populate_dais - fill in an array of DAI drivers for a Function
 * @dev: Pointer to the device against which allocations will be done.
 * @function: Pointer to the Function information.
 * @dais: Array of DAI drivers to be populated.
 * @ops: DAI ops to be attached to each of the created DAI drivers.
 *
 * This function populates an array of ASoC DAI drivers from the DisCo
 * information for a particular SDCA Function. Typically,
 * snd_soc_asoc_count_component will be used to allocate an
 * appropriately sized array before calling this function.
 *
 * Return: Returns zero on success, and a negative error code on failure.
 */
int sdca_asoc_populate_dais(struct device *dev, struct sdca_function_data *function,
			    struct snd_soc_dai_driver *dais,
			    const struct snd_soc_dai_ops *ops)
{
	int i, j;
	int ret;

	for (i = 0, j = 0; i < function->num_entities - 1; i++) {
		struct sdca_entity *entity = &function->entities[i];
		struct snd_soc_pcm_stream *stream;
		const char *stream_suffix;

		switch (entity->type) {
		case SDCA_ENTITY_TYPE_IT:
			stream = &dais[j].playback;
			stream_suffix = "Playback";
			break;
		case SDCA_ENTITY_TYPE_OT:
			stream = &dais[j].capture;
			stream_suffix = "Capture";
			break;
		default:
			continue;
		}

		/* Can't check earlier as only terminals have an iot member. */
		if (!entity->iot.is_dataport)
			continue;

		stream->stream_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s",
						     entity->label, stream_suffix);
		if (!stream->stream_name)
			return -ENOMEM;
		/* Channels will be further limited by constraints */
		stream->channels_min = 1;
		stream->channels_max = SDCA_MAX_CHANNEL_COUNT;

		ret = populate_rate_format(dev, function, entity, stream);
		if (ret)
			return ret;

		dais[j].id = i;
		dais[j].name = entity->label;
		dais[j].ops = ops;
		j++;
	}

	return 0;
}
EXPORT_SYMBOL_NS(sdca_asoc_populate_dais, "SND_SOC_SDCA");

/**
 * sdca_asoc_populate_component - fill in a component driver for a Function
 * @dev: Pointer to the device against which allocations will be done.
 * @function: Pointer to the Function information.
 * @copmonent_drv: Pointer to the component driver to be populated.
 *
 * This function populates a snd_soc_component_driver structure based
 * on the DisCo information for a particular SDCA Function. It does
 * all allocation internally.
 *
 * Return: Returns zero on success, and a negative error code on failure.
 */
int sdca_asoc_populate_component(struct device *dev,
				 struct sdca_function_data *function,
				 struct snd_soc_component_driver *component_drv,
				 struct snd_soc_dai_driver **dai_drv, int *num_dai_drv,
				 const struct snd_soc_dai_ops *ops)
{
	struct snd_soc_dapm_widget *widgets;
	struct snd_soc_dapm_route *routes;
	struct snd_kcontrol_new *controls;
	struct snd_soc_dai_driver *dais;
	int num_widgets, num_routes, num_controls, num_dais;
	int ret;

	ret = sdca_asoc_count_component(dev, function, &num_widgets, &num_routes,
					&num_controls, &num_dais);
	if (ret)
		return ret;

	widgets = devm_kcalloc(dev, num_widgets, sizeof(*widgets), GFP_KERNEL);
	if (!widgets)
		return -ENOMEM;

	routes = devm_kcalloc(dev, num_routes, sizeof(*routes), GFP_KERNEL);
	if (!routes)
		return -ENOMEM;

	controls = devm_kcalloc(dev, num_controls, sizeof(*controls), GFP_KERNEL);
	if (!controls)
		return -ENOMEM;

	dais = devm_kcalloc(dev, num_dais, sizeof(*dais), GFP_KERNEL);
	if (!dais)
		return -ENOMEM;

	ret = sdca_asoc_populate_dapm(dev, function, widgets, routes);
	if (ret)
		return ret;

	ret = sdca_asoc_populate_controls(dev, function, controls);
	if (ret)
		return ret;

	ret = sdca_asoc_populate_dais(dev, function, dais, ops);
	if (ret)
		return ret;

	component_drv->dapm_widgets = widgets;
	component_drv->num_dapm_widgets = num_widgets;
	component_drv->dapm_routes = routes;
	component_drv->num_dapm_routes = num_routes;
	component_drv->controls = controls;
	component_drv->num_controls = num_controls;

	*dai_drv = dais;
	*num_dai_drv = num_dais;

	return 0;
}
EXPORT_SYMBOL_NS(sdca_asoc_populate_component, "SND_SOC_SDCA");