[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250210082035.8670-6-val@packett.cool>
Date: Mon, 10 Feb 2025 05:19:20 -0300
From: Val Packett <val@...kett.cool>
To:
Cc: Val Packett <val@...kett.cool>,
Daniel Scally <djrscally@...il.com>,
Mauro Carvalho Chehab <mchehab@...nel.org>,
Rob Herring <robh@...nel.org>,
Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>,
Sakari Ailus <sakari.ailus@...ux.intel.com>,
linux-media@...r.kernel.org,
devicetree@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: [PATCH 5/5] media: i2c: dw9719: add support for dw9718s
The DW9718S is a similar part that uses a different register set but
follows the same method of operation otherwise. Add support for it
to the existing dw9719 driver. While here, ensure suspend-resume works.
Tested on the Moto E5 (motorola-nora) smartphone.
Signed-off-by: Val Packett <val@...kett.cool>
---
drivers/media/i2c/dw9719.c | 104 ++++++++++++++++++++++++++++++++++---
1 file changed, 97 insertions(+), 7 deletions(-)
diff --git a/drivers/media/i2c/dw9719.c b/drivers/media/i2c/dw9719.c
index 74a57c2f59ae..4a07684af52e 100644
--- a/drivers/media/i2c/dw9719.c
+++ b/drivers/media/i2c/dw9719.c
@@ -23,6 +23,22 @@
/* 100 us is not enough on resume */
#define DW9719_T_OPR_US 200
+#define DW9718_CONTROL CCI_REG8(1)
+#define DW9718_CONTROL_SW_LINEAR BIT(0)
+#define DW9718_CONTROL_SAC_SHIFT 1
+#define DW9718_CONTROL_SAC_MASK 0x7
+#define DW9718_CONTROL_OCP_DISABLE BIT(4)
+#define DW9718_CONTROL_UVLO_DISABLE BIT(5)
+
+#define DW9718_VCM_CURRENT CCI_REG16(2)
+
+#define DW9718_SW CCI_REG8(4)
+#define DW9718_SW_VCM_FREQ_MASK 0xF
+#define DW9718_DEFAULT_VCM_FREQ 0
+
+#define DW9718_SACT CCI_REG8(5)
+#define DW9718_SACT_PERIOD_8_8MS 0x19
+
#define DW9719_INFO CCI_REG8(0)
#define DW9719_ID 0xF1
@@ -48,12 +64,25 @@ struct dw9719_device {
u32 sac_mode;
u32 vcm_freq;
+ const struct dw9719_cfg {
+ int reg_current;
+ int default_vcm_freq;
+ int (*power_up)(struct dw9719_device *dw9719);
+ int (*detect)(struct dw9719_device *dw9719);
+ } *cfg;
+
struct dw9719_v4l2_ctrls {
struct v4l2_ctrl_handler handler;
struct v4l2_ctrl *focus;
} ctrls;
};
+static int dw9718_detect(struct dw9719_device *dw9719)
+{
+ /* Unfortunately, there is no ID register */
+ return 0;
+}
+
static int dw9719_detect(struct dw9719_device *dw9719)
{
int ret;
@@ -73,9 +102,50 @@ static int dw9719_detect(struct dw9719_device *dw9719)
static int dw9719_power_down(struct dw9719_device *dw9719)
{
+ int ret;
+
+ cci_write(dw9719->regmap, DW9718_CONTROL, 1, &ret);
+ if (ret)
+ return ret;
+
+ /* Need tOPR to transition from STANDBY to SHUTDOWN */
+ usleep_range(DW9719_T_OPR_US, DW9719_T_OPR_US + 10);
+
return regulator_disable(dw9719->regulator);
}
+static int dw9718_power_up(struct dw9719_device *dw9719)
+{
+ int ret;
+
+ ret = regulator_enable(dw9719->regulator);
+ if (ret)
+ return ret;
+
+ /* Need tOPR to transition from SHUTDOWN to STANDBY */
+ usleep_range(DW9719_T_OPR_US, DW9719_T_OPR_US + 10);
+
+ /* Datasheet says [OCP/UVLO] should be disabled below 2.5V */
+ cci_write(dw9719->regmap, DW9718_CONTROL,
+ DW9718_CONTROL_SW_LINEAR |
+ ((dw9719->sac_mode & DW9718_CONTROL_SAC_MASK)
+ << DW9718_CONTROL_SAC_SHIFT) |
+ DW9718_CONTROL_OCP_DISABLE |
+ DW9718_CONTROL_UVLO_DISABLE,
+ &ret);
+ cci_write(dw9719->regmap, DW9718_SACT,
+ DW9718_SACT_PERIOD_8_8MS,
+ &ret);
+ cci_write(dw9719->regmap, DW9718_SW,
+ dw9719->vcm_freq & DW9718_SW_VCM_FREQ_MASK,
+ &ret);
+
+ if (ret)
+ dw9719_power_down(dw9719);
+
+ return ret;
+}
+
static int dw9719_power_up(struct dw9719_device *dw9719)
{
int ret;
@@ -103,7 +173,7 @@ static int dw9719_power_up(struct dw9719_device *dw9719)
static int dw9719_t_focus_abs(struct dw9719_device *dw9719, s32 value)
{
- return cci_write(dw9719->regmap, DW9719_VCM_CURRENT, value, NULL);
+ return cci_write(dw9719->regmap, dw9719->cfg->reg_current, value, NULL);
}
static int dw9719_set_ctrl(struct v4l2_ctrl *ctrl)
@@ -161,7 +231,7 @@ static int dw9719_resume(struct device *dev)
int ret;
int val;
- ret = dw9719_power_up(dw9719);
+ ret = dw9719->cfg->power_up(dw9719);
if (ret)
return ret;
@@ -235,13 +305,17 @@ static int dw9719_probe(struct i2c_client *client)
if (!dw9719)
return -ENOMEM;
+ dw9719->cfg = i2c_get_match_data(client);
+ if (!dw9719->cfg)
+ return -ENODEV;
+
dw9719->regmap = devm_cci_regmap_init_i2c(client, 8);
if (IS_ERR(dw9719->regmap))
return PTR_ERR(dw9719->regmap);
dw9719->dev = &client->dev;
dw9719->sac_mode = DW9719_MODE_SAC3;
- dw9719->vcm_freq = DW9719_DEFAULT_VCM_FREQ;
+ dw9719->vcm_freq = dw9719->cfg->default_vcm_freq;
/* Optional indication of SAC mode select */
device_property_read_u32(&client->dev, "dongwoon,sac-mode",
@@ -277,11 +351,11 @@ static int dw9719_probe(struct i2c_client *client)
* will work.
*/
- ret = dw9719_power_up(dw9719);
+ ret = dw9719->cfg->power_up(dw9719);
if (ret)
goto err_cleanup_media;
- ret = dw9719_detect(dw9719);
+ ret = dw9719->cfg->detect(dw9719);
if (ret)
goto err_powerdown;
@@ -328,14 +402,30 @@ static void dw9719_remove(struct i2c_client *client)
pm_runtime_set_suspended(&client->dev);
}
+static const struct dw9719_cfg dw9718_cfg = {
+ .reg_current = DW9718_VCM_CURRENT,
+ .default_vcm_freq = DW9718_DEFAULT_VCM_FREQ,
+ .power_up = dw9718_power_up,
+ .detect = dw9718_detect,
+};
+
+static const struct dw9719_cfg dw9719_cfg = {
+ .reg_current = DW9719_VCM_CURRENT,
+ .default_vcm_freq = DW9719_DEFAULT_VCM_FREQ,
+ .power_up = dw9719_power_up,
+ .detect = dw9719_detect,
+};
+
static const struct i2c_device_id dw9719_id_table[] = {
- { "dw9719" },
+ { "dw9718s", .driver_data = (kernel_ulong_t)&dw9718_cfg, },
+ { "dw9719", .driver_data = (kernel_ulong_t)&dw9719_cfg, },
{ }
};
MODULE_DEVICE_TABLE(i2c, dw9719_id_table);
static const struct of_device_id dw9719_of_table[] = {
- { .compatible = "dongwoon,dw9719" },
+ { .compatible = "dongwoon,dw9718s", .data = &dw9718_cfg },
+ { .compatible = "dongwoon,dw9719", .data = &dw9719_cfg },
{ { 0 } }
};
MODULE_DEVICE_TABLE(of, dw9719_of_table);
--
2.48.1
Powered by blists - more mailing lists