From patchwork Wed Sep 30 09:32:01 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 7294001 Return-Path: X-Original-To: patchwork-linux-fbdev@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 38CF39F32B for ; Wed, 30 Sep 2015 09:32:35 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 1E7E620638 for ; Wed, 30 Sep 2015 09:32:34 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id B623620687 for ; Wed, 30 Sep 2015 09:32:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932089AbbI3Jcb (ORCPT ); Wed, 30 Sep 2015 05:32:31 -0400 Received: from arroyo.ext.ti.com ([192.94.94.40]:57641 "EHLO arroyo.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755193AbbI3Jc1 (ORCPT ); Wed, 30 Sep 2015 05:32:27 -0400 Received: from dlelxv90.itg.ti.com ([172.17.2.17]) by arroyo.ext.ti.com (8.13.7/8.13.7) with ESMTP id t8U9WGlv014429; Wed, 30 Sep 2015 04:32:16 -0500 Received: from DFLE72.ent.ti.com (dfle72.ent.ti.com [128.247.5.109]) by dlelxv90.itg.ti.com (8.14.3/8.13.8) with ESMTP id t8U9WFKP009384; Wed, 30 Sep 2015 04:32:15 -0500 Received: from dlep32.itg.ti.com (157.170.170.100) by DFLE72.ent.ti.com (128.247.5.109) with Microsoft SMTP Server id 14.3.224.2; Wed, 30 Sep 2015 04:32:15 -0500 Received: from deskari.ti.com (ileax41-snat.itg.ti.com [10.172.224.153]) by dlep32.itg.ti.com (8.14.3/8.13.8) with ESMTP id t8U9W90p019947; Wed, 30 Sep 2015 04:32:14 -0500 From: Tomi Valkeinen To: Jacek Anaszewski , Jingoo Han , Lee Jones , , CC: Andrew Lunn , Tomi Valkeinen Subject: [PATCHv4 2/3] backlight: add led-backlight driver Date: Wed, 30 Sep 2015 12:32:01 +0300 Message-ID: <1443605522-1118-3-git-send-email-tomi.valkeinen@ti.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1443605522-1118-1-git-send-email-tomi.valkeinen@ti.com> References: <1443605522-1118-1-git-send-email-tomi.valkeinen@ti.com> MIME-Version: 1.0 Sender: linux-fbdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds a led-backlight driver (led_bl), which is similar to pwm_bl except the driver uses a LED class driver to adjust the brightness in the HW. Signed-off-by: Tomi Valkeinen --- drivers/video/backlight/Kconfig | 7 ++ drivers/video/backlight/Makefile | 1 + drivers/video/backlight/led_bl.c | 246 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 254 insertions(+) create mode 100644 drivers/video/backlight/led_bl.c diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 0505b796d743..d1336196aba2 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -453,6 +453,13 @@ config BACKLIGHT_BD6107 help If you have a Rohm BD6107 say Y to enable the backlight driver. +config BACKLIGHT_LED + tristate "Generic LED based Backlight Driver" + depends on LEDS_CLASS && OF + help + If you have a LCD backlight adjustable by LED class driver, say Y + to enable this driver. + endif # BACKLIGHT_CLASS_DEVICE endif # BACKLIGHT_LCD_SUPPORT diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index d67073f9d421..ecd321daee21 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -54,3 +54,4 @@ obj-$(CONFIG_BACKLIGHT_SKY81452) += sky81452-backlight.o obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o obj-$(CONFIG_BACKLIGHT_TPS65217) += tps65217_bl.o obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o +obj-$(CONFIG_BACKLIGHT_LED) += led_bl.o diff --git a/drivers/video/backlight/led_bl.c b/drivers/video/backlight/led_bl.c new file mode 100644 index 000000000000..1befc8ce5964 --- /dev/null +++ b/drivers/video/backlight/led_bl.c @@ -0,0 +1,246 @@ +/* + * Copyright 2015 Texas Instruments + * + * Author: Tomi Valkeinen + * + * Based on pwm_bl.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +struct led_bl_data { + struct device *dev; + struct backlight_device *bl_dev; + + unsigned int *levels; + bool enabled; + struct regulator *power_supply; + struct gpio_desc *enable_gpio; + + struct led_classdev *led_cdev; + + unsigned int max_brightness; + unsigned int default_brightness; +}; + +static void led_bl_set_brightness(struct led_bl_data *priv, int brightness) +{ + int err; + + if (!priv->enabled) { + if (priv->power_supply) { + err = regulator_enable(priv->power_supply); + + if (err < 0) + dev_err(priv->dev, + "failed to enable power supply\n"); + } + + if (priv->enable_gpio) + gpiod_set_value_cansleep(priv->enable_gpio, 1); + } + + led_set_brightness(priv->led_cdev, priv->levels[brightness]); + + priv->enabled = true; +} + +static void led_bl_power_off(struct led_bl_data *priv) +{ + if (!priv->enabled) + return; + + led_set_brightness(priv->led_cdev, LED_OFF); + + if (priv->enable_gpio) + gpiod_set_value_cansleep(priv->enable_gpio, 0); + + if (priv->power_supply) + regulator_disable(priv->power_supply); + + priv->enabled = false; +} + +static int led_bl_update_status(struct backlight_device *bl) +{ + struct led_bl_data *priv = bl_get_data(bl); + int brightness = bl->props.brightness; + + if (bl->props.power != FB_BLANK_UNBLANK || + bl->props.fb_blank != FB_BLANK_UNBLANK || + bl->props.state & BL_CORE_FBBLANK) + brightness = 0; + + if (brightness > 0) + led_bl_set_brightness(priv, brightness); + else + led_bl_power_off(priv); + + return 0; +} + +static const struct backlight_ops led_bl_ops = { + .update_status = led_bl_update_status, +}; + +static int led_bl_parse_dt(struct device *dev, + struct led_bl_data *priv) +{ + struct device_node *node = dev->of_node; + int num_levels; + u32 *levels; + u32 value; + int ret; + + if (!node) + return -ENODEV; + + num_levels = of_property_count_u32_elems(node, "brightness-levels"); + if (num_levels < 0) { + dev_err(dev, "failed to find 'brightness-levels'\n"); + return num_levels; + } + + levels = devm_kzalloc(dev, sizeof(u32) * num_levels, GFP_KERNEL); + if (!levels) + return -ENOMEM; + + ret = of_property_read_u32_array(node, "brightness-levels", + levels, + num_levels); + if (ret < 0) { + dev_err(dev, "failed to parse 'brightness-levels'\n"); + return ret; + } + + ret = of_property_read_u32(node, "default-brightness-level", &value); + if (ret < 0) { + dev_err(dev, "failed to parse 'default-brightness-level'\n"); + return ret; + } + + if (value >= num_levels) { + dev_err(dev, "invalid default-brightness-level\n"); + return -EINVAL; + } + + priv->levels = levels; + priv->max_brightness = num_levels - 1; + priv->default_brightness = value; + + priv->led_cdev = of_led_get(node); + if (IS_ERR(priv->led_cdev)) { + dev_err(dev, "failed to get LED device\n"); + return PTR_ERR(priv->led_cdev); + } + + return 0; +} + +static int led_bl_probe(struct platform_device *pdev) +{ + struct backlight_properties props; + struct led_bl_data *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + priv->dev = &pdev->dev; + + ret = led_bl_parse_dt(&pdev->dev, priv); + if (ret < 0) { + dev_err(&pdev->dev, "failed to parse DT data\n"); + return ret; + } + + priv->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(priv->enable_gpio)) { + ret = PTR_ERR(priv->enable_gpio); + goto err; + } + + priv->power_supply = devm_regulator_get_optional(&pdev->dev, "power"); + if (IS_ERR(priv->power_supply)) { + ret = PTR_ERR(priv->power_supply); + + if (ret == -ENODEV) { + priv->power_supply = NULL; + } else { + ret = PTR_ERR(priv->power_supply); + goto err; + } + } + + memset(&props, 0, sizeof(struct backlight_properties)); + props.type = BACKLIGHT_RAW; + props.max_brightness = priv->max_brightness; + priv->bl_dev = backlight_device_register(dev_name(&pdev->dev), + &pdev->dev, priv, &led_bl_ops, &props); + if (IS_ERR(priv->bl_dev)) { + dev_err(&pdev->dev, "failed to register backlight\n"); + ret = PTR_ERR(priv->bl_dev); + goto err; + } + + priv->bl_dev->props.brightness = priv->default_brightness; + backlight_update_status(priv->bl_dev); + + return 0; + +err: + if (priv->led_cdev) + led_put(priv->led_cdev); + + return ret; +} + +static int led_bl_remove(struct platform_device *pdev) +{ + struct led_bl_data *priv = platform_get_drvdata(pdev); + struct backlight_device *bl = priv->bl_dev; + + backlight_device_unregister(bl); + + led_bl_power_off(priv); + + led_put(priv->led_cdev); + + return 0; +} + +static const struct of_device_id led_bl_of_match[] = { + { .compatible = "led-backlight" }, + { } +}; + +MODULE_DEVICE_TABLE(of, led_bl_of_match); + +static struct platform_driver led_bl_driver = { + .driver = { + .name = "led-backlight", + .of_match_table = of_match_ptr(led_bl_of_match), + }, + .probe = led_bl_probe, + .remove = led_bl_remove, +}; + +module_platform_driver(led_bl_driver); + +MODULE_DESCRIPTION("LED based Backlight Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:led-backlight");