From patchwork Fri Mar 20 15:03:31 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacek Anaszewski X-Patchwork-Id: 28899 Received: from mail.tu-berlin.de ([130.149.7.33]) by www.linuxtv.org with esmtp (Exim 4.72) (envelope-from ) id 1YYyTS-00031V-Fb; Fri, 20 Mar 2015 16:04:34 +0100 X-tubIT-Incoming-IP: 209.132.180.67 Received: from vger.kernel.org ([209.132.180.67]) by mail.tu-berlin.de (exim-4.76/mailfrontend-5) with esmtp id 1YYyTP-0007dg-9O; Fri, 20 Mar 2015 16:04:34 +0100 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751795AbbCTPE2 (ORCPT + 1 other); Fri, 20 Mar 2015 11:04:28 -0400 Received: from mailout4.samsung.com ([203.254.224.34]:42350 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752018AbbCTPE0 (ORCPT ); Fri, 20 Mar 2015 11:04:26 -0400 Received: from epcpsbgm1.samsung.com (epcpsbgm1 [203.254.230.26]) by mailout4.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0NLI00KJJN7C8NA0@mailout4.samsung.com>; Sat, 21 Mar 2015 00:04:24 +0900 (KST) X-AuditID: cbfee61a-f79c06d000004e71-57-550c36f8d74e Received: from epmmp1.local.host ( [203.254.227.16]) by epcpsbgm1.samsung.com (EPCPMTA) with SMTP id 6D.F8.20081.8F63C055; Sat, 21 Mar 2015 00:04:24 +0900 (KST) Received: from AMDC2362.DIGITAL.local ([106.120.53.23]) by mmp1.samsung.com (Oracle Communications Messaging Server 7u4-24.01 (7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTPA id <0NLI00EAZN5YFDD0@mmp1.samsung.com>; Sat, 21 Mar 2015 00:04:24 +0900 (KST) From: Jacek Anaszewski To: linux-leds@vger.kernel.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org Cc: kyungmin.park@samsung.com, pavel@ucw.cz, cooloney@gmail.com, rpurdie@rpsys.net, sakari.ailus@iki.fi, s.nawrocki@samsung.com, Jacek Anaszewski Subject: [PATCH v1 11/11] leds: aat1290: add support for V4L2 Flash sub-device Date: Fri, 20 Mar 2015 16:03:31 +0100 Message-id: <1426863811-12516-12-git-send-email-j.anaszewski@samsung.com> X-Mailer: git-send-email 1.7.9.5 In-reply-to: <1426863811-12516-1-git-send-email-j.anaszewski@samsung.com> References: <1426863811-12516-1-git-send-email-j.anaszewski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFlrOLMWRmVeSWpSXmKPExsVy+t9jAd0fZjyhBg0vdC2O7pzIZDH/yDlW i96rzxktzja9YbfY+mYdo0XPhq2sFndPHWWz2L3rKavF4TftrBZn9q9kc+Dy2DnrLrvH4a8L WTz2zP/B6tG3ZRWjx4rV39k9Pm+SC2CL4rJJSc3JLEst0rdL4Mo4f7qdseCkb8WuhuuMDYxT HLoYOTkkBEwkXl/rYIOwxSQu3FsPZHNxCAksYpR4OG0mK4TTziQx/csRsCo2AUOJny9eM4HY IgKREu+OXmIGKWIW2MAocXlCIwtIQljAT2Lr41nMIDaLgKrE5kdXGUFsXgFPiQvr9gI1cwCt U5CYM8kGxOQECp/YaQFSISTgIXHmzhP2CYy8CxgZVjGKphYkFxQnpeca6hUn5haX5qXrJefn bmIEB98zqR2MKxssDjEKcDAq8fAeKOUOFWJNLCuuzD3EKMHBrCTC66TCEyrEm5JYWZValB9f VJqTWnyIUZqDRUmcV8m+LURIID2xJDU7NbUgtQgmy8TBKdXAKOAxU3DXss5NVxMDtsoVRYQc X3vCc897gdNmF+aLX+s+E3VzXddD4XjH+sf58SkVi5VipKMMuczf6fC0Zf2072eqc3zD9nq3 BJ9h/b7QS6+PfeO4ebj7tW/yxKlfLRLfyOTt2FQm01oZfO3ECRlpv0tP3nfM2B3VLzeDQU6w jUPCNbvKK2KHEktxRqKhFnNRcSIA3ybMUzoCAAA= Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-PMX-Version: 6.0.0.2142326, Antispam-Engine: 2.7.2.2107409, Antispam-Data: 2015.3.20.145120 X-PMX-Spam: Gauge=IIIIIIII, Probability=8%, Report=' MULTIPLE_RCPTS 0.1, HTML_00_01 0.05, HTML_00_10 0.05, CHILD_EX_X3 0, REFERENCES 0, URI_ENDS_IN_HTML 0, __ANY_URI 0, __CP_MEDIA_BODY 0, __CP_NAME_BODY 0, __CP_URI_IN_BODY 0, __FRAUD_BADTHINGS 0, __FRAUD_BODY_WEBMAIL 0, __FRAUD_WEBMAIL 0, __HAS_FROM 0, __HAS_MSGID 0, __HAS_X_MAILER 0, __HAS_X_MAILING_LIST 0, __IN_REP_TO 0, __MIME_TEXT_ONLY 0, __MULTIPLE_RCPTS_CC_X2 0, __REFERENCES 0, __SANE_MSGID 0, __SUBJ_ALPHA_END 0, __TO_MALFORMED_2 0, __TO_NO_NAME 0, __URI_NO_WWW 0, __URI_NS ' Add support for V4L2 Flash sub-device to the aat1290 LED Flash class driver. The support allows for V4L2 Flash sub-device to take the control of the LED Flash class device. Signed-off-by: Jacek Anaszewski Acked-by: Kyungmin Park Cc: Bryan Wu Cc: Richard Purdie Cc: Sakari Ailus --- drivers/leds/Kconfig | 1 + drivers/leds/leds-aat1290.c | 186 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 185 insertions(+), 2 deletions(-) diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index c3b5b027..d73f43f 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -47,6 +47,7 @@ config LEDS_AAT1290 depends on LEDS_CLASS_FLASH depends on GPIOLIB depends on OF + depends on PINCTRL help This option enables support for the LEDs on the AAT1290. diff --git a/drivers/leds/leds-aat1290.c b/drivers/leds/leds-aat1290.c index 456f9a9d..6100b6b 100644 --- a/drivers/leds/leds-aat1290.c +++ b/drivers/leds/leds-aat1290.c @@ -17,9 +17,11 @@ #include #include #include +#include #include #include #include +#include #define AAT1290_MOVIE_MODE_CURRENT_ADDR 17 #define AAT1290_MAX_MM_CURR_PERCENT_0 16 @@ -46,6 +48,11 @@ #define AAT1290_NAME "aat1290" +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) +/* Number of AAT1290 devices in the system. */ +static unsigned int dev_count; +#endif + struct aat1290_led_settings { struct led_flash_setting torch_brightness; @@ -60,11 +67,17 @@ struct aat1290_led { /* related LED Flash class device */ struct led_classdev_flash fled_cdev; + /* V4L2 Flash device */ + struct v4l2_flash *v4l2_flash; /* FLEN pin */ struct gpio_desc *gpio_fl_en; /* EN|SET pin */ struct gpio_desc *gpio_en_set; +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) + /* movie mode current scale */ + int *mm_current_scale; +#endif /* maximum flash timeout */ u32 max_flash_tm; @@ -231,11 +244,16 @@ static int aat1290_led_flash_timeout_set(struct led_classdev_flash *fled_cdev, return 0; } -static int aat1290_led_parse_dt(struct aat1290_led *led) +static int aat1290_led_parse_dt(struct aat1290_led *led, + struct device_node **sub_node, + struct v4l2_flash_ctrl_config *v4l2_flash_config) { struct led_classdev *led_cdev = &led->fled_cdev.led_cdev; struct device *dev = &led->pdev->dev; struct device_node *child_node; +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) + struct pinctrl *pinctrl; +#endif int ret = 0; led->gpio_fl_en = devm_gpiod_get(dev, "flen"); @@ -252,6 +270,17 @@ static int aat1290_led_parse_dt(struct aat1290_led *led) return ret; } +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) + pinctrl = devm_pinctrl_get_select_default(&led->pdev->dev); + if (IS_ERR(pinctrl)) { + v4l2_flash_config->has_external_strobe = false; + dev_info(dev, + "No support for external strobe detected.\n"); + } else { + v4l2_flash_config->has_external_strobe = true; + } +#endif + child_node = of_get_next_available_child(dev->of_node, NULL); if (!child_node) { dev_err(dev, "No DT child node found for connected LED.\n"); @@ -277,6 +306,8 @@ static int aat1290_led_parse_dt(struct aat1290_led *led) return ret; } + *sub_node = child_node; + return ret; } @@ -285,6 +316,15 @@ static void aat1290_init_flash_settings(struct aat1290_led *led, { struct led_flash_setting *setting; +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) + /* Init flash intensity setting */ + setting = &s->torch_brightness; + setting->min = led->mm_current_scale[0]; + setting->max = led->mm_current_scale[AAT1290_MM_CURRENT_SCALE_SIZE - 1]; + setting->step = 1; + setting->val = setting->max; +#endif + /* Init flash timeout setting */ setting = &s->flash_timeout; setting->min = led->max_flash_tm / AAT1290_FLASH_TM_NUM_LEVELS; @@ -293,6 +333,113 @@ static void aat1290_init_flash_settings(struct aat1290_led *led, setting->val = setting->max; } +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) +enum led_brightness aat1290_intensity_to_brightness( + struct v4l2_flash *v4l2_flash, + s32 intensity) +{ + struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; + struct aat1290_led *led = fled_cdev_to_led(fled_cdev); + int i; + + for (i = AAT1290_MM_CURRENT_SCALE_SIZE - 1; i >= 0; --i) + if (intensity >= led->mm_current_scale[i]) + return i + 1; + + return 1; +} + +s32 aat1290_brightness_to_intensity(struct v4l2_flash *v4l2_flash, + enum led_brightness brightness) +{ + struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; + struct aat1290_led *led = fled_cdev_to_led(fled_cdev); + + return led->mm_current_scale[brightness - 1]; +} + +static int aat1290_led_external_strobe_set(struct v4l2_flash *v4l2_flash, + bool enable) +{ + struct aat1290_led *led = fled_cdev_to_led(v4l2_flash->fled_cdev); + struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; + struct led_classdev *led_cdev = &fled_cdev->led_cdev; + struct pinctrl *pinctrl; + + gpiod_direction_output(led->gpio_fl_en, 0); + gpiod_direction_output(led->gpio_en_set, 0); + + led->movie_mode = false; + led_cdev->brightness = 0; + + pinctrl = devm_pinctrl_get_select(&led->pdev->dev, + enable ? "isp" : "host"); + if (IS_ERR(pinctrl)) { + dev_warn(&led->pdev->dev, "Unable to switch strobe source.\n"); + return PTR_ERR(pinctrl); + } + + return 0; +} + +int init_mm_current_scale(struct aat1290_led *led) +{ + int max_mm_current_percent[] = { 20, 22, 25, 28, 32, 36, 40, 45, 50, 56, + 63, 71, 79, 89, 100 }; + int i, max_mm_current = AAT1290_MAX_MM_CURRENT(led->max_flash_current); + + led->mm_current_scale = devm_kzalloc(&led->pdev->dev, + sizeof(max_mm_current_percent), + GFP_KERNEL); + if (!led->mm_current_scale) + return -ENOMEM; + + for (i = 0; i < AAT1290_MM_CURRENT_SCALE_SIZE; ++i) + led->mm_current_scale[i] = max_mm_current * + max_mm_current_percent[i] / 100; + + return 0; +} + +static void aat1290_init_v4l2_ctrl_config(struct aat1290_led *led, + struct aat1290_led_settings *s, + struct v4l2_flash_ctrl_config *config) +{ + struct led_flash_setting *setting; + struct v4l2_ctrl_config *c; + char suffix[10]; + + if (++dev_count > 1) + snprintf(suffix, sizeof(suffix), "_%d", dev_count); + + snprintf(config->dev_name, sizeof(config->dev_name), "%s%s", + AAT1290_NAME, dev_count > 1 ? suffix : ""); + + c = &config->intensity; + setting = &s->torch_brightness; + c->min = setting->min; + c->max = setting->max; + c->step = setting->step; + c->def = setting->val; + + c = &config->flash_timeout; + setting = &s->flash_timeout; + c->min = setting->min; + c->max = setting->max; + c->step = setting->step; + c->def = setting->val; +} + +static const struct v4l2_flash_ops v4l2_flash_ops = { + .external_strobe_set = aat1290_led_external_strobe_set, + .intensity_to_led_brightness = aat1290_intensity_to_brightness, + .led_brightness_to_intensity = aat1290_brightness_to_intensity, +}; +#else +#define aat1290_init_v4l2_ctrl_config(led, s, config) +#define init_mm_current_scale(led) (0) +#endif + static const struct led_flash_ops flash_ops = { .strobe_set = aat1290_led_flash_strobe_set, .timeout_set = aat1290_led_flash_timeout_set, @@ -301,10 +448,12 @@ static const struct led_flash_ops flash_ops = { static int aat1290_led_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct device_node *sub_node = NULL; struct aat1290_led *led; struct led_classdev *led_cdev; struct led_classdev_flash *fled_cdev; struct aat1290_led_settings settings; + struct v4l2_flash_ctrl_config v4l2_flash_config = {}; int ret; led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); @@ -317,16 +466,27 @@ static int aat1290_led_probe(struct platform_device *pdev) fled_cdev = &led->fled_cdev; led_cdev = &fled_cdev->led_cdev; - ret = aat1290_led_parse_dt(led); + ret = aat1290_led_parse_dt(led, &sub_node, &v4l2_flash_config); if (ret < 0) return ret; if (!led_cdev->name) led_cdev->name = AAT1290_NAME; + /* + * Init non-linear movie mode current scale basing + * on the max flash current from Device Tree binding. + */ + ret = init_mm_current_scale(led); + if (ret < 0) + return ret; + /* Init flash settings */ aat1290_init_flash_settings(led, &settings); + /* Init V4L2 Flash controls basing on initialized settings */ + aat1290_init_v4l2_ctrl_config(led, &settings, &v4l2_flash_config); + fled_cdev->timeout = settings.flash_timeout; fled_cdev->ops = &flash_ops; @@ -345,18 +505,40 @@ static int aat1290_led_probe(struct platform_device *pdev) mutex_init(&led->lock); + led_cdev->dev->of_node = sub_node; + + /* Create V4L2 Flash subdev. */ + led->v4l2_flash = v4l2_flash_init(fled_cdev, + &v4l2_flash_ops, + &v4l2_flash_config); + if (IS_ERR(led->v4l2_flash)) { + ret = PTR_ERR(led->v4l2_flash); + goto error_v4l2_flash_init; + } + return 0; + +error_v4l2_flash_init: + led_classdev_flash_unregister(fled_cdev); + mutex_destroy(&led->lock); + + return ret; } static int aat1290_led_remove(struct platform_device *pdev) { struct aat1290_led *led = platform_get_drvdata(pdev); + v4l2_flash_release(led->v4l2_flash); led_classdev_flash_unregister(&led->fled_cdev); cancel_work_sync(&led->work_brightness_set); mutex_destroy(&led->lock); +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) + --dev_count; +#endif + return 0; }