fbdev: dpu20: panel: added sysfs for supporting CABC of 3FA0 Panel.
authorChiHun Won <chihun.won@samsung.com>
Mon, 23 Jul 2018 07:17:02 +0000 (16:17 +0900)
committerhskang <hs1218.kang@samsung.com>
Thu, 9 Aug 2018 05:09:42 +0000 (01:09 -0400)
Change-Id: Ifae35559fb2137f9c43063cc4b8e61666e8592de
Signed-off-by: ChiHun Won <chihun.won@samsung.com>
drivers/video/fbdev/exynos/dpu20/Kconfig
drivers/video/fbdev/exynos/dpu20/panels/s6e3fa0_mipi_lcd.c
drivers/video/fbdev/exynos/dpu20/panels/s6e3fa0_param.h

index 1866591394754a8cf9a16e1809ebaad6ec8362a8..a681bfb9760de82b870781117e75888a30e94fc3 100644 (file)
@@ -105,6 +105,13 @@ config SUPPORT_KERNEL_4_9
         depends on EXYNOS_DPU20
         default n
 
+config EXYNOS_PANEL_CABC
+       bool "Support CABC using DDI command"
+       depends on EXYNOS_DPU20
+       default n
+       help
+         If this menu is enabled, you can control CABC mode of panel.
+
 endif
 
 source "drivers/video/fbdev/exynos/dpu20/panels/Kconfig"
index 0ff37a6990cdc3238e09c648795611a028bef643..e5972294e08586be32e558b4d05047baa4e2d635 100644 (file)
 unsigned char set_brightness[2] = {0x51, 0x7F};
 int backlightlevel_log;
 
