diff options
author | Jacek Anaszewski <j.anaszewski@samsung.com> | 2015-06-19 03:33:22 -0400 |
---|---|---|
committer | Bryan Wu <cooloney@gmail.com> | 2015-06-22 16:56:46 -0400 |
commit | ac69b9038f8214e2a8ee0eb3a113af473a7518e4 (patch) | |
tree | fa3eab2ff21e1146e998dc5d976088cbb01c39bf /drivers/leds/leds-aat1290.c | |
parent | 8bdc89390ed645c7d7c1e23470d24fbd523e7168 (diff) |
leds: aat1290: add support for V4L2 Flash sub-device
Add support for V4L2 Flash sub-device to the aat1290 LED Flash class
driver. The support allows for V4L2 Flash sub-device to take the control
of the LED Flash class device.
Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Bryan Wu <cooloney@gmail.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
Cc: Sakari Ailus <sakari.ailus@iki.fi>
Signed-off-by: Bryan Wu <cooloney@gmail.com>
Diffstat (limited to 'drivers/leds/leds-aat1290.c')
-rw-r--r-- | drivers/leds/leds-aat1290.c | 137 |
1 files changed, 131 insertions, 6 deletions
diff --git a/drivers/leds/leds-aat1290.c b/drivers/leds/leds-aat1290.c index 6ea1d5465309..5de2f19d3c0d 100644 --- a/drivers/leds/leds-aat1290.c +++ b/drivers/leds/leds-aat1290.c | |||
@@ -17,9 +17,11 @@ | |||
17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
18 | #include <linux/mutex.h> | 18 | #include <linux/mutex.h> |
19 | #include <linux/of.h> | 19 | #include <linux/of.h> |
20 | #include <linux/pinctrl/consumer.h> | ||
20 | #include <linux/platform_device.h> | 21 | #include <linux/platform_device.h> |
21 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
22 | #include <linux/workqueue.h> | 23 | #include <linux/workqueue.h> |
24 | #include <media/v4l2-flash-led-class.h> | ||
23 | 25 | ||
24 | #define AAT1290_MOVIE_MODE_CURRENT_ADDR 17 | 26 | #define AAT1290_MOVIE_MODE_CURRENT_ADDR 17 |
25 | #define AAT1290_MAX_MM_CURR_PERCENT_0 16 | 27 | #define AAT1290_MAX_MM_CURR_PERCENT_0 16 |
@@ -52,6 +54,8 @@ struct aat1290_led_config_data { | |||
52 | u32 max_flash_current; | 54 | u32 max_flash_current; |
53 | /* maximum flash timeout */ | 55 | /* maximum flash timeout */ |
54 | u32 max_flash_tm; | 56 | u32 max_flash_tm; |
57 | /* external strobe capability */ | ||
58 | bool has_external_strobe; | ||
55 | /* max LED brightness level */ | 59 | /* max LED brightness level */ |
56 | enum led_brightness max_brightness; | 60 | enum led_brightness max_brightness; |
57 | }; | 61 | }; |
@@ -64,6 +68,8 @@ struct aat1290_led { | |||
64 | 68 | ||
65 | /* corresponding LED Flash class device */ | 69 | /* corresponding LED Flash class device */ |
66 | struct led_classdev_flash fled_cdev; | 70 | struct led_classdev_flash fled_cdev; |
71 | /* V4L2 Flash device */ | ||
72 | struct v4l2_flash *v4l2_flash; | ||
67 | 73 | ||
68 | /* FLEN pin */ | 74 | /* FLEN pin */ |
69 | struct gpio_desc *gpio_fl_en; | 75 | struct gpio_desc *gpio_fl_en; |
@@ -230,11 +236,15 @@ static int aat1290_led_flash_timeout_set(struct led_classdev_flash *fled_cdev, | |||
230 | } | 236 | } |
231 | 237 | ||
232 | static int aat1290_led_parse_dt(struct aat1290_led *led, | 238 | static int aat1290_led_parse_dt(struct aat1290_led *led, |
233 | struct aat1290_led_config_data *cfg) | 239 | struct aat1290_led_config_data *cfg, |
240 | struct device_node **sub_node) | ||
234 | { | 241 | { |
235 | struct led_classdev *led_cdev = &led->fled_cdev.led_cdev; | 242 | struct led_classdev *led_cdev = &led->fled_cdev.led_cdev; |
236 | struct device *dev = &led->pdev->dev; | 243 | struct device *dev = &led->pdev->dev; |
237 | struct device_node *child_node; | 244 | struct device_node *child_node; |
245 | #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) | ||
246 | struct pinctrl *pinctrl; | ||
247 | #endif | ||
238 | int ret = 0; | 248 | int ret = 0; |
239 | 249 | ||
240 | led->gpio_fl_en = devm_gpiod_get(dev, "flen"); | 250 | led->gpio_fl_en = devm_gpiod_get(dev, "flen"); |
@@ -251,6 +261,17 @@ static int aat1290_led_parse_dt(struct aat1290_led *led, | |||
251 | return ret; | 261 | return ret; |
252 | } | 262 | } |
253 | 263 | ||
264 | #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) | ||
265 | pinctrl = devm_pinctrl_get_select_default(&led->pdev->dev); | ||
266 | if (IS_ERR(pinctrl)) { | ||
267 | cfg->has_external_strobe = false; | ||
268 | dev_info(dev, | ||
269 | "No support for external strobe detected.\n"); | ||
270 | } else { | ||
271 | cfg->has_external_strobe = true; | ||
272 | } | ||
273 | #endif | ||
274 | |||
254 | child_node = of_get_next_available_child(dev->of_node, NULL); | 275 | child_node = of_get_next_available_child(dev->of_node, NULL); |
255 | if (!child_node) { | 276 | if (!child_node) { |
256 | dev_err(dev, "No DT child node found for connected LED.\n"); | 277 | dev_err(dev, "No DT child node found for connected LED.\n"); |
@@ -288,6 +309,8 @@ static int aat1290_led_parse_dt(struct aat1290_led *led, | |||
288 | 309 | ||
289 | of_node_put(child_node); | 310 | of_node_put(child_node); |
290 | 311 | ||
312 | *sub_node = child_node; | ||
313 | |||
291 | return ret; | 314 | return ret; |
292 | } | 315 | } |
293 | 316 | ||
@@ -316,7 +339,8 @@ int init_mm_current_scale(struct aat1290_led *led, | |||
316 | int i, max_mm_current = | 339 | int i, max_mm_current = |
317 | AAT1290_MAX_MM_CURRENT(cfg->max_flash_current); | 340 | AAT1290_MAX_MM_CURRENT(cfg->max_flash_current); |
318 | 341 | ||
319 | led->mm_current_scale = kzalloc(sizeof(max_mm_current_percent), | 342 | led->mm_current_scale = devm_kzalloc(&led->pdev->dev, |
343 | sizeof(max_mm_current_percent), | ||
320 | GFP_KERNEL); | 344 | GFP_KERNEL); |
321 | if (!led->mm_current_scale) | 345 | if (!led->mm_current_scale) |
322 | return -ENOMEM; | 346 | return -ENOMEM; |
@@ -329,11 +353,12 @@ int init_mm_current_scale(struct aat1290_led *led, | |||
329 | } | 353 | } |
330 | 354 | ||
331 | static int aat1290_led_get_configuration(struct aat1290_led *led, | 355 | static int aat1290_led_get_configuration(struct aat1290_led *led, |
332 | struct aat1290_led_config_data *cfg) | 356 | struct aat1290_led_config_data *cfg, |
357 | struct device_node **sub_node) | ||
333 | { | 358 | { |
334 | int ret; | 359 | int ret; |
335 | 360 | ||
336 | ret = aat1290_led_parse_dt(led, cfg); | 361 | ret = aat1290_led_parse_dt(led, cfg, sub_node); |
337 | if (ret < 0) | 362 | if (ret < 0) |
338 | return ret; | 363 | return ret; |
339 | /* | 364 | /* |
@@ -346,7 +371,10 @@ static int aat1290_led_get_configuration(struct aat1290_led *led, | |||
346 | 371 | ||
347 | aat1290_led_validate_mm_current(led, cfg); | 372 | aat1290_led_validate_mm_current(led, cfg); |
348 | 373 | ||
349 | kfree(led->mm_current_scale); | 374 | #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) |
375 | #else | ||
376 | devm_kfree(&led->pdev->dev, led->mm_current_scale); | ||
377 | #endif | ||
350 | 378 | ||
351 | return 0; | 379 | return 0; |
352 | } | 380 | } |
@@ -365,6 +393,88 @@ static void aat1290_init_flash_timeout(struct aat1290_led *led, | |||
365 | setting->val = setting->max; | 393 | setting->val = setting->max; |
366 | } | 394 | } |
367 | 395 | ||
396 | #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) | ||
397 | static enum led_brightness aat1290_intensity_to_brightness( | ||
398 | struct v4l2_flash *v4l2_flash, | ||
399 | s32 intensity) | ||
400 | { | ||
401 | struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; | ||
402 | struct aat1290_led *led = fled_cdev_to_led(fled_cdev); | ||
403 | int i; | ||
404 | |||
405 | for (i = AAT1290_MM_CURRENT_SCALE_SIZE - 1; i >= 0; --i) | ||
406 | if (intensity >= led->mm_current_scale[i]) | ||
407 | return i + 1; | ||
408 | |||
409 | return 1; | ||
410 | } | ||
411 | |||
412 | static s32 aat1290_brightness_to_intensity(struct v4l2_flash *v4l2_flash, | ||
413 | enum led_brightness brightness) | ||
414 | { | ||
415 | struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; | ||
416 | struct aat1290_led *led = fled_cdev_to_led(fled_cdev); | ||
417 | |||
418 | return led->mm_current_scale[brightness - 1]; | ||
419 | } | ||
420 | |||
421 | static int aat1290_led_external_strobe_set(struct v4l2_flash *v4l2_flash, | ||
422 | bool enable) | ||
423 | { | ||
424 | struct aat1290_led *led = fled_cdev_to_led(v4l2_flash->fled_cdev); | ||
425 | struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; | ||
426 | struct led_classdev *led_cdev = &fled_cdev->led_cdev; | ||
427 | struct pinctrl *pinctrl; | ||
428 | |||
429 | gpiod_direction_output(led->gpio_fl_en, 0); | ||
430 | gpiod_direction_output(led->gpio_en_set, 0); | ||
431 | |||
432 | led->movie_mode = false; | ||
433 | led_cdev->brightness = 0; | ||
434 | |||
435 | pinctrl = devm_pinctrl_get_select(&led->pdev->dev, | ||
436 | enable ? "isp" : "host"); | ||
437 | if (IS_ERR(pinctrl)) { | ||
438 | dev_warn(&led->pdev->dev, "Unable to switch strobe source.\n"); | ||
439 | return PTR_ERR(pinctrl); | ||
440 | } | ||
441 | |||
442 | return 0; | ||
443 | } | ||
444 | |||
445 | static void aat1290_init_v4l2_flash_config(struct aat1290_led *led, | ||
446 | struct aat1290_led_config_data *led_cfg, | ||
447 | struct v4l2_flash_config *v4l2_sd_cfg) | ||
448 | { | ||
449 | struct led_classdev *led_cdev = &led->fled_cdev.led_cdev; | ||
450 | struct led_flash_setting *s; | ||
451 | |||
452 | strlcpy(v4l2_sd_cfg->dev_name, led_cdev->name, | ||
453 | sizeof(v4l2_sd_cfg->dev_name)); | ||
454 | |||
455 | s = &v4l2_sd_cfg->torch_intensity; | ||
456 | s->min = led->mm_current_scale[0]; | ||
457 | s->max = led_cfg->max_mm_current; | ||
458 | s->step = 1; | ||
459 | s->val = s->max; | ||
460 | |||
461 | v4l2_sd_cfg->has_external_strobe = led_cfg->has_external_strobe; | ||
462 | } | ||
463 | |||
464 | static const struct v4l2_flash_ops v4l2_flash_ops = { | ||
465 | .external_strobe_set = aat1290_led_external_strobe_set, | ||
466 | .intensity_to_led_brightness = aat1290_intensity_to_brightness, | ||
467 | .led_brightness_to_intensity = aat1290_brightness_to_intensity, | ||
468 | }; | ||
469 | #else | ||
470 | static inline void aat1290_init_v4l2_flash_config(struct aat1290_led *led, | ||
471 | struct aat1290_led_config_data *led_cfg, | ||
472 | struct v4l2_flash_config *v4l2_sd_cfg) | ||
473 | { | ||
474 | } | ||
475 | static const struct v4l2_flash_ops v4l2_flash_ops; | ||
476 | #endif | ||
477 | |||
368 | static const struct led_flash_ops flash_ops = { | 478 | static const struct led_flash_ops flash_ops = { |
369 | .strobe_set = aat1290_led_flash_strobe_set, | 479 | .strobe_set = aat1290_led_flash_strobe_set, |
370 | .timeout_set = aat1290_led_flash_timeout_set, | 480 | .timeout_set = aat1290_led_flash_timeout_set, |
@@ -373,10 +483,12 @@ static const struct led_flash_ops flash_ops = { | |||
373 | static int aat1290_led_probe(struct platform_device *pdev) | 483 | static int aat1290_led_probe(struct platform_device *pdev) |
374 | { | 484 | { |
375 | struct device *dev = &pdev->dev; | 485 | struct device *dev = &pdev->dev; |
486 | struct device_node *sub_node = NULL; | ||
376 | struct aat1290_led *led; | 487 | struct aat1290_led *led; |
377 | struct led_classdev *led_cdev; | 488 | struct led_classdev *led_cdev; |
378 | struct led_classdev_flash *fled_cdev; | 489 | struct led_classdev_flash *fled_cdev; |
379 | struct aat1290_led_config_data led_cfg = {}; | 490 | struct aat1290_led_config_data led_cfg = {}; |
491 | struct v4l2_flash_config v4l2_sd_cfg = {}; | ||
380 | int ret; | 492 | int ret; |
381 | 493 | ||
382 | led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); | 494 | led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); |
@@ -390,7 +502,7 @@ static int aat1290_led_probe(struct platform_device *pdev) | |||
390 | fled_cdev->ops = &flash_ops; | 502 | fled_cdev->ops = &flash_ops; |
391 | led_cdev = &fled_cdev->led_cdev; | 503 | led_cdev = &fled_cdev->led_cdev; |
392 | 504 | ||
393 | ret = aat1290_led_get_configuration(led, &led_cfg); | 505 | ret = aat1290_led_get_configuration(led, &led_cfg, &sub_node); |
394 | if (ret < 0) | 506 | if (ret < 0) |
395 | return ret; | 507 | return ret; |
396 | 508 | ||
@@ -410,8 +522,20 @@ static int aat1290_led_probe(struct platform_device *pdev) | |||
410 | if (ret < 0) | 522 | if (ret < 0) |
411 | goto err_flash_register; | 523 | goto err_flash_register; |
412 | 524 | ||
525 | aat1290_init_v4l2_flash_config(led, &led_cfg, &v4l2_sd_cfg); | ||
526 | |||
527 | /* Create V4L2 Flash subdev. */ | ||
528 | led->v4l2_flash = v4l2_flash_init(dev, sub_node, fled_cdev, NULL, | ||
529 | &v4l2_flash_ops, &v4l2_sd_cfg); | ||
530 | if (IS_ERR(led->v4l2_flash)) { | ||
531 | ret = PTR_ERR(led->v4l2_flash); | ||
532 | goto error_v4l2_flash_init; | ||
533 | } | ||
534 | |||
413 | return 0; | 535 | return 0; |
414 | 536 | ||
537 | error_v4l2_flash_init: | ||
538 | led_classdev_flash_unregister(fled_cdev); | ||
415 | err_flash_register: | 539 | err_flash_register: |
416 | mutex_destroy(&led->lock); | 540 | mutex_destroy(&led->lock); |
417 | 541 | ||
@@ -422,6 +546,7 @@ static int aat1290_led_remove(struct platform_device *pdev) | |||
422 | { | 546 | { |
423 | struct aat1290_led *led = platform_get_drvdata(pdev); | 547 | struct aat1290_led *led = platform_get_drvdata(pdev); |
424 | 548 | ||
549 | v4l2_flash_release(led->v4l2_flash); | ||
425 | led_classdev_flash_unregister(&led->fled_cdev); | 550 | led_classdev_flash_unregister(&led->fled_cdev); |
426 | cancel_work_sync(&led->work_brightness_set); | 551 | cancel_work_sync(&led->work_brightness_set); |
427 | 552 | ||