18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * V4L2 flash LED sub-device registration helpers. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Samsung Electronics Co., Ltd 68c2ecf20Sopenharmony_ci * Author: Jacek Anaszewski <j.anaszewski@samsung.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/led-class-flash.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/mutex.h> 128c2ecf20Sopenharmony_ci#include <linux/property.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/types.h> 158c2ecf20Sopenharmony_ci#include <media/v4l2-flash-led-class.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define has_flash_op(v4l2_flash, op) \ 188c2ecf20Sopenharmony_ci (v4l2_flash && v4l2_flash->ops && v4l2_flash->ops->op) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define call_flash_op(v4l2_flash, op, arg) \ 218c2ecf20Sopenharmony_ci (has_flash_op(v4l2_flash, op) ? \ 228c2ecf20Sopenharmony_ci v4l2_flash->ops->op(v4l2_flash, arg) : \ 238c2ecf20Sopenharmony_ci -EINVAL) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cienum ctrl_init_data_id { 268c2ecf20Sopenharmony_ci LED_MODE, 278c2ecf20Sopenharmony_ci TORCH_INTENSITY, 288c2ecf20Sopenharmony_ci FLASH_INTENSITY, 298c2ecf20Sopenharmony_ci INDICATOR_INTENSITY, 308c2ecf20Sopenharmony_ci FLASH_TIMEOUT, 318c2ecf20Sopenharmony_ci STROBE_SOURCE, 328c2ecf20Sopenharmony_ci /* 338c2ecf20Sopenharmony_ci * Only above values are applicable to 348c2ecf20Sopenharmony_ci * the 'ctrls' array in the struct v4l2_flash. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci FLASH_STROBE, 378c2ecf20Sopenharmony_ci STROBE_STOP, 388c2ecf20Sopenharmony_ci STROBE_STATUS, 398c2ecf20Sopenharmony_ci FLASH_FAULT, 408c2ecf20Sopenharmony_ci NUM_FLASH_CTRLS, 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic enum led_brightness __intensity_to_led_brightness( 448c2ecf20Sopenharmony_ci struct v4l2_ctrl *ctrl, s32 intensity) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci intensity -= ctrl->minimum; 478c2ecf20Sopenharmony_ci intensity /= (u32) ctrl->step; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci /* 508c2ecf20Sopenharmony_ci * Indicator LEDs, unlike torch LEDs, are turned on/off basing on 518c2ecf20Sopenharmony_ci * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only. 528c2ecf20Sopenharmony_ci * Therefore it must be possible to set it to 0 level which in 538c2ecf20Sopenharmony_ci * the LED subsystem reflects LED_OFF state. 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_ci if (ctrl->minimum) 568c2ecf20Sopenharmony_ci ++intensity; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci return intensity; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic s32 __led_brightness_to_intensity(struct v4l2_ctrl *ctrl, 628c2ecf20Sopenharmony_ci enum led_brightness brightness) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci /* 658c2ecf20Sopenharmony_ci * Indicator LEDs, unlike torch LEDs, are turned on/off basing on 668c2ecf20Sopenharmony_ci * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only. 678c2ecf20Sopenharmony_ci * Do not decrement brightness read from the LED subsystem for 688c2ecf20Sopenharmony_ci * indicator LED as it may equal 0. For torch LEDs this function 698c2ecf20Sopenharmony_ci * is called only when V4L2_FLASH_LED_MODE_TORCH is set and the 708c2ecf20Sopenharmony_ci * brightness read is guaranteed to be greater than 0. In the mode 718c2ecf20Sopenharmony_ci * V4L2_FLASH_LED_MODE_NONE the cached torch intensity value is used. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci if (ctrl->id != V4L2_CID_FLASH_INDICATOR_INTENSITY) 748c2ecf20Sopenharmony_ci --brightness; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return (brightness * ctrl->step) + ctrl->minimum; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash, 808c2ecf20Sopenharmony_ci struct v4l2_ctrl *ctrl) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; 838c2ecf20Sopenharmony_ci enum led_brightness brightness; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (has_flash_op(v4l2_flash, intensity_to_led_brightness)) 868c2ecf20Sopenharmony_ci brightness = call_flash_op(v4l2_flash, 878c2ecf20Sopenharmony_ci intensity_to_led_brightness, 888c2ecf20Sopenharmony_ci ctrl->val); 898c2ecf20Sopenharmony_ci else 908c2ecf20Sopenharmony_ci brightness = __intensity_to_led_brightness(ctrl, ctrl->val); 918c2ecf20Sopenharmony_ci /* 928c2ecf20Sopenharmony_ci * In case a LED Flash class driver provides ops for custom 938c2ecf20Sopenharmony_ci * brightness <-> intensity conversion, it also must have defined 948c2ecf20Sopenharmony_ci * related v4l2 control step == 1. In such a case a backward conversion 958c2ecf20Sopenharmony_ci * from led brightness to v4l2 intensity is required to find out the 968c2ecf20Sopenharmony_ci * the aligned intensity value. 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci if (has_flash_op(v4l2_flash, led_brightness_to_intensity)) 998c2ecf20Sopenharmony_ci ctrl->val = call_flash_op(v4l2_flash, 1008c2ecf20Sopenharmony_ci led_brightness_to_intensity, 1018c2ecf20Sopenharmony_ci brightness); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (ctrl == ctrls[TORCH_INTENSITY]) { 1048c2ecf20Sopenharmony_ci if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH) 1058c2ecf20Sopenharmony_ci return; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci led_set_brightness_sync(&v4l2_flash->fled_cdev->led_cdev, 1088c2ecf20Sopenharmony_ci brightness); 1098c2ecf20Sopenharmony_ci } else { 1108c2ecf20Sopenharmony_ci led_set_brightness_sync(v4l2_flash->iled_cdev, 1118c2ecf20Sopenharmony_ci brightness); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash, 1168c2ecf20Sopenharmony_ci struct v4l2_ctrl *ctrl) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; 1198c2ecf20Sopenharmony_ci struct led_classdev *led_cdev; 1208c2ecf20Sopenharmony_ci int ret; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (ctrl == ctrls[TORCH_INTENSITY]) { 1238c2ecf20Sopenharmony_ci /* 1248c2ecf20Sopenharmony_ci * Update torch brightness only if in TORCH_MODE. In other modes 1258c2ecf20Sopenharmony_ci * torch led is turned off, which would spuriously inform the 1268c2ecf20Sopenharmony_ci * user space that V4L2_CID_FLASH_TORCH_INTENSITY control value 1278c2ecf20Sopenharmony_ci * has changed to 0. 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ci if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH) 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci led_cdev = &v4l2_flash->fled_cdev->led_cdev; 1328c2ecf20Sopenharmony_ci } else { 1338c2ecf20Sopenharmony_ci led_cdev = v4l2_flash->iled_cdev; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci ret = led_update_brightness(led_cdev); 1378c2ecf20Sopenharmony_ci if (ret < 0) 1388c2ecf20Sopenharmony_ci return ret; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (has_flash_op(v4l2_flash, led_brightness_to_intensity)) 1418c2ecf20Sopenharmony_ci ctrl->val = call_flash_op(v4l2_flash, 1428c2ecf20Sopenharmony_ci led_brightness_to_intensity, 1438c2ecf20Sopenharmony_ci led_cdev->brightness); 1448c2ecf20Sopenharmony_ci else 1458c2ecf20Sopenharmony_ci ctrl->val = __led_brightness_to_intensity(ctrl, 1468c2ecf20Sopenharmony_ci led_cdev->brightness); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int v4l2_flash_g_volatile_ctrl(struct v4l2_ctrl *c) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c); 1548c2ecf20Sopenharmony_ci struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 1558c2ecf20Sopenharmony_ci bool is_strobing; 1568c2ecf20Sopenharmony_ci int ret; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci switch (c->id) { 1598c2ecf20Sopenharmony_ci case V4L2_CID_FLASH_TORCH_INTENSITY: 1608c2ecf20Sopenharmony_ci case V4L2_CID_FLASH_INDICATOR_INTENSITY: 1618c2ecf20Sopenharmony_ci return v4l2_flash_update_led_brightness(v4l2_flash, c); 1628c2ecf20Sopenharmony_ci case V4L2_CID_FLASH_INTENSITY: 1638c2ecf20Sopenharmony_ci ret = led_update_flash_brightness(fled_cdev); 1648c2ecf20Sopenharmony_ci if (ret < 0) 1658c2ecf20Sopenharmony_ci return ret; 1668c2ecf20Sopenharmony_ci /* 1678c2ecf20Sopenharmony_ci * No conversion is needed as LED Flash class also uses 1688c2ecf20Sopenharmony_ci * microamperes for flash intensity units. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ci c->val = fled_cdev->brightness.val; 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci case V4L2_CID_FLASH_STROBE_STATUS: 1738c2ecf20Sopenharmony_ci ret = led_get_flash_strobe(fled_cdev, &is_strobing); 1748c2ecf20Sopenharmony_ci if (ret < 0) 1758c2ecf20Sopenharmony_ci return ret; 1768c2ecf20Sopenharmony_ci c->val = is_strobing; 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci case V4L2_CID_FLASH_FAULT: 1798c2ecf20Sopenharmony_ci /* LED faults map directly to V4L2 flash faults */ 1808c2ecf20Sopenharmony_ci return led_get_flash_fault(fled_cdev, &c->val); 1818c2ecf20Sopenharmony_ci default: 1828c2ecf20Sopenharmony_ci return -EINVAL; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic bool __software_strobe_mode_inactive(struct v4l2_ctrl **ctrls) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci return ((ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH) || 1898c2ecf20Sopenharmony_ci (ctrls[STROBE_SOURCE] && (ctrls[STROBE_SOURCE]->val != 1908c2ecf20Sopenharmony_ci V4L2_FLASH_STROBE_SOURCE_SOFTWARE))); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int v4l2_flash_s_ctrl(struct v4l2_ctrl *c) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c); 1968c2ecf20Sopenharmony_ci struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 1978c2ecf20Sopenharmony_ci struct led_classdev *led_cdev = fled_cdev ? &fled_cdev->led_cdev : NULL; 1988c2ecf20Sopenharmony_ci struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; 1998c2ecf20Sopenharmony_ci bool external_strobe; 2008c2ecf20Sopenharmony_ci int ret = 0; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci switch (c->id) { 2038c2ecf20Sopenharmony_ci case V4L2_CID_FLASH_LED_MODE: 2048c2ecf20Sopenharmony_ci switch (c->val) { 2058c2ecf20Sopenharmony_ci case V4L2_FLASH_LED_MODE_NONE: 2068c2ecf20Sopenharmony_ci led_set_brightness_sync(led_cdev, LED_OFF); 2078c2ecf20Sopenharmony_ci return led_set_flash_strobe(fled_cdev, false); 2088c2ecf20Sopenharmony_ci case V4L2_FLASH_LED_MODE_FLASH: 2098c2ecf20Sopenharmony_ci /* Turn the torch LED off */ 2108c2ecf20Sopenharmony_ci led_set_brightness_sync(led_cdev, LED_OFF); 2118c2ecf20Sopenharmony_ci if (ctrls[STROBE_SOURCE]) { 2128c2ecf20Sopenharmony_ci external_strobe = (ctrls[STROBE_SOURCE]->val == 2138c2ecf20Sopenharmony_ci V4L2_FLASH_STROBE_SOURCE_EXTERNAL); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci ret = call_flash_op(v4l2_flash, 2168c2ecf20Sopenharmony_ci external_strobe_set, 2178c2ecf20Sopenharmony_ci external_strobe); 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci return ret; 2208c2ecf20Sopenharmony_ci case V4L2_FLASH_LED_MODE_TORCH: 2218c2ecf20Sopenharmony_ci if (ctrls[STROBE_SOURCE]) { 2228c2ecf20Sopenharmony_ci ret = call_flash_op(v4l2_flash, 2238c2ecf20Sopenharmony_ci external_strobe_set, 2248c2ecf20Sopenharmony_ci false); 2258c2ecf20Sopenharmony_ci if (ret < 0) 2268c2ecf20Sopenharmony_ci return ret; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci /* Stop flash strobing */ 2298c2ecf20Sopenharmony_ci ret = led_set_flash_strobe(fled_cdev, false); 2308c2ecf20Sopenharmony_ci if (ret < 0) 2318c2ecf20Sopenharmony_ci return ret; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci v4l2_flash_set_led_brightness(v4l2_flash, 2348c2ecf20Sopenharmony_ci ctrls[TORCH_INTENSITY]); 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci break; 2388c2ecf20Sopenharmony_ci case V4L2_CID_FLASH_STROBE_SOURCE: 2398c2ecf20Sopenharmony_ci external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL); 2408c2ecf20Sopenharmony_ci /* 2418c2ecf20Sopenharmony_ci * For some hardware arrangements setting strobe source may 2428c2ecf20Sopenharmony_ci * affect torch mode. Therefore, if not in the flash mode, 2438c2ecf20Sopenharmony_ci * cache only this setting. It will be applied upon switching 2448c2ecf20Sopenharmony_ci * to flash mode. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_ci if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH) 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return call_flash_op(v4l2_flash, external_strobe_set, 2508c2ecf20Sopenharmony_ci external_strobe); 2518c2ecf20Sopenharmony_ci case V4L2_CID_FLASH_STROBE: 2528c2ecf20Sopenharmony_ci if (__software_strobe_mode_inactive(ctrls)) 2538c2ecf20Sopenharmony_ci return -EBUSY; 2548c2ecf20Sopenharmony_ci return led_set_flash_strobe(fled_cdev, true); 2558c2ecf20Sopenharmony_ci case V4L2_CID_FLASH_STROBE_STOP: 2568c2ecf20Sopenharmony_ci if (__software_strobe_mode_inactive(ctrls)) 2578c2ecf20Sopenharmony_ci return -EBUSY; 2588c2ecf20Sopenharmony_ci return led_set_flash_strobe(fled_cdev, false); 2598c2ecf20Sopenharmony_ci case V4L2_CID_FLASH_TIMEOUT: 2608c2ecf20Sopenharmony_ci /* 2618c2ecf20Sopenharmony_ci * No conversion is needed as LED Flash class also uses 2628c2ecf20Sopenharmony_ci * microseconds for flash timeout units. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ci return led_set_flash_timeout(fled_cdev, c->val); 2658c2ecf20Sopenharmony_ci case V4L2_CID_FLASH_INTENSITY: 2668c2ecf20Sopenharmony_ci /* 2678c2ecf20Sopenharmony_ci * No conversion is needed as LED Flash class also uses 2688c2ecf20Sopenharmony_ci * microamperes for flash intensity units. 2698c2ecf20Sopenharmony_ci */ 2708c2ecf20Sopenharmony_ci return led_set_flash_brightness(fled_cdev, c->val); 2718c2ecf20Sopenharmony_ci case V4L2_CID_FLASH_TORCH_INTENSITY: 2728c2ecf20Sopenharmony_ci case V4L2_CID_FLASH_INDICATOR_INTENSITY: 2738c2ecf20Sopenharmony_ci v4l2_flash_set_led_brightness(v4l2_flash, c); 2748c2ecf20Sopenharmony_ci return 0; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return -EINVAL; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops v4l2_flash_ctrl_ops = { 2818c2ecf20Sopenharmony_ci .g_volatile_ctrl = v4l2_flash_g_volatile_ctrl, 2828c2ecf20Sopenharmony_ci .s_ctrl = v4l2_flash_s_ctrl, 2838c2ecf20Sopenharmony_ci}; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic void __lfs_to_v4l2_ctrl_config(struct led_flash_setting *s, 2868c2ecf20Sopenharmony_ci struct v4l2_ctrl_config *c) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci c->min = s->min; 2898c2ecf20Sopenharmony_ci c->max = s->max; 2908c2ecf20Sopenharmony_ci c->step = s->step; 2918c2ecf20Sopenharmony_ci c->def = s->val; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic void __fill_ctrl_init_data(struct v4l2_flash *v4l2_flash, 2958c2ecf20Sopenharmony_ci struct v4l2_flash_config *flash_cfg, 2968c2ecf20Sopenharmony_ci struct v4l2_flash_ctrl_data *ctrl_init_data) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 2998c2ecf20Sopenharmony_ci struct led_classdev *led_cdev = fled_cdev ? &fled_cdev->led_cdev : NULL; 3008c2ecf20Sopenharmony_ci struct v4l2_ctrl_config *ctrl_cfg; 3018c2ecf20Sopenharmony_ci u32 mask; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* Init INDICATOR_INTENSITY ctrl data */ 3048c2ecf20Sopenharmony_ci if (v4l2_flash->iled_cdev) { 3058c2ecf20Sopenharmony_ci ctrl_init_data[INDICATOR_INTENSITY].cid = 3068c2ecf20Sopenharmony_ci V4L2_CID_FLASH_INDICATOR_INTENSITY; 3078c2ecf20Sopenharmony_ci ctrl_cfg = &ctrl_init_data[INDICATOR_INTENSITY].config; 3088c2ecf20Sopenharmony_ci __lfs_to_v4l2_ctrl_config(&flash_cfg->intensity, 3098c2ecf20Sopenharmony_ci ctrl_cfg); 3108c2ecf20Sopenharmony_ci ctrl_cfg->id = V4L2_CID_FLASH_INDICATOR_INTENSITY; 3118c2ecf20Sopenharmony_ci ctrl_cfg->min = 0; 3128c2ecf20Sopenharmony_ci ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | 3138c2ecf20Sopenharmony_ci V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (!led_cdev || WARN_ON(!(led_cdev->flags & LED_DEV_CAP_FLASH))) 3178c2ecf20Sopenharmony_ci return; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* Init FLASH_FAULT ctrl data */ 3208c2ecf20Sopenharmony_ci if (flash_cfg->flash_faults) { 3218c2ecf20Sopenharmony_ci ctrl_init_data[FLASH_FAULT].cid = V4L2_CID_FLASH_FAULT; 3228c2ecf20Sopenharmony_ci ctrl_cfg = &ctrl_init_data[FLASH_FAULT].config; 3238c2ecf20Sopenharmony_ci ctrl_cfg->id = V4L2_CID_FLASH_FAULT; 3248c2ecf20Sopenharmony_ci ctrl_cfg->max = flash_cfg->flash_faults; 3258c2ecf20Sopenharmony_ci ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | 3268c2ecf20Sopenharmony_ci V4L2_CTRL_FLAG_READ_ONLY; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* Init FLASH_LED_MODE ctrl data */ 3308c2ecf20Sopenharmony_ci mask = 1 << V4L2_FLASH_LED_MODE_NONE | 3318c2ecf20Sopenharmony_ci 1 << V4L2_FLASH_LED_MODE_TORCH; 3328c2ecf20Sopenharmony_ci if (led_cdev->flags & LED_DEV_CAP_FLASH) 3338c2ecf20Sopenharmony_ci mask |= 1 << V4L2_FLASH_LED_MODE_FLASH; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci ctrl_init_data[LED_MODE].cid = V4L2_CID_FLASH_LED_MODE; 3368c2ecf20Sopenharmony_ci ctrl_cfg = &ctrl_init_data[LED_MODE].config; 3378c2ecf20Sopenharmony_ci ctrl_cfg->id = V4L2_CID_FLASH_LED_MODE; 3388c2ecf20Sopenharmony_ci ctrl_cfg->max = V4L2_FLASH_LED_MODE_TORCH; 3398c2ecf20Sopenharmony_ci ctrl_cfg->menu_skip_mask = ~mask; 3408c2ecf20Sopenharmony_ci ctrl_cfg->def = V4L2_FLASH_LED_MODE_NONE; 3418c2ecf20Sopenharmony_ci ctrl_cfg->flags = 0; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* Init TORCH_INTENSITY ctrl data */ 3448c2ecf20Sopenharmony_ci ctrl_init_data[TORCH_INTENSITY].cid = V4L2_CID_FLASH_TORCH_INTENSITY; 3458c2ecf20Sopenharmony_ci ctrl_cfg = &ctrl_init_data[TORCH_INTENSITY].config; 3468c2ecf20Sopenharmony_ci __lfs_to_v4l2_ctrl_config(&flash_cfg->intensity, ctrl_cfg); 3478c2ecf20Sopenharmony_ci ctrl_cfg->id = V4L2_CID_FLASH_TORCH_INTENSITY; 3488c2ecf20Sopenharmony_ci ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | 3498c2ecf20Sopenharmony_ci V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* Init FLASH_STROBE ctrl data */ 3528c2ecf20Sopenharmony_ci ctrl_init_data[FLASH_STROBE].cid = V4L2_CID_FLASH_STROBE; 3538c2ecf20Sopenharmony_ci ctrl_cfg = &ctrl_init_data[FLASH_STROBE].config; 3548c2ecf20Sopenharmony_ci ctrl_cfg->id = V4L2_CID_FLASH_STROBE; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* Init STROBE_STOP ctrl data */ 3578c2ecf20Sopenharmony_ci ctrl_init_data[STROBE_STOP].cid = V4L2_CID_FLASH_STROBE_STOP; 3588c2ecf20Sopenharmony_ci ctrl_cfg = &ctrl_init_data[STROBE_STOP].config; 3598c2ecf20Sopenharmony_ci ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STOP; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* Init FLASH_STROBE_SOURCE ctrl data */ 3628c2ecf20Sopenharmony_ci if (flash_cfg->has_external_strobe) { 3638c2ecf20Sopenharmony_ci mask = (1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE) | 3648c2ecf20Sopenharmony_ci (1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL); 3658c2ecf20Sopenharmony_ci ctrl_init_data[STROBE_SOURCE].cid = 3668c2ecf20Sopenharmony_ci V4L2_CID_FLASH_STROBE_SOURCE; 3678c2ecf20Sopenharmony_ci ctrl_cfg = &ctrl_init_data[STROBE_SOURCE].config; 3688c2ecf20Sopenharmony_ci ctrl_cfg->id = V4L2_CID_FLASH_STROBE_SOURCE; 3698c2ecf20Sopenharmony_ci ctrl_cfg->max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL; 3708c2ecf20Sopenharmony_ci ctrl_cfg->menu_skip_mask = ~mask; 3718c2ecf20Sopenharmony_ci ctrl_cfg->def = V4L2_FLASH_STROBE_SOURCE_SOFTWARE; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* Init STROBE_STATUS ctrl data */ 3758c2ecf20Sopenharmony_ci if (has_flash_op(fled_cdev, strobe_get)) { 3768c2ecf20Sopenharmony_ci ctrl_init_data[STROBE_STATUS].cid = 3778c2ecf20Sopenharmony_ci V4L2_CID_FLASH_STROBE_STATUS; 3788c2ecf20Sopenharmony_ci ctrl_cfg = &ctrl_init_data[STROBE_STATUS].config; 3798c2ecf20Sopenharmony_ci ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STATUS; 3808c2ecf20Sopenharmony_ci ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | 3818c2ecf20Sopenharmony_ci V4L2_CTRL_FLAG_READ_ONLY; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* Init FLASH_TIMEOUT ctrl data */ 3858c2ecf20Sopenharmony_ci if (has_flash_op(fled_cdev, timeout_set)) { 3868c2ecf20Sopenharmony_ci ctrl_init_data[FLASH_TIMEOUT].cid = V4L2_CID_FLASH_TIMEOUT; 3878c2ecf20Sopenharmony_ci ctrl_cfg = &ctrl_init_data[FLASH_TIMEOUT].config; 3888c2ecf20Sopenharmony_ci __lfs_to_v4l2_ctrl_config(&fled_cdev->timeout, ctrl_cfg); 3898c2ecf20Sopenharmony_ci ctrl_cfg->id = V4L2_CID_FLASH_TIMEOUT; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* Init FLASH_INTENSITY ctrl data */ 3938c2ecf20Sopenharmony_ci if (has_flash_op(fled_cdev, flash_brightness_set)) { 3948c2ecf20Sopenharmony_ci ctrl_init_data[FLASH_INTENSITY].cid = V4L2_CID_FLASH_INTENSITY; 3958c2ecf20Sopenharmony_ci ctrl_cfg = &ctrl_init_data[FLASH_INTENSITY].config; 3968c2ecf20Sopenharmony_ci __lfs_to_v4l2_ctrl_config(&fled_cdev->brightness, ctrl_cfg); 3978c2ecf20Sopenharmony_ci ctrl_cfg->id = V4L2_CID_FLASH_INTENSITY; 3988c2ecf20Sopenharmony_ci ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | 3998c2ecf20Sopenharmony_ci V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash, 4048c2ecf20Sopenharmony_ci struct v4l2_flash_config *flash_cfg) 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct v4l2_flash_ctrl_data *ctrl_init_data; 4088c2ecf20Sopenharmony_ci struct v4l2_ctrl *ctrl; 4098c2ecf20Sopenharmony_ci struct v4l2_ctrl_config *ctrl_cfg; 4108c2ecf20Sopenharmony_ci int i, ret, num_ctrls = 0; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci v4l2_flash->ctrls = devm_kcalloc(v4l2_flash->sd.dev, 4138c2ecf20Sopenharmony_ci STROBE_SOURCE + 1, 4148c2ecf20Sopenharmony_ci sizeof(*v4l2_flash->ctrls), 4158c2ecf20Sopenharmony_ci GFP_KERNEL); 4168c2ecf20Sopenharmony_ci if (!v4l2_flash->ctrls) 4178c2ecf20Sopenharmony_ci return -ENOMEM; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* allocate memory dynamically so as not to exceed stack frame size */ 4208c2ecf20Sopenharmony_ci ctrl_init_data = kcalloc(NUM_FLASH_CTRLS, sizeof(*ctrl_init_data), 4218c2ecf20Sopenharmony_ci GFP_KERNEL); 4228c2ecf20Sopenharmony_ci if (!ctrl_init_data) 4238c2ecf20Sopenharmony_ci return -ENOMEM; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci __fill_ctrl_init_data(v4l2_flash, flash_cfg, ctrl_init_data); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci for (i = 0; i < NUM_FLASH_CTRLS; ++i) 4288c2ecf20Sopenharmony_ci if (ctrl_init_data[i].cid) 4298c2ecf20Sopenharmony_ci ++num_ctrls; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&v4l2_flash->hdl, num_ctrls); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci for (i = 0; i < NUM_FLASH_CTRLS; ++i) { 4348c2ecf20Sopenharmony_ci ctrl_cfg = &ctrl_init_data[i].config; 4358c2ecf20Sopenharmony_ci if (!ctrl_init_data[i].cid) 4368c2ecf20Sopenharmony_ci continue; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (ctrl_cfg->id == V4L2_CID_FLASH_LED_MODE || 4398c2ecf20Sopenharmony_ci ctrl_cfg->id == V4L2_CID_FLASH_STROBE_SOURCE) 4408c2ecf20Sopenharmony_ci ctrl = v4l2_ctrl_new_std_menu(&v4l2_flash->hdl, 4418c2ecf20Sopenharmony_ci &v4l2_flash_ctrl_ops, 4428c2ecf20Sopenharmony_ci ctrl_cfg->id, 4438c2ecf20Sopenharmony_ci ctrl_cfg->max, 4448c2ecf20Sopenharmony_ci ctrl_cfg->menu_skip_mask, 4458c2ecf20Sopenharmony_ci ctrl_cfg->def); 4468c2ecf20Sopenharmony_ci else 4478c2ecf20Sopenharmony_ci ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl, 4488c2ecf20Sopenharmony_ci &v4l2_flash_ctrl_ops, 4498c2ecf20Sopenharmony_ci ctrl_cfg->id, 4508c2ecf20Sopenharmony_ci ctrl_cfg->min, 4518c2ecf20Sopenharmony_ci ctrl_cfg->max, 4528c2ecf20Sopenharmony_ci ctrl_cfg->step, 4538c2ecf20Sopenharmony_ci ctrl_cfg->def); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (ctrl) 4568c2ecf20Sopenharmony_ci ctrl->flags |= ctrl_cfg->flags; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (i <= STROBE_SOURCE) 4598c2ecf20Sopenharmony_ci v4l2_flash->ctrls[i] = ctrl; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci kfree(ctrl_init_data); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (v4l2_flash->hdl.error) { 4658c2ecf20Sopenharmony_ci ret = v4l2_flash->hdl.error; 4668c2ecf20Sopenharmony_ci goto error_free_handler; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci v4l2_ctrl_handler_setup(&v4l2_flash->hdl); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci v4l2_flash->sd.ctrl_handler = &v4l2_flash->hdl; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci return 0; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cierror_free_handler: 4768c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&v4l2_flash->hdl); 4778c2ecf20Sopenharmony_ci return ret; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic int __sync_device_with_v4l2_controls(struct v4l2_flash *v4l2_flash) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 4838c2ecf20Sopenharmony_ci struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; 4848c2ecf20Sopenharmony_ci int ret = 0; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (ctrls[TORCH_INTENSITY]) 4878c2ecf20Sopenharmony_ci v4l2_flash_set_led_brightness(v4l2_flash, 4888c2ecf20Sopenharmony_ci ctrls[TORCH_INTENSITY]); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (ctrls[INDICATOR_INTENSITY]) 4918c2ecf20Sopenharmony_ci v4l2_flash_set_led_brightness(v4l2_flash, 4928c2ecf20Sopenharmony_ci ctrls[INDICATOR_INTENSITY]); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (ctrls[FLASH_TIMEOUT]) { 4958c2ecf20Sopenharmony_ci ret = led_set_flash_timeout(fled_cdev, 4968c2ecf20Sopenharmony_ci ctrls[FLASH_TIMEOUT]->val); 4978c2ecf20Sopenharmony_ci if (ret < 0) 4988c2ecf20Sopenharmony_ci return ret; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (ctrls[FLASH_INTENSITY]) { 5028c2ecf20Sopenharmony_ci ret = led_set_flash_brightness(fled_cdev, 5038c2ecf20Sopenharmony_ci ctrls[FLASH_INTENSITY]->val); 5048c2ecf20Sopenharmony_ci if (ret < 0) 5058c2ecf20Sopenharmony_ci return ret; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* 5098c2ecf20Sopenharmony_ci * For some hardware arrangements setting strobe source may affect 5108c2ecf20Sopenharmony_ci * torch mode. Synchronize strobe source setting only if not in torch 5118c2ecf20Sopenharmony_ci * mode. For torch mode case it will get synchronized upon switching 5128c2ecf20Sopenharmony_ci * to flash mode. 5138c2ecf20Sopenharmony_ci */ 5148c2ecf20Sopenharmony_ci if (ctrls[STROBE_SOURCE] && 5158c2ecf20Sopenharmony_ci ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH) 5168c2ecf20Sopenharmony_ci ret = call_flash_op(v4l2_flash, external_strobe_set, 5178c2ecf20Sopenharmony_ci ctrls[STROBE_SOURCE]->val); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci return ret; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci/* 5238c2ecf20Sopenharmony_ci * V4L2 subdev internal operations 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic int v4l2_flash_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd); 5298c2ecf20Sopenharmony_ci struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 5308c2ecf20Sopenharmony_ci struct led_classdev *led_cdev = fled_cdev ? &fled_cdev->led_cdev : NULL; 5318c2ecf20Sopenharmony_ci struct led_classdev *led_cdev_ind = v4l2_flash->iled_cdev; 5328c2ecf20Sopenharmony_ci int ret = 0; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (!v4l2_fh_is_singular(&fh->vfh)) 5358c2ecf20Sopenharmony_ci return 0; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (led_cdev) { 5388c2ecf20Sopenharmony_ci mutex_lock(&led_cdev->led_access); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci led_sysfs_disable(led_cdev); 5418c2ecf20Sopenharmony_ci led_trigger_remove(led_cdev); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci mutex_unlock(&led_cdev->led_access); 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (led_cdev_ind) { 5478c2ecf20Sopenharmony_ci mutex_lock(&led_cdev_ind->led_access); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci led_sysfs_disable(led_cdev_ind); 5508c2ecf20Sopenharmony_ci led_trigger_remove(led_cdev_ind); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci mutex_unlock(&led_cdev_ind->led_access); 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci ret = __sync_device_with_v4l2_controls(v4l2_flash); 5568c2ecf20Sopenharmony_ci if (ret < 0) 5578c2ecf20Sopenharmony_ci goto out_sync_device; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci return 0; 5608c2ecf20Sopenharmony_ciout_sync_device: 5618c2ecf20Sopenharmony_ci if (led_cdev) { 5628c2ecf20Sopenharmony_ci mutex_lock(&led_cdev->led_access); 5638c2ecf20Sopenharmony_ci led_sysfs_enable(led_cdev); 5648c2ecf20Sopenharmony_ci mutex_unlock(&led_cdev->led_access); 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (led_cdev_ind) { 5688c2ecf20Sopenharmony_ci mutex_lock(&led_cdev_ind->led_access); 5698c2ecf20Sopenharmony_ci led_sysfs_enable(led_cdev_ind); 5708c2ecf20Sopenharmony_ci mutex_unlock(&led_cdev_ind->led_access); 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci return ret; 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic int v4l2_flash_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd); 5798c2ecf20Sopenharmony_ci struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 5808c2ecf20Sopenharmony_ci struct led_classdev *led_cdev = fled_cdev ? &fled_cdev->led_cdev : NULL; 5818c2ecf20Sopenharmony_ci struct led_classdev *led_cdev_ind = v4l2_flash->iled_cdev; 5828c2ecf20Sopenharmony_ci int ret = 0; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if (!v4l2_fh_is_singular(&fh->vfh)) 5858c2ecf20Sopenharmony_ci return 0; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (led_cdev) { 5888c2ecf20Sopenharmony_ci mutex_lock(&led_cdev->led_access); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (v4l2_flash->ctrls[STROBE_SOURCE]) 5918c2ecf20Sopenharmony_ci ret = v4l2_ctrl_s_ctrl( 5928c2ecf20Sopenharmony_ci v4l2_flash->ctrls[STROBE_SOURCE], 5938c2ecf20Sopenharmony_ci V4L2_FLASH_STROBE_SOURCE_SOFTWARE); 5948c2ecf20Sopenharmony_ci led_sysfs_enable(led_cdev); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci mutex_unlock(&led_cdev->led_access); 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (led_cdev_ind) { 6008c2ecf20Sopenharmony_ci mutex_lock(&led_cdev_ind->led_access); 6018c2ecf20Sopenharmony_ci led_sysfs_enable(led_cdev_ind); 6028c2ecf20Sopenharmony_ci mutex_unlock(&led_cdev_ind->led_access); 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci return ret; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops v4l2_flash_subdev_internal_ops = { 6098c2ecf20Sopenharmony_ci .open = v4l2_flash_open, 6108c2ecf20Sopenharmony_ci .close = v4l2_flash_close, 6118c2ecf20Sopenharmony_ci}; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops v4l2_flash_subdev_ops; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic struct v4l2_flash *__v4l2_flash_init( 6168c2ecf20Sopenharmony_ci struct device *dev, struct fwnode_handle *fwn, 6178c2ecf20Sopenharmony_ci struct led_classdev_flash *fled_cdev, struct led_classdev *iled_cdev, 6188c2ecf20Sopenharmony_ci const struct v4l2_flash_ops *ops, struct v4l2_flash_config *config) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci struct v4l2_flash *v4l2_flash; 6218c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 6228c2ecf20Sopenharmony_ci int ret; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (!config) 6258c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci v4l2_flash = devm_kzalloc(dev, sizeof(*v4l2_flash), GFP_KERNEL); 6288c2ecf20Sopenharmony_ci if (!v4l2_flash) 6298c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci sd = &v4l2_flash->sd; 6328c2ecf20Sopenharmony_ci v4l2_flash->fled_cdev = fled_cdev; 6338c2ecf20Sopenharmony_ci v4l2_flash->iled_cdev = iled_cdev; 6348c2ecf20Sopenharmony_ci v4l2_flash->ops = ops; 6358c2ecf20Sopenharmony_ci sd->dev = dev; 6368c2ecf20Sopenharmony_ci sd->fwnode = fwn ? fwn : dev_fwnode(dev); 6378c2ecf20Sopenharmony_ci v4l2_subdev_init(sd, &v4l2_flash_subdev_ops); 6388c2ecf20Sopenharmony_ci sd->internal_ops = &v4l2_flash_subdev_internal_ops; 6398c2ecf20Sopenharmony_ci sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 6408c2ecf20Sopenharmony_ci strscpy(sd->name, config->dev_name, sizeof(sd->name)); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&sd->entity, 0, NULL); 6438c2ecf20Sopenharmony_ci if (ret < 0) 6448c2ecf20Sopenharmony_ci return ERR_PTR(ret); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci sd->entity.function = MEDIA_ENT_F_FLASH; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci ret = v4l2_flash_init_controls(v4l2_flash, config); 6498c2ecf20Sopenharmony_ci if (ret < 0) 6508c2ecf20Sopenharmony_ci goto err_init_controls; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci fwnode_handle_get(sd->fwnode); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci ret = v4l2_async_register_subdev(sd); 6558c2ecf20Sopenharmony_ci if (ret < 0) 6568c2ecf20Sopenharmony_ci goto err_async_register_sd; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci return v4l2_flash; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cierr_async_register_sd: 6618c2ecf20Sopenharmony_ci fwnode_handle_put(sd->fwnode); 6628c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(sd->ctrl_handler); 6638c2ecf20Sopenharmony_cierr_init_controls: 6648c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci return ERR_PTR(ret); 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistruct v4l2_flash *v4l2_flash_init( 6708c2ecf20Sopenharmony_ci struct device *dev, struct fwnode_handle *fwn, 6718c2ecf20Sopenharmony_ci struct led_classdev_flash *fled_cdev, 6728c2ecf20Sopenharmony_ci const struct v4l2_flash_ops *ops, 6738c2ecf20Sopenharmony_ci struct v4l2_flash_config *config) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci return __v4l2_flash_init(dev, fwn, fled_cdev, NULL, ops, config); 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_flash_init); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistruct v4l2_flash *v4l2_flash_indicator_init( 6808c2ecf20Sopenharmony_ci struct device *dev, struct fwnode_handle *fwn, 6818c2ecf20Sopenharmony_ci struct led_classdev *iled_cdev, 6828c2ecf20Sopenharmony_ci struct v4l2_flash_config *config) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci return __v4l2_flash_init(dev, fwn, NULL, iled_cdev, NULL, config); 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_flash_indicator_init); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_civoid v4l2_flash_release(struct v4l2_flash *v4l2_flash) 6898c2ecf20Sopenharmony_ci{ 6908c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(v4l2_flash)) 6938c2ecf20Sopenharmony_ci return; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci sd = &v4l2_flash->sd; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci v4l2_async_unregister_subdev(sd); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci fwnode_handle_put(sd->fwnode); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(sd->ctrl_handler); 7028c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 7038c2ecf20Sopenharmony_ci} 7048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_flash_release); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>"); 7078c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("V4L2 Flash sub-device helpers"); 7088c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 709