+#if defined(CONFIG_EXYNOS_PANEL_CABC)
+struct panel_device {
+       struct device *dev;
+       struct dsim_device *dsim;
+       struct mutex lock;
+       int cabc_mode;
+};
+
+struct panel_device *panel_drvdata;
+struct class *panel_class;
+#endif
+
 static int s6e3fa0_get_brightness(struct backlight_device *bd)
 {
        return bd->props.brightness;
@@ -208,8 +220,121 @@ static const struct backlight_ops s6e3fa0_backlight_ops = {
        .update_status = s6e3fa0_set_brightness,
 };
 
+#if defined(CONFIG_EXYNOS_PANEL_CABC)
+static int s6e3fa0_cabc_mode(struct dsim_device *dsim, int mode)
+{
+       int ret = 0;
+       int count;
+       unsigned char buf[] = {0x0, 0x0};
+       unsigned char SEQ_CABC_CMD[] = {0x55, 0x00, 0x00};
+       unsigned char cmd = MIPI_DCS_WRITE_POWER_SAVE; /* 0x55 */
+
+       dsim_dbg("%s: CABC mode[%d] write/read\n", __func__, mode);
+
+       switch (mode) {
+       /* read */
+       case CABC_READ_MODE:
+               cmd = MIPI_DCS_GET_POWER_SAVE; /* 0x56 */
+               ret = dsim_read_data(dsim, MIPI_DSI_DCS_READ, cmd, 0x1, buf);
+               if (ret < 0) {
+                       pr_err("CABC REG(0x%02X) read failure!\n", cmd);
+                       count = 0;
+               } else {
+                       pr_info("CABC REG(0x%02X) read success: 0x%02x\n",
+                               cmd, *(unsigned int *)buf & 0xFF);
+                       count = 1;
+               }
+               return count;
+
+       /* write */
+       case POWER_SAVE_OFF:
+               SEQ_CABC_CMD[1] = CABC_OFF;
+               break;
+       case POWER_SAVE_LOW:
+               SEQ_CABC_CMD[1] = CABC_USER_IMAGE;
+               break;
+       case POWER_SAVE_MEDIUM:
+               SEQ_CABC_CMD[1] = CABC_STILL_PICTURE;
+               break;
+       case POWER_SAVE_HIGH:
+               SEQ_CABC_CMD[1] = CABC_MOVING_IMAGE;
+               break;
+       default:
+               pr_err("Unavailable CABC mode(%d)!\n", mode);
+               return -EINVAL;
+       }
+
+       ret = dsim_write_data(dsim, MIPI_DSI_DCS_LONG_WRITE,
+                       (unsigned long)SEQ_CABC_CMD /*cmd*/,
+                       ARRAY_SIZE(SEQ_CABC_CMD));
+       if (ret < 0) {
+               pr_err("CABC write command failure!\n");
+               count = 0;
+       } else {
+               dsim_dbg("CABC write command success!\n");
+               count = ARRAY_SIZE(SEQ_CABC_CMD);
+       }
+
+       return count;
+}
+
+static ssize_t panel_cabc_mode_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       ssize_t count = 0;
+       int ret = 0;
+       struct panel_device *panel = dev_get_drvdata(dev);
+
+       mutex_lock(&panel->lock);
+
+       ret = s6e3fa0_cabc_mode(panel->dsim, CABC_READ_MODE);
+
+       mutex_unlock(&panel->lock);
+
+       count = snprintf(buf, PAGE_SIZE, "cabc_mode = %d, ret = %d\n",
+                       panel->cabc_mode, ret);
+
+       return count;
+}
+
+static ssize_t panel_cabc_mode_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       int ret;
+       unsigned int value = 0;
+       struct panel_device *panel = dev_get_drvdata(dev);
+
+       ret = kstrtouint(buf, 0, &value);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&panel->lock);
+       panel->cabc_mode = value;
+       mutex_unlock(&panel->lock);
+
+       pr_info("%s: %d\n", __func__, value);
+
+       s6e3fa0_cabc_mode(panel->dsim, panel->cabc_mode);
+
+       return count;
+}
+
+static DEVICE_ATTR(cabc_mode, 0660, panel_cabc_mode_show,
+                       panel_cabc_mode_store);
+
+static struct attribute *panel_attrs[] = {
+       &dev_attr_cabc_mode.attr,
+       NULL,
+};
+ATTRIBUTE_GROUPS(panel);
+#endif
+
 static int s6e3fa0_probe(struct dsim_device *dsim)
 {
+       int ret = 1;
+       struct panel_device *panel;
+       static unsigned int panel_no;
+
        dsim->bd = backlight_device_register("backlight_0", dsim->dev,
                NULL, &s6e3fa0_backlight_ops, NULL);
        if (IS_ERR(dsim->bd))
@@ -218,13 +343,68 @@ static int s6e3fa0_probe(struct dsim_device *dsim)
        dsim->bd->props.max_brightness = MAX_BRIGHTNESS;
        dsim->bd->props.brightness = DEFAULT_BRIGHTNESS;
 
-       return 1;
+#if defined(CONFIG_EXYNOS_PANEL_CABC)
+       panel = kzalloc(sizeof(struct panel_device), GFP_KERNEL);
+       if (!panel) {
+               pr_err("failed to allocate panel\n");
+               ret = -ENOMEM;
+               goto exit0;
+       }
+
+       panel_drvdata = panel;
+
+       panel->dsim = dsim;
+       panel->cabc_mode = 0;
+
+       if (IS_ERR_OR_NULL(panel_class)) {
+               panel_class = class_create(THIS_MODULE, "panel");
+               if (IS_ERR_OR_NULL(panel_class)) {
+                       pr_err("failed to create panel class\n");
+                       ret = -EINVAL;
+                       goto exit1;
+               }
+
+               panel_class->dev_groups = panel_groups;
+       }
+
+       panel->dev = device_create(panel_class, dsim->dev, 0,
+                       &panel, !panel_no ? "panel" : "panel%d", panel_no);
+       if (IS_ERR_OR_NULL(panel->dev)) {
+               pr_err("failed to create panel device\n");
+               ret = -EINVAL;
+               goto exit2;
+       }
+
+       mutex_init(&panel->lock);
+       dev_set_drvdata(panel->dev, panel);
+
+       panel_no++;
+
+       return ret;
+
+exit2:
+       class_destroy(panel_class);
+exit1:
+       kfree(panel);
+exit0:
+#endif
+       return ret;
 }
 
 static int s6e3fa0_displayon(struct dsim_device *dsim)
 {
+#if defined(CONFIG_EXYNOS_PANEL_CABC)
+       struct panel_device *panel = panel_drvdata;
+#endif
+
        lcd_init(dsim->id, &dsim->lcd_info);
        lcd_enable(dsim->id);
+
+#if defined(CONFIG_EXYNOS_PANEL_CABC)
+       if (panel)
+               s6e3fa0_cabc_mode(dsim, panel->cabc_mode);
+#endif
+
        return 1;
 }
 
index baf8453d0d036311439368f3410eae44fba6f818..4293fec2a0fbde663c737c19acc4af7ed7f817b3 100644 (file)
 #ifndef __S6E3FA0_PARAM_H__
 #define __S6E3FA0_PARAM_H__
 
+#if defined(CONFIG_EXYNOS_PANEL_CABC)
+/*
+ * cabc_mode[1:0] must be re-mapped according to DDI command
+ * 3FA0 is OLED, so CABC command is not supported.
+ * Following values represent for ACL2 control of 3FA0.
+ * - [2'b00] ACL off
+ * - [2'b01] ACL low
+ * - [2'b10] ACL mid
+ * - [2'b11] ACL high
+ */
+enum cabc_mode {
+       CABC_OFF = 0,
+       CABC_USER_IMAGE,
+       CABC_STILL_PICTURE,
+       CABC_MOVING_IMAGE,
+       CABC_READ_MODE = 0x80,
+};
+
+enum power_mode {
+       POWER_SAVE_OFF = 0,
+       POWER_SAVE_LOW = 1,
+       POWER_SAVE_MEDIUM = 2,
+       POWER_SAVE_HIGH = 3,
+       POWER_SAVE_MAX = 4,
+};
+#endif
+
 /* MIPI commands list */
 static const unsigned char SEQ_TEST_KEY_ON_C0[] = {
        0xc0,