]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
media: dw9719: Add DW9761 support
authorHans de Goede <hdegoede@redhat.com>
Tue, 5 Nov 2024 20:36:58 +0000 (21:36 +0100)
committerHans Verkuil <hverkuil@xs4all.nl>
Sat, 15 Feb 2025 14:23:02 +0000 (15:23 +0100)
Add support for the DW9761 VCM controller, which is very similar to
the DW9719.

The new support is based on
drivers/external_drivers/camera/drivers/media/i2c/micam/dw9761.c
from the Xiaomi kernel sources for the Mi Pad 2.

The DW9761 support has been tested on a Xiaomi Mi Pad 2 tablet and
DW9719 support has been tested (to avoid regressions) on a Microsoft
Surface Go tablet.

Link: https://github.com/MiCode/Xiaomi_Kernel_OpenSource/
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Tested-by: André Apitzsch <git@apitzsch.eu>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
drivers/media/i2c/dw9719.c

index c626ed845928cc842794cad7da72080d1ade32a9..032fbcb981f20f4e93202415e62f67379897a048 100644 (file)
@@ -2,8 +2,10 @@
 // Copyright (c) 2012 Intel Corporation
 
 /*
- * Based on linux/modules/camera/drivers/media/i2c/imx/dw9719.c in this repo:
- * https://github.com/ZenfoneArea/android_kernel_asus_zenfone5
+ * Based on linux/modules/camera/drivers/media/i2c/imx/dw9719.c from:
+ * https://github.com/ZenfoneArea/android_kernel_asus_zenfone5 and
+ * latte-l-oss/drivers/external_drivers/camera/drivers/media/i2c/micam/dw9761.c
+ * from: https://github.com/MiCode/Xiaomi_Kernel_OpenSource/
  */
 
 #include <linux/delay.h>
 
 #define DW9719_INFO                    CCI_REG8(0)
 #define DW9719_ID                      0xF1
+#define DW9761_ID                      0xF4
 
 #define DW9719_CONTROL                 CCI_REG8(2)
+#define DW9719_STANDBY                 0x00
+#define DW9719_SHUTDOWN                        0x01
 #define DW9719_ENABLE_RINGING          0x02
 
 #define DW9719_VCM_CURRENT             CCI_REG16(3)
 
+#define DW9719_STATUS                  CCI_REG16(5)
+#define DW9719_STATUS_BUSY             BIT(0)
+
 #define DW9719_MODE                    CCI_REG8(6)
 #define DW9719_MODE_SAC_SHIFT          4
-#define DW9719_MODE_SAC3               4
+#define DW9719_DEFAULT_SAC             4
+#define DW9761_DEFAULT_SAC             6
 
 #define DW9719_VCM_FREQ                        CCI_REG8(7)
 #define DW9719_DEFAULT_VCM_FREQ                0x60
+#define DW9761_DEFAULT_VCM_FREQ                0x3E
+
+#define DW9761_VCM_PRELOAD             CCI_REG8(8)
+#define DW9761_DEFAULT_VCM_PRELOAD     0x73
+
 
 #define to_dw9719_device(x) container_of(x, struct dw9719_device, sd)
 
+enum dw9719_model {
+       DW9719,
+       DW9761,
+};
+
 struct dw9719_device {
        struct v4l2_subdev sd;
        struct device *dev;
        struct regmap *regmap;
        struct regulator *regulator;
+       enum dw9719_model model;
+       u32 mode_low_bits;
        u32 sac_mode;
        u32 vcm_freq;
 
@@ -52,30 +73,14 @@ struct dw9719_device {
        } ctrls;
 };
 
-static int dw9719_detect(struct dw9719_device *dw9719)
-{
-       int ret;
-       u64 val;
-
-       ret = cci_read(dw9719->regmap, DW9719_INFO, &val, NULL);
-       if (ret < 0)
-               return ret;
-
-       if (val != DW9719_ID) {
-               dev_err(dw9719->dev, "Failed to detect correct id\n");
-               return -ENXIO;
-       }
-
-       return 0;
-}
-
 static int dw9719_power_down(struct dw9719_device *dw9719)
 {
        return regulator_disable(dw9719->regulator);
 }
 
-static int dw9719_power_up(struct dw9719_device *dw9719)
+static int dw9719_power_up(struct dw9719_device *dw9719, bool detect)
 {
+       u64 val;
        int ret;
 
        ret = regulator_enable(dw9719->regulator);
@@ -83,16 +88,54 @@ static int dw9719_power_up(struct dw9719_device *dw9719)
                return ret;
 
        /* Jiggle SCL pin to wake up device */
-       cci_write(dw9719->regmap, DW9719_CONTROL, 1, &ret);
-
+       cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_SHUTDOWN, &ret);
+       fsleep(100);
+       cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_STANDBY, &ret);
        /* Need 100us to transit from SHUTDOWN to STANDBY */
        fsleep(100);
 
