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 <richard.oliver@raspberrypi.com>
This commit is contained in:
Richard Oliver
2025-06-05 13:08:33 +01:00
parent 893915d5ce
commit 451e2c7286

View File

@@ -2360,9 +2360,11 @@ static int imx500_state_transition(struct imx500 *imx500, const u8 *fw,
} }
/* Do SPI transfer */ /* 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); 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; imx500->fw_progress += size;
@@ -2683,11 +2685,28 @@ static int imx500_power_on(struct device *dev)
struct imx500 *imx500 = to_imx500(sd); struct imx500 *imx500 = to_imx500(sd);
int ret; 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); ret = regulator_bulk_enable(IMX500_NUM_SUPPLIES, imx500->supplies);
if (ret) { if (ret) {
dev_err(&client->dev, "%s: failed to enable regulators\n", dev_err(&client->dev, "%s: failed to enable regulators\n",
__func__); __func__);
return ret; goto gpio_reset_put;
} }
/* T4 - 1us /* 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.". * as 0ms but also "XCLR pin should be set to 'High' after INCK supplied.".
* T4 and T5 are shown as overlapping. * 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 /* T7 - 9ms
* "INCK start and CXLR rising till Send Streaming Command wait time" * "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: reg_off:
regulator_bulk_disable(IMX500_NUM_SUPPLIES, imx500->supplies); 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; return ret;
} }
@@ -2734,7 +2764,19 @@ static int imx500_power_off(struct device *dev)
* Note, this is not the reverse order of power up. * Note, this is not the reverse order of power up.
*/ */
clk_disable_unprepare(imx500->xclk); 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); regulator_bulk_disable(IMX500_NUM_SUPPLIES, imx500->supplies);
/* Force reprogramming of the common registers when powered up again. */ /* Force reprogramming of the common registers when powered up again. */
@@ -3052,14 +3094,14 @@ static int imx500_probe(struct i2c_client *client)
return ret; return ret;
} }
imx500->led_gpio = devm_gpiod_get_optional(dev, "led", GPIOD_OUT_LOW); /* GPIOs are acquired in imx500_power_on() to avoid preventing
* regulator power down when shared with other drivers.
imx500->reset_gpio = */
devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
/* /*
* The sensor must be powered for imx500_identify_module() * 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); ret = imx500_power_on(dev);
if (ret) if (ret)