From 451e2c72862c15ed4d1508def9fc24f35bbdd350 Mon Sep 17 00:00:00 2001 From: Richard Oliver Date: Thu, 5 Jun 2025 13:08:33 +0100 Subject: [PATCH] media: i2c: imx500: GPIO acquire/release semantics When the imx500 driver is used as part of the 'AI Camera', the poweroff state is never reached as the camera and gpio driver share a regulator. By releasing the GPIOs when they are not in use, 'AI Camera' is able to achieve a powered-down state. Signed-off-by: Richard Oliver --- drivers/media/i2c/imx500.c | 62 ++++++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/drivers/media/i2c/imx500.c b/drivers/media/i2c/imx500.c index 28de78272697..3931214e9b0c 100644 --- a/drivers/media/i2c/imx500.c +++ b/drivers/media/i2c/imx500.c @@ -2360,9 +2360,11 @@ static int imx500_state_transition(struct imx500 *imx500, const u8 *fw, } /* Do SPI transfer */ - gpiod_set_value_cansleep(imx500->led_gpio, 1); + if (imx500->led_gpio) + gpiod_set_value_cansleep(imx500->led_gpio, 1); ret = imx500_spi_write(imx500, data, size); - gpiod_set_value_cansleep(imx500->led_gpio, 0); + if (imx500->led_gpio) + gpiod_set_value_cansleep(imx500->led_gpio, 0); imx500->fw_progress += size; @@ -2683,11 +2685,28 @@ static int imx500_power_on(struct device *dev) struct imx500 *imx500 = to_imx500(sd); int ret; + /* Acquire GPIOs first to ensure reset is asserted before power is applied */ + imx500->led_gpio = devm_gpiod_get_optional(dev, "led", GPIOD_OUT_LOW); + if (IS_ERR(imx500->led_gpio)) { + ret = PTR_ERR(imx500->led_gpio); + dev_err(&client->dev, "%s: failed to get led gpio\n", __func__); + return ret; + } + + imx500->reset_gpio = + devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(imx500->reset_gpio)) { + ret = PTR_ERR(imx500->reset_gpio); + dev_err(&client->dev, "%s: failed to get reset gpio\n", + __func__); + goto gpio_led_put; + } + ret = regulator_bulk_enable(IMX500_NUM_SUPPLIES, imx500->supplies); if (ret) { dev_err(&client->dev, "%s: failed to enable regulators\n", __func__); - return ret; + goto gpio_reset_put; } /* T4 - 1us @@ -2707,7 +2726,8 @@ static int imx500_power_on(struct device *dev) * as 0ms but also "XCLR pin should be set to 'High' after INCK supplied.". * T4 and T5 are shown as overlapping. */ - gpiod_set_value_cansleep(imx500->reset_gpio, 1); + if (imx500->reset_gpio) + gpiod_set_value_cansleep(imx500->reset_gpio, 1); /* T7 - 9ms * "INCK start and CXLR rising till Send Streaming Command wait time" @@ -2718,6 +2738,16 @@ static int imx500_power_on(struct device *dev) reg_off: regulator_bulk_disable(IMX500_NUM_SUPPLIES, imx500->supplies); +gpio_reset_put: + if (imx500->reset_gpio) { + devm_gpiod_put(dev, imx500->reset_gpio); + imx500->reset_gpio = NULL; + } +gpio_led_put: + if (imx500->led_gpio) { + devm_gpiod_put(dev, imx500->led_gpio); + imx500->led_gpio = NULL; + } return ret; } @@ -2734,7 +2764,19 @@ static int imx500_power_off(struct device *dev) * Note, this is not the reverse order of power up. */ clk_disable_unprepare(imx500->xclk); - gpiod_set_value_cansleep(imx500->reset_gpio, 0); + if (imx500->reset_gpio) + gpiod_set_value_cansleep(imx500->reset_gpio, 0); + + /* Release GPIOs before disabling regulators */ + if (imx500->reset_gpio) { + devm_gpiod_put(&client->dev, imx500->reset_gpio); + imx500->reset_gpio = NULL; + } + if (imx500->led_gpio) { + devm_gpiod_put(&client->dev, imx500->led_gpio); + imx500->led_gpio = NULL; + } + regulator_bulk_disable(IMX500_NUM_SUPPLIES, imx500->supplies); /* Force reprogramming of the common registers when powered up again. */ @@ -3052,14 +3094,14 @@ static int imx500_probe(struct i2c_client *client) return ret; } - imx500->led_gpio = devm_gpiod_get_optional(dev, "led", GPIOD_OUT_LOW); - - imx500->reset_gpio = - devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + /* GPIOs are acquired in imx500_power_on() to avoid preventing + * regulator power down when shared with other drivers. + */ /* * The sensor must be powered for imx500_identify_module() - * to be able to read the CHIP_ID register + * to be able to read the CHIP_ID register. This also ensures + * GPIOs are available. */ ret = imx500_power_on(dev); if (ret)