+       if (detect) {
+               ret = cci_read(dw9719->regmap, DW9719_INFO, &val, NULL);
+               if (ret < 0)
+                       return ret;
+
+               switch (val) {
+               case DW9719_ID:
+                       dw9719->model = DW9719;
+                       dw9719->mode_low_bits = 0x00;
+                       dw9719->sac_mode = DW9719_DEFAULT_SAC;
+                       dw9719->vcm_freq = DW9719_DEFAULT_VCM_FREQ;
+                       break;
+               case DW9761_ID:
+                       dw9719->model = DW9761;
+                       dw9719->mode_low_bits = 0x01;
+                       dw9719->sac_mode = DW9761_DEFAULT_SAC;
+                       dw9719->vcm_freq = DW9761_DEFAULT_VCM_FREQ;
+                       break;
+               default:
+                       dev_err(dw9719->dev,
+                               "Error unknown device id 0x%02llx\n", val);
+                       return -ENXIO;
+               }
+
+               /* Optional indication of SAC mode select */
+               device_property_read_u32(dw9719->dev, "dongwoon,sac-mode",
+                                        &dw9719->sac_mode);
+
+               /* Optional indication of VCM frequency */
+               device_property_read_u32(dw9719->dev, "dongwoon,vcm-freq",
+                                        &dw9719->vcm_freq);
+       }
+
        cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_ENABLE_RINGING, &ret);
-       cci_write(dw9719->regmap, DW9719_MODE,
-                 dw9719->sac_mode << DW9719_MODE_SAC_SHIFT, &ret);
+       cci_write(dw9719->regmap, DW9719_MODE, dw9719->mode_low_bits |
+                         (dw9719->sac_mode << DW9719_MODE_SAC_SHIFT), &ret);
        cci_write(dw9719->regmap, DW9719_VCM_FREQ, dw9719->vcm_freq, &ret);
 
+       if (dw9719->model == DW9761)
+               cci_write(dw9719->regmap, DW9761_VCM_PRELOAD,
+                         DW9761_DEFAULT_VCM_PRELOAD, &ret);
+
        if (ret)
                dw9719_power_down(dw9719);
 
@@ -159,7 +202,7 @@ static int dw9719_resume(struct device *dev)
        int ret;
        int val;
 
-       ret = dw9719_power_up(dw9719);
+       ret = dw9719_power_up(dw9719, false);
        if (ret)
                return ret;
 
@@ -237,16 +280,6 @@ static int dw9719_probe(struct i2c_client *client)
                return PTR_ERR(dw9719->regmap);
 
        dw9719->dev = &client->dev;
-       dw9719->sac_mode = DW9719_MODE_SAC3;
-       dw9719->vcm_freq = DW9719_DEFAULT_VCM_FREQ;
-
-       /* Optional indication of SAC mode select */
-       device_property_read_u32(&client->dev, "dongwoon,sac-mode",
-                                &dw9719->sac_mode);
-
-       /* Optional indication of VCM frequency */
-       device_property_read_u32(&client->dev, "dongwoon,vcm-freq",
-                                &dw9719->vcm_freq);
 
        dw9719->regulator = devm_regulator_get(&client->dev, "vdd");
        if (IS_ERR(dw9719->regulator))
@@ -274,14 +307,10 @@ static int dw9719_probe(struct i2c_client *client)
         * will work.
         */
 
-       ret = dw9719_power_up(dw9719);
+       ret = dw9719_power_up(dw9719, true);
        if (ret)
                goto err_cleanup_media;
 
-       ret = dw9719_detect(dw9719);
-       if (ret)
-               goto err_powerdown;
-
        pm_runtime_set_active(&client->dev);
        pm_runtime_get_noresume(&client->dev);
        pm_runtime_enable(&client->dev);
@@ -299,7 +328,6 @@ static int dw9719_probe(struct i2c_client *client)
 err_pm_runtime:
        pm_runtime_disable(&client->dev);
        pm_runtime_put_noidle(&client->dev);
-err_powerdown:
        dw9719_power_down(dw9719);
 err_cleanup_media:
        media_entity_cleanup(&dw9719->sd.entity);
@@ -327,6 +355,7 @@ static void dw9719_remove(struct i2c_client *client)
 
 static const struct i2c_device_id dw9719_id_table[] = {
        { "dw9719" },
+       { "dw9761" },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, dw9719_id_table);