123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710 |
- /*
- * V4L2 flash LED sub-device registration helpers.
- *
- * Copyright (C) 2015 Samsung Electronics Co., Ltd
- * Author: Jacek Anaszewski <j.anaszewski@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
- #include <linux/led-class-flash.h>
- #include <linux/module.h>
- #include <linux/mutex.h>
- #include <linux/of.h>
- #include <linux/slab.h>
- #include <linux/types.h>
- #include <media/v4l2-flash-led-class.h>
- #define has_flash_op(v4l2_flash, op) \
- (v4l2_flash && v4l2_flash->ops->op)
- #define call_flash_op(v4l2_flash, op, arg) \
- (has_flash_op(v4l2_flash, op) ? \
- v4l2_flash->ops->op(v4l2_flash, arg) : \
- -EINVAL)
- enum ctrl_init_data_id {
- LED_MODE,
- TORCH_INTENSITY,
- FLASH_INTENSITY,
- INDICATOR_INTENSITY,
- FLASH_TIMEOUT,
- STROBE_SOURCE,
- /*
- * Only above values are applicable to
- * the 'ctrls' array in the struct v4l2_flash.
- */
- FLASH_STROBE,
- STROBE_STOP,
- STROBE_STATUS,
- FLASH_FAULT,
- NUM_FLASH_CTRLS,
- };
- static enum led_brightness __intensity_to_led_brightness(
- struct v4l2_ctrl *ctrl, s32 intensity)
- {
- intensity -= ctrl->minimum;
- intensity /= (u32) ctrl->step;
- /*
- * Indicator LEDs, unlike torch LEDs, are turned on/off basing on
- * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only.
- * Therefore it must be possible to set it to 0 level which in
- * the LED subsystem reflects LED_OFF state.
- */
- if (ctrl->minimum)
- ++intensity;
- return intensity;
- }
- static s32 __led_brightness_to_intensity(struct v4l2_ctrl *ctrl,
- enum led_brightness brightness)
- {
- /*
- * Indicator LEDs, unlike torch LEDs, are turned on/off basing on
- * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only.
- * Do not decrement brightness read from the LED subsystem for
- * indicator LED as it may equal 0. For torch LEDs this function
- * is called only when V4L2_FLASH_LED_MODE_TORCH is set and the
- * brightness read is guaranteed to be greater than 0. In the mode
- * V4L2_FLASH_LED_MODE_NONE the cached torch intensity value is used.
- */
- if (ctrl->id != V4L2_CID_FLASH_INDICATOR_INTENSITY)
- --brightness;
- return (brightness * ctrl->step) + ctrl->minimum;
- }
- static void v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash,
- struct v4l2_ctrl *ctrl)
- {
- struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
- enum led_brightness brightness;
- if (has_flash_op(v4l2_flash, intensity_to_led_brightness))
- brightness = call_flash_op(v4l2_flash,
- intensity_to_led_brightness,
- ctrl->val);
- else
- brightness = __intensity_to_led_brightness(ctrl, ctrl->val);
- /*
- * In case a LED Flash class driver provides ops for custom
- * brightness <-> intensity conversion, it also must have defined
- * related v4l2 control step == 1. In such a case a backward conversion
- * from led brightness to v4l2 intensity is required to find out the
- * the aligned intensity value.
- */
- if (has_flash_op(v4l2_flash, led_brightness_to_intensity))
- ctrl->val = call_flash_op(v4l2_flash,
- led_brightness_to_intensity,
- brightness);
- if (ctrl == ctrls[TORCH_INTENSITY]) {
- if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
- return;
- led_set_brightness(&v4l2_flash->fled_cdev->led_cdev,
- brightness);
- } else {
- led_set_brightness(&v4l2_flash->iled_cdev->led_cdev,
- brightness);
- }
- }
- static int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash,
- struct v4l2_ctrl *ctrl)
- {
- struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
- struct led_classdev *led_cdev;
- int ret;
- if (ctrl == ctrls[TORCH_INTENSITY]) {
- /*
- * Update torch brightness only if in TORCH_MODE. In other modes
- * torch led is turned off, which would spuriously inform the
- * user space that V4L2_CID_FLASH_TORCH_INTENSITY control value
- * has changed to 0.
- */
- if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
- return 0;
- led_cdev = &v4l2_flash->fled_cdev->led_cdev;
- } else {
- led_cdev = &v4l2_flash->iled_cdev->led_cdev;
- }
- ret = led_update_brightness(led_cdev);
- if (ret < 0)
- return ret;
- if (has_flash_op(v4l2_flash, led_brightness_to_intensity))
- ctrl->val = call_flash_op(v4l2_flash,
- led_brightness_to_intensity,
- led_cdev->brightness);
- else
- ctrl->val = __led_brightness_to_intensity(ctrl,
- led_cdev->brightness);
- return 0;
- }
- static int v4l2_flash_g_volatile_ctrl(struct v4l2_ctrl *c)
- {
- struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
- struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
- bool is_strobing;
- int ret;
- switch (c->id) {
- case V4L2_CID_FLASH_TORCH_INTENSITY:
- case V4L2_CID_FLASH_INDICATOR_INTENSITY:
- return v4l2_flash_update_led_brightness(v4l2_flash, c);
- case V4L2_CID_FLASH_INTENSITY:
- ret = led_update_flash_brightness(fled_cdev);
- if (ret < 0)
- return ret;
- /*
- * No conversion is needed as LED Flash class also uses
- * microamperes for flash intensity units.
- */
- c->val = fled_cdev->brightness.val;
- return 0;
- case V4L2_CID_FLASH_STROBE_STATUS:
- ret = led_get_flash_strobe(fled_cdev, &is_strobing);
- if (ret < 0)
- return ret;
- c->val = is_strobing;
- return 0;
- case V4L2_CID_FLASH_FAULT:
- /* LED faults map directly to V4L2 flash faults */
- return led_get_flash_fault(fled_cdev, &c->val);
- default:
- return -EINVAL;
- }
- }
- static bool __software_strobe_mode_inactive(struct v4l2_ctrl **ctrls)
- {
- return ((ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH) ||
- (ctrls[STROBE_SOURCE] && (ctrls[STROBE_SOURCE]->val !=
- V4L2_FLASH_STROBE_SOURCE_SOFTWARE)));
- }
- static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c)
- {
- struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
- struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
- struct led_classdev *led_cdev = &fled_cdev->led_cdev;
- struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
- bool external_strobe;
- int ret = 0;
- switch (c->id) {
- case V4L2_CID_FLASH_LED_MODE:
- switch (c->val) {
- case V4L2_FLASH_LED_MODE_NONE:
- led_set_brightness(led_cdev, LED_OFF);
- return led_set_flash_strobe(fled_cdev, false);
- case V4L2_FLASH_LED_MODE_FLASH:
- /* Turn the torch LED off */
- led_set_brightness(led_cdev, LED_OFF);
- if (ctrls[STROBE_SOURCE]) {
- external_strobe = (ctrls[STROBE_SOURCE]->val ==
- V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
- ret = call_flash_op(v4l2_flash,
- external_strobe_set,
- external_strobe);
- }
- return ret;
- case V4L2_FLASH_LED_MODE_TORCH:
- if (ctrls[STROBE_SOURCE]) {
- ret = call_flash_op(v4l2_flash,
- external_strobe_set,
- false);
- if (ret < 0)
- return ret;
- }
- /* Stop flash strobing */
- ret = led_set_flash_strobe(fled_cdev, false);
- if (ret < 0)
- return ret;
- v4l2_flash_set_led_brightness(v4l2_flash,
- ctrls[TORCH_INTENSITY]);
- return 0;
- }
- break;
- case V4L2_CID_FLASH_STROBE_SOURCE:
- external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
- /*
- * For some hardware arrangements setting strobe source may
- * affect torch mode. Therefore, if not in the flash mode,
- * cache only this setting. It will be applied upon switching
- * to flash mode.
- */
- if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH)
- return 0;
- return call_flash_op(v4l2_flash, external_strobe_set,
- external_strobe);
- case V4L2_CID_FLASH_STROBE:
- if (__software_strobe_mode_inactive(ctrls))
- return -EBUSY;
- return led_set_flash_strobe(fled_cdev, true);
- case V4L2_CID_FLASH_STROBE_STOP:
- if (__software_strobe_mode_inactive(ctrls))
- return -EBUSY;
- return led_set_flash_strobe(fled_cdev, false);
- case V4L2_CID_FLASH_TIMEOUT:
- /*
- * No conversion is needed as LED Flash class also uses
- * microseconds for flash timeout units.
- */
- return led_set_flash_timeout(fled_cdev, c->val);
- case V4L2_CID_FLASH_INTENSITY:
- /*
- * No conversion is needed as LED Flash class also uses
- * microamperes for flash intensity units.
- */
- return led_set_flash_brightness(fled_cdev, c->val);
- case V4L2_CID_FLASH_TORCH_INTENSITY:
- case V4L2_CID_FLASH_INDICATOR_INTENSITY:
- v4l2_flash_set_led_brightness(v4l2_flash, c);
- return 0;
- }
- return -EINVAL;
- }
- static const struct v4l2_ctrl_ops v4l2_flash_ctrl_ops = {
- .g_volatile_ctrl = v4l2_flash_g_volatile_ctrl,
- .s_ctrl = v4l2_flash_s_ctrl,
- };
- static void __lfs_to_v4l2_ctrl_config(struct led_flash_setting *s,
- struct v4l2_ctrl_config *c)
- {
- c->min = s->min;
- c->max = s->max;
- c->step = s->step;
- c->def = s->val;
- }
- static void __fill_ctrl_init_data(struct v4l2_flash *v4l2_flash,
- struct v4l2_flash_config *flash_cfg,
- struct v4l2_flash_ctrl_data *ctrl_init_data)
- {
- struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
- const struct led_flash_ops *fled_cdev_ops = fled_cdev->ops;
- struct led_classdev *led_cdev = &fled_cdev->led_cdev;
- struct v4l2_ctrl_config *ctrl_cfg;
- u32 mask;
- /* Init FLASH_FAULT ctrl data */
- if (flash_cfg->flash_faults) {
- ctrl_init_data[FLASH_FAULT].cid = V4L2_CID_FLASH_FAULT;
- ctrl_cfg = &ctrl_init_data[FLASH_FAULT].config;
- ctrl_cfg->id = V4L2_CID_FLASH_FAULT;
- ctrl_cfg->max = flash_cfg->flash_faults;
- ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
- V4L2_CTRL_FLAG_READ_ONLY;
- }
- /* Init FLASH_LED_MODE ctrl data */
- mask = 1 << V4L2_FLASH_LED_MODE_NONE |
- 1 << V4L2_FLASH_LED_MODE_TORCH;
- if (led_cdev->flags & LED_DEV_CAP_FLASH)
- mask |= 1 << V4L2_FLASH_LED_MODE_FLASH;
- ctrl_init_data[LED_MODE].cid = V4L2_CID_FLASH_LED_MODE;
- ctrl_cfg = &ctrl_init_data[LED_MODE].config;
- ctrl_cfg->id = V4L2_CID_FLASH_LED_MODE;
- ctrl_cfg->max = V4L2_FLASH_LED_MODE_TORCH;
- ctrl_cfg->menu_skip_mask = ~mask;
- ctrl_cfg->def = V4L2_FLASH_LED_MODE_NONE;
- ctrl_cfg->flags = 0;
- /* Init TORCH_INTENSITY ctrl data */
- ctrl_init_data[TORCH_INTENSITY].cid = V4L2_CID_FLASH_TORCH_INTENSITY;
- ctrl_cfg = &ctrl_init_data[TORCH_INTENSITY].config;
- __lfs_to_v4l2_ctrl_config(&flash_cfg->torch_intensity, ctrl_cfg);
- ctrl_cfg->id = V4L2_CID_FLASH_TORCH_INTENSITY;
- ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
- V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
- /* Init INDICATOR_INTENSITY ctrl data */
- if (v4l2_flash->iled_cdev) {
- ctrl_init_data[INDICATOR_INTENSITY].cid =
- V4L2_CID_FLASH_INDICATOR_INTENSITY;
- ctrl_cfg = &ctrl_init_data[INDICATOR_INTENSITY].config;
- __lfs_to_v4l2_ctrl_config(&flash_cfg->indicator_intensity,
- ctrl_cfg);
- ctrl_cfg->id = V4L2_CID_FLASH_INDICATOR_INTENSITY;
- ctrl_cfg->min = 0;
- ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
- V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
- }
- if (!(led_cdev->flags & LED_DEV_CAP_FLASH))
- return;
- /* Init FLASH_STROBE ctrl data */
- ctrl_init_data[FLASH_STROBE].cid = V4L2_CID_FLASH_STROBE;
- ctrl_cfg = &ctrl_init_data[FLASH_STROBE].config;
- ctrl_cfg->id = V4L2_CID_FLASH_STROBE;
- /* Init STROBE_STOP ctrl data */
- ctrl_init_data[STROBE_STOP].cid = V4L2_CID_FLASH_STROBE_STOP;
- ctrl_cfg = &ctrl_init_data[STROBE_STOP].config;
- ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STOP;
- /* Init FLASH_STROBE_SOURCE ctrl data */
- if (flash_cfg->has_external_strobe) {
- mask = (1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE) |
- (1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
- ctrl_init_data[STROBE_SOURCE].cid =
- V4L2_CID_FLASH_STROBE_SOURCE;
- ctrl_cfg = &ctrl_init_data[STROBE_SOURCE].config;
- ctrl_cfg->id = V4L2_CID_FLASH_STROBE_SOURCE;
- ctrl_cfg->max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
- ctrl_cfg->menu_skip_mask = ~mask;
- ctrl_cfg->def = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
- }
- /* Init STROBE_STATUS ctrl data */
- if (fled_cdev_ops->strobe_get) {
- ctrl_init_data[STROBE_STATUS].cid =
- V4L2_CID_FLASH_STROBE_STATUS;
- ctrl_cfg = &ctrl_init_data[STROBE_STATUS].config;
- ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STATUS;
- ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
- V4L2_CTRL_FLAG_READ_ONLY;
- }
- /* Init FLASH_TIMEOUT ctrl data */
- if (fled_cdev_ops->timeout_set) {
- ctrl_init_data[FLASH_TIMEOUT].cid = V4L2_CID_FLASH_TIMEOUT;
- ctrl_cfg = &ctrl_init_data[FLASH_TIMEOUT].config;
- __lfs_to_v4l2_ctrl_config(&fled_cdev->timeout, ctrl_cfg);
- ctrl_cfg->id = V4L2_CID_FLASH_TIMEOUT;
- }
- /* Init FLASH_INTENSITY ctrl data */
- if (fled_cdev_ops->flash_brightness_set) {
- ctrl_init_data[FLASH_INTENSITY].cid = V4L2_CID_FLASH_INTENSITY;
- ctrl_cfg = &ctrl_init_data[FLASH_INTENSITY].config;
- __lfs_to_v4l2_ctrl_config(&fled_cdev->brightness, ctrl_cfg);
- ctrl_cfg->id = V4L2_CID_FLASH_INTENSITY;
- ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
- V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
- }
- }
- static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash,
- struct v4l2_flash_config *flash_cfg)
- {
- struct v4l2_flash_ctrl_data *ctrl_init_data;
- struct v4l2_ctrl *ctrl;
- struct v4l2_ctrl_config *ctrl_cfg;
- int i, ret, num_ctrls = 0;
- v4l2_flash->ctrls = devm_kzalloc(v4l2_flash->sd.dev,
- sizeof(*v4l2_flash->ctrls) *
- (STROBE_SOURCE + 1), GFP_KERNEL);
- if (!v4l2_flash->ctrls)
- return -ENOMEM;
- /* allocate memory dynamically so as not to exceed stack frame size */
- ctrl_init_data = kcalloc(NUM_FLASH_CTRLS, sizeof(*ctrl_init_data),
- GFP_KERNEL);
- if (!ctrl_init_data)
- return -ENOMEM;
- __fill_ctrl_init_data(v4l2_flash, flash_cfg, ctrl_init_data);
- for (i = 0; i < NUM_FLASH_CTRLS; ++i)
- if (ctrl_init_data[i].cid)
- ++num_ctrls;
- v4l2_ctrl_handler_init(&v4l2_flash->hdl, num_ctrls);
- for (i = 0; i < NUM_FLASH_CTRLS; ++i) {
- ctrl_cfg = &ctrl_init_data[i].config;
- if (!ctrl_init_data[i].cid)
- continue;
- if (ctrl_cfg->id == V4L2_CID_FLASH_LED_MODE ||
- ctrl_cfg->id == V4L2_CID_FLASH_STROBE_SOURCE)
- ctrl = v4l2_ctrl_new_std_menu(&v4l2_flash->hdl,
- &v4l2_flash_ctrl_ops,
- ctrl_cfg->id,
- ctrl_cfg->max,
- ctrl_cfg->menu_skip_mask,
- ctrl_cfg->def);
- else
- ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl,
- &v4l2_flash_ctrl_ops,
- ctrl_cfg->id,
- ctrl_cfg->min,
- ctrl_cfg->max,
- ctrl_cfg->step,
- ctrl_cfg->def);
- if (ctrl)
- ctrl->flags |= ctrl_cfg->flags;
- if (i <= STROBE_SOURCE)
- v4l2_flash->ctrls[i] = ctrl;
- }
- kfree(ctrl_init_data);
- if (v4l2_flash->hdl.error) {
- ret = v4l2_flash->hdl.error;
- goto error_free_handler;
- }
- v4l2_ctrl_handler_setup(&v4l2_flash->hdl);
- v4l2_flash->sd.ctrl_handler = &v4l2_flash->hdl;
- return 0;
- error_free_handler:
- v4l2_ctrl_handler_free(&v4l2_flash->hdl);
- return ret;
- }
- static int __sync_device_with_v4l2_controls(struct v4l2_flash *v4l2_flash)
- {
- struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
- struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
- int ret = 0;
- v4l2_flash_set_led_brightness(v4l2_flash, ctrls[TORCH_INTENSITY]);
- if (ctrls[INDICATOR_INTENSITY])
- v4l2_flash_set_led_brightness(v4l2_flash,
- ctrls[INDICATOR_INTENSITY]);
- if (ctrls[FLASH_TIMEOUT]) {
- ret = led_set_flash_timeout(fled_cdev,
- ctrls[FLASH_TIMEOUT]->val);
- if (ret < 0)
- return ret;
- }
- if (ctrls[FLASH_INTENSITY]) {
- ret = led_set_flash_brightness(fled_cdev,
- ctrls[FLASH_INTENSITY]->val);
- if (ret < 0)
- return ret;
- }
- /*
- * For some hardware arrangements setting strobe source may affect
- * torch mode. Synchronize strobe source setting only if not in torch
- * mode. For torch mode case it will get synchronized upon switching
- * to flash mode.
- */
- if (ctrls[STROBE_SOURCE] &&
- ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
- ret = call_flash_op(v4l2_flash, external_strobe_set,
- ctrls[STROBE_SOURCE]->val);
- return ret;
- }
- /*
- * V4L2 subdev internal operations
- */
- static int v4l2_flash_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
- {
- struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd);
- struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
- struct led_classdev *led_cdev = &fled_cdev->led_cdev;
- struct led_classdev_flash *iled_cdev = v4l2_flash->iled_cdev;
- struct led_classdev *led_cdev_ind = NULL;
- int ret = 0;
- if (!v4l2_fh_is_singular(&fh->vfh))
- return 0;
- mutex_lock(&led_cdev->led_access);
- led_sysfs_disable(led_cdev);
- led_trigger_remove(led_cdev);
- mutex_unlock(&led_cdev->led_access);
- if (iled_cdev) {
- led_cdev_ind = &iled_cdev->led_cdev;
- mutex_lock(&led_cdev_ind->led_access);
- led_sysfs_disable(led_cdev_ind);
- led_trigger_remove(led_cdev_ind);
- mutex_unlock(&led_cdev_ind->led_access);
- }
- ret = __sync_device_with_v4l2_controls(v4l2_flash);
- if (ret < 0)
- goto out_sync_device;
- return 0;
- out_sync_device:
- mutex_lock(&led_cdev->led_access);
- led_sysfs_enable(led_cdev);
- mutex_unlock(&led_cdev->led_access);
- if (led_cdev_ind) {
- mutex_lock(&led_cdev_ind->led_access);
- led_sysfs_enable(led_cdev_ind);
- mutex_unlock(&led_cdev_ind->led_access);
- }
- return ret;
- }
- static int v4l2_flash_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
- {
- struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd);
- struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
- struct led_classdev *led_cdev = &fled_cdev->led_cdev;
- struct led_classdev_flash *iled_cdev = v4l2_flash->iled_cdev;
- int ret = 0;
- if (!v4l2_fh_is_singular(&fh->vfh))
- return 0;
- mutex_lock(&led_cdev->led_access);
- if (v4l2_flash->ctrls[STROBE_SOURCE])
- ret = v4l2_ctrl_s_ctrl(v4l2_flash->ctrls[STROBE_SOURCE],
- V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
- led_sysfs_enable(led_cdev);
- mutex_unlock(&led_cdev->led_access);
- if (iled_cdev) {
- struct led_classdev *led_cdev_ind = &iled_cdev->led_cdev;
- mutex_lock(&led_cdev_ind->led_access);
- led_sysfs_enable(led_cdev_ind);
- mutex_unlock(&led_cdev_ind->led_access);
- }
- return ret;
- }
- static const struct v4l2_subdev_internal_ops v4l2_flash_subdev_internal_ops = {
- .open = v4l2_flash_open,
- .close = v4l2_flash_close,
- };
- static const struct v4l2_subdev_core_ops v4l2_flash_core_ops = {
- .queryctrl = v4l2_subdev_queryctrl,
- .querymenu = v4l2_subdev_querymenu,
- };
- static const struct v4l2_subdev_ops v4l2_flash_subdev_ops = {
- .core = &v4l2_flash_core_ops,
- };
- struct v4l2_flash *v4l2_flash_init(
- struct device *dev, struct device_node *of_node,
- struct led_classdev_flash *fled_cdev,
- struct led_classdev_flash *iled_cdev,
- const struct v4l2_flash_ops *ops,
- struct v4l2_flash_config *config)
- {
- struct v4l2_flash *v4l2_flash;
- struct led_classdev *led_cdev;
- struct v4l2_subdev *sd;
- int ret;
- if (!fled_cdev || !ops || !config)
- return ERR_PTR(-EINVAL);
- led_cdev = &fled_cdev->led_cdev;
- v4l2_flash = devm_kzalloc(led_cdev->dev, sizeof(*v4l2_flash),
- GFP_KERNEL);
- if (!v4l2_flash)
- return ERR_PTR(-ENOMEM);
- sd = &v4l2_flash->sd;
- v4l2_flash->fled_cdev = fled_cdev;
- v4l2_flash->iled_cdev = iled_cdev;
- v4l2_flash->ops = ops;
- sd->dev = dev;
- sd->of_node = of_node;
- v4l2_subdev_init(sd, &v4l2_flash_subdev_ops);
- sd->internal_ops = &v4l2_flash_subdev_internal_ops;
- sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- strlcpy(sd->name, config->dev_name, sizeof(sd->name));
- ret = media_entity_init(&sd->entity, 0, NULL, 0);
- if (ret < 0)
- return ERR_PTR(ret);
- sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
- ret = v4l2_flash_init_controls(v4l2_flash, config);
- if (ret < 0)
- goto err_init_controls;
- if (sd->of_node)
- of_node_get(sd->of_node);
- else
- of_node_get(led_cdev->dev->of_node);
- ret = v4l2_async_register_subdev(sd);
- if (ret < 0)
- goto err_async_register_sd;
- return v4l2_flash;
- err_async_register_sd:
- of_node_put(led_cdev->dev->of_node);
- v4l2_ctrl_handler_free(sd->ctrl_handler);
- err_init_controls:
- media_entity_cleanup(&sd->entity);
- return ERR_PTR(ret);
- }
- EXPORT_SYMBOL_GPL(v4l2_flash_init);
- void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
- {
- struct v4l2_subdev *sd;
- struct led_classdev *led_cdev;
- if (IS_ERR_OR_NULL(v4l2_flash))
- return;
- sd = &v4l2_flash->sd;
- led_cdev = &v4l2_flash->fled_cdev->led_cdev;
- v4l2_async_unregister_subdev(sd);
- if (sd->of_node)
- of_node_put(sd->of_node);
- else
- of_node_put(led_cdev->dev->of_node);
- v4l2_ctrl_handler_free(sd->ctrl_handler);
- media_entity_cleanup(&sd->entity);
- }
- EXPORT_SYMBOL_GPL(v4l2_flash_release);
- MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
- MODULE_DESCRIPTION("V4L2 Flash sub-device helpers");
- MODULE_LICENSE("GPL v2");
|