maru_bl : Add new backlight device for Tizen emulator 71/17971/1
authorjinhyung.jo <jinhyung.jo@samsung.com>
Thu, 13 Mar 2014 09:16:29 +0000 (18:16 +0900)
committerjinhyung.jo <jinhyung.jo@samsung.com>
Thu, 13 Mar 2014 09:16:29 +0000 (18:16 +0900)
Add new backlight(& lcd) device for Tizen emulator

Change-Id: I76299ceac5d347fb69d8f4c6f7c60097d95b9b2a
Signed-off-by: Jinhyung Jo <jinhyung.jo@samsung.com>
arch/x86/configs/i386_tizen_emul_defconfig
drivers/maru/Kconfig
drivers/maru/Makefile
drivers/maru/maru_bl.c [new file with mode: 0644]
drivers/video/backlight/backlight.c
include/linux/backlight.h
include/linux/pci_ids.h

index 382791e..e037f98 100644 (file)
@@ -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
index 2ddbc68..951eeb6 100644 (file)
@@ -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
index 51eaa53..a8f7aaa 100644 (file)
@@ -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 (file)
index 0000000..d317bfd
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+ * MARU Virtual Backlight Driver
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ *  Jinhyung Jo <jinhyung.jo@samsung.com>
+ *  YeongKyoon Lee <yeongkyoon.lee@samsung.com>
+ *  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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/notifier.h>
+#include <linux/ctype.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/lcd.h>
+
+#include <linux/uaccess.h>
+
+#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 <jinhyung.jo@samsung.com>");
+MODULE_DESCRIPTION("MARU Virtual Backlight Driver for x86");
index 94a403a..5c7dbd8 100644 (file)
@@ -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,
index 53b7794..cd2ca7b 100644 (file)
@@ -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
index 97fbecd..d7376e9 100644 (file)
 
 #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