From patchwork Sun Jan 10 09:11:38 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: richard.dorsch@gmail.com X-Patchwork-Id: 565333 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id E82FD1402A1 for ; Sun, 10 Jan 2016 20:13:29 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=vqA8neiL; dkim-atps=neutral Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757016AbcAJJM6 (ORCPT ); Sun, 10 Jan 2016 04:12:58 -0500 Received: from mail-pa0-f68.google.com ([209.85.220.68]:34350 "EHLO mail-pa0-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756918AbcAJJLm (ORCPT ); Sun, 10 Jan 2016 04:11:42 -0500 Received: by mail-pa0-f68.google.com with SMTP id yy13so23382567pab.1; Sun, 10 Jan 2016 01:11:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=b5n7RYD2wf0cDGXu9gcH7ZkC8nDhXQCyACau8qwHwmA=; b=vqA8neiLT74Bi1gV96285qwSDSyRENzdHHzkSBGRQz9V2y8eE24TA2qd0an4+R+tHO +bQE0p4TjDY8giCP0Fdd1Mwi8DHyVOSdUAXnX9kG15MdmVxOpqY5c7vXT/dqbgBW1Lpt XcQSBbhn3gdF1m0kwJ+H7ufYBN3Sq4P6x9bvFP7mAfg588v1DkdJ+C6DkrrE1paUQ5hZ hJQ4yzA8RQJnJEvwzyZCxtMn7gy3dJb2UAMs0wwZXQKrd+5EKKhubNdBBfT8TAAcH1pr lruHl+LJizF5pwQ2NErFK/ogsDyyhil2p5HuifuWd/Ak1dcwGiUjTQXZg7SyEnFDEMA9 SebA== X-Received: by 10.66.252.136 with SMTP id zs8mr98800824pac.110.1452417102188; Sun, 10 Jan 2016 01:11:42 -0800 (PST) Received: from localhost.localdomain.localdomain (cpe-172-89-102-93.socal.res.rr.com. [172.89.102.93]) by smtp.gmail.com with ESMTPSA id ty5sm1949898pac.48.2016.01.10.01.11.41 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 10 Jan 2016 01:11:41 -0800 (PST) From: richard.dorsch@gmail.com To: linux-kernel@vger.kernel.org Cc: lm-sensors@lm-sensors.org, linux-i2c@vger.kernel.org, linux-watchdog@vger.kernel.org, linux-gpio@vger.kernel.org, lee.jones@linaro.org, jdelvare@suse.com, linux@roeck-us.net, wim@iguana.be, jo.sunga@advantech.com, Richard Vidal-Dorsch Subject: [PATCH v2 5/6] Add Advantech iManager Backlight driver Date: Sun, 10 Jan 2016 01:11:38 -0800 Message-Id: <1452417098-28667-1-git-send-email-richard.dorsch@gmail.com> X-Mailer: git-send-email 2.6.4 Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org From: Richard Vidal-Dorsch Signed-off-by: Richard Vidal-Dorsch --- drivers/video/backlight/Kconfig | 12 ++ drivers/video/backlight/Makefile | 2 + drivers/video/backlight/imanager-bl.c | 199 +++++++++++++++++++++++++++++++ drivers/video/backlight/imanager-ec-bl.c | 118 ++++++++++++++++++ include/linux/mfd/imanager/backlight.h | 37 ++++++ 5 files changed, 368 insertions(+) create mode 100644 drivers/video/backlight/imanager-bl.c create mode 100644 drivers/video/backlight/imanager-ec-bl.c create mode 100644 include/linux/mfd/imanager/backlight.h diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 5ffa4b4..8003573 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -251,6 +251,18 @@ config BACKLIGHT_HP700 If you have an HP Jornada 700 series, say Y to include backlight control driver. +config BACKLIGHT_IMANAGER + tristate "Advantech iManager backlight/brightness" + depends on MFD_IMANAGER + help + This enables support for Advantech iManager Backlight and + Brightness control of some Advantech SOM, MIO, AIMB, and + PCM modules/boards. + Requires mfd-core and imanager-core to function properly. + + This driver can also be built as a module. If so, the module + will be called imanager_bl. + config BACKLIGHT_CARILLO_RANCH tristate "Intel Carillo Ranch Backlight Driver" depends on LCD_CLASS_DEVICE && PCI && X86 && FB_LE80578 diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 16ec534..15bf136 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -26,6 +26,8 @@ obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o obj-$(CONFIG_BACKLIGHT_APPLE) += apple_bl.o obj-$(CONFIG_BACKLIGHT_AS3711) += as3711_bl.o obj-$(CONFIG_BACKLIGHT_BD6107) += bd6107.o +imanager_bl-objs := imanager-bl.o imanager-ec-bl.o +obj-$(CONFIG_BACKLIGHT_IMANAGER) += imanager_bl.o obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o diff --git a/drivers/video/backlight/imanager-bl.c b/drivers/video/backlight/imanager-bl.c new file mode 100644 index 0000000..f773ce7 --- /dev/null +++ b/drivers/video/backlight/imanager-bl.c @@ -0,0 +1,199 @@ +/* + * Advantech iManager Backlight driver + * Partially derived from wm831x_bl + * + * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA + * Author: Richard Vidal-Dorsch + * + * 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; either version 2 of the License, or (at your + * option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BL_MAX_BRIGHTNESS 100 + +static bool polarity = PWM_POLARITY_NORMAL; +module_param(polarity, bool, 0); +MODULE_PARM_DESC(polarity, "Select backlight polarity (inverted := 1)"); + +static ushort unit = UNIT_1; +module_param(unit, ushort, 0); +MODULE_PARM_DESC(unit, "Select backlight control unit [0, 1] (defaults to 0)"); + +struct imanager_backlight_data { + struct imanager_device_data *idev; + struct backlight_device *bl; +}; + +static int get_brightness(struct backlight_device *dev) +{ + struct imanager_backlight_data *data = bl_get_data(dev); + int ret; + + mutex_lock(&data->idev->lock); + + ret = bl_core_get_pulse_width(unit); + /* Reverse percentage if polarity is set */ + if (polarity) + ret = 100 - ret; + + mutex_unlock(&data->idev->lock); + + return ret; +} + +static int set_brightness(struct backlight_device *dev) +{ + struct imanager_backlight_data *data = bl_get_data(dev); + u8 brightness = (u8) dev->props.brightness; + int ret; + + if (brightness > BL_MAX_BRIGHTNESS) + return -EINVAL; + + if (dev->props.power != FB_BLANK_UNBLANK) + brightness = 0; + + if (dev->props.fb_blank != FB_BLANK_UNBLANK) + brightness = 0; + + if (dev->props.state & BL_CORE_SUSPENDED) + brightness = 0; + + mutex_lock(&data->idev->lock); + + /* Inversed percentage if polarity is set */ + if (polarity) + brightness = 100 - brightness; + ret = bl_core_set_pulse_width(unit, brightness); + + mutex_unlock(&data->idev->lock); + + return ret; +} + +static const struct backlight_ops imanager_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .get_brightness = get_brightness, + .update_status = set_brightness, +}; + +static int imanager_backlight_init(struct device *dev, + struct imanager_backlight_data *data) +{ + struct backlight_device *bd; + struct backlight_properties props; + + memset(&props, 0, sizeof(props)); + props.type = BACKLIGHT_PLATFORM; + props.max_brightness = BL_MAX_BRIGHTNESS; + bd = backlight_device_register("imanager_backlight", dev, data, + &imanager_bl_ops, &props); + + if (IS_ERR(bd)) { + data->bl = NULL; + dev_err(dev, "Unable to register backlight device\n"); + return PTR_ERR(bd); + } + + data->bl = bd; + + bd->props.brightness = get_brightness(bd); + bd->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bd); + + return 0; +} + +static int imanager_backlight_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct imanager_device_data *idev = dev_get_drvdata(dev->parent); + struct imanager_backlight_data *data; + int ret; + + if (!idev) { + dev_err(dev, "Invalid platform data\n"); + return -EINVAL; + } + + ret = bl_core_init(); + if (ret) { + dev_err(dev, "Failed to initialize backlight core\n"); + return -EIO; + } + + ret = bl_core_set_state(unit, BL_CTRL_ENABLE); + if (ret < 0) { + dev_err(dev, "Failed to enable backlight control (%d)\n", + unit); + return -EIO; + } + + if (polarity) + ret = bl_core_set_polarity(PWM_POLARITY_INVERSED); + else + ret = bl_core_set_polarity(PWM_POLARITY_NORMAL); + if (ret < 0) { + dev_err(dev, "Failed to set backlight polarity\n"); + return -EIO; + } + + /* init brightness to 60% */ + bl_core_set_pulse_width(unit, 60); + + data = devm_kzalloc(dev, sizeof(struct imanager_backlight_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->idev = idev; + + ret = imanager_backlight_init(dev, data); + if (ret) + return ret; + + platform_set_drvdata(pdev, data); + + return 0; +} + +static int imanager_backlight_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct imanager_backlight_data *data = dev_get_drvdata(dev); + + backlight_device_unregister(data->bl); + + return 0; +} + +static struct platform_driver imanager_backlight_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "imanager_backlight", + }, + .probe = imanager_backlight_probe, + .remove = imanager_backlight_remove, +}; + +module_platform_driver(imanager_backlight_driver); + +MODULE_DESCRIPTION("Advantech iManager Backlight driver"); +MODULE_AUTHOR("Richard Vidal-Dorsch "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:imanager_backlight"); diff --git a/drivers/video/backlight/imanager-ec-bl.c b/drivers/video/backlight/imanager-ec-bl.c new file mode 100644 index 0000000..04d2478 --- /dev/null +++ b/drivers/video/backlight/imanager-ec-bl.c @@ -0,0 +1,118 @@ +/* + * Advantech iManager Backlight/Brightness Core + * + * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA + * Author: Richard Vidal-Dorsch + * + * 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; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct brightness_level { + u32 value : 7, /* Brightness Value - LSB [6..0] */ + enable : 1; /* Brightness Enable - MSB [7] */ +}; + +struct backlight_ctrl { + u32 enable : 1, /* Backlight Control Enable - LSB [0] */ + pwmpol : 1, /* PWM Polarity - bit [1] */ + blpol : 1, /* Backlight Polarity - bit [2] */ + dnc : 5; /* Don't care - bit [7..3] */ +}; + +static const struct imanager_backlight_device *bl; + +int bl_core_get_pulse_width(u32 unit) +{ + int ret; + + if (WARN_ON(unit >= EC_BLC_MAX_NUM)) + return -EINVAL; + + ret = imanager_read_byte(EC_CMD_HWP_RD, bl->attr[unit].did); + if (ret < 0) + pr_err("Failed reading PWM (unit=%d)\n", unit); + + return ret; +} + +int bl_core_set_pulse_width(u32 unit, u32 pwm) +{ + int ret; + + if (WARN_ON(unit >= EC_BLC_MAX_NUM)) + return -EINVAL; + + pwm = pwm > 100 ? 100 : pwm; + + ret = imanager_write_byte(EC_CMD_HWP_WR, bl->attr[unit].did, pwm); + if (ret < 0) + pr_err("Failed writing PWM (val=%d, unit=%d)\n", pwm, unit); + + return ret; +} + +int bl_core_set_state(u32 unit, bool enable) +{ + int ret; + u8 val8; + struct brightness_level *pl = (struct brightness_level *)&val8; + + if (WARN_ON(unit >= EC_BLC_MAX_NUM)) + return -EINVAL; + + ret = imanager_acpiram_read_byte(bl->brightness[unit]); + if (ret < 0) + return ret; + val8 = ret; + + pl->enable = enable ? 1 : 0; + + ret = imanager_acpiram_write_byte(bl->attr[unit].did, val8); + if (ret) + return ret; + + return 0; +} + +int bl_core_set_polarity(u32 polarity) +{ + int ret; + u8 val8; + struct backlight_ctrl *ctrl = (struct backlight_ctrl *)&val8; + + ret = imanager_acpiram_read_byte(EC_ACPIRAM_BLC_CTRL); + if (ret < 0) + return ret; + val8 = ret; + + ctrl->blpol = polarity ? 1 : 0; + + ret = imanager_acpiram_write_byte(EC_ACPIRAM_BLC_CTRL, val8); + if (ret) + return ret; + + return 0; +} + +int bl_core_init(void) +{ + bl = imanager_get_backlight_device(); + if (!bl) + return -ENODEV; + + return 0; +} + diff --git a/include/linux/mfd/imanager/backlight.h b/include/linux/mfd/imanager/backlight.h new file mode 100644 index 0000000..93ea7cf5 --- /dev/null +++ b/include/linux/mfd/imanager/backlight.h @@ -0,0 +1,37 @@ +/* + * Advantech iManager Backlight core + * + * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA + * Author: Richard Vidal-Dorsch + * + * 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; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __BACKLIGHT_H__ +#define __BACKLIGHT_H__ + +#include + +enum backlight_control { + BL_CTRL_DISABLE, + BL_CTRL_ENABLE, +}; + +enum backlight_unit { + UNIT_1, + UNIT_2, +}; + +int bl_core_init(void); + +int bl_core_set_state(u32 unit, bool enable); + +int bl_core_set_polarity(u32 polarity); + +int bl_core_get_pulse_width(u32 unit); +int bl_core_set_pulse_width(u32 unit, u32 pwm); + +#endif