ASoC: SDCA: Add support for GE Entity properties
Add support for parsing the Group Entity properties from DisCo/ACPI. Group Entities allow control of several other Entities, typically Selector Units, from a single control. Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com> Link: https://patch.msgid.link/20250312172205.4152686-7-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
@@ -44,6 +44,11 @@ struct sdca_function_desc;
|
||||
*/
|
||||
#define SDCA_MAX_DELAY_COUNT 256
|
||||
|
||||
/*
|
||||
* Sanity check on size of affected controls data, can be expanded if needed.
|
||||
*/
|
||||
#define SDCA_MAX_AFFECTED_COUNT 2048
|
||||
|
||||
/**
|
||||
* enum sdca_function_type - SDCA Function Type codes
|
||||
* @SDCA_FUNCTION_TYPE_SMART_AMP: Amplifier with protection features.
|
||||
@@ -927,11 +932,51 @@ enum sdca_entity_type {
|
||||
SDCA_ENTITY_TYPE_HIDE = 0x31,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sdca_ge_control - control entry in the affected controls list
|
||||
* @id: Entity ID of the Control affected.
|
||||
* @sel: Control Selector of the Control affected.
|
||||
* @cn: Control Number of the Control affected.
|
||||
* @val: Value written to Control for this Mode.
|
||||
*/
|
||||
struct sdca_ge_control {
|
||||
int id;
|
||||
int sel;
|
||||
int cn;
|
||||
int val;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sdca_ge_mode - mode entry in the affected controls list
|
||||
* @controls: Dynamically allocated array of controls written for this Mode.
|
||||
* @num_controls: Number of controls written in this Mode.
|
||||
* @val: GE Selector Mode value.
|
||||
*/
|
||||
struct sdca_ge_mode {
|
||||
struct sdca_ge_control *controls;
|
||||
int num_controls;
|
||||
int val;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sdca_entity_ge - information specific to Group Entities
|
||||
* @kctl: ALSA control pointer that can be used by linked Entities.
|
||||
* @modes: Dynamically allocated array of Modes and the Controls written
|
||||
* in each mode.
|
||||
* @num_modes: Number of Modes.
|
||||
*/
|
||||
struct sdca_entity_ge {
|
||||
struct snd_kcontrol_new *kctl;
|
||||
struct sdca_ge_mode *modes;
|
||||
int num_modes;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sdca_entity - information for one SDCA Entity
|
||||
* @label: String such as "OT 12".
|
||||
* @id: Identifier used for addressing.
|
||||
* @type: Type code for the Entity.
|
||||
* @group: Pointer to Group Entity controlling this one, NULL if N/A.
|
||||
* @sources: Dynamically allocated array pointing to each input Entity
|
||||
* connected to this Entity.
|
||||
* @controls: Dynamically allocated array of Controls.
|
||||
@@ -940,12 +985,14 @@ enum sdca_entity_type {
|
||||
* @iot: Input/Output Terminal specific Entity properties.
|
||||
* @cs: Clock Source specific Entity properties.
|
||||
* @pde: Power Domain Entity specific Entity properties.
|
||||
* @ge: Group Entity specific Entity properties.
|
||||
*/
|
||||
struct sdca_entity {
|
||||
const char *label;
|
||||
int id;
|
||||
enum sdca_entity_type type;
|
||||
|
||||
struct sdca_entity *group;
|
||||
struct sdca_entity **sources;
|
||||
struct sdca_control *controls;
|
||||
int num_sources;
|
||||
@@ -954,6 +1001,7 @@ struct sdca_entity {
|
||||
struct sdca_entity_iot iot;
|
||||
struct sdca_entity_cs cs;
|
||||
struct sdca_entity_pde pde;
|
||||
struct sdca_entity_ge ge;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1136,6 +1136,92 @@ static int find_sdca_entity_pde(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct raw_ge_mode {
|
||||
u8 val;
|
||||
u8 num_controls;
|
||||
struct {
|
||||
u8 id;
|
||||
u8 sel;
|
||||
u8 cn;
|
||||
__le32 val;
|
||||
} __packed controls[] __counted_by(num_controls);
|
||||
} __packed;
|
||||
|
||||
static int find_sdca_entity_ge(struct device *dev,
|
||||
struct fwnode_handle *entity_node,
|
||||
struct sdca_entity *entity)
|
||||
{
|
||||
struct sdca_entity_ge *group = &entity->ge;
|
||||
u8 *affected_list __free(kfree) = NULL;
|
||||
u8 *affected_iter;
|
||||
int num_affected;
|
||||
int i, j;
|
||||
|
||||
num_affected = fwnode_property_count_u8(entity_node,
|
||||
"mipi-sdca-ge-selectedmode-controls-affected");
|
||||
if (!num_affected || num_affected == -EINVAL) {
|
||||
return 0;
|
||||
} else if (num_affected < 0) {
|
||||
dev_err(dev, "%s: failed to read affected controls: %d\n",
|
||||
entity->label, num_affected);
|
||||
return num_affected;
|
||||
} else if (num_affected > SDCA_MAX_AFFECTED_COUNT) {
|
||||
dev_err(dev, "%s: maximum affected controls size exceeded\n",
|
||||
entity->label);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
affected_list = kcalloc(num_affected, sizeof(*affected_list), GFP_KERNEL);
|
||||
if (!affected_list)
|
||||
return -ENOMEM;
|
||||
|
||||
fwnode_property_read_u8_array(entity_node,
|
||||
"mipi-sdca-ge-selectedmode-controls-affected",
|
||||
affected_list, num_affected);
|
||||
|
||||
group->num_modes = *affected_list;
|
||||
affected_iter = affected_list + 1;
|
||||
|
||||
group->modes = devm_kcalloc(dev, group->num_modes, sizeof(*group->modes),
|
||||
GFP_KERNEL);
|
||||
if (!group->modes)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < group->num_modes; i++) {
|
||||
struct raw_ge_mode *raw = (struct raw_ge_mode *)affected_iter;
|
||||
struct sdca_ge_mode *mode = &group->modes[i];
|
||||
|
||||
affected_iter += sizeof(*raw);
|
||||
if (affected_iter > affected_list + num_affected)
|
||||
goto bad_list;
|
||||
|
||||
mode->val = raw->val;
|
||||
mode->num_controls = raw->num_controls;
|
||||
|
||||
affected_iter += mode->num_controls * sizeof(raw->controls[0]);
|
||||
if (affected_iter > affected_list + num_affected)
|
||||
goto bad_list;
|
||||
|
||||
mode->controls = devm_kcalloc(dev, mode->num_controls,
|
||||
sizeof(*mode->controls), GFP_KERNEL);
|
||||
if (!mode->controls)
|
||||
return -ENOMEM;
|
||||
|
||||
for (j = 0; j < mode->num_controls; j++) {
|
||||
mode->controls[j].id = raw->controls[j].id;
|
||||
mode->controls[j].sel = raw->controls[j].sel;
|
||||
mode->controls[j].cn = raw->controls[j].cn;
|
||||
mode->controls[j].val = le32_to_cpu(raw->controls[j].val);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
bad_list:
|
||||
dev_err(dev, "%s: malformed affected controls list\n", entity->label);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int find_sdca_entity(struct device *dev,
|
||||
struct fwnode_handle *function_node,
|
||||
struct fwnode_handle *entity_node,
|
||||
@@ -1174,6 +1260,9 @@ static int find_sdca_entity(struct device *dev,
|
||||
case SDCA_ENTITY_TYPE_PDE:
|
||||
ret = find_sdca_entity_pde(dev, entity_node, entity);
|
||||
break;
|
||||
case SDCA_ENTITY_TYPE_GE:
|
||||
ret = find_sdca_entity_ge(dev, entity_node, entity);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1384,6 +1473,42 @@ static int find_sdca_entity_connection_pde(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_sdca_entity_connection_ge(struct device *dev,
|
||||
struct sdca_function_data *function,
|
||||
struct fwnode_handle *entity_node,
|
||||
struct sdca_entity *entity)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < entity->ge.num_modes; i++) {
|
||||
struct sdca_ge_mode *mode = &entity->ge.modes[i];
|
||||
|
||||
for (j = 0; j < mode->num_controls; j++) {
|
||||
struct sdca_ge_control *affected = &mode->controls[j];
|
||||
struct sdca_entity *managed;
|
||||
|
||||
managed = find_sdca_entity_by_id(function, affected->id);
|
||||
if (!managed) {
|
||||
dev_err(dev, "%s: failed to find entity with id %#x\n",
|
||||
entity->label, affected->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (managed->group && managed->group != entity) {
|
||||
dev_err(dev,
|
||||
"%s: entity controlled by two groups %s, %s\n",
|
||||
managed->label, managed->group->label,
|
||||
entity->label);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
managed->group = entity;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_sdca_entity_connection(struct device *dev,
|
||||
struct sdca_function_data *function,
|
||||
struct fwnode_handle *entity_node,
|
||||
@@ -1404,6 +1529,10 @@ static int find_sdca_entity_connection(struct device *dev,
|
||||
ret = find_sdca_entity_connection_pde(dev, function,
|
||||
entity_node, entity);
|
||||
break;
|
||||
case SDCA_ENTITY_TYPE_GE:
|
||||
ret = find_sdca_entity_connection_ge(dev, function,
|
||||
entity_node, entity);
|
||||
break;
|
||||
default:
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user