diff options
author | Sakari Ailus <sakari.ailus@iki.fi> | 2011-05-05 14:39:25 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-07-27 16:55:39 -0400 |
commit | 13abadad177074b84f6697639e1d4f64a55b1773 (patch) | |
tree | 007a906c959474d2ced1848ad2a4c791a8b5f1ca /drivers/media | |
parent | 7ba85fa4fdbbe800e92a937ca567120ef32fe49f (diff) |
[media] adp1653: Add driver for LED flash controller
This patch adds the driver for the adp1653 LED flash controller. This
controller supports a high power led in flash and torch modes and an
indicator light, sometimes also called privacy light.
The adp1653 is used on the Nokia N900.
Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>
Signed-off-by: Tuukka Toivonen <tuukkat76@gmail.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: David Cohen <dacohen@gmail.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/video/Kconfig | 9 | ||||
-rw-r--r-- | drivers/media/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/video/adp1653.c | 491 |
3 files changed, 501 insertions, 0 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 2cc9552f639..b1f7e19640d 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig | |||
@@ -489,6 +489,15 @@ config VIDEO_TCM825X | |||
489 | This is a driver for the Toshiba TCM825x VGA camera sensor. | 489 | This is a driver for the Toshiba TCM825x VGA camera sensor. |
490 | It is used for example in Nokia N800. | 490 | It is used for example in Nokia N800. |
491 | 491 | ||
492 | comment "Flash devices" | ||
493 | |||
494 | config VIDEO_ADP1653 | ||
495 | tristate "ADP1653 flash support" | ||
496 | depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER | ||
497 | ---help--- | ||
498 | This is a driver for the ADP1653 flash controller. It is used for | ||
499 | example in Nokia N900. | ||
500 | |||
492 | comment "Video improvement chips" | 501 | comment "Video improvement chips" |
493 | 502 | ||
494 | config VIDEO_UPD64031A | 503 | config VIDEO_UPD64031A |
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 459db028ace..5138a65f5e1 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile | |||
@@ -70,6 +70,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o | |||
70 | obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o | 70 | obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o |
71 | obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o | 71 | obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o |
72 | obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ | 72 | obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ |
73 | obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o | ||
73 | 74 | ||
74 | obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o | 75 | obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o |
75 | obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o | 76 | obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o |
diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c new file mode 100644 index 00000000000..be7befd6094 --- /dev/null +++ b/drivers/media/video/adp1653.c | |||
@@ -0,0 +1,491 @@ | |||
1 | /* | ||
2 | * drivers/media/video/adp1653.c | ||
3 | * | ||
4 | * Copyright (C) 2008--2011 Nokia Corporation | ||
5 | * | ||
6 | * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
7 | * | ||
8 | * Contributors: | ||
9 | * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
10 | * Tuukka Toivonen <tuukkat76@gmail.com> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * version 2 as published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
24 | * 02110-1301 USA | ||
25 | * | ||
26 | * TODO: | ||
27 | * - fault interrupt handling | ||
28 | * - hardware strobe | ||
29 | * - power doesn't need to be ON if all lights are off | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | #include <linux/delay.h> | ||
34 | #include <linux/i2c.h> | ||
35 | #include <linux/slab.h> | ||
36 | #include <linux/version.h> | ||
37 | #include <media/adp1653.h> | ||
38 | #include <media/v4l2-device.h> | ||
39 | |||
40 | #define TIMEOUT_MAX 820000 | ||
41 | #define TIMEOUT_STEP 54600 | ||
42 | #define TIMEOUT_MIN (TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \ | ||
43 | * TIMEOUT_STEP) | ||
44 | #define TIMEOUT_US_TO_CODE(t) ((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \ | ||
45 | / TIMEOUT_STEP) | ||
46 | #define TIMEOUT_CODE_TO_US(c) (TIMEOUT_MAX - (c) * TIMEOUT_STEP) | ||
47 | |||
48 | /* Write values into ADP1653 registers. */ | ||
49 | static int adp1653_update_hw(struct adp1653_flash *flash) | ||
50 | { | ||
51 | struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); | ||
52 | u8 out_sel; | ||
53 | u8 config = 0; | ||
54 | int rval; | ||
55 | |||
56 | out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG( | ||
57 | flash->indicator_intensity->val) | ||
58 | << ADP1653_REG_OUT_SEL_ILED_SHIFT; | ||
59 | |||
60 | switch (flash->led_mode->val) { | ||
61 | case V4L2_FLASH_LED_MODE_NONE: | ||
62 | break; | ||
63 | case V4L2_FLASH_LED_MODE_FLASH: | ||
64 | /* Flash mode, light on with strobe, duration from timer */ | ||
65 | config = ADP1653_REG_CONFIG_TMR_CFG; | ||
66 | config |= TIMEOUT_US_TO_CODE(flash->flash_timeout->val) | ||
67 | << ADP1653_REG_CONFIG_TMR_SET_SHIFT; | ||
68 | break; | ||
69 | case V4L2_FLASH_LED_MODE_TORCH: | ||
70 | /* Torch mode, light immediately on, duration indefinite */ | ||
71 | out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG( | ||
72 | flash->torch_intensity->val) | ||
73 | << ADP1653_REG_OUT_SEL_HPLED_SHIFT; | ||
74 | break; | ||
75 | } | ||
76 | |||
77 | rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel); | ||
78 | if (rval < 0) | ||
79 | return rval; | ||
80 | |||
81 | rval = i2c_smbus_write_byte_data(client, ADP1653_REG_CONFIG, config); | ||
82 | if (rval < 0) | ||
83 | return rval; | ||
84 | |||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static int adp1653_get_fault(struct adp1653_flash *flash) | ||
89 | { | ||
90 | struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); | ||
91 | int fault; | ||
92 | int rval; | ||
93 | |||
94 | fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT); | ||
95 | if (IS_ERR_VALUE(fault)) | ||
96 | return fault; | ||
97 | |||
98 | flash->fault |= fault; | ||
99 | |||
100 | if (!flash->fault) | ||
101 | return 0; | ||
102 | |||
103 | /* Clear faults. */ | ||
104 | rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0); | ||
105 | if (IS_ERR_VALUE(rval)) | ||
106 | return rval; | ||
107 | |||
108 | flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE; | ||
109 | |||
110 | rval = adp1653_update_hw(flash); | ||
111 | if (IS_ERR_VALUE(rval)) | ||
112 | return rval; | ||
113 | |||
114 | return flash->fault; | ||
115 | } | ||
116 | |||
117 | static int adp1653_strobe(struct adp1653_flash *flash, int enable) | ||
118 | { | ||
119 | struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); | ||
120 | u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG( | ||
121 | flash->indicator_intensity->val) | ||
122 | << ADP1653_REG_OUT_SEL_ILED_SHIFT; | ||
123 | int rval; | ||
124 | |||
125 | if (flash->led_mode->val != V4L2_FLASH_LED_MODE_FLASH) | ||
126 | return -EBUSY; | ||
127 | |||
128 | if (!enable) | ||
129 | return i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, | ||
130 | out_sel); | ||
131 | |||
132 | out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG( | ||
133 | flash->flash_intensity->val) | ||
134 | << ADP1653_REG_OUT_SEL_HPLED_SHIFT; | ||
135 | rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel); | ||
136 | if (rval) | ||
137 | return rval; | ||
138 | |||
139 | /* Software strobe using i2c */ | ||
140 | rval = i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, | ||
141 | ADP1653_REG_SW_STROBE_SW_STROBE); | ||
142 | if (rval) | ||
143 | return rval; | ||
144 | return i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 0); | ||
145 | } | ||
146 | |||
147 | /* -------------------------------------------------------------------------- | ||
148 | * V4L2 controls | ||
149 | */ | ||
150 | |||
151 | static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl) | ||
152 | { | ||
153 | struct adp1653_flash *flash = | ||
154 | container_of(ctrl->handler, struct adp1653_flash, ctrls); | ||
155 | int rval; | ||
156 | |||
157 | rval = adp1653_get_fault(flash); | ||
158 | if (IS_ERR_VALUE(rval)) | ||
159 | return rval; | ||
160 | |||
161 | ctrl->cur.val = 0; | ||
162 | |||
163 | if (flash->fault & ADP1653_REG_FAULT_FLT_SCP) | ||
164 | ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; | ||
165 | if (flash->fault & ADP1653_REG_FAULT_FLT_OT) | ||
166 | ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE; | ||
167 | if (flash->fault & ADP1653_REG_FAULT_FLT_TMR) | ||
168 | ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT; | ||
169 | if (flash->fault & ADP1653_REG_FAULT_FLT_OV) | ||
170 | ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE; | ||
171 | |||
172 | flash->fault = 0; | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static int adp1653_set_ctrl(struct v4l2_ctrl *ctrl) | ||
178 | { | ||
179 | struct adp1653_flash *flash = | ||
180 | container_of(ctrl->handler, struct adp1653_flash, ctrls); | ||
181 | int rval; | ||
182 | |||
183 | rval = adp1653_get_fault(flash); | ||
184 | if (IS_ERR_VALUE(rval)) | ||
185 | return rval; | ||
186 | if ((rval & (ADP1653_REG_FAULT_FLT_SCP | | ||
187 | ADP1653_REG_FAULT_FLT_OT | | ||
188 | ADP1653_REG_FAULT_FLT_OV)) && | ||
189 | (ctrl->id == V4L2_CID_FLASH_STROBE || | ||
190 | ctrl->id == V4L2_CID_FLASH_TORCH_INTENSITY || | ||
191 | ctrl->id == V4L2_CID_FLASH_LED_MODE)) | ||
192 | return -EBUSY; | ||
193 | |||
194 | switch (ctrl->id) { | ||
195 | case V4L2_CID_FLASH_STROBE: | ||
196 | return adp1653_strobe(flash, 1); | ||
197 | case V4L2_CID_FLASH_STROBE_STOP: | ||
198 | return adp1653_strobe(flash, 0); | ||
199 | } | ||
200 | |||
201 | return adp1653_update_hw(flash); | ||
202 | } | ||
203 | |||
204 | static const struct v4l2_ctrl_ops adp1653_ctrl_ops = { | ||
205 | .g_volatile_ctrl = adp1653_get_ctrl, | ||
206 | .s_ctrl = adp1653_set_ctrl, | ||
207 | }; | ||
208 | |||
209 | static int adp1653_init_controls(struct adp1653_flash *flash) | ||
210 | { | ||
211 | struct v4l2_ctrl *fault; | ||
212 | |||
213 | v4l2_ctrl_handler_init(&flash->ctrls, 9); | ||
214 | |||
215 | flash->led_mode = | ||
216 | v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops, | ||
217 | V4L2_CID_FLASH_LED_MODE, | ||
218 | V4L2_FLASH_LED_MODE_TORCH, ~0x7, 0); | ||
219 | v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops, | ||
220 | V4L2_CID_FLASH_STROBE_SOURCE, | ||
221 | V4L2_FLASH_STROBE_SOURCE_SOFTWARE, ~0x1, 0); | ||
222 | v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, | ||
223 | V4L2_CID_FLASH_STROBE, 0, 0, 0, 0); | ||
224 | v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, | ||
225 | V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0); | ||
226 | flash->flash_timeout = | ||
227 | v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, | ||
228 | V4L2_CID_FLASH_TIMEOUT, TIMEOUT_MIN, | ||
229 | flash->platform_data->max_flash_timeout, | ||
230 | TIMEOUT_STEP, | ||
231 | flash->platform_data->max_flash_timeout); | ||
232 | flash->flash_intensity = | ||
233 | v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, | ||
234 | V4L2_CID_FLASH_INTENSITY, | ||
235 | ADP1653_FLASH_INTENSITY_MIN, | ||
236 | flash->platform_data->max_flash_intensity, | ||
237 | 1, flash->platform_data->max_flash_intensity); | ||
238 | flash->torch_intensity = | ||
239 | v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, | ||
240 | V4L2_CID_FLASH_TORCH_INTENSITY, | ||
241 | ADP1653_TORCH_INTENSITY_MIN, | ||
242 | flash->platform_data->max_torch_intensity, | ||
243 | ADP1653_FLASH_INTENSITY_STEP, | ||
244 | flash->platform_data->max_torch_intensity); | ||
245 | flash->indicator_intensity = | ||
246 | v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, | ||
247 | V4L2_CID_FLASH_INDICATOR_INTENSITY, | ||
248 | ADP1653_INDICATOR_INTENSITY_MIN, | ||
249 | flash->platform_data->max_indicator_intensity, | ||
250 | ADP1653_INDICATOR_INTENSITY_STEP, | ||
251 | ADP1653_INDICATOR_INTENSITY_MIN); | ||
252 | fault = v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, | ||
253 | V4L2_CID_FLASH_FAULT, 0, | ||
254 | V4L2_FLASH_FAULT_OVER_VOLTAGE | ||
255 | | V4L2_FLASH_FAULT_OVER_TEMPERATURE | ||
256 | | V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0); | ||
257 | |||
258 | if (flash->ctrls.error) | ||
259 | return flash->ctrls.error; | ||
260 | |||
261 | fault->is_volatile = 1; | ||
262 | |||
263 | flash->subdev.ctrl_handler = &flash->ctrls; | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | /* -------------------------------------------------------------------------- | ||
268 | * V4L2 subdev operations | ||
269 | */ | ||
270 | |||
271 | static int | ||
272 | adp1653_init_device(struct adp1653_flash *flash) | ||
273 | { | ||
274 | struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); | ||
275 | int rval; | ||
276 | |||
277 | /* Clear FAULT register by writing zero to OUT_SEL */ | ||
278 | rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0); | ||
279 | if (rval < 0) { | ||
280 | dev_err(&client->dev, "failed writing fault register\n"); | ||
281 | return -EIO; | ||
282 | } | ||
283 | |||
284 | mutex_lock(&flash->ctrls.lock); | ||
285 | /* Reset faults before reading new ones. */ | ||
286 | flash->fault = 0; | ||
287 | rval = adp1653_get_fault(flash); | ||
288 | mutex_unlock(&flash->ctrls.lock); | ||
289 | if (rval > 0) { | ||
290 | dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval); | ||
291 | return -EIO; | ||
292 | } | ||
293 | |||
294 | mutex_lock(&flash->ctrls.lock); | ||
295 | rval = adp1653_update_hw(flash); | ||
296 | mutex_unlock(&flash->ctrls.lock); | ||
297 | if (rval) { | ||
298 | dev_err(&client->dev, | ||
299 | "adp1653_update_hw failed at %s\n", __func__); | ||
300 | return -EIO; | ||
301 | } | ||
302 | |||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | static int | ||
307 | __adp1653_set_power(struct adp1653_flash *flash, int on) | ||
308 | { | ||
309 | int ret; | ||
310 | |||
311 | ret = flash->platform_data->power(&flash->subdev, on); | ||
312 | if (ret < 0) | ||
313 | return ret; | ||
314 | |||
315 | if (!on) | ||
316 | return 0; | ||
317 | |||
318 | ret = adp1653_init_device(flash); | ||
319 | if (ret < 0) | ||
320 | flash->platform_data->power(&flash->subdev, 0); | ||
321 | |||
322 | return ret; | ||
323 | } | ||
324 | |||
325 | static int | ||
326 | adp1653_set_power(struct v4l2_subdev *subdev, int on) | ||
327 | { | ||
328 | struct adp1653_flash *flash = to_adp1653_flash(subdev); | ||
329 | int ret = 0; | ||
330 | |||
331 | mutex_lock(&flash->power_lock); | ||
332 | |||
333 | /* If the power count is modified from 0 to != 0 or from != 0 to 0, | ||
334 | * update the power state. | ||
335 | */ | ||
336 | if (flash->power_count == !on) { | ||
337 | ret = __adp1653_set_power(flash, !!on); | ||
338 | if (ret < 0) | ||
339 | goto done; | ||
340 | } | ||
341 | |||
342 | /* Update the power count. */ | ||
343 | flash->power_count += on ? 1 : -1; | ||
344 | WARN_ON(flash->power_count < 0); | ||
345 | |||
346 | done: | ||
347 | mutex_unlock(&flash->power_lock); | ||
348 | return ret; | ||
349 | } | ||
350 | |||
351 | static int adp1653_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | ||
352 | { | ||
353 | return adp1653_set_power(sd, 1); | ||
354 | } | ||
355 | |||
356 | static int adp1653_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | ||
357 | { | ||
358 | return adp1653_set_power(sd, 0); | ||
359 | } | ||
360 | |||
361 | static const struct v4l2_subdev_core_ops adp1653_core_ops = { | ||
362 | .s_power = adp1653_set_power, | ||
363 | }; | ||
364 | |||
365 | static const struct v4l2_subdev_ops adp1653_ops = { | ||
366 | .core = &adp1653_core_ops, | ||
367 | }; | ||
368 | |||
369 | static const struct v4l2_subdev_internal_ops adp1653_internal_ops = { | ||
370 | .open = adp1653_open, | ||
371 | .close = adp1653_close, | ||
372 | }; | ||
373 | |||
374 | /* -------------------------------------------------------------------------- | ||
375 | * I2C driver | ||
376 | */ | ||
377 | #ifdef CONFIG_PM | ||
378 | |||
379 | static int adp1653_suspend(struct device *dev) | ||
380 | { | ||
381 | struct i2c_client *client = to_i2c_client(dev); | ||
382 | struct v4l2_subdev *subdev = i2c_get_clientdata(client); | ||
383 | struct adp1653_flash *flash = to_adp1653_flash(subdev); | ||
384 | |||
385 | if (!flash->power_count) | ||
386 | return 0; | ||
387 | |||
388 | return __adp1653_set_power(flash, 0); | ||
389 | } | ||
390 | |||
391 | static int adp1653_resume(struct device *dev) | ||
392 | { | ||
393 | struct i2c_client *client = to_i2c_client(dev); | ||
394 | struct v4l2_subdev *subdev = i2c_get_clientdata(client); | ||
395 | struct adp1653_flash *flash = to_adp1653_flash(subdev); | ||
396 | |||
397 | if (!flash->power_count) | ||
398 | return 0; | ||
399 | |||
400 | return __adp1653_set_power(flash, 1); | ||
401 | } | ||
402 | |||
403 | #else | ||
404 | |||
405 | #define adp1653_suspend NULL | ||
406 | #define adp1653_resume NULL | ||
407 | |||
408 | #endif /* CONFIG_PM */ | ||
409 | |||
410 | static int adp1653_probe(struct i2c_client *client, | ||
411 | const struct i2c_device_id *devid) | ||
412 | { | ||
413 | struct adp1653_flash *flash; | ||
414 | int ret; | ||
415 | |||
416 | flash = kzalloc(sizeof(*flash), GFP_KERNEL); | ||
417 | if (flash == NULL) | ||
418 | return -ENOMEM; | ||
419 | |||
420 | flash->platform_data = client->dev.platform_data; | ||
421 | |||
422 | mutex_init(&flash->power_lock); | ||
423 | |||
424 | v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops); | ||
425 | flash->subdev.internal_ops = &adp1653_internal_ops; | ||
426 | flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
427 | |||
428 | adp1653_init_controls(flash); | ||
429 | |||
430 | ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0); | ||
431 | if (ret < 0) | ||
432 | kfree(flash); | ||
433 | |||
434 | return ret; | ||
435 | } | ||
436 | |||
437 | static int __exit adp1653_remove(struct i2c_client *client) | ||
438 | { | ||
439 | struct v4l2_subdev *subdev = i2c_get_clientdata(client); | ||
440 | struct adp1653_flash *flash = to_adp1653_flash(subdev); | ||
441 | |||
442 | v4l2_device_unregister_subdev(&flash->subdev); | ||
443 | v4l2_ctrl_handler_free(&flash->ctrls); | ||
444 | media_entity_cleanup(&flash->subdev.entity); | ||
445 | kfree(flash); | ||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | static const struct i2c_device_id adp1653_id_table[] = { | ||
450 | { ADP1653_NAME, 0 }, | ||
451 | { } | ||
452 | }; | ||
453 | MODULE_DEVICE_TABLE(i2c, adp1653_id_table); | ||
454 | |||
455 | static struct dev_pm_ops adp1653_pm_ops = { | ||
456 | .suspend = adp1653_suspend, | ||
457 | .resume = adp1653_resume, | ||
458 | }; | ||
459 | |||
460 | static struct i2c_driver adp1653_i2c_driver = { | ||
461 | .driver = { | ||
462 | .name = ADP1653_NAME, | ||
463 | .pm = &adp1653_pm_ops, | ||
464 | }, | ||
465 | .probe = adp1653_probe, | ||
466 | .remove = __exit_p(adp1653_remove), | ||
467 | .id_table = adp1653_id_table, | ||
468 | }; | ||
469 | |||
470 | static int __init adp1653_init(void) | ||
471 | { | ||
472 | int rval; | ||
473 | |||
474 | rval = i2c_add_driver(&adp1653_i2c_driver); | ||
475 | if (rval) | ||
476 | printk(KERN_ALERT "%s: failed at i2c_add_driver\n", __func__); | ||
477 | |||
478 | return rval; | ||
479 | } | ||
480 | |||
481 | static void __exit adp1653_exit(void) | ||
482 | { | ||
483 | i2c_del_driver(&adp1653_i2c_driver); | ||
484 | } | ||
485 | |||
486 | module_init(adp1653_init); | ||
487 | module_exit(adp1653_exit); | ||
488 | |||
489 | MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>"); | ||
490 | MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver"); | ||
491 | MODULE_LICENSE("GPL"); | ||