From ca6a25422ab3865f4491c909111efe9d67db1661 Mon Sep 17 00:00:00 2001 From: "jinhyung.jo" Date: Thu, 13 Mar 2014 18:16:29 +0900 Subject: [PATCH] maru_bl : Add new backlight device for Tizen emulator Add new backlight(& lcd) device for Tizen emulator Change-Id: I76299ceac5d347fb69d8f4c6f7c60097d95b9b2a Signed-off-by: Jinhyung Jo --- arch/x86/configs/i386_tizen_emul_defconfig | 4 +- drivers/maru/Kconfig | 7 + drivers/maru/Makefile | 1 + drivers/maru/maru_bl.c | 366 +++++++++++++++++++++++++++++ drivers/video/backlight/backlight.c | 13 +- include/linux/backlight.h | 2 + include/linux/pci_ids.h | 3 + 7 files changed, 394 insertions(+), 2 deletions(-) create mode 100644 drivers/maru/maru_bl.c diff --git a/arch/x86/configs/i386_tizen_emul_defconfig b/arch/x86/configs/i386_tizen_emul_defconfig index 382791e..e037f98 100644 --- a/arch/x86/configs/i386_tizen_emul_defconfig +++ b/arch/x86/configs/i386_tizen_emul_defconfig @@ -2185,7 +2185,8 @@ CONFIG_FB_TILEBLITTING=y # CONFIG_FB_AUO_K190X is not set # CONFIG_EXYNOS_VIDEO is not set CONFIG_BACKLIGHT_LCD_SUPPORT=y -# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_PLATFORM=y CONFIG_BACKLIGHT_CLASS_DEVICE=y CONFIG_BACKLIGHT_GENERIC=y # CONFIG_BACKLIGHT_APPLE is not set @@ -2814,6 +2815,7 @@ CONFIG_IOMMU_SUPPORT=y # CONFIG_MARU=y CONFIG_MARU_VIRTIO_TOUCHSCREEN=y +CONFIG_MARU_BACKLIGHT=y CONFIG_MARU_VIRTIO_HWKEY=y CONFIG_MARU_VIRTIO_KEYBOARD=y CONFIG_MARU_VIRTIO_NFC=y diff --git a/drivers/maru/Kconfig b/drivers/maru/Kconfig index 2ddbc68..951eeb6 100644 --- a/drivers/maru/Kconfig +++ b/drivers/maru/Kconfig @@ -6,6 +6,13 @@ config MARU_VIRTIO_TOUCHSCREEN tristate "MARU Virtio Touchscreen Driver" depends on MARU != n +config MARU_BACKLIGHT + tristate "MARU Backlight Driver" + depends on MARU && BACKLIGHT_CLASS_DEVICE + default y + help + Say Y to enable the backlight driver of MARU. + config MARU_VIRTIO_EVDI tristate "MARU VirtIO Emulator Virtual Device Interface Driver" depends on MARU != n diff --git a/drivers/maru/Makefile b/drivers/maru/Makefile index 51eaa53..a8f7aaa 100644 --- a/drivers/maru/Makefile +++ b/drivers/maru/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_MARU_VIRTIO_TOUCHSCREEN) += maru_virtio_touchscreen.o +obj-$(CONFIG_MARU_BACKLIGHT) += maru_bl.o obj-$(CONFIG_MARU_VIRTIO_HWKEY) += maru_virtio_hwkey.o obj-$(CONFIG_MARU_VIRTIO_KEYBOARD) += maru_virtio_keyboard.o obj-$(CONFIG_MARU_VIRTIO_NFC) += maru_virtio_nfc.o diff --git a/drivers/maru/maru_bl.c b/drivers/maru/maru_bl.c new file mode 100644 index 0000000..d317bfd --- /dev/null +++ b/drivers/maru/maru_bl.c @@ -0,0 +1,366 @@ +/* + * MARU Virtual Backlight Driver + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Jo + * YeongKyoon Lee + * Dohyung Hong + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MARUBL_DRIVER_NAME "maru_backlight" + +#define MIN_BRIGHTNESS 0 +#define MAX_BRIGHTNESS 100 + +static struct pci_device_id marubl_pci_table[] = { + { + .vendor = PCI_VENDOR_ID_TIZEN, + .device = PCI_DEVICE_ID_VIRTUAL_BRIGHTNESS, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, marubl_pci_table); + +/* MARU virtual brightness(backlight) device structure */ +struct marubl { + struct backlight_device *bl_dev; + struct lcd_device *lcd_dev; + unsigned int prev_brightness; + unsigned int brightness; + resource_size_t reg_start, reg_size; + /* memory mapped registers */ + unsigned char __iomem *marubl_mmreg; + int power_off; + int hbm_on; +}; + +/* ========================================================================== */ +static struct marubl *marubl_device; +/* ========================================================================== */ + +static int min_brightness = MIN_BRIGHTNESS; +static int max_brightness = MAX_BRIGHTNESS; + +static int marubl_get_intensity(struct backlight_device *bd) +{ + return marubl_device->brightness; +} + +static int marubl_send_intensity(struct backlight_device *bd) +{ + int intensity = bd->props.brightness; + unsigned int off = 0; + + if (bd->props.power != FB_BLANK_UNBLANK) { + intensity = 0; + off = 1; + } + if (bd->props.state & BL_CORE_FBBLANK) { + intensity = 0; + off = 1; + } + if (bd->props.state & BL_CORE_SUSPENDED) { + intensity = 0; + off = 1; + } + if (marubl_device->hbm_on && !off && intensity != MAX_BRIGHTNESS) { + marubl_device->hbm_on = 0; + printk(KERN_INFO "HBM is turned off because brightness reduced.\n"); + } + + writel(intensity, marubl_device->marubl_mmreg); + writel(off, marubl_device->marubl_mmreg + 0x04); + marubl_device->brightness = intensity; + marubl_device->power_off = off ? 1 : 0; + + return 0; +} + +static const struct backlight_ops marubl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .get_brightness = marubl_get_intensity, + .update_status = marubl_send_intensity, +}; + +int maru_lcd_get_power(struct lcd_device *ld) +{ + int ret = 0; + + if (marubl_device->power_off) { + ret = FB_BLANK_POWERDOWN; + } else { + ret = FB_BLANK_UNBLANK; + } + return ret; +} + +static struct lcd_ops maru_lcd_ops = { + .get_power = maru_lcd_get_power, +}; + +static ssize_t hbm_show_status(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int rc; + + rc = sprintf(buf, "%s\n", marubl_device->hbm_on ? "on" : "off"); + printk(KERN_INFO "[%s] get: %d\n", __func__, marubl_device->hbm_on); + + return rc; +} + +static ssize_t hbm_store_status(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = -1; + + if (strcmp(buf, "on") == 0) { + ret = 1; + } else if (strcmp(buf, "off") == 0) { + ret = 0; + } else { + return -EINVAL; + } + + /* If the same as the previous state, ignore it */ + if (ret == marubl_device->hbm_on) + return count; + + if (ret) { + /* Save previous level, set to MAX level */ + mutex_lock(&marubl_device->bl_dev->ops_lock); + marubl_device->prev_brightness = + marubl_device->bl_dev->props.brightness; + marubl_device->bl_dev->props.brightness = MAX_BRIGHTNESS; + marubl_send_intensity(marubl_device->bl_dev); + mutex_unlock(&marubl_device->bl_dev->ops_lock); + } else { + /* Restore previous level */ + mutex_lock(&marubl_device->bl_dev->ops_lock); + marubl_device->bl_dev->props.brightness = + marubl_device->prev_brightness; + marubl_send_intensity(marubl_device->bl_dev); + mutex_unlock(&marubl_device->bl_dev->ops_lock); + } + marubl_device->hbm_on = ret; + printk(KERN_INFO "[%s] hbm = %d\n", __func__, ret); + + return count; +} + +static struct device_attribute hbm_device_attr = + __ATTR(hbm, 0644, hbm_show_status, hbm_store_status); + +/* pci probe function +*/ +static int marubl_probe(struct pci_dev *pci_dev, + const struct pci_device_id *ent) +{ + int ret; + struct backlight_device *bd; + struct lcd_device *ld; + struct backlight_properties props; + + marubl_device = kmalloc(sizeof(struct marubl), GFP_KERNEL); + if (marubl_device == NULL) { + printk(KERN_ERR "marubl: kmalloc() is failed.\n"); + return -ENOMEM; + } + + memset(marubl_device, 0, sizeof(struct marubl)); + + ret = pci_enable_device(pci_dev); + if (ret < 0) { + printk(KERN_ERR "marubl: pci_enable_device is failed.\n"); + kfree(marubl_device); + marubl_device = NULL; + return ret; + } + + ret = -EIO; + + /* 1 : IORESOURCE_MEM */ + marubl_device->reg_start = pci_resource_start(pci_dev, 1); + marubl_device->reg_size = pci_resource_len(pci_dev, 1); + if (!request_mem_region(marubl_device->reg_start, + marubl_device->reg_size, + MARUBL_DRIVER_NAME)) { + pci_disable_device(pci_dev); + kfree(marubl_device); + marubl_device = NULL; + return ret; + } + + /* memory areas mapped kernel space */ + marubl_device->marubl_mmreg = ioremap(marubl_device->reg_start, + marubl_device->reg_size); + if (!marubl_device->marubl_mmreg) { + release_mem_region(marubl_device->reg_start, + marubl_device->reg_size); + pci_disable_device(pci_dev); + kfree(marubl_device); + marubl_device = NULL; + return ret; + } + + pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, 64); + pci_set_master(pci_dev); + + /* + * register High Brightness Mode + */ + ret = device_create_file(&pci_dev->dev, &hbm_device_attr); + if (ret < 0) { + iounmap(marubl_device->marubl_mmreg); + release_mem_region(marubl_device->reg_start, + marubl_device->reg_size); + pci_disable_device(pci_dev); + kfree(marubl_device); + marubl_device = NULL; + return ret; + } + + /* + * register backlight device + */ + memset(&props, 0, sizeof(struct backlight_properties)); + props.min_brightness = min_brightness; + props.max_brightness = max_brightness; + props.type = BACKLIGHT_PLATFORM; + bd = backlight_device_register("emulator", + &pci_dev->dev, + NULL, + &marubl_ops, + &props); + if (IS_ERR(bd)) { + ret = PTR_ERR(bd); + iounmap(marubl_device->marubl_mmreg); + release_mem_region(marubl_device->reg_start, + marubl_device->reg_size); + pci_disable_device(pci_dev); + kfree(marubl_device); + marubl_device = NULL; + return ret; + } + + ld = lcd_device_register("emulator", &pci_dev->dev, NULL, &maru_lcd_ops); + if (IS_ERR(ld)) { + ret = PTR_ERR(ld); + iounmap(marubl_device->marubl_mmreg); + release_mem_region(marubl_device->reg_start, + marubl_device->reg_size); + pci_disable_device(pci_dev); + kfree(marubl_device); + marubl_device = NULL; + return ret; + } + + bd->props.brightness = (unsigned int)readl(marubl_device->marubl_mmreg); + bd->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bd); + + marubl_device->bl_dev = bd; + marubl_device->lcd_dev = ld; + + printk(KERN_INFO "marubl: MARU Virtual Backlight driver is loaded.\n"); + return 0; +} + +static void marubl_exit(struct pci_dev *pcidev) +{ + /* + * Unregister backlight device + */ + struct backlight_device *bd = marubl_device->bl_dev; + struct lcd_device *ld = marubl_device->lcd_dev; + + bd->props.power = 0; + bd->props.brightness = 0; + backlight_update_status(bd); + + lcd_device_unregister(ld); + backlight_device_unregister(bd); + device_remove_file(&pcidev->dev, &hbm_device_attr); + + /* + * Unregister pci device & delete device + */ + iounmap(marubl_device->marubl_mmreg); + release_mem_region(marubl_device->reg_start, marubl_device->reg_size); + pci_disable_device(pcidev); + kfree(marubl_device); + marubl_device = NULL; +} + +/* + * register pci driver + */ +static struct pci_driver marubl_pci_driver = { + .name = MARUBL_DRIVER_NAME, + .id_table = marubl_pci_table, + .probe = marubl_probe, + .remove = marubl_exit, +#ifdef CONFIG_PM + /* .suspend = marubl_suspend, */ + /* .resume = marubl_resume, */ +#endif +}; + +static int __init marubl_module_init(void) +{ + return pci_register_driver(&marubl_pci_driver); +} + +static void __exit marubl_module_exit(void) +{ + pci_unregister_driver(&marubl_pci_driver); +} + +/* + * if this is compiled into the kernel, we need to ensure that the + * class is registered before users of the class try to register lcd's + */ +module_init(marubl_module_init); +module_exit(marubl_module_exit); + +MODULE_LICENSE("GPL2"); +MODULE_AUTHOR("Jinhyung Jo "); +MODULE_DESCRIPTION("MARU Virtual Backlight Driver for x86"); diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index 94a403a..5c7dbd8 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -161,7 +161,8 @@ static ssize_t brightness_store(struct device *dev, mutex_lock(&bd->ops_lock); if (bd->ops) { - if (brightness > bd->props.max_brightness) + if (brightness > bd->props.max_brightness || + brightness < bd->props.min_brightness) rc = -EINVAL; else { pr_debug("set brightness to %lu\n", brightness); @@ -187,6 +188,15 @@ static ssize_t type_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(type); +static ssize_t min_brightness_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct backlight_device *bd = to_backlight_device(dev); + + return sprintf(buf, "%d\n", bd->props.min_brightness); +} +static DEVICE_ATTR_RO(min_brightness); + static ssize_t max_brightness_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -256,6 +266,7 @@ static struct attribute *bl_device_attrs[] = { &dev_attr_bl_power.attr, &dev_attr_brightness.attr, &dev_attr_actual_brightness.attr, + &dev_attr_min_brightness.attr, &dev_attr_max_brightness.attr, &dev_attr_type.attr, NULL, diff --git a/include/linux/backlight.h b/include/linux/backlight.h index 53b7794..cd2ca7b 100644 --- a/include/linux/backlight.h +++ b/include/linux/backlight.h @@ -61,6 +61,8 @@ struct backlight_ops { struct backlight_properties { /* Current User requested brightness (0 - max_brightness) */ int brightness; + /* Minimal value for brightness (read-only) */ + int min_brightness; /* Maximal value for brightness (read-only) */ int max_brightness; /* Current FB Power mode (0: full on, 1..3: power saving diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 97fbecd..d7376e9 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2091,6 +2091,9 @@ #define PCI_VENDOR_ID_SAMSUNG 0x144d +#define PCI_VENDOR_ID_TIZEN 0xC9B5 +#define PCI_DEVICE_ID_VIRTUAL_BRIGHTNESS 0x1014 + #define PCI_VENDOR_ID_GIGABYTE 0x1458 #define PCI_VENDOR_ID_AMBIT 0x1468 -- 2.7.4