media: atomisp_gmin_platform: Add enable-count to gmin_[v1p8|v2p8]_ctrl()
authorHans de Goede <hdegoede@redhat.com>
Sun, 16 Jan 2022 21:52:00 +0000 (22:52 +0100)
committerMauro Carvalho Chehab <mchehab@kernel.org>
Tue, 8 Feb 2022 05:28:37 +0000 (06:28 +0100)
On devices with 2 sensors the 2 sensors may get probed simultaneously
and the v1p8 and v2p8 regulators are ususally shared between the
2 sensors.

This means that the probe() function of sensor 1 may end up calling
gmin_v1p8_ctrl(..., false) turning the regulator off while sensor 2's
probe() function still needs it to be on, causing the probe() of
sensor 2 to sometimes fail.

Fix this by adding an enable-count for both regulators and only
disabling them again when that goes to 0.

Note all this really should be converted to use the standard kernel
regulator framework, I have doing this on my long term TODO list,
this fix is only meant as a temporary workaround for the issue.

Link: https://lore.kernel.org/linux-media/20220116215204.307649-6-hdegoede@redhat.com
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c

index a29e508..960c64e 100644 (file)
@@ -118,6 +118,10 @@ static const char *pmic_name[] = {
        [PMIC_CRYSTALCOVE]      = "Crystal Cove PMIC",
 };
 
+static DEFINE_MUTEX(gmin_regulator_mutex);
+static int gmin_v1p8_enable_count;
+static int gmin_v2p8_enable_count;
+
 /* The atomisp uses type==0 for the end-of-list marker, so leave space. */
 static struct intel_v4l2_subdev_table pdata_subdevs[MAX_SUBDEVS + 1];
 
@@ -851,38 +855,58 @@ static int gmin_v1p8_ctrl(struct v4l2_subdev *subdev, int on)
 
        gs->v1p8_on = on;
 
+       ret = 0;
+       mutex_lock(&gmin_regulator_mutex);
+       if (on) {
+               gmin_v1p8_enable_count++;
+               if (gmin_v1p8_enable_count > 1)
+                       goto out; /* Already on */
+       } else {
+               gmin_v1p8_enable_count--;
+               if (gmin_v1p8_enable_count > 0)
+                       goto out; /* Still needed */
+       }
+
        if (gs->v1p8_gpio >= 0)
                gpio_set_value(gs->v1p8_gpio, on);
 
        if (gs->v1p8_reg) {
                regulator_set_voltage(gs->v1p8_reg, 1800000, 1800000);
                if (on)
-                       return regulator_enable(gs->v1p8_reg);
+                       ret = regulator_enable(gs->v1p8_reg);
                else
-                       return regulator_disable(gs->v1p8_reg);
+                       ret = regulator_disable(gs->v1p8_reg);
+
+               goto out;
        }
 
        switch (pmic_id) {
        case PMIC_AXP:
                if (on)
-                       return axp_v1p8_on(subdev->dev, gs);
+                       ret = axp_v1p8_on(subdev->dev, gs);
                else
-                       return axp_v1p8_off(subdev->dev, gs);
+                       ret = axp_v1p8_off(subdev->dev, gs);
+               break;
        case PMIC_TI:
                value = on ? LDO_1P8V_ON : LDO_1P8V_OFF;
 
-               return gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr,
-                                     LDO10_REG, value, 0xff);
+               ret = gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr,
+                                    LDO10_REG, value, 0xff);
+               break;
        case PMIC_CRYSTALCOVE:
                value = on ? CRYSTAL_ON : CRYSTAL_OFF;
 
-               return gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr,
-                                     CRYSTAL_1P8V_REG, value, 0xff);
+               ret = gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr,
+                                    CRYSTAL_1P8V_REG, value, 0xff);
+               break;
        default:
-               dev_err(subdev->dev, "Couldn't set power mode for v1p2\n");
+               dev_err(subdev->dev, "Couldn't set power mode for v1p8\n");
+               ret = -EINVAL;
        }
 
-       return -EINVAL;
+out:
+       mutex_unlock(&gmin_regulator_mutex);
+       return ret;
 }
 
 static int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on)
@@ -908,37 +932,57 @@ static int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on)
                return 0;
        gs->v2p8_on = on;
 
+       ret = 0;
+       mutex_lock(&gmin_regulator_mutex);
+       if (on) {
+               gmin_v2p8_enable_count++;
+               if (gmin_v2p8_enable_count > 1)
+                       goto out; /* Already on */
+       } else {
+               gmin_v2p8_enable_count--;
+               if (gmin_v2p8_enable_count > 0)
+                       goto out; /* Still needed */
+       }
+
        if (gs->v2p8_gpio >= 0)
                gpio_set_value(gs->v2p8_gpio, on);
 
        if (gs->v2p8_reg) {
                regulator_set_voltage(gs->v2p8_reg, 2900000, 2900000);
                if (on)
-                       return regulator_enable(gs->v2p8_reg);
+                       ret = regulator_enable(gs->v2p8_reg);
                else
-                       return regulator_disable(gs->v2p8_reg);
+                       ret = regulator_disable(gs->v2p8_reg);
+
+               goto out;
        }
 
        switch (pmic_id) {
        case PMIC_AXP:
-               return axp_regulator_set(subdev->dev, gs, ALDO1_SEL_REG,
-                                        ALDO1_2P8V, ALDO1_CTRL3_REG,
-                                        ALDO1_CTRL3_SHIFT, on);
+               ret = axp_regulator_set(subdev->dev, gs, ALDO1_SEL_REG,
+                                       ALDO1_2P8V, ALDO1_CTRL3_REG,
+                                       ALDO1_CTRL3_SHIFT, on);
+               break;
        case PMIC_TI:
                value = on ? LDO_2P8V_ON : LDO_2P8V_OFF;
 
-               return gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr,
-                                     LDO9_REG, value, 0xff);
+               ret = gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr,
+                                    LDO9_REG, value, 0xff);
+               break;
        case PMIC_CRYSTALCOVE:
                value = on ? CRYSTAL_ON : CRYSTAL_OFF;
 
-               return gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr,
-                                     CRYSTAL_2P8V_REG, value, 0xff);
+               ret = gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr,
+                                    CRYSTAL_2P8V_REG, value, 0xff);
+               break;
        default:
-               dev_err(subdev->dev, "Couldn't set power mode for v1p2\n");
+               dev_err(subdev->dev, "Couldn't set power mode for v2p8\n");
+               ret = -EINVAL;
        }
 
-       return -EINVAL;
+out:
+       mutex_unlock(&gmin_regulator_mutex);
+       return ret;
 }
 
 static int gmin_acpi_pm_ctrl(struct v4l2_subdev *subdev, int on)