mirror of https://github.com/torvalds/linux.git
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:
parent
629dd55cf7
commit
996bf834d0
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
Loading…
Reference in New Issue