ASoC: SDCA: Add code to parse Function information

Add a helper function to parse all the Function and Entity
information from ACPI. In SDCA each device may have several Functions
and each corresponds to a specific audio capability such as say
amplifier playback or microphone capture. Each Function then contains
a number of Entities that represent individual parts of the audio
signal chain and are linked together in a graph similar to DAPM.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
Link: https://patch.msgid.link/20250205113801.3699902-3-ckeepax@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Pierre-Louis Bossart 2025-02-05 11:37:53 +00:00 committed by Mark Brown
parent 629dd55cf7
commit 996bf834d0
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
3 changed files with 365 additions and 0 deletions

View File

@ -18,11 +18,13 @@ struct sdw_slave;
/**
* struct sdca_function_desc - short descriptor for an SDCA Function
* @node: firmware node for the Function.
* @name: Human-readable string.
* @type: Function topology type.
* @adr: ACPI address (used for SDCA register access).
*/
struct sdca_function_desc {
struct fwnode_handle *node;
const char *name;
u32 type;
u8 adr;

View File

@ -11,6 +11,15 @@
#include <linux/bits.h>
struct device;
struct sdca_function_desc;
/*
* The addressing space for SDCA relies on 7 bits for Entities, so a
* maximum of 128 Entities per function can be represented.
*/
#define SDCA_MAX_ENTITY_COUNT 128
/**
* enum sdca_function_type - SDCA Function Type codes
* @SDCA_FUNCTION_TYPE_SMART_AMP: Amplifier with protection features.
@ -90,4 +99,90 @@ enum sdca_entity0_controls {
SDCA_CTL_ENTITY_0_FUNCTION_BUSY = BIT(7),
};
/**
* enum sdca_entity_type - SDCA Entity Type codes
* @SDCA_ENTITY_TYPE_IT: Input Terminal.
* @SDCA_ENTITY_TYPE_OT: Output Terminal.
* @SDCA_ENTITY_TYPE_MU: Mixer Unit.
* @SDCA_ENTITY_TYPE_SU: Selector Unit.
* @SDCA_ENTITY_TYPE_FU: Feature Unit.
* @SDCA_ENTITY_TYPE_XU: Extension Unit.
* @SDCA_ENTITY_TYPE_CS: Clock Source.
* @SDCA_ENTITY_TYPE_CX: Clock selector.
* @SDCA_ENTITY_TYPE_PDE: Power-Domain Entity.
* @SDCA_ENTITY_TYPE_GE: Group Entity.
* @SDCA_ENTITY_TYPE_SPE: Security & Privacy Entity.
* @SDCA_ENTITY_TYPE_CRU: Channel Remapping Unit.
* @SDCA_ENTITY_TYPE_UDMPU: Up-Down Mixer Processing Unit.
* @SDCA_ENTITY_TYPE_MFPU: Multi-Function Processing Unit.
* @SDCA_ENTITY_TYPE_SMPU: Smart Microphone Processing Unit.
* @SDCA_ENTITY_TYPE_SAPU: Smart Amp Processing Unit.
* @SDCA_ENTITY_TYPE_PPU: Posture Processing Unit.
* @SDCA_ENTITY_TYPE_TG: Tone Generator.
* @SDCA_ENTITY_TYPE_HIDE: Human Interface Device Entity.
*
* SDCA Entity Types from SDCA specification v1.0 Section 6.1.2
* all Entity Types not described are reserved.
*/
enum sdca_entity_type {
SDCA_ENTITY_TYPE_IT = 0x02,
SDCA_ENTITY_TYPE_OT = 0x03,
SDCA_ENTITY_TYPE_MU = 0x05,
SDCA_ENTITY_TYPE_SU = 0x06,
SDCA_ENTITY_TYPE_FU = 0x07,
SDCA_ENTITY_TYPE_XU = 0x0A,
SDCA_ENTITY_TYPE_CS = 0x0B,
SDCA_ENTITY_TYPE_CX = 0x0C,
SDCA_ENTITY_TYPE_PDE = 0x11,
SDCA_ENTITY_TYPE_GE = 0x12,
SDCA_ENTITY_TYPE_SPE = 0x13,
SDCA_ENTITY_TYPE_CRU = 0x20,
SDCA_ENTITY_TYPE_UDMPU = 0x21,
SDCA_ENTITY_TYPE_MFPU = 0x22,
SDCA_ENTITY_TYPE_SMPU = 0x23,
SDCA_ENTITY_TYPE_SAPU = 0x24,
SDCA_ENTITY_TYPE_PPU = 0x25,
SDCA_ENTITY_TYPE_TG = 0x30,
SDCA_ENTITY_TYPE_HIDE = 0x31,
};
/**
* 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.
* @sources: Dynamically allocated array pointing to each input Entity
* connected to this Entity.
* @num_sources: Number of sources for the Entity.
*/
struct sdca_entity {
const char *label;
int id;
enum sdca_entity_type type;
struct sdca_entity **sources;
int num_sources;
};
/**
* struct sdca_function_data - top-level information for one SDCA function
* @desc: Pointer to short descriptor from initial parsing.
* @entities: Dynamically allocated array of Entities.
* @num_entities: Number of Entities reported in this Function.
* @busy_max_delay: Maximum Function busy delay in microseconds, before an
* error should be reported.
*/
struct sdca_function_data {
struct sdca_function_desc *desc;
struct sdca_entity *entities;
int num_entities;
unsigned int busy_max_delay;
};
int sdca_parse_function(struct device *dev,
struct sdca_function_desc *desc,
struct sdca_function_data *function);
#endif

View File

@ -18,6 +18,11 @@
#include <sound/sdca.h>
#include <sound/sdca_function.h>
/*
* Should be long enough to encompass all the MIPI DisCo properties.
*/
#define SDCA_PROPERTY_LENGTH 64
static int patch_sdca_function_type(u32 interface_revision, u32 *function_type)
{
/*
@ -150,6 +155,7 @@ static int find_sdca_function(struct acpi_device *adev, void *data)
sdca_data->function[function_index].adr = addr;
sdca_data->function[function_index].type = function_type;
sdca_data->function[function_index].name = function_name;
sdca_data->function[function_index].node = function_node;
sdca_data->num_functions++;
return 0;
@ -179,5 +185,267 @@ void sdca_lookup_functions(struct sdw_slave *slave)
}
EXPORT_SYMBOL_NS(sdca_lookup_functions, "SND_SOC_SDCA");
static int find_sdca_entity(struct device *dev,
struct fwnode_handle *function_node,
struct fwnode_handle *entity_node,
struct sdca_entity *entity)
{
u32 tmp;
int ret;
ret = fwnode_property_read_string(entity_node, "mipi-sdca-entity-label",
&entity->label);
if (ret) {
dev_err(dev, "%pfwP: entity %#x: label missing: %d\n",
function_node, entity->id, ret);
return ret;
}
ret = fwnode_property_read_u32(entity_node, "mipi-sdca-entity-type", &tmp);
if (ret) {
dev_err(dev, "%s: type missing: %d\n", entity->label, ret);
return ret;
}
entity->type = tmp;
dev_info(dev, "%s: entity %#x type %#x\n",
entity->label, entity->id, entity->type);
return 0;
}
static int find_sdca_entities(struct device *dev,
struct fwnode_handle *function_node,
struct sdca_function_data *function)
{
struct sdca_entity *entities;
u32 *entity_list;
int num_entities;
int i, ret;
num_entities = fwnode_property_count_u32(function_node,
"mipi-sdca-entity-id-list");
if (num_entities <= 0) {
dev_err(dev, "%pfwP: entity id list missing: %d\n",
function_node, num_entities);
return -EINVAL;
} else if (num_entities > SDCA_MAX_ENTITY_COUNT) {
dev_err(dev, "%pfwP: maximum number of entities exceeded\n",
function_node);
return -EINVAL;
}
entities = devm_kcalloc(dev, num_entities, sizeof(*entities), GFP_KERNEL);
if (!entities)
return -ENOMEM;
entity_list = kcalloc(num_entities, sizeof(*entity_list), GFP_KERNEL);
if (!entity_list)
return -ENOMEM;
fwnode_property_read_u32_array(function_node, "mipi-sdca-entity-id-list",
entity_list, num_entities);
for (i = 0; i < num_entities; i++)
entities[i].id = entity_list[i];
kfree(entity_list);
/* now read subproperties */
for (i = 0; i < num_entities; i++) {
char entity_property[SDCA_PROPERTY_LENGTH];
struct fwnode_handle *entity_node;
/* DisCo uses upper-case for hex numbers */
snprintf(entity_property, sizeof(entity_property),
"mipi-sdca-entity-id-0x%X-subproperties", entities[i].id);
entity_node = fwnode_get_named_child_node(function_node, entity_property);
if (!entity_node) {
dev_err(dev, "%pfwP: entity node %s not found\n",
function_node, entity_property);
return -EINVAL;
}
ret = find_sdca_entity(dev, function_node, entity_node, &entities[i]);
fwnode_handle_put(entity_node);
if (ret)
return ret;
}
function->num_entities = num_entities;
function->entities = entities;
return 0;
}
static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data *function,
const char *entity_label)
{
int i;
for (i = 0; i < function->num_entities; i++) {
struct sdca_entity *entity = &function->entities[i];
if (!strcmp(entity->label, entity_label))
return entity;
}
return NULL;
}
static int find_sdca_entity_connection(struct device *dev,
struct sdca_function_data *function,
struct fwnode_handle *entity_node,
struct sdca_entity *entity)
{
struct sdca_entity **pins;
int num_pins, pin;
u64 pin_list;
int i, ret;
ret = fwnode_property_read_u64(entity_node, "mipi-sdca-input-pin-list", &pin_list);
if (ret == -EINVAL) {
/* Allow missing pin lists, assume no pins. */
dev_warn(dev, "%s: missing pin list\n", entity->label);
return 0;
} else if (ret) {
dev_err(dev, "%s: failed to read pin list: %d\n", entity->label, ret);
return ret;
} else if (pin_list & BIT(0)) {
/*
* Each bit set in the pin-list refers to an entity_id in this
* Function. Entity 0 is an illegal connection since it is used
* for Function-level configurations.
*/
dev_err(dev, "%s: pin 0 used as input\n", entity->label);
return -EINVAL;
} else if (!pin_list) {
return 0;
}
num_pins = hweight64(pin_list);
pins = devm_kcalloc(dev, num_pins, sizeof(*pins), GFP_KERNEL);
if (!pins)
return -ENOMEM;
i = 0;
for_each_set_bit(pin, (unsigned long *)&pin_list, BITS_PER_TYPE(pin_list)) {
char pin_property[SDCA_PROPERTY_LENGTH];
struct fwnode_handle *connected_node;
struct sdca_entity *connected_entity;
const char *connected_label;
snprintf(pin_property, sizeof(pin_property), "mipi-sdca-input-pin-%d", pin);
connected_node = fwnode_get_named_child_node(entity_node, pin_property);
if (!connected_node) {
dev_err(dev, "%s: pin node %s not found\n",
entity->label, pin_property);
return -EINVAL;
}
ret = fwnode_property_read_string(connected_node, "mipi-sdca-entity-label",
&connected_label);
if (ret) {
dev_err(dev, "%s: pin %d label missing: %d\n",
entity->label, pin, ret);
fwnode_handle_put(connected_node);
return ret;
}
connected_entity = find_sdca_entity_by_label(function, connected_label);
if (!connected_entity) {
dev_err(dev, "%s: failed to find entity with label %s\n",
entity->label, connected_label);
fwnode_handle_put(connected_node);
return -EINVAL;
}
pins[i] = connected_entity;
dev_info(dev, "%s -> %s\n", connected_entity->label, entity->label);
i++;
fwnode_handle_put(connected_node);
}
entity->num_sources = num_pins;
entity->sources = pins;
return 0;
}
static int find_sdca_connections(struct device *dev,
struct fwnode_handle *function_node,
struct sdca_function_data *function)
{
int i;
for (i = 0; i < function->num_entities; i++) {
struct sdca_entity *entity = &function->entities[i];
char entity_property[SDCA_PROPERTY_LENGTH];
struct fwnode_handle *entity_node;
int ret;
/* DisCo uses upper-case for hex numbers */
snprintf(entity_property, sizeof(entity_property),
"mipi-sdca-entity-id-0x%X-subproperties",
entity->id);
entity_node = fwnode_get_named_child_node(function_node, entity_property);
if (!entity_node) {
dev_err(dev, "%pfwP: entity node %s not found\n",
function_node, entity_property);
return -EINVAL;
}
ret = find_sdca_entity_connection(dev, function, entity_node, entity);
fwnode_handle_put(entity_node);
if (ret)
return ret;
}
return 0;
}
/**
* sdca_parse_function - parse ACPI DisCo for a Function
* @dev: Pointer to device against which function data will be allocated.
* @function_desc: Pointer to the Function short descriptor.
* @function: Pointer to the Function information, to be populated.
*
* Return: Returns 0 for success.
*/
int sdca_parse_function(struct device *dev,
struct sdca_function_desc *function_desc,
struct sdca_function_data *function)
{
u32 tmp;
int ret;
function->desc = function_desc;
ret = fwnode_property_read_u32(function_desc->node,
"mipi-sdca-function-busy-max-delay", &tmp);
if (!ret)
function->busy_max_delay = tmp;
dev_info(dev, "%pfwP: name %s delay %dus\n", function->desc->node,
function->desc->name, function->busy_max_delay);
ret = find_sdca_entities(dev, function_desc->node, function);
if (ret)
return ret;
ret = find_sdca_connections(dev, function_desc->node, function);
if (ret)
return ret;
return 0;
}
EXPORT_SYMBOL_NS(sdca_parse_function, "SND_SOC_SDCA");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("SDCA library");