diff options
Diffstat (limited to 'drivers/spi/spi-cs42l43.c')
| -rw-r--r-- | drivers/spi/spi-cs42l43.c | 127 | 
1 files changed, 119 insertions, 8 deletions
diff --git a/drivers/spi/spi-cs42l43.c b/drivers/spi/spi-cs42l43.c index aabef9fc84bd..9d747ea69926 100644 --- a/drivers/spi/spi-cs42l43.c +++ b/drivers/spi/spi-cs42l43.c @@ -5,10 +5,14 @@  // Copyright (C) 2022-2023 Cirrus Logic, Inc. and  //                         Cirrus Logic International Semiconductor Ltd. +#include <linux/acpi.h> +#include <linux/array_size.h>  #include <linux/bits.h>  #include <linux/bitfield.h>  #include <linux/device.h>  #include <linux/errno.h> +#include <linux/gpio/machine.h> +#include <linux/gpio/property.h>  #include <linux/mfd/cs42l43.h>  #include <linux/mfd/cs42l43-regs.h>  #include <linux/mod_devicetable.h> @@ -16,6 +20,7 @@  #include <linux/of.h>  #include <linux/platform_device.h>  #include <linux/pm_runtime.h> +#include <linux/property.h>  #include <linux/regmap.h>  #include <linux/spi/spi.h>  #include <linux/units.h> @@ -39,6 +44,44 @@ static const unsigned int cs42l43_clock_divs[] = {  	2, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30  }; +static const struct software_node ampl = { +	.name			= "cs35l56-left", +}; + +static const struct software_node ampr = { +	.name			= "cs35l56-right", +}; + +static struct spi_board_info ampl_info = { +	.modalias		= "cs35l56", +	.max_speed_hz		= 20 * HZ_PER_MHZ, +	.chip_select		= 0, +	.mode			= SPI_MODE_0, +	.swnode			= &l, +}; + +static struct spi_board_info ampr_info = { +	.modalias		= "cs35l56", +	.max_speed_hz		= 20 * HZ_PER_MHZ, +	.chip_select		= 1, +	.mode			= SPI_MODE_0, +	.swnode			= &r, +}; + +static const struct software_node cs42l43_gpiochip_swnode = { +	.name			= "cs42l43-pinctrl", +}; + +static const struct software_node_ref_args cs42l43_cs_refs[] = { +	SOFTWARE_NODE_REFERENCE(&cs42l43_gpiochip_swnode, 0, GPIO_ACTIVE_LOW), +	SOFTWARE_NODE_REFERENCE(&swnode_gpio_undefined), +}; + +static const struct property_entry cs42l43_cs_props[] = { +	PROPERTY_ENTRY_REF_ARRAY("cs-gpios", cs42l43_cs_refs), +	{} +}; +  static int cs42l43_spi_tx(struct regmap *regmap, const u8 *buf, unsigned int len)  {  	const u8 *end = buf + len; @@ -203,16 +246,59 @@ static size_t cs42l43_spi_max_length(struct spi_device *spi)  	return CS42L43_SPI_MAX_LENGTH;  } +static bool cs42l43_has_sidecar(struct fwnode_handle *fwnode) +{ +	static const u32 func_smart_amp = 0x1; +	struct fwnode_handle *child_fwnode, *ext_fwnode; +	unsigned int val; +	u32 function; +	int ret; + +	fwnode_for_each_child_node(fwnode, child_fwnode) { +		acpi_handle handle = ACPI_HANDLE_FWNODE(child_fwnode); + +		ret = acpi_get_local_address(handle, &function); +		if (ret || function != func_smart_amp) +			continue; + +		ext_fwnode = fwnode_get_named_child_node(child_fwnode, +				"mipi-sdca-function-expansion-subproperties"); +		if (!ext_fwnode) +			continue; + +		ret = fwnode_property_read_u32(ext_fwnode, +					       "01fa-sidecar-instances", +					       &val); + +		fwnode_handle_put(ext_fwnode); + +		if (ret) +			continue; + +		fwnode_handle_put(child_fwnode); + +		return !!val; +	} + +	return false; +} +  static void cs42l43_release_of_node(void *data)  {  	fwnode_handle_put(data);  } +static void cs42l43_release_sw_node(void *data) +{ +	software_node_unregister(&cs42l43_gpiochip_swnode); +} +  static int cs42l43_spi_probe(struct platform_device *pdev)  {  	struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent);  	struct cs42l43_spi *priv;  	struct fwnode_handle *fwnode = dev_fwnode(cs42l43->dev); +	bool has_sidecar = cs42l43_has_sidecar(fwnode);  	int ret;  	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); @@ -259,21 +345,45 @@ static int cs42l43_spi_probe(struct platform_device *pdev)  	if (is_of_node(fwnode)) {  		fwnode = fwnode_get_named_child_node(fwnode, "spi"); -		ret = devm_add_action(priv->dev, cs42l43_release_of_node, fwnode); -		if (ret) { -			fwnode_handle_put(fwnode); +		ret = devm_add_action_or_reset(priv->dev, cs42l43_release_of_node, fwnode); +		if (ret)  			return ret; -		}  	} -	device_set_node(&priv->ctlr->dev, fwnode); +	if (has_sidecar) { +		ret = software_node_register(&cs42l43_gpiochip_swnode); +		if (ret) +			return dev_err_probe(priv->dev, ret, +					     "Failed to register gpio swnode\n"); + +		ret = devm_add_action_or_reset(priv->dev, cs42l43_release_sw_node, NULL); +		if (ret) +			return ret; + +		ret = device_create_managed_software_node(&priv->ctlr->dev, +							  cs42l43_cs_props, NULL); +		if (ret) +			return dev_err_probe(priv->dev, ret, "Failed to add swnode\n"); +	} else { +		device_set_node(&priv->ctlr->dev, fwnode); +	}  	ret = devm_spi_register_controller(priv->dev, priv->ctlr); -	if (ret) { -		dev_err(priv->dev, "Failed to register SPI controller: %d\n", ret); +	if (ret) +		return dev_err_probe(priv->dev, ret, +				     "Failed to register SPI controller\n"); + +	if (has_sidecar) { +		if (!spi_new_device(priv->ctlr, &l_info)) +			return dev_err_probe(priv->dev, -ENODEV, +					     "Failed to create left amp slave\n"); + +		if (!spi_new_device(priv->ctlr, &r_info)) +			return dev_err_probe(priv->dev, -ENODEV, +					     "Failed to create right amp slave\n");  	} -	return ret; +	return 0;  }  static const struct platform_device_id cs42l43_spi_id_table[] = { @@ -291,6 +401,7 @@ static struct platform_driver cs42l43_spi_driver = {  };  module_platform_driver(cs42l43_spi_driver); +MODULE_IMPORT_NS(GPIO_SWNODE);  MODULE_DESCRIPTION("CS42L43 SPI Driver");  MODULE_AUTHOR("Lucas Tanure <[email protected]>");  MODULE_AUTHOR("Maciej Strozek <[email protected]>");  |