]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ASoC: SDCA: Add code to parse Function information
authorPierre-Louis Bossart <pierre-louis.bossart@linux.dev>
Wed, 5 Feb 2025 11:37:53 +0000 (11:37 +0000)
committerMark Brown <broonie@kernel.org>
Fri, 7 Feb 2025 17:34:01 +0000 (17:34 +0000)
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>
include/sound/sdca.h
include/sound/sdca_function.h
sound/soc/sdca/sdca_functions.c

index 31b39f2297b9c36995195af2c8c6b109c2acf882..5a5d6de78d72834915f8f04352055a823f4558a6 100644 (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;
index 1d1d3a1da52ddb321bd61775f017b32836ac3c40..3f4031d285d631a386ef3af8029226c2cd72b89f 100644 (file)
 
 #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
index 00d6d044c816bbf9e6d6e363e28861afec4a5bf2..72b82280918d7ce9b89f6fa993f51a14474c49dc 100644 (file)
 #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");