xref: /openbmc/linux/drivers/media/i2c/dw9719.c (revision fed60fc5)
1*fed60fc5SDaniel Scally // SPDX-License-Identifier: GPL-2.0
2*fed60fc5SDaniel Scally // Copyright (c) 2012 Intel Corporation
3*fed60fc5SDaniel Scally 
4*fed60fc5SDaniel Scally /*
5*fed60fc5SDaniel Scally  * Based on linux/modules/camera/drivers/media/i2c/imx/dw9719.c in this repo:
6*fed60fc5SDaniel Scally  * https://github.com/ZenfoneArea/android_kernel_asus_zenfone5
7*fed60fc5SDaniel Scally  */
8*fed60fc5SDaniel Scally 
9*fed60fc5SDaniel Scally #include <linux/delay.h>
10*fed60fc5SDaniel Scally #include <linux/i2c.h>
11*fed60fc5SDaniel Scally #include <linux/pm_runtime.h>
12*fed60fc5SDaniel Scally #include <linux/regulator/consumer.h>
13*fed60fc5SDaniel Scally #include <linux/types.h>
14*fed60fc5SDaniel Scally 
15*fed60fc5SDaniel Scally #include <media/v4l2-cci.h>
16*fed60fc5SDaniel Scally #include <media/v4l2-common.h>
17*fed60fc5SDaniel Scally #include <media/v4l2-ctrls.h>
18*fed60fc5SDaniel Scally #include <media/v4l2-subdev.h>
19*fed60fc5SDaniel Scally 
20*fed60fc5SDaniel Scally #define DW9719_MAX_FOCUS_POS	1023
21*fed60fc5SDaniel Scally #define DW9719_CTRL_STEPS	16
22*fed60fc5SDaniel Scally #define DW9719_CTRL_DELAY_US	1000
23*fed60fc5SDaniel Scally 
24*fed60fc5SDaniel Scally #define DW9719_INFO			CCI_REG8(0)
25*fed60fc5SDaniel Scally #define DW9719_ID			0xF1
26*fed60fc5SDaniel Scally 
27*fed60fc5SDaniel Scally #define DW9719_CONTROL			CCI_REG8(2)
28*fed60fc5SDaniel Scally #define DW9719_ENABLE_RINGING		0x02
29*fed60fc5SDaniel Scally 
30*fed60fc5SDaniel Scally #define DW9719_VCM_CURRENT		CCI_REG16(3)
31*fed60fc5SDaniel Scally 
32*fed60fc5SDaniel Scally #define DW9719_MODE			CCI_REG8(6)
33*fed60fc5SDaniel Scally #define DW9719_MODE_SAC_SHIFT		4
34*fed60fc5SDaniel Scally #define DW9719_MODE_SAC3		4
35*fed60fc5SDaniel Scally 
36*fed60fc5SDaniel Scally #define DW9719_VCM_FREQ			CCI_REG8(7)
37*fed60fc5SDaniel Scally #define DW9719_DEFAULT_VCM_FREQ		0x60
38*fed60fc5SDaniel Scally 
39*fed60fc5SDaniel Scally #define to_dw9719_device(x) container_of(x, struct dw9719_device, sd)
40*fed60fc5SDaniel Scally 
41*fed60fc5SDaniel Scally struct dw9719_device {
42*fed60fc5SDaniel Scally 	struct v4l2_subdev sd;
43*fed60fc5SDaniel Scally 	struct device *dev;
44*fed60fc5SDaniel Scally 	struct regmap *regmap;
45*fed60fc5SDaniel Scally 	struct regulator *regulator;
46*fed60fc5SDaniel Scally 	u32 sac_mode;
47*fed60fc5SDaniel Scally 	u32 vcm_freq;
48*fed60fc5SDaniel Scally 
49*fed60fc5SDaniel Scally 	struct dw9719_v4l2_ctrls {
50*fed60fc5SDaniel Scally 		struct v4l2_ctrl_handler handler;
51*fed60fc5SDaniel Scally 		struct v4l2_ctrl *focus;
52*fed60fc5SDaniel Scally 	} ctrls;
53*fed60fc5SDaniel Scally };
54*fed60fc5SDaniel Scally 
dw9719_detect(struct dw9719_device * dw9719)55*fed60fc5SDaniel Scally static int dw9719_detect(struct dw9719_device *dw9719)
56*fed60fc5SDaniel Scally {
57*fed60fc5SDaniel Scally 	int ret;
58*fed60fc5SDaniel Scally 	u64 val;
59*fed60fc5SDaniel Scally 
60*fed60fc5SDaniel Scally 	ret = cci_read(dw9719->regmap, DW9719_INFO, &val, NULL);
61*fed60fc5SDaniel Scally 	if (ret < 0)
62*fed60fc5SDaniel Scally 		return ret;
63*fed60fc5SDaniel Scally 
64*fed60fc5SDaniel Scally 	if (val != DW9719_ID) {
65*fed60fc5SDaniel Scally 		dev_err(dw9719->dev, "Failed to detect correct id\n");
66*fed60fc5SDaniel Scally 		return -ENXIO;
67*fed60fc5SDaniel Scally 	}
68*fed60fc5SDaniel Scally 
69*fed60fc5SDaniel Scally 	return 0;
70*fed60fc5SDaniel Scally }
71*fed60fc5SDaniel Scally 
dw9719_power_down(struct dw9719_device * dw9719)72*fed60fc5SDaniel Scally static int dw9719_power_down(struct dw9719_device *dw9719)
73*fed60fc5SDaniel Scally {
74*fed60fc5SDaniel Scally 	return regulator_disable(dw9719->regulator);
75*fed60fc5SDaniel Scally }
76*fed60fc5SDaniel Scally 
dw9719_power_up(struct dw9719_device * dw9719)77*fed60fc5SDaniel Scally static int dw9719_power_up(struct dw9719_device *dw9719)
78*fed60fc5SDaniel Scally {
79*fed60fc5SDaniel Scally 	int ret;
80*fed60fc5SDaniel Scally 
81*fed60fc5SDaniel Scally 	ret = regulator_enable(dw9719->regulator);
82*fed60fc5SDaniel Scally 	if (ret)
83*fed60fc5SDaniel Scally 		return ret;
84*fed60fc5SDaniel Scally 
85*fed60fc5SDaniel Scally 	/* Jiggle SCL pin to wake up device */
86*fed60fc5SDaniel Scally 	cci_write(dw9719->regmap, DW9719_CONTROL, 1, &ret);
87*fed60fc5SDaniel Scally 
88*fed60fc5SDaniel Scally 	/* Need 100us to transit from SHUTDOWN to STANDBY */
89*fed60fc5SDaniel Scally 	fsleep(100);
90*fed60fc5SDaniel Scally 
91*fed60fc5SDaniel Scally 	cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_ENABLE_RINGING, &ret);
92*fed60fc5SDaniel Scally 	cci_write(dw9719->regmap, DW9719_MODE,
93*fed60fc5SDaniel Scally 		  dw9719->sac_mode << DW9719_MODE_SAC_SHIFT, &ret);
94*fed60fc5SDaniel Scally 	cci_write(dw9719->regmap, DW9719_VCM_FREQ, dw9719->vcm_freq, &ret);
95*fed60fc5SDaniel Scally 
96*fed60fc5SDaniel Scally 	if (ret)
97*fed60fc5SDaniel Scally 		dw9719_power_down(dw9719);
98*fed60fc5SDaniel Scally 
99*fed60fc5SDaniel Scally 	return ret;
100*fed60fc5SDaniel Scally }
101*fed60fc5SDaniel Scally 
dw9719_t_focus_abs(struct dw9719_device * dw9719,s32 value)102*fed60fc5SDaniel Scally static int dw9719_t_focus_abs(struct dw9719_device *dw9719, s32 value)
103*fed60fc5SDaniel Scally {
104*fed60fc5SDaniel Scally 	return cci_write(dw9719->regmap, DW9719_VCM_CURRENT, value, NULL);
105*fed60fc5SDaniel Scally }
106*fed60fc5SDaniel Scally 
dw9719_set_ctrl(struct v4l2_ctrl * ctrl)107*fed60fc5SDaniel Scally static int dw9719_set_ctrl(struct v4l2_ctrl *ctrl)
108*fed60fc5SDaniel Scally {
109*fed60fc5SDaniel Scally 	struct dw9719_device *dw9719 = container_of(ctrl->handler,
110*fed60fc5SDaniel Scally 						    struct dw9719_device,
111*fed60fc5SDaniel Scally 						    ctrls.handler);
112*fed60fc5SDaniel Scally 	int ret;
113*fed60fc5SDaniel Scally 
114*fed60fc5SDaniel Scally 	/* Only apply changes to the controls if the device is powered up */
115*fed60fc5SDaniel Scally 	if (!pm_runtime_get_if_in_use(dw9719->dev))
116*fed60fc5SDaniel Scally 		return 0;
117*fed60fc5SDaniel Scally 
118*fed60fc5SDaniel Scally 	switch (ctrl->id) {
119*fed60fc5SDaniel Scally 	case V4L2_CID_FOCUS_ABSOLUTE:
120*fed60fc5SDaniel Scally 		ret = dw9719_t_focus_abs(dw9719, ctrl->val);
121*fed60fc5SDaniel Scally 		break;
122*fed60fc5SDaniel Scally 	default:
123*fed60fc5SDaniel Scally 		ret = -EINVAL;
124*fed60fc5SDaniel Scally 	}
125*fed60fc5SDaniel Scally 
126*fed60fc5SDaniel Scally 	pm_runtime_put(dw9719->dev);
127*fed60fc5SDaniel Scally 
128*fed60fc5SDaniel Scally 	return ret;
129*fed60fc5SDaniel Scally }
130*fed60fc5SDaniel Scally 
131*fed60fc5SDaniel Scally static const struct v4l2_ctrl_ops dw9719_ctrl_ops = {
132*fed60fc5SDaniel Scally 	.s_ctrl = dw9719_set_ctrl,
133*fed60fc5SDaniel Scally };
134*fed60fc5SDaniel Scally 
dw9719_suspend(struct device * dev)135*fed60fc5SDaniel Scally static int dw9719_suspend(struct device *dev)
136*fed60fc5SDaniel Scally {
137*fed60fc5SDaniel Scally 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
138*fed60fc5SDaniel Scally 	struct dw9719_device *dw9719 = to_dw9719_device(sd);
139*fed60fc5SDaniel Scally 	int ret;
140*fed60fc5SDaniel Scally 	int val;
141*fed60fc5SDaniel Scally 
142*fed60fc5SDaniel Scally 	for (val = dw9719->ctrls.focus->val; val >= 0;
143*fed60fc5SDaniel Scally 	     val -= DW9719_CTRL_STEPS) {
144*fed60fc5SDaniel Scally 		ret = dw9719_t_focus_abs(dw9719, val);
145*fed60fc5SDaniel Scally 		if (ret)
146*fed60fc5SDaniel Scally 			return ret;
147*fed60fc5SDaniel Scally 
148*fed60fc5SDaniel Scally 		usleep_range(DW9719_CTRL_DELAY_US, DW9719_CTRL_DELAY_US + 10);
149*fed60fc5SDaniel Scally 	}
150*fed60fc5SDaniel Scally 
151*fed60fc5SDaniel Scally 	return dw9719_power_down(dw9719);
152*fed60fc5SDaniel Scally }
153*fed60fc5SDaniel Scally 
dw9719_resume(struct device * dev)154*fed60fc5SDaniel Scally static int dw9719_resume(struct device *dev)
155*fed60fc5SDaniel Scally {
156*fed60fc5SDaniel Scally 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
157*fed60fc5SDaniel Scally 	struct dw9719_device *dw9719 = to_dw9719_device(sd);
158*fed60fc5SDaniel Scally 	int current_focus = dw9719->ctrls.focus->val;
159*fed60fc5SDaniel Scally 	int ret;
160*fed60fc5SDaniel Scally 	int val;
161*fed60fc5SDaniel Scally 
162*fed60fc5SDaniel Scally 	ret = dw9719_power_up(dw9719);
163*fed60fc5SDaniel Scally 	if (ret)
164*fed60fc5SDaniel Scally 		return ret;
165*fed60fc5SDaniel Scally 
166*fed60fc5SDaniel Scally 	for (val = current_focus % DW9719_CTRL_STEPS; val < current_focus;
167*fed60fc5SDaniel Scally 	     val += DW9719_CTRL_STEPS) {
168*fed60fc5SDaniel Scally 		ret = dw9719_t_focus_abs(dw9719, val);
169*fed60fc5SDaniel Scally 		if (ret)
170*fed60fc5SDaniel Scally 			goto err_power_down;
171*fed60fc5SDaniel Scally 
172*fed60fc5SDaniel Scally 		usleep_range(DW9719_CTRL_DELAY_US, DW9719_CTRL_DELAY_US + 10);
173*fed60fc5SDaniel Scally 	}
174*fed60fc5SDaniel Scally 
175*fed60fc5SDaniel Scally 	return 0;
176*fed60fc5SDaniel Scally 
177*fed60fc5SDaniel Scally err_power_down:
178*fed60fc5SDaniel Scally 	dw9719_power_down(dw9719);
179*fed60fc5SDaniel Scally 	return ret;
180*fed60fc5SDaniel Scally }
181*fed60fc5SDaniel Scally 
dw9719_open(struct v4l2_subdev * sd,struct v4l2_subdev_fh * fh)182*fed60fc5SDaniel Scally static int dw9719_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
183*fed60fc5SDaniel Scally {
184*fed60fc5SDaniel Scally 	return pm_runtime_resume_and_get(sd->dev);
185*fed60fc5SDaniel Scally }
186*fed60fc5SDaniel Scally 
dw9719_close(struct v4l2_subdev * sd,struct v4l2_subdev_fh * fh)187*fed60fc5SDaniel Scally static int dw9719_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
188*fed60fc5SDaniel Scally {
189*fed60fc5SDaniel Scally 	pm_runtime_put(sd->dev);
190*fed60fc5SDaniel Scally 
191*fed60fc5SDaniel Scally 	return 0;
192*fed60fc5SDaniel Scally }
193*fed60fc5SDaniel Scally 
194*fed60fc5SDaniel Scally static const struct v4l2_subdev_internal_ops dw9719_internal_ops = {
195*fed60fc5SDaniel Scally 	.open = dw9719_open,
196*fed60fc5SDaniel Scally 	.close = dw9719_close,
197*fed60fc5SDaniel Scally };
198*fed60fc5SDaniel Scally 
dw9719_init_controls(struct dw9719_device * dw9719)199*fed60fc5SDaniel Scally static int dw9719_init_controls(struct dw9719_device *dw9719)
200*fed60fc5SDaniel Scally {
201*fed60fc5SDaniel Scally 	const struct v4l2_ctrl_ops *ops = &dw9719_ctrl_ops;
202*fed60fc5SDaniel Scally 	int ret;
203*fed60fc5SDaniel Scally 
204*fed60fc5SDaniel Scally 	v4l2_ctrl_handler_init(&dw9719->ctrls.handler, 1);
205*fed60fc5SDaniel Scally 
206*fed60fc5SDaniel Scally 	dw9719->ctrls.focus = v4l2_ctrl_new_std(&dw9719->ctrls.handler, ops,
207*fed60fc5SDaniel Scally 						V4L2_CID_FOCUS_ABSOLUTE, 0,
208*fed60fc5SDaniel Scally 						DW9719_MAX_FOCUS_POS, 1, 0);
209*fed60fc5SDaniel Scally 
210*fed60fc5SDaniel Scally 	if (dw9719->ctrls.handler.error) {
211*fed60fc5SDaniel Scally 		dev_err(dw9719->dev, "Error initialising v4l2 ctrls\n");
212*fed60fc5SDaniel Scally 		ret = dw9719->ctrls.handler.error;
213*fed60fc5SDaniel Scally 		goto err_free_handler;
214*fed60fc5SDaniel Scally 	}
215*fed60fc5SDaniel Scally 
216*fed60fc5SDaniel Scally 	dw9719->sd.ctrl_handler = &dw9719->ctrls.handler;
217*fed60fc5SDaniel Scally 	return 0;
218*fed60fc5SDaniel Scally 
219*fed60fc5SDaniel Scally err_free_handler:
220*fed60fc5SDaniel Scally 	v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
221*fed60fc5SDaniel Scally 	return ret;
222*fed60fc5SDaniel Scally }
223*fed60fc5SDaniel Scally 
224*fed60fc5SDaniel Scally static const struct v4l2_subdev_ops dw9719_ops = { };
225*fed60fc5SDaniel Scally 
dw9719_probe(struct i2c_client * client)226*fed60fc5SDaniel Scally static int dw9719_probe(struct i2c_client *client)
227*fed60fc5SDaniel Scally {
228*fed60fc5SDaniel Scally 	struct dw9719_device *dw9719;
229*fed60fc5SDaniel Scally 	int ret;
230*fed60fc5SDaniel Scally 
231*fed60fc5SDaniel Scally 	dw9719 = devm_kzalloc(&client->dev, sizeof(*dw9719), GFP_KERNEL);
232*fed60fc5SDaniel Scally 	if (!dw9719)
233*fed60fc5SDaniel Scally 		return -ENOMEM;
234*fed60fc5SDaniel Scally 
235*fed60fc5SDaniel Scally 	dw9719->regmap = devm_cci_regmap_init_i2c(client, 8);
236*fed60fc5SDaniel Scally 	if (IS_ERR(dw9719->regmap))
237*fed60fc5SDaniel Scally 		return PTR_ERR(dw9719->regmap);
238*fed60fc5SDaniel Scally 
239*fed60fc5SDaniel Scally 	dw9719->dev = &client->dev;
240*fed60fc5SDaniel Scally 	dw9719->sac_mode = DW9719_MODE_SAC3;
241*fed60fc5SDaniel Scally 	dw9719->vcm_freq = DW9719_DEFAULT_VCM_FREQ;
242*fed60fc5SDaniel Scally 
243*fed60fc5SDaniel Scally 	/* Optional indication of SAC mode select */
244*fed60fc5SDaniel Scally 	device_property_read_u32(&client->dev, "dongwoon,sac-mode",
245*fed60fc5SDaniel Scally 				 &dw9719->sac_mode);
246*fed60fc5SDaniel Scally 
247*fed60fc5SDaniel Scally 	/* Optional indication of VCM frequency */
248*fed60fc5SDaniel Scally 	device_property_read_u32(&client->dev, "dongwoon,vcm-freq",
249*fed60fc5SDaniel Scally 				 &dw9719->vcm_freq);
250*fed60fc5SDaniel Scally 
251*fed60fc5SDaniel Scally 	dw9719->regulator = devm_regulator_get(&client->dev, "vdd");
252*fed60fc5SDaniel Scally 	if (IS_ERR(dw9719->regulator))
253*fed60fc5SDaniel Scally 		return dev_err_probe(&client->dev, PTR_ERR(dw9719->regulator),
254*fed60fc5SDaniel Scally 				     "getting regulator\n");
255*fed60fc5SDaniel Scally 
256*fed60fc5SDaniel Scally 	v4l2_i2c_subdev_init(&dw9719->sd, client, &dw9719_ops);
257*fed60fc5SDaniel Scally 	dw9719->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
258*fed60fc5SDaniel Scally 	dw9719->sd.internal_ops = &dw9719_internal_ops;
259*fed60fc5SDaniel Scally 
260*fed60fc5SDaniel Scally 	ret = dw9719_init_controls(dw9719);
261*fed60fc5SDaniel Scally 	if (ret)
262*fed60fc5SDaniel Scally 		return ret;
263*fed60fc5SDaniel Scally 
264*fed60fc5SDaniel Scally 	ret = media_entity_pads_init(&dw9719->sd.entity, 0, NULL);
265*fed60fc5SDaniel Scally 	if (ret < 0)
266*fed60fc5SDaniel Scally 		goto err_free_ctrl_handler;
267*fed60fc5SDaniel Scally 
268*fed60fc5SDaniel Scally 	dw9719->sd.entity.function = MEDIA_ENT_F_LENS;
269*fed60fc5SDaniel Scally 
270*fed60fc5SDaniel Scally 	/*
271*fed60fc5SDaniel Scally 	 * We need the driver to work in the event that pm runtime is disable in
272*fed60fc5SDaniel Scally 	 * the kernel, so power up and verify the chip now. In the event that
273*fed60fc5SDaniel Scally 	 * runtime pm is disabled this will leave the chip on, so that the lens
274*fed60fc5SDaniel Scally 	 * will work.
275*fed60fc5SDaniel Scally 	 */
276*fed60fc5SDaniel Scally 
277*fed60fc5SDaniel Scally 	ret = dw9719_power_up(dw9719);
278*fed60fc5SDaniel Scally 	if (ret)
279*fed60fc5SDaniel Scally 		goto err_cleanup_media;
280*fed60fc5SDaniel Scally 
281*fed60fc5SDaniel Scally 	ret = dw9719_detect(dw9719);
282*fed60fc5SDaniel Scally 	if (ret)
283*fed60fc5SDaniel Scally 		goto err_powerdown;
284*fed60fc5SDaniel Scally 
285*fed60fc5SDaniel Scally 	pm_runtime_set_active(&client->dev);
286*fed60fc5SDaniel Scally 	pm_runtime_get_noresume(&client->dev);
287*fed60fc5SDaniel Scally 	pm_runtime_enable(&client->dev);
288*fed60fc5SDaniel Scally 
289*fed60fc5SDaniel Scally 	ret = v4l2_async_register_subdev(&dw9719->sd);
290*fed60fc5SDaniel Scally 	if (ret < 0)
291*fed60fc5SDaniel Scally 		goto err_pm_runtime;
292*fed60fc5SDaniel Scally 
293*fed60fc5SDaniel Scally 	pm_runtime_set_autosuspend_delay(&client->dev, 1000);
294*fed60fc5SDaniel Scally 	pm_runtime_use_autosuspend(&client->dev);
295*fed60fc5SDaniel Scally 	pm_runtime_put_autosuspend(&client->dev);
296*fed60fc5SDaniel Scally 
297*fed60fc5SDaniel Scally 	return ret;
298*fed60fc5SDaniel Scally 
299*fed60fc5SDaniel Scally err_pm_runtime:
300*fed60fc5SDaniel Scally 	pm_runtime_disable(&client->dev);
301*fed60fc5SDaniel Scally 	pm_runtime_put_noidle(&client->dev);
302*fed60fc5SDaniel Scally err_powerdown:
303*fed60fc5SDaniel Scally 	dw9719_power_down(dw9719);
304*fed60fc5SDaniel Scally err_cleanup_media:
305*fed60fc5SDaniel Scally 	media_entity_cleanup(&dw9719->sd.entity);
306*fed60fc5SDaniel Scally err_free_ctrl_handler:
307*fed60fc5SDaniel Scally 	v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
308*fed60fc5SDaniel Scally 
309*fed60fc5SDaniel Scally 	return ret;
310*fed60fc5SDaniel Scally }
311*fed60fc5SDaniel Scally 
dw9719_remove(struct i2c_client * client)312*fed60fc5SDaniel Scally static void dw9719_remove(struct i2c_client *client)
313*fed60fc5SDaniel Scally {
314*fed60fc5SDaniel Scally 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
315*fed60fc5SDaniel Scally 	struct dw9719_device *dw9719 =
316*fed60fc5SDaniel Scally 		container_of(sd, struct dw9719_device, sd);
317*fed60fc5SDaniel Scally 
318*fed60fc5SDaniel Scally 	v4l2_async_unregister_subdev(sd);
319*fed60fc5SDaniel Scally 	v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
320*fed60fc5SDaniel Scally 	media_entity_cleanup(&dw9719->sd.entity);
321*fed60fc5SDaniel Scally 
322*fed60fc5SDaniel Scally 	pm_runtime_disable(&client->dev);
323*fed60fc5SDaniel Scally 	if (!pm_runtime_status_suspended(&client->dev))
324*fed60fc5SDaniel Scally 		dw9719_power_down(dw9719);
325*fed60fc5SDaniel Scally 	pm_runtime_set_suspended(&client->dev);
326*fed60fc5SDaniel Scally }
327*fed60fc5SDaniel Scally 
328*fed60fc5SDaniel Scally static const struct i2c_device_id dw9719_id_table[] = {
329*fed60fc5SDaniel Scally 	{ "dw9719" },
330*fed60fc5SDaniel Scally 	{ }
331*fed60fc5SDaniel Scally };
332*fed60fc5SDaniel Scally MODULE_DEVICE_TABLE(i2c, dw9719_id_table);
333*fed60fc5SDaniel Scally 
334*fed60fc5SDaniel Scally static DEFINE_RUNTIME_DEV_PM_OPS(dw9719_pm_ops, dw9719_suspend, dw9719_resume,
335*fed60fc5SDaniel Scally 				 NULL);
336*fed60fc5SDaniel Scally 
337*fed60fc5SDaniel Scally static struct i2c_driver dw9719_i2c_driver = {
338*fed60fc5SDaniel Scally 	.driver = {
339*fed60fc5SDaniel Scally 		.name = "dw9719",
340*fed60fc5SDaniel Scally 		.pm = pm_sleep_ptr(&dw9719_pm_ops),
341*fed60fc5SDaniel Scally 	},
342*fed60fc5SDaniel Scally 	.probe = dw9719_probe,
343*fed60fc5SDaniel Scally 	.remove = dw9719_remove,
344*fed60fc5SDaniel Scally 	.id_table = dw9719_id_table,
345*fed60fc5SDaniel Scally };
346*fed60fc5SDaniel Scally module_i2c_driver(dw9719_i2c_driver);
347*fed60fc5SDaniel Scally 
348*fed60fc5SDaniel Scally MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>");
349*fed60fc5SDaniel Scally MODULE_DESCRIPTION("DW9719 VCM Driver");
350*fed60fc5SDaniel Scally MODULE_LICENSE("GPL");
351