8521afad65ac9a74fb92632a441da9d3e96c2a6a
[openwrt/staging/xback.git] /
1 From 2977dc08b02dd7097791a4d3c94796f33a165abc Mon Sep 17 00:00:00 2001
2 From: Dave Stevenson <dave.stevenson@raspberrypi.com>
3 Date: Tue, 3 Jan 2023 15:38:08 +0000
4 Subject: [PATCH] media: dw9807-vcm: Add support for DW9817
5 bidirectional VCM driver
6
7 The DW9817 is effectively the same as DW9807 from a programming
8 interface, however it drives +/-100mA instead of 0-100mA. This means
9 that the power on ramp needs to take the lens from the midpoint, and
10 power off return it there. It also changes the default position for
11 the module.
12
13 Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
14 ---
15 drivers/media/i2c/dw9807-vcm.c | 115 +++++++++++++++++++++++++--------
16 1 file changed, 88 insertions(+), 27 deletions(-)
17
18 --- a/drivers/media/i2c/dw9807-vcm.c
19 +++ b/drivers/media/i2c/dw9807-vcm.c
20 @@ -1,6 +1,14 @@
21 // SPDX-License-Identifier: GPL-2.0
22 // Copyright (C) 2018 Intel Corporation
23
24 +/*
25 + * DW9807 is a 10-bit DAC driver, capable of sinking up to 100mA.
26 + *
27 + * DW9817 is a bidirectional 10-bit driver, driving up to +/- 100mA.
28 + * Operationally it is identical to DW9807, except that the idle position is
29 + * the mid-point, not 0.
30 + */
31 +
32 #include <linux/acpi.h>
33 #include <linux/delay.h>
34 #include <linux/i2c.h>
35 @@ -38,10 +46,16 @@
36
37 #define MAX_RETRY 10
38
39 +struct dw9807_cfg {
40 + unsigned int idle_pos;
41 + unsigned int default_pos;
42 +};
43 +
44 struct dw9807_device {
45 struct v4l2_ctrl_handler ctrls_vcm;
46 struct v4l2_subdev sd;
47 u16 current_val;
48 + u16 idle_pos;
49 };
50
51 static inline struct dw9807_device *sd_to_dw9807_vcm(
52 @@ -109,6 +123,40 @@ static int dw9807_set_dac(struct i2c_cli
53 return 0;
54 }
55
56 +/*
57 + * The lens position is gradually moved in units of DW9807_CTRL_STEPS,
58 + * to make the movements smoothly. In all cases, even when "start" and
59 + * "end" are the same, the lens will be set to the "end" position.
60 + *
61 + * (We don't use hardware slew rate control, because it differs widely
62 + * between otherwise-compatible ICs, and may need lens-specific tuning.)
63 + */
64 +static int dw9807_ramp(struct i2c_client *client, int start, int end)
65 +{
66 + int step, val, ret;
67 +
68 + if (start < end)
69 + step = DW9807_CTRL_STEPS;
70 + else
71 + step = -DW9807_CTRL_STEPS;
72 +
73 + val = start;
74 + while (true) {
75 + val += step;
76 + if (step * (val - end) >= 0)
77 + val = end;
78 + ret = dw9807_set_dac(client, val);
79 + if (ret)
80 + dev_err_ratelimited(&client->dev, "%s I2C failure: %d",
81 + __func__, ret);
82 + if (val == end)
83 + break;
84 + usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10);
85 + }
86 +
87 + return ret;
88 +}
89 +
90 static int dw9807_set_ctrl(struct v4l2_ctrl *ctrl)
91 {
92 struct dw9807_device *dev_vcm = container_of(ctrl->handler,
93 @@ -118,7 +166,7 @@ static int dw9807_set_ctrl(struct v4l2_c
94 struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd);
95
96 dev_vcm->current_val = ctrl->val;
97 - return dw9807_set_dac(client, ctrl->val);
98 + return dw9807_ramp(client, ctrl->val, ctrl->val);
99 }
100
101 return -EINVAL;
102 @@ -163,7 +211,8 @@ static int dw9807_init_controls(struct d
103 v4l2_ctrl_handler_init(hdl, 1);
104
105 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE,
106 - 0, DW9807_MAX_FOCUS_POS, DW9807_FOCUS_STEPS, 0);
107 + 0, DW9807_MAX_FOCUS_POS, DW9807_FOCUS_STEPS,
108 + dev_vcm->current_val);
109
110 dev_vcm->sd.ctrl_handler = hdl;
111 if (hdl->error) {
112 @@ -175,9 +224,32 @@ static int dw9807_init_controls(struct d
113 return 0;
114 }
115
116 +/* Compatible devices; in fact there are many similar chips.
117 + * "data" holds the powered-off (zero current) lens position and a
118 + * default/initial control value (which need not be the same as the powered-off
119 + * value).
120 + */
121 +static const struct dw9807_cfg dw9807_cfg = {
122 + .idle_pos = 0,
123 + .default_pos = 0
124 +};
125 +
126 +static const struct dw9807_cfg dw9817_cfg = {
127 + .idle_pos = 512,
128 + .default_pos = 480,
129 +};
130 +
131 +static const struct of_device_id dw9807_of_table[] = {
132 + { .compatible = "dongwoon,dw9807-vcm", .data = &dw9807_cfg },
133 + { .compatible = "dongwoon,dw9817-vcm", .data = &dw9817_cfg },
134 + { /* sentinel */ }
135 +};
136 +
137 static int dw9807_probe(struct i2c_client *client)
138 {
139 struct dw9807_device *dw9807_dev;
140 + const struct of_device_id *match;
141 + const struct dw9807_cfg *cfg;
142 int rval;
143
144 dw9807_dev = devm_kzalloc(&client->dev, sizeof(*dw9807_dev),
145 @@ -185,6 +257,13 @@ static int dw9807_probe(struct i2c_clien
146 if (dw9807_dev == NULL)
147 return -ENOMEM;
148
149 + match = i2c_of_match_device(dw9807_of_table, client);
150 + if (match) {
151 + cfg = (const struct dw9807_cfg *)match->data;
152 + dw9807_dev->idle_pos = cfg->idle_pos;
153 + dw9807_dev->current_val = cfg->default_pos;
154 + }
155 +
156 v4l2_i2c_subdev_init(&dw9807_dev->sd, client, &dw9807_ops);
157 dw9807_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
158 dw9807_dev->sd.internal_ops = &dw9807_int_ops;
159 @@ -203,7 +282,8 @@ static int dw9807_probe(struct i2c_clien
160 if (rval < 0)
161 goto err_cleanup;
162
163 - pm_runtime_set_active(&client->dev);
164 + if (!dw9807_dev->vdd)
165 + pm_runtime_set_active(&client->dev);
166 pm_runtime_enable(&client->dev);
167 pm_runtime_idle(&client->dev);
168
169 @@ -237,15 +317,10 @@ static int __maybe_unused dw9807_vcm_sus
170 struct v4l2_subdev *sd = i2c_get_clientdata(client);
171 struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd);
172 const char tx_data[2] = { DW9807_CTL_ADDR, 0x01 };
173 - int ret, val;
174 + int ret;
175
176 - for (val = dw9807_dev->current_val & ~(DW9807_CTRL_STEPS - 1);
177 - val >= 0; val -= DW9807_CTRL_STEPS) {
178 - ret = dw9807_set_dac(client, val);
179 - if (ret)
180 - dev_err_once(dev, "%s I2C failure: %d", __func__, ret);
181 - usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10);
182 - }
183 + if (abs(dw9807_dev->current_val - dw9807_dev->idle_pos) > DW9807_CTRL_STEPS)
184 + dw9807_ramp(client, dw9807_dev->current_val, dw9807_dev->idle_pos);
185
186 /* Power down */
187 ret = i2c_master_send(client, tx_data, sizeof(tx_data));
188 @@ -269,7 +344,7 @@ static int __maybe_unused dw9807_vcm_re
189 struct v4l2_subdev *sd = i2c_get_clientdata(client);
190 struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd);
191 const char tx_data[2] = { DW9807_CTL_ADDR, 0x00 };
192 - int ret, val;
193 + int ret;
194
195 /* Power on */
196 ret = i2c_master_send(client, tx_data, sizeof(tx_data));
197 @@ -278,25 +353,11 @@ static int __maybe_unused dw9807_vcm_re
198 return ret;
199 }
200
201 - for (val = dw9807_dev->current_val % DW9807_CTRL_STEPS;
202 - val < dw9807_dev->current_val + DW9807_CTRL_STEPS - 1;
203 - val += DW9807_CTRL_STEPS) {
204 - ret = dw9807_set_dac(client, val);
205 - if (ret)
206 - dev_err_ratelimited(dev, "%s I2C failure: %d",
207 - __func__, ret);
208 - usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10);
209 - }
210 + dw9807_ramp(client, dw9807_dev->idle_pos, dw9807_dev->current_val);
211
212 return 0;
213 }
214
215 -static const struct of_device_id dw9807_of_table[] = {
216 - { .compatible = "dongwoon,dw9807-vcm" },
217 - /* Compatibility for older firmware, NEVER USE THIS IN FIRMWARE! */
218 - { .compatible = "dongwoon,dw9807" },
219 - { /* sentinel */ }
220 -};
221 MODULE_DEVICE_TABLE(of, dw9807_of_table);
222
223 static const struct dev_pm_ops dw9807_pm_ops = {