mirror of
https://github.com/torvalds/linux.git
synced 2026-05-29 17:43:52 +02:00
ASoC: SDCA: Add support for PDE Entity properties
Add support for parsing the Power Domain Entity properties from DisCo/ACPI. 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-11-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
e80b8e5c53
commit
9da195880f
|
|
@ -39,6 +39,11 @@ struct sdca_function_desc;
|
|||
*/
|
||||
#define SDCA_MAX_CHANNEL_COUNT 32
|
||||
|
||||
/*
|
||||
* Sanity check on number of PDE delays, can be expanded if needed.
|
||||
*/
|
||||
#define SDCA_MAX_DELAY_COUNT 256
|
||||
|
||||
/**
|
||||
* enum sdca_function_type - SDCA Function Type codes
|
||||
* @SDCA_FUNCTION_TYPE_SMART_AMP: Amplifier with protection features.
|
||||
|
|
@ -807,6 +812,47 @@ struct sdca_entity_cs {
|
|||
unsigned int max_delay;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum sdca_pde_power_state - SDCA Power States
|
||||
*
|
||||
* SDCA Power State values from SDCA specification v1.0 Section 7.12.4.
|
||||
*/
|
||||
enum sdca_pde_power_state {
|
||||
SDCA_PDE_PS0 = 0x0,
|
||||
SDCA_PDE_PS1 = 0x1,
|
||||
SDCA_PDE_PS2 = 0x2,
|
||||
SDCA_PDE_PS3 = 0x3,
|
||||
SDCA_PDE_PS4 = 0x4,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sdca_pde_delay - describes the delay changing between 2 power states
|
||||
* @from_ps: The power state being exited.
|
||||
* @to_ps: The power state being entered.
|
||||
* @us: The delay in microseconds switching between the two states.
|
||||
*/
|
||||
struct sdca_pde_delay {
|
||||
int from_ps;
|
||||
int to_ps;
|
||||
unsigned int us;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sdca_entity_pde - information specific to Power Domain Entities
|
||||
* @managed: Dynamically allocated array pointing to each Entity
|
||||
* controlled by this PDE.
|
||||
* @max_delay: Dynamically allocated array of delays for switching
|
||||
* between power states.
|
||||
* @num_managed: Number of Entities controlled by this PDE.
|
||||
* @num_max_delay: Number of delays specified for state changes.
|
||||
*/
|
||||
struct sdca_entity_pde {
|
||||
struct sdca_entity **managed;
|
||||
struct sdca_pde_delay *max_delay;
|
||||
int num_managed;
|
||||
int num_max_delay;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum sdca_entity_type - SDCA Entity Type codes
|
||||
* @SDCA_ENTITY_TYPE_ENTITY_0: Entity 0, not actually from the
|
||||
|
|
@ -870,6 +916,7 @@ enum sdca_entity_type {
|
|||
* @num_controls: Number of Controls for the Entity.
|
||||
* @iot: Input/Output Terminal specific Entity properties.
|
||||
* @cs: Clock Source specific Entity properties.
|
||||
* @pde: Power Domain Entity specific Entity properties.
|
||||
*/
|
||||
struct sdca_entity {
|
||||
const char *label;
|
||||
|
|
@ -883,6 +930,7 @@ struct sdca_entity {
|
|||
union {
|
||||
struct sdca_entity_iot iot;
|
||||
struct sdca_entity_cs cs;
|
||||
struct sdca_entity_pde pde;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -908,6 +908,66 @@ static int find_sdca_entity_cs(struct device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int find_sdca_entity_pde(struct device *dev,
|
||||
struct fwnode_handle *entity_node,
|
||||
struct sdca_entity *entity)
|
||||
{
|
||||
static const int mult_delay = 3;
|
||||
struct sdca_entity_pde *power = &entity->pde;
|
||||
struct sdca_pde_delay *delays;
|
||||
int num_delays;
|
||||
u32 *delay_list;
|
||||
int i, j;
|
||||
|
||||
num_delays = fwnode_property_count_u32(entity_node,
|
||||
"mipi-sdca-powerdomain-transition-max-delay");
|
||||
if (num_delays <= 0) {
|
||||
dev_err(dev, "%s: max delay list missing: %d\n",
|
||||
entity->label, num_delays);
|
||||
return -EINVAL;
|
||||
} else if (num_delays % mult_delay != 0) {
|
||||
dev_err(dev, "%s: delays not multiple of %d\n",
|
||||
entity->label, mult_delay);
|
||||
return -EINVAL;
|
||||
} else if (num_delays > SDCA_MAX_DELAY_COUNT) {
|
||||
dev_err(dev, "%s: maximum number of transition delays exceeded\n",
|
||||
entity->label);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* There are 3 values per delay */
|
||||
delays = devm_kcalloc(dev, num_delays / mult_delay,
|
||||
sizeof(*delays), GFP_KERNEL);
|
||||
if (!delays)
|
||||
return -ENOMEM;
|
||||
|
||||
delay_list = kcalloc(num_delays, sizeof(*delay_list), GFP_KERNEL);
|
||||
if (!delay_list)
|
||||
return -ENOMEM;
|
||||
|
||||
fwnode_property_read_u32_array(entity_node,
|
||||
"mipi-sdca-powerdomain-transition-max-delay",
|
||||
delay_list, num_delays);
|
||||
|
||||
num_delays /= mult_delay;
|
||||
|
||||
for (i = 0, j = 0; i < num_delays; i++) {
|
||||
delays[i].from_ps = delay_list[j++];
|
||||
delays[i].to_ps = delay_list[j++];
|
||||
delays[i].us = delay_list[j++];
|
||||
|
||||
dev_info(dev, "%s: from %#x to %#x delay %dus\n", entity->label,
|
||||
delays[i].from_ps, delays[i].to_ps, delays[i].us);
|
||||
}
|
||||
|
||||
power->num_max_delay = num_delays;
|
||||
power->max_delay = delays;
|
||||
|
||||
kfree(delay_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_sdca_entity(struct device *dev,
|
||||
struct fwnode_handle *function_node,
|
||||
struct fwnode_handle *entity_node,
|
||||
|
|
@ -943,6 +1003,9 @@ static int find_sdca_entity(struct device *dev,
|
|||
case SDCA_ENTITY_TYPE_CS:
|
||||
ret = find_sdca_entity_cs(dev, entity_node, entity);
|
||||
break;
|
||||
case SDCA_ENTITY_TYPE_PDE:
|
||||
ret = find_sdca_entity_pde(dev, entity_node, entity);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -1047,6 +1110,21 @@ static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data *
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static struct sdca_entity *find_sdca_entity_by_id(struct sdca_function_data *function,
|
||||
const int id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < function->num_entities; i++) {
|
||||
struct sdca_entity *entity = &function->entities[i];
|
||||
|
||||
if (entity->id == id)
|
||||
return entity;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int find_sdca_entity_connection_iot(struct device *dev,
|
||||
struct sdca_function_data *function,
|
||||
struct fwnode_handle *entity_node,
|
||||
|
|
@ -1087,6 +1165,62 @@ static int find_sdca_entity_connection_iot(struct device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int find_sdca_entity_connection_pde(struct device *dev,
|
||||
struct sdca_function_data *function,
|
||||
struct fwnode_handle *entity_node,
|
||||
struct sdca_entity *entity)
|
||||
{
|
||||
struct sdca_entity_pde *power = &entity->pde;
|
||||
struct sdca_entity **managed;
|
||||
u32 *managed_list;
|
||||
int num_managed;
|
||||
int i;
|
||||
|
||||
num_managed = fwnode_property_count_u32(entity_node,
|
||||
"mipi-sdca-powerdomain-managed-list");
|
||||
if (!num_managed) {
|
||||
return 0;
|
||||
} else if (num_managed < 0) {
|
||||
dev_err(dev, "%s: managed list missing: %d\n", entity->label, num_managed);
|
||||
return num_managed;
|
||||
} else if (num_managed > SDCA_MAX_ENTITY_COUNT) {
|
||||
dev_err(dev, "%s: maximum number of managed entities exceeded\n",
|
||||
entity->label);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
managed = devm_kcalloc(dev, num_managed, sizeof(*managed), GFP_KERNEL);
|
||||
if (!managed)
|
||||
return -ENOMEM;
|
||||
|
||||
managed_list = kcalloc(num_managed, sizeof(*managed_list), GFP_KERNEL);
|
||||
if (!managed_list)
|
||||
return -ENOMEM;
|
||||
|
||||
fwnode_property_read_u32_array(entity_node,
|
||||
"mipi-sdca-powerdomain-managed-list",
|
||||
managed_list, num_managed);
|
||||
|
||||
for (i = 0; i < num_managed; i++) {
|
||||
managed[i] = find_sdca_entity_by_id(function, managed_list[i]);
|
||||
if (!managed[i]) {
|
||||
dev_err(dev, "%s: failed to find entity with id %#x\n",
|
||||
entity->label, managed_list[i]);
|
||||
kfree(managed_list);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_info(dev, "%s -> %s\n", managed[i]->label, entity->label);
|
||||
}
|
||||
|
||||
kfree(managed_list);
|
||||
|
||||
power->num_managed = num_managed;
|
||||
power->managed = managed;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_sdca_entity_connection(struct device *dev,
|
||||
struct sdca_function_data *function,
|
||||
struct fwnode_handle *entity_node,
|
||||
|
|
@ -1103,6 +1237,10 @@ static int find_sdca_entity_connection(struct device *dev,
|
|||
ret = find_sdca_entity_connection_iot(dev, function,
|
||||
entity_node, entity);
|
||||
break;
|
||||
case SDCA_ENTITY_TYPE_PDE:
|
||||
ret = find_sdca_entity_connection_pde(dev, function,
|
||||
entity_node, entity);
|
||||
break;
|
||||
default:
|
||||
ret = 0;
|
||||
break;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user