1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 242bd6f59SJacek Anaszewski /* 342bd6f59SJacek Anaszewski * V4L2 flash LED sub-device registration helpers. 442bd6f59SJacek Anaszewski * 542bd6f59SJacek Anaszewski * Copyright (C) 2015 Samsung Electronics Co., Ltd 642bd6f59SJacek Anaszewski * Author: Jacek Anaszewski <j.anaszewski@samsung.com> 742bd6f59SJacek Anaszewski */ 842bd6f59SJacek Anaszewski 942bd6f59SJacek Anaszewski #include <linux/led-class-flash.h> 1042bd6f59SJacek Anaszewski #include <linux/module.h> 1142bd6f59SJacek Anaszewski #include <linux/mutex.h> 12048ea05bSSakari Ailus #include <linux/property.h> 1342bd6f59SJacek Anaszewski #include <linux/slab.h> 1442bd6f59SJacek Anaszewski #include <linux/types.h> 1542bd6f59SJacek Anaszewski #include <media/v4l2-flash-led-class.h> 1642bd6f59SJacek Anaszewski 1742bd6f59SJacek Anaszewski #define has_flash_op(v4l2_flash, op) \ 1852740975SSakari Ailus (v4l2_flash && v4l2_flash->ops && v4l2_flash->ops->op) 1942bd6f59SJacek Anaszewski 2042bd6f59SJacek Anaszewski #define call_flash_op(v4l2_flash, op, arg) \ 2142bd6f59SJacek Anaszewski (has_flash_op(v4l2_flash, op) ? \ 2242bd6f59SJacek Anaszewski v4l2_flash->ops->op(v4l2_flash, arg) : \ 2342bd6f59SJacek Anaszewski -EINVAL) 2442bd6f59SJacek Anaszewski 2542bd6f59SJacek Anaszewski enum ctrl_init_data_id { 2642bd6f59SJacek Anaszewski LED_MODE, 2742bd6f59SJacek Anaszewski TORCH_INTENSITY, 2842bd6f59SJacek Anaszewski FLASH_INTENSITY, 2942bd6f59SJacek Anaszewski INDICATOR_INTENSITY, 3042bd6f59SJacek Anaszewski FLASH_TIMEOUT, 3142bd6f59SJacek Anaszewski STROBE_SOURCE, 3242bd6f59SJacek Anaszewski /* 3342bd6f59SJacek Anaszewski * Only above values are applicable to 3442bd6f59SJacek Anaszewski * the 'ctrls' array in the struct v4l2_flash. 3542bd6f59SJacek Anaszewski */ 3642bd6f59SJacek Anaszewski FLASH_STROBE, 3742bd6f59SJacek Anaszewski STROBE_STOP, 3842bd6f59SJacek Anaszewski STROBE_STATUS, 3942bd6f59SJacek Anaszewski FLASH_FAULT, 4042bd6f59SJacek Anaszewski NUM_FLASH_CTRLS, 4142bd6f59SJacek Anaszewski }; 4242bd6f59SJacek Anaszewski 4342bd6f59SJacek Anaszewski static enum led_brightness __intensity_to_led_brightness( 4442bd6f59SJacek Anaszewski struct v4l2_ctrl *ctrl, s32 intensity) 4542bd6f59SJacek Anaszewski { 4642bd6f59SJacek Anaszewski intensity -= ctrl->minimum; 4742bd6f59SJacek Anaszewski intensity /= (u32) ctrl->step; 4842bd6f59SJacek Anaszewski 4942bd6f59SJacek Anaszewski /* 5042bd6f59SJacek Anaszewski * Indicator LEDs, unlike torch LEDs, are turned on/off basing on 5142bd6f59SJacek Anaszewski * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only. 5242bd6f59SJacek Anaszewski * Therefore it must be possible to set it to 0 level which in 5342bd6f59SJacek Anaszewski * the LED subsystem reflects LED_OFF state. 5442bd6f59SJacek Anaszewski */ 5542bd6f59SJacek Anaszewski if (ctrl->minimum) 5642bd6f59SJacek Anaszewski ++intensity; 5742bd6f59SJacek Anaszewski 5842bd6f59SJacek Anaszewski return intensity; 5942bd6f59SJacek Anaszewski } 6042bd6f59SJacek Anaszewski 6142bd6f59SJacek Anaszewski static s32 __led_brightness_to_intensity(struct v4l2_ctrl *ctrl, 6242bd6f59SJacek Anaszewski enum led_brightness brightness) 6342bd6f59SJacek Anaszewski { 6442bd6f59SJacek Anaszewski /* 6542bd6f59SJacek Anaszewski * Indicator LEDs, unlike torch LEDs, are turned on/off basing on 6642bd6f59SJacek Anaszewski * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only. 6742bd6f59SJacek Anaszewski * Do not decrement brightness read from the LED subsystem for 6842bd6f59SJacek Anaszewski * indicator LED as it may equal 0. For torch LEDs this function 6942bd6f59SJacek Anaszewski * is called only when V4L2_FLASH_LED_MODE_TORCH is set and the 7042bd6f59SJacek Anaszewski * brightness read is guaranteed to be greater than 0. In the mode 7142bd6f59SJacek Anaszewski * V4L2_FLASH_LED_MODE_NONE the cached torch intensity value is used. 7242bd6f59SJacek Anaszewski */ 7342bd6f59SJacek Anaszewski if (ctrl->id != V4L2_CID_FLASH_INDICATOR_INTENSITY) 7442bd6f59SJacek Anaszewski --brightness; 7542bd6f59SJacek Anaszewski 7642bd6f59SJacek Anaszewski return (brightness * ctrl->step) + ctrl->minimum; 7742bd6f59SJacek Anaszewski } 7842bd6f59SJacek Anaszewski 7942bd6f59SJacek Anaszewski static void v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash, 8042bd6f59SJacek Anaszewski struct v4l2_ctrl *ctrl) 8142bd6f59SJacek Anaszewski { 8242bd6f59SJacek Anaszewski struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; 8342bd6f59SJacek Anaszewski enum led_brightness brightness; 8442bd6f59SJacek Anaszewski 8542bd6f59SJacek Anaszewski if (has_flash_op(v4l2_flash, intensity_to_led_brightness)) 8642bd6f59SJacek Anaszewski brightness = call_flash_op(v4l2_flash, 8742bd6f59SJacek Anaszewski intensity_to_led_brightness, 8842bd6f59SJacek Anaszewski ctrl->val); 8942bd6f59SJacek Anaszewski else 9042bd6f59SJacek Anaszewski brightness = __intensity_to_led_brightness(ctrl, ctrl->val); 9142bd6f59SJacek Anaszewski /* 9242bd6f59SJacek Anaszewski * In case a LED Flash class driver provides ops for custom 9342bd6f59SJacek Anaszewski * brightness <-> intensity conversion, it also must have defined 9442bd6f59SJacek Anaszewski * related v4l2 control step == 1. In such a case a backward conversion 9542bd6f59SJacek Anaszewski * from led brightness to v4l2 intensity is required to find out the 9642bd6f59SJacek Anaszewski * the aligned intensity value. 9742bd6f59SJacek Anaszewski */ 9842bd6f59SJacek Anaszewski if (has_flash_op(v4l2_flash, led_brightness_to_intensity)) 9942bd6f59SJacek Anaszewski ctrl->val = call_flash_op(v4l2_flash, 10042bd6f59SJacek Anaszewski led_brightness_to_intensity, 10142bd6f59SJacek Anaszewski brightness); 10242bd6f59SJacek Anaszewski 10342bd6f59SJacek Anaszewski if (ctrl == ctrls[TORCH_INTENSITY]) { 10442bd6f59SJacek Anaszewski if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH) 10542bd6f59SJacek Anaszewski return; 10642bd6f59SJacek Anaszewski 107fa1706c4SJacek Anaszewski led_set_brightness_sync(&v4l2_flash->fled_cdev->led_cdev, 10842bd6f59SJacek Anaszewski brightness); 10942bd6f59SJacek Anaszewski } else { 11085f7ff97SSakari Ailus led_set_brightness_sync(v4l2_flash->iled_cdev, 11142bd6f59SJacek Anaszewski brightness); 11242bd6f59SJacek Anaszewski } 11342bd6f59SJacek Anaszewski } 11442bd6f59SJacek Anaszewski 11542bd6f59SJacek Anaszewski static int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash, 11642bd6f59SJacek Anaszewski struct v4l2_ctrl *ctrl) 11742bd6f59SJacek Anaszewski { 11842bd6f59SJacek Anaszewski struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; 11942bd6f59SJacek Anaszewski struct led_classdev *led_cdev; 12042bd6f59SJacek Anaszewski int ret; 12142bd6f59SJacek Anaszewski 12242bd6f59SJacek Anaszewski if (ctrl == ctrls[TORCH_INTENSITY]) { 12342bd6f59SJacek Anaszewski /* 12442bd6f59SJacek Anaszewski * Update torch brightness only if in TORCH_MODE. In other modes 12542bd6f59SJacek Anaszewski * torch led is turned off, which would spuriously inform the 12642bd6f59SJacek Anaszewski * user space that V4L2_CID_FLASH_TORCH_INTENSITY control value 12742bd6f59SJacek Anaszewski * has changed to 0. 12842bd6f59SJacek Anaszewski */ 12942bd6f59SJacek Anaszewski if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH) 13042bd6f59SJacek Anaszewski return 0; 13142bd6f59SJacek Anaszewski led_cdev = &v4l2_flash->fled_cdev->led_cdev; 13242bd6f59SJacek Anaszewski } else { 13385f7ff97SSakari Ailus led_cdev = v4l2_flash->iled_cdev; 13442bd6f59SJacek Anaszewski } 13542bd6f59SJacek Anaszewski 13642bd6f59SJacek Anaszewski ret = led_update_brightness(led_cdev); 13742bd6f59SJacek Anaszewski if (ret < 0) 13842bd6f59SJacek Anaszewski return ret; 13942bd6f59SJacek Anaszewski 14042bd6f59SJacek Anaszewski if (has_flash_op(v4l2_flash, led_brightness_to_intensity)) 14142bd6f59SJacek Anaszewski ctrl->val = call_flash_op(v4l2_flash, 14242bd6f59SJacek Anaszewski led_brightness_to_intensity, 14342bd6f59SJacek Anaszewski led_cdev->brightness); 14442bd6f59SJacek Anaszewski else 14542bd6f59SJacek Anaszewski ctrl->val = __led_brightness_to_intensity(ctrl, 14642bd6f59SJacek Anaszewski led_cdev->brightness); 14742bd6f59SJacek Anaszewski 14842bd6f59SJacek Anaszewski return 0; 14942bd6f59SJacek Anaszewski } 15042bd6f59SJacek Anaszewski 15142bd6f59SJacek Anaszewski static int v4l2_flash_g_volatile_ctrl(struct v4l2_ctrl *c) 15242bd6f59SJacek Anaszewski { 15342bd6f59SJacek Anaszewski struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c); 15442bd6f59SJacek Anaszewski struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 15542bd6f59SJacek Anaszewski bool is_strobing; 15642bd6f59SJacek Anaszewski int ret; 15742bd6f59SJacek Anaszewski 15842bd6f59SJacek Anaszewski switch (c->id) { 15942bd6f59SJacek Anaszewski case V4L2_CID_FLASH_TORCH_INTENSITY: 16042bd6f59SJacek Anaszewski case V4L2_CID_FLASH_INDICATOR_INTENSITY: 16142bd6f59SJacek Anaszewski return v4l2_flash_update_led_brightness(v4l2_flash, c); 16242bd6f59SJacek Anaszewski case V4L2_CID_FLASH_INTENSITY: 16342bd6f59SJacek Anaszewski ret = led_update_flash_brightness(fled_cdev); 16442bd6f59SJacek Anaszewski if (ret < 0) 16542bd6f59SJacek Anaszewski return ret; 16642bd6f59SJacek Anaszewski /* 16742bd6f59SJacek Anaszewski * No conversion is needed as LED Flash class also uses 16842bd6f59SJacek Anaszewski * microamperes for flash intensity units. 16942bd6f59SJacek Anaszewski */ 17042bd6f59SJacek Anaszewski c->val = fled_cdev->brightness.val; 17142bd6f59SJacek Anaszewski return 0; 17242bd6f59SJacek Anaszewski case V4L2_CID_FLASH_STROBE_STATUS: 17342bd6f59SJacek Anaszewski ret = led_get_flash_strobe(fled_cdev, &is_strobing); 17442bd6f59SJacek Anaszewski if (ret < 0) 17542bd6f59SJacek Anaszewski return ret; 17642bd6f59SJacek Anaszewski c->val = is_strobing; 17742bd6f59SJacek Anaszewski return 0; 17842bd6f59SJacek Anaszewski case V4L2_CID_FLASH_FAULT: 17942bd6f59SJacek Anaszewski /* LED faults map directly to V4L2 flash faults */ 18042bd6f59SJacek Anaszewski return led_get_flash_fault(fled_cdev, &c->val); 18142bd6f59SJacek Anaszewski default: 18242bd6f59SJacek Anaszewski return -EINVAL; 18342bd6f59SJacek Anaszewski } 18442bd6f59SJacek Anaszewski } 18542bd6f59SJacek Anaszewski 18642bd6f59SJacek Anaszewski static bool __software_strobe_mode_inactive(struct v4l2_ctrl **ctrls) 18742bd6f59SJacek Anaszewski { 18842bd6f59SJacek Anaszewski return ((ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH) || 18942bd6f59SJacek Anaszewski (ctrls[STROBE_SOURCE] && (ctrls[STROBE_SOURCE]->val != 19042bd6f59SJacek Anaszewski V4L2_FLASH_STROBE_SOURCE_SOFTWARE))); 19142bd6f59SJacek Anaszewski } 19242bd6f59SJacek Anaszewski 19342bd6f59SJacek Anaszewski static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c) 19442bd6f59SJacek Anaszewski { 19542bd6f59SJacek Anaszewski struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c); 19642bd6f59SJacek Anaszewski struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 197503dd28aSSakari Ailus struct led_classdev *led_cdev = fled_cdev ? &fled_cdev->led_cdev : NULL; 19842bd6f59SJacek Anaszewski struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; 19942bd6f59SJacek Anaszewski bool external_strobe; 20042bd6f59SJacek Anaszewski int ret = 0; 20142bd6f59SJacek Anaszewski 20242bd6f59SJacek Anaszewski switch (c->id) { 20342bd6f59SJacek Anaszewski case V4L2_CID_FLASH_LED_MODE: 20442bd6f59SJacek Anaszewski switch (c->val) { 20542bd6f59SJacek Anaszewski case V4L2_FLASH_LED_MODE_NONE: 206fa1706c4SJacek Anaszewski led_set_brightness_sync(led_cdev, LED_OFF); 20742bd6f59SJacek Anaszewski return led_set_flash_strobe(fled_cdev, false); 20842bd6f59SJacek Anaszewski case V4L2_FLASH_LED_MODE_FLASH: 20942bd6f59SJacek Anaszewski /* Turn the torch LED off */ 210fa1706c4SJacek Anaszewski led_set_brightness_sync(led_cdev, LED_OFF); 21142bd6f59SJacek Anaszewski if (ctrls[STROBE_SOURCE]) { 21242bd6f59SJacek Anaszewski external_strobe = (ctrls[STROBE_SOURCE]->val == 21342bd6f59SJacek Anaszewski V4L2_FLASH_STROBE_SOURCE_EXTERNAL); 21442bd6f59SJacek Anaszewski 21542bd6f59SJacek Anaszewski ret = call_flash_op(v4l2_flash, 21642bd6f59SJacek Anaszewski external_strobe_set, 21742bd6f59SJacek Anaszewski external_strobe); 21842bd6f59SJacek Anaszewski } 21942bd6f59SJacek Anaszewski return ret; 22042bd6f59SJacek Anaszewski case V4L2_FLASH_LED_MODE_TORCH: 22142bd6f59SJacek Anaszewski if (ctrls[STROBE_SOURCE]) { 22242bd6f59SJacek Anaszewski ret = call_flash_op(v4l2_flash, 22342bd6f59SJacek Anaszewski external_strobe_set, 22442bd6f59SJacek Anaszewski false); 22542bd6f59SJacek Anaszewski if (ret < 0) 22642bd6f59SJacek Anaszewski return ret; 22742bd6f59SJacek Anaszewski } 22842bd6f59SJacek Anaszewski /* Stop flash strobing */ 22942bd6f59SJacek Anaszewski ret = led_set_flash_strobe(fled_cdev, false); 23042bd6f59SJacek Anaszewski if (ret < 0) 23142bd6f59SJacek Anaszewski return ret; 23242bd6f59SJacek Anaszewski 23342bd6f59SJacek Anaszewski v4l2_flash_set_led_brightness(v4l2_flash, 23442bd6f59SJacek Anaszewski ctrls[TORCH_INTENSITY]); 23542bd6f59SJacek Anaszewski return 0; 23642bd6f59SJacek Anaszewski } 23742bd6f59SJacek Anaszewski break; 23842bd6f59SJacek Anaszewski case V4L2_CID_FLASH_STROBE_SOURCE: 23942bd6f59SJacek Anaszewski external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL); 24042bd6f59SJacek Anaszewski /* 24142bd6f59SJacek Anaszewski * For some hardware arrangements setting strobe source may 24242bd6f59SJacek Anaszewski * affect torch mode. Therefore, if not in the flash mode, 24342bd6f59SJacek Anaszewski * cache only this setting. It will be applied upon switching 24442bd6f59SJacek Anaszewski * to flash mode. 24542bd6f59SJacek Anaszewski */ 24642bd6f59SJacek Anaszewski if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH) 24742bd6f59SJacek Anaszewski return 0; 24842bd6f59SJacek Anaszewski 24942bd6f59SJacek Anaszewski return call_flash_op(v4l2_flash, external_strobe_set, 25042bd6f59SJacek Anaszewski external_strobe); 25142bd6f59SJacek Anaszewski case V4L2_CID_FLASH_STROBE: 25242bd6f59SJacek Anaszewski if (__software_strobe_mode_inactive(ctrls)) 25342bd6f59SJacek Anaszewski return -EBUSY; 25442bd6f59SJacek Anaszewski return led_set_flash_strobe(fled_cdev, true); 25542bd6f59SJacek Anaszewski case V4L2_CID_FLASH_STROBE_STOP: 25642bd6f59SJacek Anaszewski if (__software_strobe_mode_inactive(ctrls)) 25742bd6f59SJacek Anaszewski return -EBUSY; 25842bd6f59SJacek Anaszewski return led_set_flash_strobe(fled_cdev, false); 25942bd6f59SJacek Anaszewski case V4L2_CID_FLASH_TIMEOUT: 26042bd6f59SJacek Anaszewski /* 26142bd6f59SJacek Anaszewski * No conversion is needed as LED Flash class also uses 26242bd6f59SJacek Anaszewski * microseconds for flash timeout units. 26342bd6f59SJacek Anaszewski */ 26442bd6f59SJacek Anaszewski return led_set_flash_timeout(fled_cdev, c->val); 26542bd6f59SJacek Anaszewski case V4L2_CID_FLASH_INTENSITY: 26642bd6f59SJacek Anaszewski /* 26742bd6f59SJacek Anaszewski * No conversion is needed as LED Flash class also uses 26842bd6f59SJacek Anaszewski * microamperes for flash intensity units. 26942bd6f59SJacek Anaszewski */ 27042bd6f59SJacek Anaszewski return led_set_flash_brightness(fled_cdev, c->val); 27142bd6f59SJacek Anaszewski case V4L2_CID_FLASH_TORCH_INTENSITY: 27242bd6f59SJacek Anaszewski case V4L2_CID_FLASH_INDICATOR_INTENSITY: 27342bd6f59SJacek Anaszewski v4l2_flash_set_led_brightness(v4l2_flash, c); 27442bd6f59SJacek Anaszewski return 0; 27542bd6f59SJacek Anaszewski } 27642bd6f59SJacek Anaszewski 27742bd6f59SJacek Anaszewski return -EINVAL; 27842bd6f59SJacek Anaszewski } 27942bd6f59SJacek Anaszewski 28042bd6f59SJacek Anaszewski static const struct v4l2_ctrl_ops v4l2_flash_ctrl_ops = { 28142bd6f59SJacek Anaszewski .g_volatile_ctrl = v4l2_flash_g_volatile_ctrl, 28242bd6f59SJacek Anaszewski .s_ctrl = v4l2_flash_s_ctrl, 28342bd6f59SJacek Anaszewski }; 28442bd6f59SJacek Anaszewski 28542bd6f59SJacek Anaszewski static void __lfs_to_v4l2_ctrl_config(struct led_flash_setting *s, 28642bd6f59SJacek Anaszewski struct v4l2_ctrl_config *c) 28742bd6f59SJacek Anaszewski { 28842bd6f59SJacek Anaszewski c->min = s->min; 28942bd6f59SJacek Anaszewski c->max = s->max; 29042bd6f59SJacek Anaszewski c->step = s->step; 29142bd6f59SJacek Anaszewski c->def = s->val; 29242bd6f59SJacek Anaszewski } 29342bd6f59SJacek Anaszewski 29442bd6f59SJacek Anaszewski static void __fill_ctrl_init_data(struct v4l2_flash *v4l2_flash, 29542bd6f59SJacek Anaszewski struct v4l2_flash_config *flash_cfg, 29642bd6f59SJacek Anaszewski struct v4l2_flash_ctrl_data *ctrl_init_data) 29742bd6f59SJacek Anaszewski { 29842bd6f59SJacek Anaszewski struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 299503dd28aSSakari Ailus struct led_classdev *led_cdev = fled_cdev ? &fled_cdev->led_cdev : NULL; 30042bd6f59SJacek Anaszewski struct v4l2_ctrl_config *ctrl_cfg; 30142bd6f59SJacek Anaszewski u32 mask; 30242bd6f59SJacek Anaszewski 303503dd28aSSakari Ailus /* Init INDICATOR_INTENSITY ctrl data */ 304503dd28aSSakari Ailus if (v4l2_flash->iled_cdev) { 305503dd28aSSakari Ailus ctrl_init_data[INDICATOR_INTENSITY].cid = 306503dd28aSSakari Ailus V4L2_CID_FLASH_INDICATOR_INTENSITY; 307503dd28aSSakari Ailus ctrl_cfg = &ctrl_init_data[INDICATOR_INTENSITY].config; 308503dd28aSSakari Ailus __lfs_to_v4l2_ctrl_config(&flash_cfg->intensity, 309503dd28aSSakari Ailus ctrl_cfg); 310503dd28aSSakari Ailus ctrl_cfg->id = V4L2_CID_FLASH_INDICATOR_INTENSITY; 311503dd28aSSakari Ailus ctrl_cfg->min = 0; 312503dd28aSSakari Ailus ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | 313503dd28aSSakari Ailus V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; 314503dd28aSSakari Ailus } 315503dd28aSSakari Ailus 316503dd28aSSakari Ailus if (!led_cdev || WARN_ON(!(led_cdev->flags & LED_DEV_CAP_FLASH))) 317503dd28aSSakari Ailus return; 318503dd28aSSakari Ailus 31942bd6f59SJacek Anaszewski /* Init FLASH_FAULT ctrl data */ 32042bd6f59SJacek Anaszewski if (flash_cfg->flash_faults) { 32142bd6f59SJacek Anaszewski ctrl_init_data[FLASH_FAULT].cid = V4L2_CID_FLASH_FAULT; 32242bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[FLASH_FAULT].config; 32342bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_FAULT; 32442bd6f59SJacek Anaszewski ctrl_cfg->max = flash_cfg->flash_faults; 32542bd6f59SJacek Anaszewski ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | 32642bd6f59SJacek Anaszewski V4L2_CTRL_FLAG_READ_ONLY; 32742bd6f59SJacek Anaszewski } 32842bd6f59SJacek Anaszewski 32942bd6f59SJacek Anaszewski /* Init FLASH_LED_MODE ctrl data */ 33042bd6f59SJacek Anaszewski mask = 1 << V4L2_FLASH_LED_MODE_NONE | 33142bd6f59SJacek Anaszewski 1 << V4L2_FLASH_LED_MODE_TORCH; 33242bd6f59SJacek Anaszewski if (led_cdev->flags & LED_DEV_CAP_FLASH) 33342bd6f59SJacek Anaszewski mask |= 1 << V4L2_FLASH_LED_MODE_FLASH; 33442bd6f59SJacek Anaszewski 33542bd6f59SJacek Anaszewski ctrl_init_data[LED_MODE].cid = V4L2_CID_FLASH_LED_MODE; 33642bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[LED_MODE].config; 33742bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_LED_MODE; 33842bd6f59SJacek Anaszewski ctrl_cfg->max = V4L2_FLASH_LED_MODE_TORCH; 33942bd6f59SJacek Anaszewski ctrl_cfg->menu_skip_mask = ~mask; 34042bd6f59SJacek Anaszewski ctrl_cfg->def = V4L2_FLASH_LED_MODE_NONE; 34142bd6f59SJacek Anaszewski ctrl_cfg->flags = 0; 34242bd6f59SJacek Anaszewski 34342bd6f59SJacek Anaszewski /* Init TORCH_INTENSITY ctrl data */ 34442bd6f59SJacek Anaszewski ctrl_init_data[TORCH_INTENSITY].cid = V4L2_CID_FLASH_TORCH_INTENSITY; 34542bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[TORCH_INTENSITY].config; 346503dd28aSSakari Ailus __lfs_to_v4l2_ctrl_config(&flash_cfg->intensity, ctrl_cfg); 34742bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_TORCH_INTENSITY; 34842bd6f59SJacek Anaszewski ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | 34942bd6f59SJacek Anaszewski V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; 35042bd6f59SJacek Anaszewski 35142bd6f59SJacek Anaszewski /* Init FLASH_STROBE ctrl data */ 35242bd6f59SJacek Anaszewski ctrl_init_data[FLASH_STROBE].cid = V4L2_CID_FLASH_STROBE; 35342bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[FLASH_STROBE].config; 35442bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_STROBE; 35542bd6f59SJacek Anaszewski 35642bd6f59SJacek Anaszewski /* Init STROBE_STOP ctrl data */ 35742bd6f59SJacek Anaszewski ctrl_init_data[STROBE_STOP].cid = V4L2_CID_FLASH_STROBE_STOP; 35842bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[STROBE_STOP].config; 35942bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STOP; 36042bd6f59SJacek Anaszewski 36142bd6f59SJacek Anaszewski /* Init FLASH_STROBE_SOURCE ctrl data */ 36242bd6f59SJacek Anaszewski if (flash_cfg->has_external_strobe) { 36342bd6f59SJacek Anaszewski mask = (1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE) | 36442bd6f59SJacek Anaszewski (1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL); 36542bd6f59SJacek Anaszewski ctrl_init_data[STROBE_SOURCE].cid = 36642bd6f59SJacek Anaszewski V4L2_CID_FLASH_STROBE_SOURCE; 36742bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[STROBE_SOURCE].config; 36842bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_STROBE_SOURCE; 36942bd6f59SJacek Anaszewski ctrl_cfg->max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL; 37042bd6f59SJacek Anaszewski ctrl_cfg->menu_skip_mask = ~mask; 37142bd6f59SJacek Anaszewski ctrl_cfg->def = V4L2_FLASH_STROBE_SOURCE_SOFTWARE; 37242bd6f59SJacek Anaszewski } 37342bd6f59SJacek Anaszewski 37442bd6f59SJacek Anaszewski /* Init STROBE_STATUS ctrl data */ 37552740975SSakari Ailus if (has_flash_op(fled_cdev, strobe_get)) { 37642bd6f59SJacek Anaszewski ctrl_init_data[STROBE_STATUS].cid = 37742bd6f59SJacek Anaszewski V4L2_CID_FLASH_STROBE_STATUS; 37842bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[STROBE_STATUS].config; 37942bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STATUS; 38042bd6f59SJacek Anaszewski ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | 38142bd6f59SJacek Anaszewski V4L2_CTRL_FLAG_READ_ONLY; 38242bd6f59SJacek Anaszewski } 38342bd6f59SJacek Anaszewski 38442bd6f59SJacek Anaszewski /* Init FLASH_TIMEOUT ctrl data */ 38552740975SSakari Ailus if (has_flash_op(fled_cdev, timeout_set)) { 38642bd6f59SJacek Anaszewski ctrl_init_data[FLASH_TIMEOUT].cid = V4L2_CID_FLASH_TIMEOUT; 38742bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[FLASH_TIMEOUT].config; 38842bd6f59SJacek Anaszewski __lfs_to_v4l2_ctrl_config(&fled_cdev->timeout, ctrl_cfg); 38942bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_TIMEOUT; 39042bd6f59SJacek Anaszewski } 39142bd6f59SJacek Anaszewski 39242bd6f59SJacek Anaszewski /* Init FLASH_INTENSITY ctrl data */ 39352740975SSakari Ailus if (has_flash_op(fled_cdev, flash_brightness_set)) { 39442bd6f59SJacek Anaszewski ctrl_init_data[FLASH_INTENSITY].cid = V4L2_CID_FLASH_INTENSITY; 39542bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[FLASH_INTENSITY].config; 39642bd6f59SJacek Anaszewski __lfs_to_v4l2_ctrl_config(&fled_cdev->brightness, ctrl_cfg); 39742bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_INTENSITY; 39842bd6f59SJacek Anaszewski ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | 39942bd6f59SJacek Anaszewski V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; 40042bd6f59SJacek Anaszewski } 40142bd6f59SJacek Anaszewski } 40242bd6f59SJacek Anaszewski 40342bd6f59SJacek Anaszewski static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash, 40442bd6f59SJacek Anaszewski struct v4l2_flash_config *flash_cfg) 40542bd6f59SJacek Anaszewski 40642bd6f59SJacek Anaszewski { 40742bd6f59SJacek Anaszewski struct v4l2_flash_ctrl_data *ctrl_init_data; 40842bd6f59SJacek Anaszewski struct v4l2_ctrl *ctrl; 40942bd6f59SJacek Anaszewski struct v4l2_ctrl_config *ctrl_cfg; 41042bd6f59SJacek Anaszewski int i, ret, num_ctrls = 0; 41142bd6f59SJacek Anaszewski 412a86854d0SKees Cook v4l2_flash->ctrls = devm_kcalloc(v4l2_flash->sd.dev, 413a86854d0SKees Cook STROBE_SOURCE + 1, 414a86854d0SKees Cook sizeof(*v4l2_flash->ctrls), 415a86854d0SKees Cook GFP_KERNEL); 41642bd6f59SJacek Anaszewski if (!v4l2_flash->ctrls) 41742bd6f59SJacek Anaszewski return -ENOMEM; 41842bd6f59SJacek Anaszewski 41942bd6f59SJacek Anaszewski /* allocate memory dynamically so as not to exceed stack frame size */ 42042bd6f59SJacek Anaszewski ctrl_init_data = kcalloc(NUM_FLASH_CTRLS, sizeof(*ctrl_init_data), 42142bd6f59SJacek Anaszewski GFP_KERNEL); 42242bd6f59SJacek Anaszewski if (!ctrl_init_data) 42342bd6f59SJacek Anaszewski return -ENOMEM; 42442bd6f59SJacek Anaszewski 42542bd6f59SJacek Anaszewski __fill_ctrl_init_data(v4l2_flash, flash_cfg, ctrl_init_data); 42642bd6f59SJacek Anaszewski 42742bd6f59SJacek Anaszewski for (i = 0; i < NUM_FLASH_CTRLS; ++i) 42842bd6f59SJacek Anaszewski if (ctrl_init_data[i].cid) 42942bd6f59SJacek Anaszewski ++num_ctrls; 43042bd6f59SJacek Anaszewski 43142bd6f59SJacek Anaszewski v4l2_ctrl_handler_init(&v4l2_flash->hdl, num_ctrls); 43242bd6f59SJacek Anaszewski 43342bd6f59SJacek Anaszewski for (i = 0; i < NUM_FLASH_CTRLS; ++i) { 43442bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[i].config; 43542bd6f59SJacek Anaszewski if (!ctrl_init_data[i].cid) 43642bd6f59SJacek Anaszewski continue; 43742bd6f59SJacek Anaszewski 43842bd6f59SJacek Anaszewski if (ctrl_cfg->id == V4L2_CID_FLASH_LED_MODE || 43942bd6f59SJacek Anaszewski ctrl_cfg->id == V4L2_CID_FLASH_STROBE_SOURCE) 44042bd6f59SJacek Anaszewski ctrl = v4l2_ctrl_new_std_menu(&v4l2_flash->hdl, 44142bd6f59SJacek Anaszewski &v4l2_flash_ctrl_ops, 44242bd6f59SJacek Anaszewski ctrl_cfg->id, 44342bd6f59SJacek Anaszewski ctrl_cfg->max, 44442bd6f59SJacek Anaszewski ctrl_cfg->menu_skip_mask, 44542bd6f59SJacek Anaszewski ctrl_cfg->def); 44642bd6f59SJacek Anaszewski else 44742bd6f59SJacek Anaszewski ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl, 44842bd6f59SJacek Anaszewski &v4l2_flash_ctrl_ops, 44942bd6f59SJacek Anaszewski ctrl_cfg->id, 45042bd6f59SJacek Anaszewski ctrl_cfg->min, 45142bd6f59SJacek Anaszewski ctrl_cfg->max, 45242bd6f59SJacek Anaszewski ctrl_cfg->step, 45342bd6f59SJacek Anaszewski ctrl_cfg->def); 45442bd6f59SJacek Anaszewski 45542bd6f59SJacek Anaszewski if (ctrl) 45642bd6f59SJacek Anaszewski ctrl->flags |= ctrl_cfg->flags; 45742bd6f59SJacek Anaszewski 45842bd6f59SJacek Anaszewski if (i <= STROBE_SOURCE) 45942bd6f59SJacek Anaszewski v4l2_flash->ctrls[i] = ctrl; 46042bd6f59SJacek Anaszewski } 46142bd6f59SJacek Anaszewski 46242bd6f59SJacek Anaszewski kfree(ctrl_init_data); 46342bd6f59SJacek Anaszewski 46442bd6f59SJacek Anaszewski if (v4l2_flash->hdl.error) { 46542bd6f59SJacek Anaszewski ret = v4l2_flash->hdl.error; 46642bd6f59SJacek Anaszewski goto error_free_handler; 46742bd6f59SJacek Anaszewski } 46842bd6f59SJacek Anaszewski 46942bd6f59SJacek Anaszewski v4l2_ctrl_handler_setup(&v4l2_flash->hdl); 47042bd6f59SJacek Anaszewski 47142bd6f59SJacek Anaszewski v4l2_flash->sd.ctrl_handler = &v4l2_flash->hdl; 47242bd6f59SJacek Anaszewski 47342bd6f59SJacek Anaszewski return 0; 47442bd6f59SJacek Anaszewski 47542bd6f59SJacek Anaszewski error_free_handler: 47642bd6f59SJacek Anaszewski v4l2_ctrl_handler_free(&v4l2_flash->hdl); 47742bd6f59SJacek Anaszewski return ret; 47842bd6f59SJacek Anaszewski } 47942bd6f59SJacek Anaszewski 48042bd6f59SJacek Anaszewski static int __sync_device_with_v4l2_controls(struct v4l2_flash *v4l2_flash) 48142bd6f59SJacek Anaszewski { 48242bd6f59SJacek Anaszewski struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 48342bd6f59SJacek Anaszewski struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; 48442bd6f59SJacek Anaszewski int ret = 0; 48542bd6f59SJacek Anaszewski 486503dd28aSSakari Ailus if (ctrls[TORCH_INTENSITY]) 487503dd28aSSakari Ailus v4l2_flash_set_led_brightness(v4l2_flash, 488503dd28aSSakari Ailus ctrls[TORCH_INTENSITY]); 48942bd6f59SJacek Anaszewski 49042bd6f59SJacek Anaszewski if (ctrls[INDICATOR_INTENSITY]) 49142bd6f59SJacek Anaszewski v4l2_flash_set_led_brightness(v4l2_flash, 49242bd6f59SJacek Anaszewski ctrls[INDICATOR_INTENSITY]); 49342bd6f59SJacek Anaszewski 49442bd6f59SJacek Anaszewski if (ctrls[FLASH_TIMEOUT]) { 49542bd6f59SJacek Anaszewski ret = led_set_flash_timeout(fled_cdev, 49642bd6f59SJacek Anaszewski ctrls[FLASH_TIMEOUT]->val); 49742bd6f59SJacek Anaszewski if (ret < 0) 49842bd6f59SJacek Anaszewski return ret; 49942bd6f59SJacek Anaszewski } 50042bd6f59SJacek Anaszewski 50142bd6f59SJacek Anaszewski if (ctrls[FLASH_INTENSITY]) { 50242bd6f59SJacek Anaszewski ret = led_set_flash_brightness(fled_cdev, 50342bd6f59SJacek Anaszewski ctrls[FLASH_INTENSITY]->val); 50442bd6f59SJacek Anaszewski if (ret < 0) 50542bd6f59SJacek Anaszewski return ret; 50642bd6f59SJacek Anaszewski } 50742bd6f59SJacek Anaszewski 50842bd6f59SJacek Anaszewski /* 50942bd6f59SJacek Anaszewski * For some hardware arrangements setting strobe source may affect 51042bd6f59SJacek Anaszewski * torch mode. Synchronize strobe source setting only if not in torch 51142bd6f59SJacek Anaszewski * mode. For torch mode case it will get synchronized upon switching 51242bd6f59SJacek Anaszewski * to flash mode. 51342bd6f59SJacek Anaszewski */ 51442bd6f59SJacek Anaszewski if (ctrls[STROBE_SOURCE] && 51542bd6f59SJacek Anaszewski ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH) 51642bd6f59SJacek Anaszewski ret = call_flash_op(v4l2_flash, external_strobe_set, 51742bd6f59SJacek Anaszewski ctrls[STROBE_SOURCE]->val); 51842bd6f59SJacek Anaszewski 51942bd6f59SJacek Anaszewski return ret; 52042bd6f59SJacek Anaszewski } 52142bd6f59SJacek Anaszewski 52242bd6f59SJacek Anaszewski /* 52342bd6f59SJacek Anaszewski * V4L2 subdev internal operations 52442bd6f59SJacek Anaszewski */ 52542bd6f59SJacek Anaszewski 52642bd6f59SJacek Anaszewski static int v4l2_flash_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 52742bd6f59SJacek Anaszewski { 52842bd6f59SJacek Anaszewski struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd); 52942bd6f59SJacek Anaszewski struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 530503dd28aSSakari Ailus struct led_classdev *led_cdev = fled_cdev ? &fled_cdev->led_cdev : NULL; 53185f7ff97SSakari Ailus struct led_classdev *led_cdev_ind = v4l2_flash->iled_cdev; 53242bd6f59SJacek Anaszewski int ret = 0; 53342bd6f59SJacek Anaszewski 53442bd6f59SJacek Anaszewski if (!v4l2_fh_is_singular(&fh->vfh)) 53542bd6f59SJacek Anaszewski return 0; 53642bd6f59SJacek Anaszewski 537503dd28aSSakari Ailus if (led_cdev) { 53842bd6f59SJacek Anaszewski mutex_lock(&led_cdev->led_access); 53942bd6f59SJacek Anaszewski 54042bd6f59SJacek Anaszewski led_sysfs_disable(led_cdev); 54142bd6f59SJacek Anaszewski led_trigger_remove(led_cdev); 54242bd6f59SJacek Anaszewski 54342bd6f59SJacek Anaszewski mutex_unlock(&led_cdev->led_access); 544503dd28aSSakari Ailus } 54542bd6f59SJacek Anaszewski 54685f7ff97SSakari Ailus if (led_cdev_ind) { 54742bd6f59SJacek Anaszewski mutex_lock(&led_cdev_ind->led_access); 54842bd6f59SJacek Anaszewski 54942bd6f59SJacek Anaszewski led_sysfs_disable(led_cdev_ind); 55042bd6f59SJacek Anaszewski led_trigger_remove(led_cdev_ind); 55142bd6f59SJacek Anaszewski 55242bd6f59SJacek Anaszewski mutex_unlock(&led_cdev_ind->led_access); 55342bd6f59SJacek Anaszewski } 55442bd6f59SJacek Anaszewski 55542bd6f59SJacek Anaszewski ret = __sync_device_with_v4l2_controls(v4l2_flash); 55642bd6f59SJacek Anaszewski if (ret < 0) 55742bd6f59SJacek Anaszewski goto out_sync_device; 55842bd6f59SJacek Anaszewski 55942bd6f59SJacek Anaszewski return 0; 56042bd6f59SJacek Anaszewski out_sync_device: 561503dd28aSSakari Ailus if (led_cdev) { 56242bd6f59SJacek Anaszewski mutex_lock(&led_cdev->led_access); 56342bd6f59SJacek Anaszewski led_sysfs_enable(led_cdev); 56442bd6f59SJacek Anaszewski mutex_unlock(&led_cdev->led_access); 565503dd28aSSakari Ailus } 56642bd6f59SJacek Anaszewski 56742bd6f59SJacek Anaszewski if (led_cdev_ind) { 56842bd6f59SJacek Anaszewski mutex_lock(&led_cdev_ind->led_access); 56942bd6f59SJacek Anaszewski led_sysfs_enable(led_cdev_ind); 57042bd6f59SJacek Anaszewski mutex_unlock(&led_cdev_ind->led_access); 57142bd6f59SJacek Anaszewski } 57242bd6f59SJacek Anaszewski 57342bd6f59SJacek Anaszewski return ret; 57442bd6f59SJacek Anaszewski } 57542bd6f59SJacek Anaszewski 57642bd6f59SJacek Anaszewski static int v4l2_flash_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 57742bd6f59SJacek Anaszewski { 57842bd6f59SJacek Anaszewski struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd); 57942bd6f59SJacek Anaszewski struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 580503dd28aSSakari Ailus struct led_classdev *led_cdev = fled_cdev ? &fled_cdev->led_cdev : NULL; 58185f7ff97SSakari Ailus struct led_classdev *led_cdev_ind = v4l2_flash->iled_cdev; 58242bd6f59SJacek Anaszewski int ret = 0; 58342bd6f59SJacek Anaszewski 58442bd6f59SJacek Anaszewski if (!v4l2_fh_is_singular(&fh->vfh)) 58542bd6f59SJacek Anaszewski return 0; 58642bd6f59SJacek Anaszewski 587503dd28aSSakari Ailus if (led_cdev) { 58842bd6f59SJacek Anaszewski mutex_lock(&led_cdev->led_access); 58942bd6f59SJacek Anaszewski 59042bd6f59SJacek Anaszewski if (v4l2_flash->ctrls[STROBE_SOURCE]) 591503dd28aSSakari Ailus ret = v4l2_ctrl_s_ctrl( 592503dd28aSSakari Ailus v4l2_flash->ctrls[STROBE_SOURCE], 59342bd6f59SJacek Anaszewski V4L2_FLASH_STROBE_SOURCE_SOFTWARE); 59442bd6f59SJacek Anaszewski led_sysfs_enable(led_cdev); 59542bd6f59SJacek Anaszewski 59642bd6f59SJacek Anaszewski mutex_unlock(&led_cdev->led_access); 597503dd28aSSakari Ailus } 59842bd6f59SJacek Anaszewski 59985f7ff97SSakari Ailus if (led_cdev_ind) { 60042bd6f59SJacek Anaszewski mutex_lock(&led_cdev_ind->led_access); 60142bd6f59SJacek Anaszewski led_sysfs_enable(led_cdev_ind); 60242bd6f59SJacek Anaszewski mutex_unlock(&led_cdev_ind->led_access); 60342bd6f59SJacek Anaszewski } 60442bd6f59SJacek Anaszewski 60542bd6f59SJacek Anaszewski return ret; 60642bd6f59SJacek Anaszewski } 60742bd6f59SJacek Anaszewski 60842bd6f59SJacek Anaszewski static const struct v4l2_subdev_internal_ops v4l2_flash_subdev_internal_ops = { 60942bd6f59SJacek Anaszewski .open = v4l2_flash_open, 61042bd6f59SJacek Anaszewski .close = v4l2_flash_close, 61142bd6f59SJacek Anaszewski }; 61242bd6f59SJacek Anaszewski 6137853c237SHans Verkuil static const struct v4l2_subdev_ops v4l2_flash_subdev_ops; 61442bd6f59SJacek Anaszewski 615503dd28aSSakari Ailus static struct v4l2_flash *__v4l2_flash_init( 616048ea05bSSakari Ailus struct device *dev, struct fwnode_handle *fwn, 617503dd28aSSakari Ailus struct led_classdev_flash *fled_cdev, struct led_classdev *iled_cdev, 618503dd28aSSakari Ailus const struct v4l2_flash_ops *ops, struct v4l2_flash_config *config) 61942bd6f59SJacek Anaszewski { 62042bd6f59SJacek Anaszewski struct v4l2_flash *v4l2_flash; 62142bd6f59SJacek Anaszewski struct v4l2_subdev *sd; 62242bd6f59SJacek Anaszewski int ret; 62342bd6f59SJacek Anaszewski 624503dd28aSSakari Ailus if (!config) 62542bd6f59SJacek Anaszewski return ERR_PTR(-EINVAL); 62642bd6f59SJacek Anaszewski 627503dd28aSSakari Ailus v4l2_flash = devm_kzalloc(dev, sizeof(*v4l2_flash), GFP_KERNEL); 62842bd6f59SJacek Anaszewski if (!v4l2_flash) 62942bd6f59SJacek Anaszewski return ERR_PTR(-ENOMEM); 63042bd6f59SJacek Anaszewski 63142bd6f59SJacek Anaszewski sd = &v4l2_flash->sd; 63242bd6f59SJacek Anaszewski v4l2_flash->fled_cdev = fled_cdev; 63342bd6f59SJacek Anaszewski v4l2_flash->iled_cdev = iled_cdev; 63442bd6f59SJacek Anaszewski v4l2_flash->ops = ops; 63542bd6f59SJacek Anaszewski sd->dev = dev; 636503dd28aSSakari Ailus sd->fwnode = fwn ? fwn : dev_fwnode(dev); 63742bd6f59SJacek Anaszewski v4l2_subdev_init(sd, &v4l2_flash_subdev_ops); 63842bd6f59SJacek Anaszewski sd->internal_ops = &v4l2_flash_subdev_internal_ops; 63942bd6f59SJacek Anaszewski sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 640c0decac1SMauro Carvalho Chehab strscpy(sd->name, config->dev_name, sizeof(sd->name)); 64142bd6f59SJacek Anaszewski 642ab22e77cSMauro Carvalho Chehab ret = media_entity_pads_init(&sd->entity, 0, NULL); 64342bd6f59SJacek Anaszewski if (ret < 0) 64442bd6f59SJacek Anaszewski return ERR_PTR(ret); 64542bd6f59SJacek Anaszewski 6464ca72efaSMauro Carvalho Chehab sd->entity.function = MEDIA_ENT_F_FLASH; 64742bd6f59SJacek Anaszewski 64842bd6f59SJacek Anaszewski ret = v4l2_flash_init_controls(v4l2_flash, config); 64942bd6f59SJacek Anaszewski if (ret < 0) 65042bd6f59SJacek Anaszewski goto err_init_controls; 65142bd6f59SJacek Anaszewski 652048ea05bSSakari Ailus fwnode_handle_get(sd->fwnode); 65342bd6f59SJacek Anaszewski 65442bd6f59SJacek Anaszewski ret = v4l2_async_register_subdev(sd); 65542bd6f59SJacek Anaszewski if (ret < 0) 65642bd6f59SJacek Anaszewski goto err_async_register_sd; 65742bd6f59SJacek Anaszewski 65842bd6f59SJacek Anaszewski return v4l2_flash; 65942bd6f59SJacek Anaszewski 66042bd6f59SJacek Anaszewski err_async_register_sd: 661048ea05bSSakari Ailus fwnode_handle_put(sd->fwnode); 66242bd6f59SJacek Anaszewski v4l2_ctrl_handler_free(sd->ctrl_handler); 66342bd6f59SJacek Anaszewski err_init_controls: 66442bd6f59SJacek Anaszewski media_entity_cleanup(&sd->entity); 66542bd6f59SJacek Anaszewski 66642bd6f59SJacek Anaszewski return ERR_PTR(ret); 66742bd6f59SJacek Anaszewski } 668503dd28aSSakari Ailus 669503dd28aSSakari Ailus struct v4l2_flash *v4l2_flash_init( 670503dd28aSSakari Ailus struct device *dev, struct fwnode_handle *fwn, 671503dd28aSSakari Ailus struct led_classdev_flash *fled_cdev, 672503dd28aSSakari Ailus const struct v4l2_flash_ops *ops, 673503dd28aSSakari Ailus struct v4l2_flash_config *config) 674503dd28aSSakari Ailus { 675503dd28aSSakari Ailus return __v4l2_flash_init(dev, fwn, fled_cdev, NULL, ops, config); 676503dd28aSSakari Ailus } 67742bd6f59SJacek Anaszewski EXPORT_SYMBOL_GPL(v4l2_flash_init); 67842bd6f59SJacek Anaszewski 679503dd28aSSakari Ailus struct v4l2_flash *v4l2_flash_indicator_init( 680503dd28aSSakari Ailus struct device *dev, struct fwnode_handle *fwn, 681503dd28aSSakari Ailus struct led_classdev *iled_cdev, 682503dd28aSSakari Ailus struct v4l2_flash_config *config) 683503dd28aSSakari Ailus { 684503dd28aSSakari Ailus return __v4l2_flash_init(dev, fwn, NULL, iled_cdev, NULL, config); 685503dd28aSSakari Ailus } 686503dd28aSSakari Ailus EXPORT_SYMBOL_GPL(v4l2_flash_indicator_init); 687503dd28aSSakari Ailus 68842bd6f59SJacek Anaszewski void v4l2_flash_release(struct v4l2_flash *v4l2_flash) 68942bd6f59SJacek Anaszewski { 69042bd6f59SJacek Anaszewski struct v4l2_subdev *sd; 69142bd6f59SJacek Anaszewski 69242bd6f59SJacek Anaszewski if (IS_ERR_OR_NULL(v4l2_flash)) 69342bd6f59SJacek Anaszewski return; 69442bd6f59SJacek Anaszewski 69542bd6f59SJacek Anaszewski sd = &v4l2_flash->sd; 69642bd6f59SJacek Anaszewski 69742bd6f59SJacek Anaszewski v4l2_async_unregister_subdev(sd); 69842bd6f59SJacek Anaszewski 699048ea05bSSakari Ailus fwnode_handle_put(sd->fwnode); 70042bd6f59SJacek Anaszewski 70142bd6f59SJacek Anaszewski v4l2_ctrl_handler_free(sd->ctrl_handler); 70242bd6f59SJacek Anaszewski media_entity_cleanup(&sd->entity); 70342bd6f59SJacek Anaszewski } 70442bd6f59SJacek Anaszewski EXPORT_SYMBOL_GPL(v4l2_flash_release); 70542bd6f59SJacek Anaszewski 70642bd6f59SJacek Anaszewski MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>"); 70742bd6f59SJacek Anaszewski MODULE_DESCRIPTION("V4L2 Flash sub-device helpers"); 70842bd6f59SJacek Anaszewski MODULE_LICENSE("GPL v2"); 709