1*42bd6f59SJacek Anaszewski /* 2*42bd6f59SJacek Anaszewski * V4L2 flash LED sub-device registration helpers. 3*42bd6f59SJacek Anaszewski * 4*42bd6f59SJacek Anaszewski * Copyright (C) 2015 Samsung Electronics Co., Ltd 5*42bd6f59SJacek Anaszewski * Author: Jacek Anaszewski <j.anaszewski@samsung.com> 6*42bd6f59SJacek Anaszewski * 7*42bd6f59SJacek Anaszewski * This program is free software; you can redistribute it and/or modify 8*42bd6f59SJacek Anaszewski * it under the terms of the GNU General Public License version 2 as 9*42bd6f59SJacek Anaszewski * published by the Free Software Foundation. 10*42bd6f59SJacek Anaszewski */ 11*42bd6f59SJacek Anaszewski 12*42bd6f59SJacek Anaszewski #include <linux/led-class-flash.h> 13*42bd6f59SJacek Anaszewski #include <linux/module.h> 14*42bd6f59SJacek Anaszewski #include <linux/mutex.h> 15*42bd6f59SJacek Anaszewski #include <linux/of.h> 16*42bd6f59SJacek Anaszewski #include <linux/slab.h> 17*42bd6f59SJacek Anaszewski #include <linux/types.h> 18*42bd6f59SJacek Anaszewski #include <media/v4l2-flash-led-class.h> 19*42bd6f59SJacek Anaszewski 20*42bd6f59SJacek Anaszewski #define has_flash_op(v4l2_flash, op) \ 21*42bd6f59SJacek Anaszewski (v4l2_flash && v4l2_flash->ops->op) 22*42bd6f59SJacek Anaszewski 23*42bd6f59SJacek Anaszewski #define call_flash_op(v4l2_flash, op, arg) \ 24*42bd6f59SJacek Anaszewski (has_flash_op(v4l2_flash, op) ? \ 25*42bd6f59SJacek Anaszewski v4l2_flash->ops->op(v4l2_flash, arg) : \ 26*42bd6f59SJacek Anaszewski -EINVAL) 27*42bd6f59SJacek Anaszewski 28*42bd6f59SJacek Anaszewski enum ctrl_init_data_id { 29*42bd6f59SJacek Anaszewski LED_MODE, 30*42bd6f59SJacek Anaszewski TORCH_INTENSITY, 31*42bd6f59SJacek Anaszewski FLASH_INTENSITY, 32*42bd6f59SJacek Anaszewski INDICATOR_INTENSITY, 33*42bd6f59SJacek Anaszewski FLASH_TIMEOUT, 34*42bd6f59SJacek Anaszewski STROBE_SOURCE, 35*42bd6f59SJacek Anaszewski /* 36*42bd6f59SJacek Anaszewski * Only above values are applicable to 37*42bd6f59SJacek Anaszewski * the 'ctrls' array in the struct v4l2_flash. 38*42bd6f59SJacek Anaszewski */ 39*42bd6f59SJacek Anaszewski FLASH_STROBE, 40*42bd6f59SJacek Anaszewski STROBE_STOP, 41*42bd6f59SJacek Anaszewski STROBE_STATUS, 42*42bd6f59SJacek Anaszewski FLASH_FAULT, 43*42bd6f59SJacek Anaszewski NUM_FLASH_CTRLS, 44*42bd6f59SJacek Anaszewski }; 45*42bd6f59SJacek Anaszewski 46*42bd6f59SJacek Anaszewski static enum led_brightness __intensity_to_led_brightness( 47*42bd6f59SJacek Anaszewski struct v4l2_ctrl *ctrl, s32 intensity) 48*42bd6f59SJacek Anaszewski { 49*42bd6f59SJacek Anaszewski intensity -= ctrl->minimum; 50*42bd6f59SJacek Anaszewski intensity /= (u32) ctrl->step; 51*42bd6f59SJacek Anaszewski 52*42bd6f59SJacek Anaszewski /* 53*42bd6f59SJacek Anaszewski * Indicator LEDs, unlike torch LEDs, are turned on/off basing on 54*42bd6f59SJacek Anaszewski * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only. 55*42bd6f59SJacek Anaszewski * Therefore it must be possible to set it to 0 level which in 56*42bd6f59SJacek Anaszewski * the LED subsystem reflects LED_OFF state. 57*42bd6f59SJacek Anaszewski */ 58*42bd6f59SJacek Anaszewski if (ctrl->minimum) 59*42bd6f59SJacek Anaszewski ++intensity; 60*42bd6f59SJacek Anaszewski 61*42bd6f59SJacek Anaszewski return intensity; 62*42bd6f59SJacek Anaszewski } 63*42bd6f59SJacek Anaszewski 64*42bd6f59SJacek Anaszewski static s32 __led_brightness_to_intensity(struct v4l2_ctrl *ctrl, 65*42bd6f59SJacek Anaszewski enum led_brightness brightness) 66*42bd6f59SJacek Anaszewski { 67*42bd6f59SJacek Anaszewski /* 68*42bd6f59SJacek Anaszewski * Indicator LEDs, unlike torch LEDs, are turned on/off basing on 69*42bd6f59SJacek Anaszewski * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only. 70*42bd6f59SJacek Anaszewski * Do not decrement brightness read from the LED subsystem for 71*42bd6f59SJacek Anaszewski * indicator LED as it may equal 0. For torch LEDs this function 72*42bd6f59SJacek Anaszewski * is called only when V4L2_FLASH_LED_MODE_TORCH is set and the 73*42bd6f59SJacek Anaszewski * brightness read is guaranteed to be greater than 0. In the mode 74*42bd6f59SJacek Anaszewski * V4L2_FLASH_LED_MODE_NONE the cached torch intensity value is used. 75*42bd6f59SJacek Anaszewski */ 76*42bd6f59SJacek Anaszewski if (ctrl->id != V4L2_CID_FLASH_INDICATOR_INTENSITY) 77*42bd6f59SJacek Anaszewski --brightness; 78*42bd6f59SJacek Anaszewski 79*42bd6f59SJacek Anaszewski return (brightness * ctrl->step) + ctrl->minimum; 80*42bd6f59SJacek Anaszewski } 81*42bd6f59SJacek Anaszewski 82*42bd6f59SJacek Anaszewski static void v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash, 83*42bd6f59SJacek Anaszewski struct v4l2_ctrl *ctrl) 84*42bd6f59SJacek Anaszewski { 85*42bd6f59SJacek Anaszewski struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; 86*42bd6f59SJacek Anaszewski enum led_brightness brightness; 87*42bd6f59SJacek Anaszewski 88*42bd6f59SJacek Anaszewski if (has_flash_op(v4l2_flash, intensity_to_led_brightness)) 89*42bd6f59SJacek Anaszewski brightness = call_flash_op(v4l2_flash, 90*42bd6f59SJacek Anaszewski intensity_to_led_brightness, 91*42bd6f59SJacek Anaszewski ctrl->val); 92*42bd6f59SJacek Anaszewski else 93*42bd6f59SJacek Anaszewski brightness = __intensity_to_led_brightness(ctrl, ctrl->val); 94*42bd6f59SJacek Anaszewski /* 95*42bd6f59SJacek Anaszewski * In case a LED Flash class driver provides ops for custom 96*42bd6f59SJacek Anaszewski * brightness <-> intensity conversion, it also must have defined 97*42bd6f59SJacek Anaszewski * related v4l2 control step == 1. In such a case a backward conversion 98*42bd6f59SJacek Anaszewski * from led brightness to v4l2 intensity is required to find out the 99*42bd6f59SJacek Anaszewski * the aligned intensity value. 100*42bd6f59SJacek Anaszewski */ 101*42bd6f59SJacek Anaszewski if (has_flash_op(v4l2_flash, led_brightness_to_intensity)) 102*42bd6f59SJacek Anaszewski ctrl->val = call_flash_op(v4l2_flash, 103*42bd6f59SJacek Anaszewski led_brightness_to_intensity, 104*42bd6f59SJacek Anaszewski brightness); 105*42bd6f59SJacek Anaszewski 106*42bd6f59SJacek Anaszewski if (ctrl == ctrls[TORCH_INTENSITY]) { 107*42bd6f59SJacek Anaszewski if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH) 108*42bd6f59SJacek Anaszewski return; 109*42bd6f59SJacek Anaszewski 110*42bd6f59SJacek Anaszewski led_set_brightness(&v4l2_flash->fled_cdev->led_cdev, 111*42bd6f59SJacek Anaszewski brightness); 112*42bd6f59SJacek Anaszewski } else { 113*42bd6f59SJacek Anaszewski led_set_brightness(&v4l2_flash->iled_cdev->led_cdev, 114*42bd6f59SJacek Anaszewski brightness); 115*42bd6f59SJacek Anaszewski } 116*42bd6f59SJacek Anaszewski } 117*42bd6f59SJacek Anaszewski 118*42bd6f59SJacek Anaszewski static int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash, 119*42bd6f59SJacek Anaszewski struct v4l2_ctrl *ctrl) 120*42bd6f59SJacek Anaszewski { 121*42bd6f59SJacek Anaszewski struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; 122*42bd6f59SJacek Anaszewski struct led_classdev *led_cdev; 123*42bd6f59SJacek Anaszewski int ret; 124*42bd6f59SJacek Anaszewski 125*42bd6f59SJacek Anaszewski if (ctrl == ctrls[TORCH_INTENSITY]) { 126*42bd6f59SJacek Anaszewski /* 127*42bd6f59SJacek Anaszewski * Update torch brightness only if in TORCH_MODE. In other modes 128*42bd6f59SJacek Anaszewski * torch led is turned off, which would spuriously inform the 129*42bd6f59SJacek Anaszewski * user space that V4L2_CID_FLASH_TORCH_INTENSITY control value 130*42bd6f59SJacek Anaszewski * has changed to 0. 131*42bd6f59SJacek Anaszewski */ 132*42bd6f59SJacek Anaszewski if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH) 133*42bd6f59SJacek Anaszewski return 0; 134*42bd6f59SJacek Anaszewski led_cdev = &v4l2_flash->fled_cdev->led_cdev; 135*42bd6f59SJacek Anaszewski } else { 136*42bd6f59SJacek Anaszewski led_cdev = &v4l2_flash->iled_cdev->led_cdev; 137*42bd6f59SJacek Anaszewski } 138*42bd6f59SJacek Anaszewski 139*42bd6f59SJacek Anaszewski ret = led_update_brightness(led_cdev); 140*42bd6f59SJacek Anaszewski if (ret < 0) 141*42bd6f59SJacek Anaszewski return ret; 142*42bd6f59SJacek Anaszewski 143*42bd6f59SJacek Anaszewski if (has_flash_op(v4l2_flash, led_brightness_to_intensity)) 144*42bd6f59SJacek Anaszewski ctrl->val = call_flash_op(v4l2_flash, 145*42bd6f59SJacek Anaszewski led_brightness_to_intensity, 146*42bd6f59SJacek Anaszewski led_cdev->brightness); 147*42bd6f59SJacek Anaszewski else 148*42bd6f59SJacek Anaszewski ctrl->val = __led_brightness_to_intensity(ctrl, 149*42bd6f59SJacek Anaszewski led_cdev->brightness); 150*42bd6f59SJacek Anaszewski 151*42bd6f59SJacek Anaszewski return 0; 152*42bd6f59SJacek Anaszewski } 153*42bd6f59SJacek Anaszewski 154*42bd6f59SJacek Anaszewski static int v4l2_flash_g_volatile_ctrl(struct v4l2_ctrl *c) 155*42bd6f59SJacek Anaszewski { 156*42bd6f59SJacek Anaszewski struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c); 157*42bd6f59SJacek Anaszewski struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 158*42bd6f59SJacek Anaszewski bool is_strobing; 159*42bd6f59SJacek Anaszewski int ret; 160*42bd6f59SJacek Anaszewski 161*42bd6f59SJacek Anaszewski switch (c->id) { 162*42bd6f59SJacek Anaszewski case V4L2_CID_FLASH_TORCH_INTENSITY: 163*42bd6f59SJacek Anaszewski case V4L2_CID_FLASH_INDICATOR_INTENSITY: 164*42bd6f59SJacek Anaszewski return v4l2_flash_update_led_brightness(v4l2_flash, c); 165*42bd6f59SJacek Anaszewski case V4L2_CID_FLASH_INTENSITY: 166*42bd6f59SJacek Anaszewski ret = led_update_flash_brightness(fled_cdev); 167*42bd6f59SJacek Anaszewski if (ret < 0) 168*42bd6f59SJacek Anaszewski return ret; 169*42bd6f59SJacek Anaszewski /* 170*42bd6f59SJacek Anaszewski * No conversion is needed as LED Flash class also uses 171*42bd6f59SJacek Anaszewski * microamperes for flash intensity units. 172*42bd6f59SJacek Anaszewski */ 173*42bd6f59SJacek Anaszewski c->val = fled_cdev->brightness.val; 174*42bd6f59SJacek Anaszewski return 0; 175*42bd6f59SJacek Anaszewski case V4L2_CID_FLASH_STROBE_STATUS: 176*42bd6f59SJacek Anaszewski ret = led_get_flash_strobe(fled_cdev, &is_strobing); 177*42bd6f59SJacek Anaszewski if (ret < 0) 178*42bd6f59SJacek Anaszewski return ret; 179*42bd6f59SJacek Anaszewski c->val = is_strobing; 180*42bd6f59SJacek Anaszewski return 0; 181*42bd6f59SJacek Anaszewski case V4L2_CID_FLASH_FAULT: 182*42bd6f59SJacek Anaszewski /* LED faults map directly to V4L2 flash faults */ 183*42bd6f59SJacek Anaszewski return led_get_flash_fault(fled_cdev, &c->val); 184*42bd6f59SJacek Anaszewski default: 185*42bd6f59SJacek Anaszewski return -EINVAL; 186*42bd6f59SJacek Anaszewski } 187*42bd6f59SJacek Anaszewski } 188*42bd6f59SJacek Anaszewski 189*42bd6f59SJacek Anaszewski static bool __software_strobe_mode_inactive(struct v4l2_ctrl **ctrls) 190*42bd6f59SJacek Anaszewski { 191*42bd6f59SJacek Anaszewski return ((ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH) || 192*42bd6f59SJacek Anaszewski (ctrls[STROBE_SOURCE] && (ctrls[STROBE_SOURCE]->val != 193*42bd6f59SJacek Anaszewski V4L2_FLASH_STROBE_SOURCE_SOFTWARE))); 194*42bd6f59SJacek Anaszewski } 195*42bd6f59SJacek Anaszewski 196*42bd6f59SJacek Anaszewski static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c) 197*42bd6f59SJacek Anaszewski { 198*42bd6f59SJacek Anaszewski struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c); 199*42bd6f59SJacek Anaszewski struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 200*42bd6f59SJacek Anaszewski struct led_classdev *led_cdev = &fled_cdev->led_cdev; 201*42bd6f59SJacek Anaszewski struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; 202*42bd6f59SJacek Anaszewski bool external_strobe; 203*42bd6f59SJacek Anaszewski int ret = 0; 204*42bd6f59SJacek Anaszewski 205*42bd6f59SJacek Anaszewski switch (c->id) { 206*42bd6f59SJacek Anaszewski case V4L2_CID_FLASH_LED_MODE: 207*42bd6f59SJacek Anaszewski switch (c->val) { 208*42bd6f59SJacek Anaszewski case V4L2_FLASH_LED_MODE_NONE: 209*42bd6f59SJacek Anaszewski led_set_brightness(led_cdev, LED_OFF); 210*42bd6f59SJacek Anaszewski return led_set_flash_strobe(fled_cdev, false); 211*42bd6f59SJacek Anaszewski case V4L2_FLASH_LED_MODE_FLASH: 212*42bd6f59SJacek Anaszewski /* Turn the torch LED off */ 213*42bd6f59SJacek Anaszewski led_set_brightness(led_cdev, LED_OFF); 214*42bd6f59SJacek Anaszewski if (ctrls[STROBE_SOURCE]) { 215*42bd6f59SJacek Anaszewski external_strobe = (ctrls[STROBE_SOURCE]->val == 216*42bd6f59SJacek Anaszewski V4L2_FLASH_STROBE_SOURCE_EXTERNAL); 217*42bd6f59SJacek Anaszewski 218*42bd6f59SJacek Anaszewski ret = call_flash_op(v4l2_flash, 219*42bd6f59SJacek Anaszewski external_strobe_set, 220*42bd6f59SJacek Anaszewski external_strobe); 221*42bd6f59SJacek Anaszewski } 222*42bd6f59SJacek Anaszewski return ret; 223*42bd6f59SJacek Anaszewski case V4L2_FLASH_LED_MODE_TORCH: 224*42bd6f59SJacek Anaszewski if (ctrls[STROBE_SOURCE]) { 225*42bd6f59SJacek Anaszewski ret = call_flash_op(v4l2_flash, 226*42bd6f59SJacek Anaszewski external_strobe_set, 227*42bd6f59SJacek Anaszewski false); 228*42bd6f59SJacek Anaszewski if (ret < 0) 229*42bd6f59SJacek Anaszewski return ret; 230*42bd6f59SJacek Anaszewski } 231*42bd6f59SJacek Anaszewski /* Stop flash strobing */ 232*42bd6f59SJacek Anaszewski ret = led_set_flash_strobe(fled_cdev, false); 233*42bd6f59SJacek Anaszewski if (ret < 0) 234*42bd6f59SJacek Anaszewski return ret; 235*42bd6f59SJacek Anaszewski 236*42bd6f59SJacek Anaszewski v4l2_flash_set_led_brightness(v4l2_flash, 237*42bd6f59SJacek Anaszewski ctrls[TORCH_INTENSITY]); 238*42bd6f59SJacek Anaszewski return 0; 239*42bd6f59SJacek Anaszewski } 240*42bd6f59SJacek Anaszewski break; 241*42bd6f59SJacek Anaszewski case V4L2_CID_FLASH_STROBE_SOURCE: 242*42bd6f59SJacek Anaszewski external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL); 243*42bd6f59SJacek Anaszewski /* 244*42bd6f59SJacek Anaszewski * For some hardware arrangements setting strobe source may 245*42bd6f59SJacek Anaszewski * affect torch mode. Therefore, if not in the flash mode, 246*42bd6f59SJacek Anaszewski * cache only this setting. It will be applied upon switching 247*42bd6f59SJacek Anaszewski * to flash mode. 248*42bd6f59SJacek Anaszewski */ 249*42bd6f59SJacek Anaszewski if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH) 250*42bd6f59SJacek Anaszewski return 0; 251*42bd6f59SJacek Anaszewski 252*42bd6f59SJacek Anaszewski return call_flash_op(v4l2_flash, external_strobe_set, 253*42bd6f59SJacek Anaszewski external_strobe); 254*42bd6f59SJacek Anaszewski case V4L2_CID_FLASH_STROBE: 255*42bd6f59SJacek Anaszewski if (__software_strobe_mode_inactive(ctrls)) 256*42bd6f59SJacek Anaszewski return -EBUSY; 257*42bd6f59SJacek Anaszewski return led_set_flash_strobe(fled_cdev, true); 258*42bd6f59SJacek Anaszewski case V4L2_CID_FLASH_STROBE_STOP: 259*42bd6f59SJacek Anaszewski if (__software_strobe_mode_inactive(ctrls)) 260*42bd6f59SJacek Anaszewski return -EBUSY; 261*42bd6f59SJacek Anaszewski return led_set_flash_strobe(fled_cdev, false); 262*42bd6f59SJacek Anaszewski case V4L2_CID_FLASH_TIMEOUT: 263*42bd6f59SJacek Anaszewski /* 264*42bd6f59SJacek Anaszewski * No conversion is needed as LED Flash class also uses 265*42bd6f59SJacek Anaszewski * microseconds for flash timeout units. 266*42bd6f59SJacek Anaszewski */ 267*42bd6f59SJacek Anaszewski return led_set_flash_timeout(fled_cdev, c->val); 268*42bd6f59SJacek Anaszewski case V4L2_CID_FLASH_INTENSITY: 269*42bd6f59SJacek Anaszewski /* 270*42bd6f59SJacek Anaszewski * No conversion is needed as LED Flash class also uses 271*42bd6f59SJacek Anaszewski * microamperes for flash intensity units. 272*42bd6f59SJacek Anaszewski */ 273*42bd6f59SJacek Anaszewski return led_set_flash_brightness(fled_cdev, c->val); 274*42bd6f59SJacek Anaszewski case V4L2_CID_FLASH_TORCH_INTENSITY: 275*42bd6f59SJacek Anaszewski case V4L2_CID_FLASH_INDICATOR_INTENSITY: 276*42bd6f59SJacek Anaszewski v4l2_flash_set_led_brightness(v4l2_flash, c); 277*42bd6f59SJacek Anaszewski return 0; 278*42bd6f59SJacek Anaszewski } 279*42bd6f59SJacek Anaszewski 280*42bd6f59SJacek Anaszewski return -EINVAL; 281*42bd6f59SJacek Anaszewski } 282*42bd6f59SJacek Anaszewski 283*42bd6f59SJacek Anaszewski static const struct v4l2_ctrl_ops v4l2_flash_ctrl_ops = { 284*42bd6f59SJacek Anaszewski .g_volatile_ctrl = v4l2_flash_g_volatile_ctrl, 285*42bd6f59SJacek Anaszewski .s_ctrl = v4l2_flash_s_ctrl, 286*42bd6f59SJacek Anaszewski }; 287*42bd6f59SJacek Anaszewski 288*42bd6f59SJacek Anaszewski static void __lfs_to_v4l2_ctrl_config(struct led_flash_setting *s, 289*42bd6f59SJacek Anaszewski struct v4l2_ctrl_config *c) 290*42bd6f59SJacek Anaszewski { 291*42bd6f59SJacek Anaszewski c->min = s->min; 292*42bd6f59SJacek Anaszewski c->max = s->max; 293*42bd6f59SJacek Anaszewski c->step = s->step; 294*42bd6f59SJacek Anaszewski c->def = s->val; 295*42bd6f59SJacek Anaszewski } 296*42bd6f59SJacek Anaszewski 297*42bd6f59SJacek Anaszewski static void __fill_ctrl_init_data(struct v4l2_flash *v4l2_flash, 298*42bd6f59SJacek Anaszewski struct v4l2_flash_config *flash_cfg, 299*42bd6f59SJacek Anaszewski struct v4l2_flash_ctrl_data *ctrl_init_data) 300*42bd6f59SJacek Anaszewski { 301*42bd6f59SJacek Anaszewski struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 302*42bd6f59SJacek Anaszewski const struct led_flash_ops *fled_cdev_ops = fled_cdev->ops; 303*42bd6f59SJacek Anaszewski struct led_classdev *led_cdev = &fled_cdev->led_cdev; 304*42bd6f59SJacek Anaszewski struct v4l2_ctrl_config *ctrl_cfg; 305*42bd6f59SJacek Anaszewski u32 mask; 306*42bd6f59SJacek Anaszewski 307*42bd6f59SJacek Anaszewski /* Init FLASH_FAULT ctrl data */ 308*42bd6f59SJacek Anaszewski if (flash_cfg->flash_faults) { 309*42bd6f59SJacek Anaszewski ctrl_init_data[FLASH_FAULT].cid = V4L2_CID_FLASH_FAULT; 310*42bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[FLASH_FAULT].config; 311*42bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_FAULT; 312*42bd6f59SJacek Anaszewski ctrl_cfg->max = flash_cfg->flash_faults; 313*42bd6f59SJacek Anaszewski ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | 314*42bd6f59SJacek Anaszewski V4L2_CTRL_FLAG_READ_ONLY; 315*42bd6f59SJacek Anaszewski } 316*42bd6f59SJacek Anaszewski 317*42bd6f59SJacek Anaszewski /* Init FLASH_LED_MODE ctrl data */ 318*42bd6f59SJacek Anaszewski mask = 1 << V4L2_FLASH_LED_MODE_NONE | 319*42bd6f59SJacek Anaszewski 1 << V4L2_FLASH_LED_MODE_TORCH; 320*42bd6f59SJacek Anaszewski if (led_cdev->flags & LED_DEV_CAP_FLASH) 321*42bd6f59SJacek Anaszewski mask |= 1 << V4L2_FLASH_LED_MODE_FLASH; 322*42bd6f59SJacek Anaszewski 323*42bd6f59SJacek Anaszewski ctrl_init_data[LED_MODE].cid = V4L2_CID_FLASH_LED_MODE; 324*42bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[LED_MODE].config; 325*42bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_LED_MODE; 326*42bd6f59SJacek Anaszewski ctrl_cfg->max = V4L2_FLASH_LED_MODE_TORCH; 327*42bd6f59SJacek Anaszewski ctrl_cfg->menu_skip_mask = ~mask; 328*42bd6f59SJacek Anaszewski ctrl_cfg->def = V4L2_FLASH_LED_MODE_NONE; 329*42bd6f59SJacek Anaszewski ctrl_cfg->flags = 0; 330*42bd6f59SJacek Anaszewski 331*42bd6f59SJacek Anaszewski /* Init TORCH_INTENSITY ctrl data */ 332*42bd6f59SJacek Anaszewski ctrl_init_data[TORCH_INTENSITY].cid = V4L2_CID_FLASH_TORCH_INTENSITY; 333*42bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[TORCH_INTENSITY].config; 334*42bd6f59SJacek Anaszewski __lfs_to_v4l2_ctrl_config(&flash_cfg->torch_intensity, ctrl_cfg); 335*42bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_TORCH_INTENSITY; 336*42bd6f59SJacek Anaszewski ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | 337*42bd6f59SJacek Anaszewski V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; 338*42bd6f59SJacek Anaszewski 339*42bd6f59SJacek Anaszewski /* Init INDICATOR_INTENSITY ctrl data */ 340*42bd6f59SJacek Anaszewski if (v4l2_flash->iled_cdev) { 341*42bd6f59SJacek Anaszewski ctrl_init_data[INDICATOR_INTENSITY].cid = 342*42bd6f59SJacek Anaszewski V4L2_CID_FLASH_INDICATOR_INTENSITY; 343*42bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[INDICATOR_INTENSITY].config; 344*42bd6f59SJacek Anaszewski __lfs_to_v4l2_ctrl_config(&flash_cfg->indicator_intensity, 345*42bd6f59SJacek Anaszewski ctrl_cfg); 346*42bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_INDICATOR_INTENSITY; 347*42bd6f59SJacek Anaszewski ctrl_cfg->min = 0; 348*42bd6f59SJacek Anaszewski ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | 349*42bd6f59SJacek Anaszewski V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; 350*42bd6f59SJacek Anaszewski } 351*42bd6f59SJacek Anaszewski 352*42bd6f59SJacek Anaszewski if (!(led_cdev->flags & LED_DEV_CAP_FLASH)) 353*42bd6f59SJacek Anaszewski return; 354*42bd6f59SJacek Anaszewski 355*42bd6f59SJacek Anaszewski /* Init FLASH_STROBE ctrl data */ 356*42bd6f59SJacek Anaszewski ctrl_init_data[FLASH_STROBE].cid = V4L2_CID_FLASH_STROBE; 357*42bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[FLASH_STROBE].config; 358*42bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_STROBE; 359*42bd6f59SJacek Anaszewski 360*42bd6f59SJacek Anaszewski /* Init STROBE_STOP ctrl data */ 361*42bd6f59SJacek Anaszewski ctrl_init_data[STROBE_STOP].cid = V4L2_CID_FLASH_STROBE_STOP; 362*42bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[STROBE_STOP].config; 363*42bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STOP; 364*42bd6f59SJacek Anaszewski 365*42bd6f59SJacek Anaszewski /* Init FLASH_STROBE_SOURCE ctrl data */ 366*42bd6f59SJacek Anaszewski if (flash_cfg->has_external_strobe) { 367*42bd6f59SJacek Anaszewski mask = (1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE) | 368*42bd6f59SJacek Anaszewski (1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL); 369*42bd6f59SJacek Anaszewski ctrl_init_data[STROBE_SOURCE].cid = 370*42bd6f59SJacek Anaszewski V4L2_CID_FLASH_STROBE_SOURCE; 371*42bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[STROBE_SOURCE].config; 372*42bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_STROBE_SOURCE; 373*42bd6f59SJacek Anaszewski ctrl_cfg->max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL; 374*42bd6f59SJacek Anaszewski ctrl_cfg->menu_skip_mask = ~mask; 375*42bd6f59SJacek Anaszewski ctrl_cfg->def = V4L2_FLASH_STROBE_SOURCE_SOFTWARE; 376*42bd6f59SJacek Anaszewski } 377*42bd6f59SJacek Anaszewski 378*42bd6f59SJacek Anaszewski /* Init STROBE_STATUS ctrl data */ 379*42bd6f59SJacek Anaszewski if (fled_cdev_ops->strobe_get) { 380*42bd6f59SJacek Anaszewski ctrl_init_data[STROBE_STATUS].cid = 381*42bd6f59SJacek Anaszewski V4L2_CID_FLASH_STROBE_STATUS; 382*42bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[STROBE_STATUS].config; 383*42bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STATUS; 384*42bd6f59SJacek Anaszewski ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | 385*42bd6f59SJacek Anaszewski V4L2_CTRL_FLAG_READ_ONLY; 386*42bd6f59SJacek Anaszewski } 387*42bd6f59SJacek Anaszewski 388*42bd6f59SJacek Anaszewski /* Init FLASH_TIMEOUT ctrl data */ 389*42bd6f59SJacek Anaszewski if (fled_cdev_ops->timeout_set) { 390*42bd6f59SJacek Anaszewski ctrl_init_data[FLASH_TIMEOUT].cid = V4L2_CID_FLASH_TIMEOUT; 391*42bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[FLASH_TIMEOUT].config; 392*42bd6f59SJacek Anaszewski __lfs_to_v4l2_ctrl_config(&fled_cdev->timeout, ctrl_cfg); 393*42bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_TIMEOUT; 394*42bd6f59SJacek Anaszewski } 395*42bd6f59SJacek Anaszewski 396*42bd6f59SJacek Anaszewski /* Init FLASH_INTENSITY ctrl data */ 397*42bd6f59SJacek Anaszewski if (fled_cdev_ops->flash_brightness_set) { 398*42bd6f59SJacek Anaszewski ctrl_init_data[FLASH_INTENSITY].cid = V4L2_CID_FLASH_INTENSITY; 399*42bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[FLASH_INTENSITY].config; 400*42bd6f59SJacek Anaszewski __lfs_to_v4l2_ctrl_config(&fled_cdev->brightness, ctrl_cfg); 401*42bd6f59SJacek Anaszewski ctrl_cfg->id = V4L2_CID_FLASH_INTENSITY; 402*42bd6f59SJacek Anaszewski ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE | 403*42bd6f59SJacek Anaszewski V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; 404*42bd6f59SJacek Anaszewski } 405*42bd6f59SJacek Anaszewski } 406*42bd6f59SJacek Anaszewski 407*42bd6f59SJacek Anaszewski static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash, 408*42bd6f59SJacek Anaszewski struct v4l2_flash_config *flash_cfg) 409*42bd6f59SJacek Anaszewski 410*42bd6f59SJacek Anaszewski { 411*42bd6f59SJacek Anaszewski struct v4l2_flash_ctrl_data *ctrl_init_data; 412*42bd6f59SJacek Anaszewski struct v4l2_ctrl *ctrl; 413*42bd6f59SJacek Anaszewski struct v4l2_ctrl_config *ctrl_cfg; 414*42bd6f59SJacek Anaszewski int i, ret, num_ctrls = 0; 415*42bd6f59SJacek Anaszewski 416*42bd6f59SJacek Anaszewski v4l2_flash->ctrls = devm_kzalloc(v4l2_flash->sd.dev, 417*42bd6f59SJacek Anaszewski sizeof(*v4l2_flash->ctrls) * 418*42bd6f59SJacek Anaszewski (STROBE_SOURCE + 1), GFP_KERNEL); 419*42bd6f59SJacek Anaszewski if (!v4l2_flash->ctrls) 420*42bd6f59SJacek Anaszewski return -ENOMEM; 421*42bd6f59SJacek Anaszewski 422*42bd6f59SJacek Anaszewski /* allocate memory dynamically so as not to exceed stack frame size */ 423*42bd6f59SJacek Anaszewski ctrl_init_data = kcalloc(NUM_FLASH_CTRLS, sizeof(*ctrl_init_data), 424*42bd6f59SJacek Anaszewski GFP_KERNEL); 425*42bd6f59SJacek Anaszewski if (!ctrl_init_data) 426*42bd6f59SJacek Anaszewski return -ENOMEM; 427*42bd6f59SJacek Anaszewski 428*42bd6f59SJacek Anaszewski __fill_ctrl_init_data(v4l2_flash, flash_cfg, ctrl_init_data); 429*42bd6f59SJacek Anaszewski 430*42bd6f59SJacek Anaszewski for (i = 0; i < NUM_FLASH_CTRLS; ++i) 431*42bd6f59SJacek Anaszewski if (ctrl_init_data[i].cid) 432*42bd6f59SJacek Anaszewski ++num_ctrls; 433*42bd6f59SJacek Anaszewski 434*42bd6f59SJacek Anaszewski v4l2_ctrl_handler_init(&v4l2_flash->hdl, num_ctrls); 435*42bd6f59SJacek Anaszewski 436*42bd6f59SJacek Anaszewski for (i = 0; i < NUM_FLASH_CTRLS; ++i) { 437*42bd6f59SJacek Anaszewski ctrl_cfg = &ctrl_init_data[i].config; 438*42bd6f59SJacek Anaszewski if (!ctrl_init_data[i].cid) 439*42bd6f59SJacek Anaszewski continue; 440*42bd6f59SJacek Anaszewski 441*42bd6f59SJacek Anaszewski if (ctrl_cfg->id == V4L2_CID_FLASH_LED_MODE || 442*42bd6f59SJacek Anaszewski ctrl_cfg->id == V4L2_CID_FLASH_STROBE_SOURCE) 443*42bd6f59SJacek Anaszewski ctrl = v4l2_ctrl_new_std_menu(&v4l2_flash->hdl, 444*42bd6f59SJacek Anaszewski &v4l2_flash_ctrl_ops, 445*42bd6f59SJacek Anaszewski ctrl_cfg->id, 446*42bd6f59SJacek Anaszewski ctrl_cfg->max, 447*42bd6f59SJacek Anaszewski ctrl_cfg->menu_skip_mask, 448*42bd6f59SJacek Anaszewski ctrl_cfg->def); 449*42bd6f59SJacek Anaszewski else 450*42bd6f59SJacek Anaszewski ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl, 451*42bd6f59SJacek Anaszewski &v4l2_flash_ctrl_ops, 452*42bd6f59SJacek Anaszewski ctrl_cfg->id, 453*42bd6f59SJacek Anaszewski ctrl_cfg->min, 454*42bd6f59SJacek Anaszewski ctrl_cfg->max, 455*42bd6f59SJacek Anaszewski ctrl_cfg->step, 456*42bd6f59SJacek Anaszewski ctrl_cfg->def); 457*42bd6f59SJacek Anaszewski 458*42bd6f59SJacek Anaszewski if (ctrl) 459*42bd6f59SJacek Anaszewski ctrl->flags |= ctrl_cfg->flags; 460*42bd6f59SJacek Anaszewski 461*42bd6f59SJacek Anaszewski if (i <= STROBE_SOURCE) 462*42bd6f59SJacek Anaszewski v4l2_flash->ctrls[i] = ctrl; 463*42bd6f59SJacek Anaszewski } 464*42bd6f59SJacek Anaszewski 465*42bd6f59SJacek Anaszewski kfree(ctrl_init_data); 466*42bd6f59SJacek Anaszewski 467*42bd6f59SJacek Anaszewski if (v4l2_flash->hdl.error) { 468*42bd6f59SJacek Anaszewski ret = v4l2_flash->hdl.error; 469*42bd6f59SJacek Anaszewski goto error_free_handler; 470*42bd6f59SJacek Anaszewski } 471*42bd6f59SJacek Anaszewski 472*42bd6f59SJacek Anaszewski v4l2_ctrl_handler_setup(&v4l2_flash->hdl); 473*42bd6f59SJacek Anaszewski 474*42bd6f59SJacek Anaszewski v4l2_flash->sd.ctrl_handler = &v4l2_flash->hdl; 475*42bd6f59SJacek Anaszewski 476*42bd6f59SJacek Anaszewski return 0; 477*42bd6f59SJacek Anaszewski 478*42bd6f59SJacek Anaszewski error_free_handler: 479*42bd6f59SJacek Anaszewski v4l2_ctrl_handler_free(&v4l2_flash->hdl); 480*42bd6f59SJacek Anaszewski return ret; 481*42bd6f59SJacek Anaszewski } 482*42bd6f59SJacek Anaszewski 483*42bd6f59SJacek Anaszewski static int __sync_device_with_v4l2_controls(struct v4l2_flash *v4l2_flash) 484*42bd6f59SJacek Anaszewski { 485*42bd6f59SJacek Anaszewski struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 486*42bd6f59SJacek Anaszewski struct v4l2_ctrl **ctrls = v4l2_flash->ctrls; 487*42bd6f59SJacek Anaszewski int ret = 0; 488*42bd6f59SJacek Anaszewski 489*42bd6f59SJacek Anaszewski v4l2_flash_set_led_brightness(v4l2_flash, ctrls[TORCH_INTENSITY]); 490*42bd6f59SJacek Anaszewski 491*42bd6f59SJacek Anaszewski if (ctrls[INDICATOR_INTENSITY]) 492*42bd6f59SJacek Anaszewski v4l2_flash_set_led_brightness(v4l2_flash, 493*42bd6f59SJacek Anaszewski ctrls[INDICATOR_INTENSITY]); 494*42bd6f59SJacek Anaszewski 495*42bd6f59SJacek Anaszewski if (ctrls[FLASH_TIMEOUT]) { 496*42bd6f59SJacek Anaszewski ret = led_set_flash_timeout(fled_cdev, 497*42bd6f59SJacek Anaszewski ctrls[FLASH_TIMEOUT]->val); 498*42bd6f59SJacek Anaszewski if (ret < 0) 499*42bd6f59SJacek Anaszewski return ret; 500*42bd6f59SJacek Anaszewski } 501*42bd6f59SJacek Anaszewski 502*42bd6f59SJacek Anaszewski if (ctrls[FLASH_INTENSITY]) { 503*42bd6f59SJacek Anaszewski ret = led_set_flash_brightness(fled_cdev, 504*42bd6f59SJacek Anaszewski ctrls[FLASH_INTENSITY]->val); 505*42bd6f59SJacek Anaszewski if (ret < 0) 506*42bd6f59SJacek Anaszewski return ret; 507*42bd6f59SJacek Anaszewski } 508*42bd6f59SJacek Anaszewski 509*42bd6f59SJacek Anaszewski /* 510*42bd6f59SJacek Anaszewski * For some hardware arrangements setting strobe source may affect 511*42bd6f59SJacek Anaszewski * torch mode. Synchronize strobe source setting only if not in torch 512*42bd6f59SJacek Anaszewski * mode. For torch mode case it will get synchronized upon switching 513*42bd6f59SJacek Anaszewski * to flash mode. 514*42bd6f59SJacek Anaszewski */ 515*42bd6f59SJacek Anaszewski if (ctrls[STROBE_SOURCE] && 516*42bd6f59SJacek Anaszewski ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH) 517*42bd6f59SJacek Anaszewski ret = call_flash_op(v4l2_flash, external_strobe_set, 518*42bd6f59SJacek Anaszewski ctrls[STROBE_SOURCE]->val); 519*42bd6f59SJacek Anaszewski 520*42bd6f59SJacek Anaszewski return ret; 521*42bd6f59SJacek Anaszewski } 522*42bd6f59SJacek Anaszewski 523*42bd6f59SJacek Anaszewski /* 524*42bd6f59SJacek Anaszewski * V4L2 subdev internal operations 525*42bd6f59SJacek Anaszewski */ 526*42bd6f59SJacek Anaszewski 527*42bd6f59SJacek Anaszewski static int v4l2_flash_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 528*42bd6f59SJacek Anaszewski { 529*42bd6f59SJacek Anaszewski struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd); 530*42bd6f59SJacek Anaszewski struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 531*42bd6f59SJacek Anaszewski struct led_classdev *led_cdev = &fled_cdev->led_cdev; 532*42bd6f59SJacek Anaszewski struct led_classdev_flash *iled_cdev = v4l2_flash->iled_cdev; 533*42bd6f59SJacek Anaszewski struct led_classdev *led_cdev_ind = NULL; 534*42bd6f59SJacek Anaszewski int ret = 0; 535*42bd6f59SJacek Anaszewski 536*42bd6f59SJacek Anaszewski if (!v4l2_fh_is_singular(&fh->vfh)) 537*42bd6f59SJacek Anaszewski return 0; 538*42bd6f59SJacek Anaszewski 539*42bd6f59SJacek Anaszewski mutex_lock(&led_cdev->led_access); 540*42bd6f59SJacek Anaszewski 541*42bd6f59SJacek Anaszewski led_sysfs_disable(led_cdev); 542*42bd6f59SJacek Anaszewski led_trigger_remove(led_cdev); 543*42bd6f59SJacek Anaszewski 544*42bd6f59SJacek Anaszewski mutex_unlock(&led_cdev->led_access); 545*42bd6f59SJacek Anaszewski 546*42bd6f59SJacek Anaszewski if (iled_cdev) { 547*42bd6f59SJacek Anaszewski led_cdev_ind = &iled_cdev->led_cdev; 548*42bd6f59SJacek Anaszewski 549*42bd6f59SJacek Anaszewski mutex_lock(&led_cdev_ind->led_access); 550*42bd6f59SJacek Anaszewski 551*42bd6f59SJacek Anaszewski led_sysfs_disable(led_cdev_ind); 552*42bd6f59SJacek Anaszewski led_trigger_remove(led_cdev_ind); 553*42bd6f59SJacek Anaszewski 554*42bd6f59SJacek Anaszewski mutex_unlock(&led_cdev_ind->led_access); 555*42bd6f59SJacek Anaszewski } 556*42bd6f59SJacek Anaszewski 557*42bd6f59SJacek Anaszewski ret = __sync_device_with_v4l2_controls(v4l2_flash); 558*42bd6f59SJacek Anaszewski if (ret < 0) 559*42bd6f59SJacek Anaszewski goto out_sync_device; 560*42bd6f59SJacek Anaszewski 561*42bd6f59SJacek Anaszewski return 0; 562*42bd6f59SJacek Anaszewski out_sync_device: 563*42bd6f59SJacek Anaszewski mutex_lock(&led_cdev->led_access); 564*42bd6f59SJacek Anaszewski led_sysfs_enable(led_cdev); 565*42bd6f59SJacek Anaszewski mutex_unlock(&led_cdev->led_access); 566*42bd6f59SJacek Anaszewski 567*42bd6f59SJacek Anaszewski if (led_cdev_ind) { 568*42bd6f59SJacek Anaszewski mutex_lock(&led_cdev_ind->led_access); 569*42bd6f59SJacek Anaszewski led_sysfs_enable(led_cdev_ind); 570*42bd6f59SJacek Anaszewski mutex_unlock(&led_cdev_ind->led_access); 571*42bd6f59SJacek Anaszewski } 572*42bd6f59SJacek Anaszewski 573*42bd6f59SJacek Anaszewski return ret; 574*42bd6f59SJacek Anaszewski } 575*42bd6f59SJacek Anaszewski 576*42bd6f59SJacek Anaszewski static int v4l2_flash_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 577*42bd6f59SJacek Anaszewski { 578*42bd6f59SJacek Anaszewski struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd); 579*42bd6f59SJacek Anaszewski struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; 580*42bd6f59SJacek Anaszewski struct led_classdev *led_cdev = &fled_cdev->led_cdev; 581*42bd6f59SJacek Anaszewski struct led_classdev_flash *iled_cdev = v4l2_flash->iled_cdev; 582*42bd6f59SJacek Anaszewski int ret = 0; 583*42bd6f59SJacek Anaszewski 584*42bd6f59SJacek Anaszewski if (!v4l2_fh_is_singular(&fh->vfh)) 585*42bd6f59SJacek Anaszewski return 0; 586*42bd6f59SJacek Anaszewski 587*42bd6f59SJacek Anaszewski mutex_lock(&led_cdev->led_access); 588*42bd6f59SJacek Anaszewski 589*42bd6f59SJacek Anaszewski if (v4l2_flash->ctrls[STROBE_SOURCE]) 590*42bd6f59SJacek Anaszewski ret = v4l2_ctrl_s_ctrl(v4l2_flash->ctrls[STROBE_SOURCE], 591*42bd6f59SJacek Anaszewski V4L2_FLASH_STROBE_SOURCE_SOFTWARE); 592*42bd6f59SJacek Anaszewski led_sysfs_enable(led_cdev); 593*42bd6f59SJacek Anaszewski 594*42bd6f59SJacek Anaszewski mutex_unlock(&led_cdev->led_access); 595*42bd6f59SJacek Anaszewski 596*42bd6f59SJacek Anaszewski if (iled_cdev) { 597*42bd6f59SJacek Anaszewski struct led_classdev *led_cdev_ind = &iled_cdev->led_cdev; 598*42bd6f59SJacek Anaszewski 599*42bd6f59SJacek Anaszewski mutex_lock(&led_cdev_ind->led_access); 600*42bd6f59SJacek Anaszewski led_sysfs_enable(led_cdev_ind); 601*42bd6f59SJacek Anaszewski mutex_unlock(&led_cdev_ind->led_access); 602*42bd6f59SJacek Anaszewski } 603*42bd6f59SJacek Anaszewski 604*42bd6f59SJacek Anaszewski return ret; 605*42bd6f59SJacek Anaszewski } 606*42bd6f59SJacek Anaszewski 607*42bd6f59SJacek Anaszewski static const struct v4l2_subdev_internal_ops v4l2_flash_subdev_internal_ops = { 608*42bd6f59SJacek Anaszewski .open = v4l2_flash_open, 609*42bd6f59SJacek Anaszewski .close = v4l2_flash_close, 610*42bd6f59SJacek Anaszewski }; 611*42bd6f59SJacek Anaszewski 612*42bd6f59SJacek Anaszewski static const struct v4l2_subdev_core_ops v4l2_flash_core_ops = { 613*42bd6f59SJacek Anaszewski .queryctrl = v4l2_subdev_queryctrl, 614*42bd6f59SJacek Anaszewski .querymenu = v4l2_subdev_querymenu, 615*42bd6f59SJacek Anaszewski }; 616*42bd6f59SJacek Anaszewski 617*42bd6f59SJacek Anaszewski static const struct v4l2_subdev_ops v4l2_flash_subdev_ops = { 618*42bd6f59SJacek Anaszewski .core = &v4l2_flash_core_ops, 619*42bd6f59SJacek Anaszewski }; 620*42bd6f59SJacek Anaszewski 621*42bd6f59SJacek Anaszewski struct v4l2_flash *v4l2_flash_init( 622*42bd6f59SJacek Anaszewski struct device *dev, struct device_node *of_node, 623*42bd6f59SJacek Anaszewski struct led_classdev_flash *fled_cdev, 624*42bd6f59SJacek Anaszewski struct led_classdev_flash *iled_cdev, 625*42bd6f59SJacek Anaszewski const struct v4l2_flash_ops *ops, 626*42bd6f59SJacek Anaszewski struct v4l2_flash_config *config) 627*42bd6f59SJacek Anaszewski { 628*42bd6f59SJacek Anaszewski struct v4l2_flash *v4l2_flash; 629*42bd6f59SJacek Anaszewski struct led_classdev *led_cdev; 630*42bd6f59SJacek Anaszewski struct v4l2_subdev *sd; 631*42bd6f59SJacek Anaszewski int ret; 632*42bd6f59SJacek Anaszewski 633*42bd6f59SJacek Anaszewski if (!fled_cdev || !ops || !config) 634*42bd6f59SJacek Anaszewski return ERR_PTR(-EINVAL); 635*42bd6f59SJacek Anaszewski 636*42bd6f59SJacek Anaszewski led_cdev = &fled_cdev->led_cdev; 637*42bd6f59SJacek Anaszewski 638*42bd6f59SJacek Anaszewski v4l2_flash = devm_kzalloc(led_cdev->dev, sizeof(*v4l2_flash), 639*42bd6f59SJacek Anaszewski GFP_KERNEL); 640*42bd6f59SJacek Anaszewski if (!v4l2_flash) 641*42bd6f59SJacek Anaszewski return ERR_PTR(-ENOMEM); 642*42bd6f59SJacek Anaszewski 643*42bd6f59SJacek Anaszewski sd = &v4l2_flash->sd; 644*42bd6f59SJacek Anaszewski v4l2_flash->fled_cdev = fled_cdev; 645*42bd6f59SJacek Anaszewski v4l2_flash->iled_cdev = iled_cdev; 646*42bd6f59SJacek Anaszewski v4l2_flash->ops = ops; 647*42bd6f59SJacek Anaszewski sd->dev = dev; 648*42bd6f59SJacek Anaszewski sd->of_node = of_node; 649*42bd6f59SJacek Anaszewski v4l2_subdev_init(sd, &v4l2_flash_subdev_ops); 650*42bd6f59SJacek Anaszewski sd->internal_ops = &v4l2_flash_subdev_internal_ops; 651*42bd6f59SJacek Anaszewski sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 652*42bd6f59SJacek Anaszewski strlcpy(sd->name, config->dev_name, sizeof(sd->name)); 653*42bd6f59SJacek Anaszewski 654*42bd6f59SJacek Anaszewski ret = media_entity_init(&sd->entity, 0, NULL, 0); 655*42bd6f59SJacek Anaszewski if (ret < 0) 656*42bd6f59SJacek Anaszewski return ERR_PTR(ret); 657*42bd6f59SJacek Anaszewski 658*42bd6f59SJacek Anaszewski sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; 659*42bd6f59SJacek Anaszewski 660*42bd6f59SJacek Anaszewski ret = v4l2_flash_init_controls(v4l2_flash, config); 661*42bd6f59SJacek Anaszewski if (ret < 0) 662*42bd6f59SJacek Anaszewski goto err_init_controls; 663*42bd6f59SJacek Anaszewski 664*42bd6f59SJacek Anaszewski if (sd->of_node) 665*42bd6f59SJacek Anaszewski of_node_get(sd->of_node); 666*42bd6f59SJacek Anaszewski else 667*42bd6f59SJacek Anaszewski of_node_get(led_cdev->dev->of_node); 668*42bd6f59SJacek Anaszewski 669*42bd6f59SJacek Anaszewski ret = v4l2_async_register_subdev(sd); 670*42bd6f59SJacek Anaszewski if (ret < 0) 671*42bd6f59SJacek Anaszewski goto err_async_register_sd; 672*42bd6f59SJacek Anaszewski 673*42bd6f59SJacek Anaszewski return v4l2_flash; 674*42bd6f59SJacek Anaszewski 675*42bd6f59SJacek Anaszewski err_async_register_sd: 676*42bd6f59SJacek Anaszewski of_node_put(led_cdev->dev->of_node); 677*42bd6f59SJacek Anaszewski v4l2_ctrl_handler_free(sd->ctrl_handler); 678*42bd6f59SJacek Anaszewski err_init_controls: 679*42bd6f59SJacek Anaszewski media_entity_cleanup(&sd->entity); 680*42bd6f59SJacek Anaszewski 681*42bd6f59SJacek Anaszewski return ERR_PTR(ret); 682*42bd6f59SJacek Anaszewski } 683*42bd6f59SJacek Anaszewski EXPORT_SYMBOL_GPL(v4l2_flash_init); 684*42bd6f59SJacek Anaszewski 685*42bd6f59SJacek Anaszewski void v4l2_flash_release(struct v4l2_flash *v4l2_flash) 686*42bd6f59SJacek Anaszewski { 687*42bd6f59SJacek Anaszewski struct v4l2_subdev *sd; 688*42bd6f59SJacek Anaszewski struct led_classdev *led_cdev; 689*42bd6f59SJacek Anaszewski 690*42bd6f59SJacek Anaszewski if (IS_ERR_OR_NULL(v4l2_flash)) 691*42bd6f59SJacek Anaszewski return; 692*42bd6f59SJacek Anaszewski 693*42bd6f59SJacek Anaszewski sd = &v4l2_flash->sd; 694*42bd6f59SJacek Anaszewski led_cdev = &v4l2_flash->fled_cdev->led_cdev; 695*42bd6f59SJacek Anaszewski 696*42bd6f59SJacek Anaszewski v4l2_async_unregister_subdev(sd); 697*42bd6f59SJacek Anaszewski 698*42bd6f59SJacek Anaszewski if (sd->of_node) 699*42bd6f59SJacek Anaszewski of_node_put(sd->of_node); 700*42bd6f59SJacek Anaszewski else 701*42bd6f59SJacek Anaszewski of_node_put(led_cdev->dev->of_node); 702*42bd6f59SJacek Anaszewski 703*42bd6f59SJacek Anaszewski v4l2_ctrl_handler_free(sd->ctrl_handler); 704*42bd6f59SJacek Anaszewski media_entity_cleanup(&sd->entity); 705*42bd6f59SJacek Anaszewski } 706*42bd6f59SJacek Anaszewski EXPORT_SYMBOL_GPL(v4l2_flash_release); 707*42bd6f59SJacek Anaszewski 708*42bd6f59SJacek Anaszewski MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>"); 709*42bd6f59SJacek Anaszewski MODULE_DESCRIPTION("V4L2 Flash sub-device helpers"); 710*42bd6f59SJacek Anaszewski MODULE_LICENSE("GPL v2"); 711