summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/panel/panel-hikey.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/panel/panel-hikey.c')
-rw-r--r--drivers/gpu/drm/panel/panel-hikey.c529
1 files changed, 529 insertions, 0 deletions
diff --git a/drivers/gpu/drm/panel/panel-hikey.c b/drivers/gpu/drm/panel/panel-hikey.c
new file mode 100644
index 000000000000..e116a47dc377
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-hikey.c
@@ -0,0 +1,529 @@
+/*
+ * HiKey LCD panel driver
+ * TODO: Add backlight adjustment support.
+ *
+ * Copyright (c) 2016 Linaro Limited.
+ * Copyright (c) 2016 Hisilicon Limited.
+ * Copyright (C) 2016 LeMaker
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/gpio/consumer.h>
+#include <video/mipi_display.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#define REGFLAG_DELAY 0XFFE
+
+struct hikey_panel {
+ struct drm_panel base;
+ struct mipi_dsi_device *dsi;
+
+ bool prepared;
+ bool enabled;
+
+ struct gpio_desc *gpio_pwr_en;
+ struct gpio_desc *gpio_bl_en;
+ struct gpio_desc *gpio_pwm;
+};
+
+struct dsi_panel_cmd {
+ u32 cmd; /* cmd: DCS command */
+ u32 len; /* command payload length */
+ u8 data[64]; /* buffer containing the command payload */
+};
+
+static struct dsi_panel_cmd n070icn_init_cmds[] = {
+ {0xFF,4,{0xAA,0x55,0xA5,0x80}},//========== Internal setting ==========
+
+ {0x6F,2,{0x11,0x00}},// MIPI related Timing Setting
+ {0xF7,2,{0x20,0x00}},
+
+ {0x6F,1,{0x06}},// Improve ESD option
+ {0xF7,1,{0xA0}},
+ {0x6F,1,{0x19}},
+ {0xF7,1,{0x12}},
+ {0xF4,1,{0x03}},
+
+ {0x6F,1,{0x08}},// Vcom floating
+ {0xFA,1,{0x40}},
+ {0x6F,1,{0x11}},
+ {0xF3,1,{0x01}},
+
+ {0xF0,5,{0x55,0xAA,0x52,0x08,0x00}},//========== page0 relative ==========
+ {0xC8,1,{0x80}},
+
+ {0xB1,2,{0x6C,0x01}},// Set WXGA resolution
+
+ {0xB6,1,{0x08}},// Set source output hold time
+
+ {0x6F,1,{0x02}},//EQ control function
+ {0xB8,1,{0x08}},
+
+ {0xBB,2,{0x54,0x54}},// Set bias current for GOP and SOP
+
+ {0xBC,2,{0x05,0x05}},// Inversion setting
+
+ {0xC7,1,{0x01}},// zigzag setting
+
+ {0xBD,5,{0x02,0xB0,0x0C,0x0A,0x00}},// DSP Timing Settings update for BIST
+
+ {0xF0,5,{0x55,0xAA,0x52,0x08,0x01}},//========== page1 relative ==========
+
+ {0xB0,2,{0x05,0x05}},// Setting AVDD, AVEE clamp
+ {0xB1,2,{0x05,0x05}},
+
+ {0xBC,2,{0x3A,0x01}},// VGMP, VGMN, VGSP, VGSN setting
+ {0xBD,2,{0x3E,0x01}},
+
+ {0xCA,1,{0x00}},// gate signal control
+
+ {0xC0,1,{0x04}},// power IC control
+
+ {0xB2,2,{0x00,0x00}},// VCL SET -2.5V
+
+ {0xBE,1,{0x80}},// VCOM = -1.888V
+
+ {0xB3,2,{0x19,0x19}},// Setting VGH=15V, VGL=-11V
+ {0xB4,2,{0x12,0x12}},
+
+ {0xB9,2,{0x24,0x24}},// power control for VGH, VGL
+ {0xBA,2,{0x14,0x14}},
+
+ {0xF0,5,{0x55,0xAA,0x52,0x08,0x02}},//========== page2 relative ==========
+
+ {0xEE,1,{0x01}},//gamma setting
+ {0xEF,4,{0x09,0x06,0x15,0x18}},//Gradient Control for Gamma Voltage
+
+ {0xB0,6,{0x00,0x00,0x00,0x08,0x00,0x17}},
+ {0x6F,1,{0x06}},
+ {0xB0,6,{0x00,0x25,0x00,0x30,0x00,0x45}},
+ {0x6F,1,{0x0C}},
+ {0xB0,4,{0x00,0x56,0x00,0x7A}},
+ {0xB1,6,{0x00,0xA3,0x00,0xE7,0x01,0x20}},
+ {0x6F,1,{0x06}},
+ {0xB1,6,{0x01,0x7A,0x01,0xC2,0x01,0xC5}},
+ {0x6F,1,{0x0C}},
+ {0xB1,4,{0x02,0x06,0x02,0x5F}},
+ {0xB2,6,{0x02,0x92,0x02,0xD0,0x02,0xFC}},
+ {0x6F,1,{0x06}},
+ {0xB2,6,{0x03,0x35,0x03,0x5D,0x03,0x8B}},
+ {0x6F,1,{0x0C}},
+ {0xB2,4,{0x03,0xA2,0x03,0xBF}},
+ {0xB3,4,{0x03,0xD2,0x03,0xFF}},
+
+ //========== GOA relative ==========
+ {0xF0,5,{0x55,0xAA,0x52,0x08,0x06}},// PAGE6 : GOUT Mapping, VGLO select
+ {0xB0,2,{0x00,0x17}},
+ {0xB1,2,{0x16,0x15}},
+ {0xB2,2,{0x14,0x13}},
+ {0xB3,2,{0x12,0x11}},
+ {0xB4,2,{0x10,0x2D}},
+ {0xB5,2,{0x01,0x08}},
+ {0xB6,2,{0x09,0x31}},
+ {0xB7,2,{0x31,0x31}},
+ {0xB8,2,{0x31,0x31}},
+ {0xB9,2,{0x31,0x31}},
+ {0xBA,2,{0x31,0x31}},
+ {0xBB,2,{0x31,0x31}},
+ {0xBC,2,{0x31,0x31}},
+ {0xBD,2,{0x31,0x09}},
+ {0xBE,2,{0x08,0x01}},
+ {0xBF,2,{0x2D,0x10}},
+ {0xC0,2,{0x11,0x12}},
+ {0xC1,2,{0x13,0x14}},
+ {0xC2,2,{0x15,0x16}},
+ {0xC3,2,{0x17,0x00}},
+ {0xE5,2,{0x31,0x31}},
+ {0xC4,2,{0x00,0x17}},
+ {0xC5,2,{0x16,0x15}},
+ {0xC6,2,{0x14,0x13}},
+ {0xC7,2,{0x12,0x11}},
+ {0xC8,2,{0x10,0x2D}},
+ {0xC9,2,{0x01,0x08}},
+ {0xCA,2,{0x09,0x31}},
+ {0xCB,2,{0x31,0x31}},
+ {0xCC,2,{0x31,0x31}},
+ {0xCD,2,{0x31,0x31}},
+ {0xCE,2,{0x31,0x31}},
+ {0xCF,2,{0x31,0x31}},
+ {0xD0,2,{0x31,0x31}},
+ {0xD1,2,{0x31,0x09}},
+ {0xD2,2,{0x08,0x01}},
+ {0xD3,2,{0x2D,0x10}},
+ {0xD4,2,{0x11,0x12}},
+ {0xD5,2,{0x13,0x14}},
+ {0xD6,2,{0x15,0x16}},
+ {0xD7,2,{0x17,0x00}},
+ {0xE6,2,{0x31,0x31}},
+ {0xD8,5,{0x00,0x00,0x00,0x00,0x00}},//VGL level select
+ {0xD9,5,{0x00,0x00,0x00,0x00,0x00}},
+ {0xE7,1,{0x00}},
+
+ // PAGE3 :
+ {0xF0,5,{0x55,0xAA,0x52,0x08,0x03}},//gate timing control
+ {0xB0,2,{0x20,0x00}},
+ {0xB1,2,{0x20,0x00}},
+ {0xB2,5,{0x05,0x00,0x42,0x00,0x00}},
+ {0xB6,5,{0x05,0x00,0x42,0x00,0x00}},
+ {0xBA,5,{0x53,0x00,0x42,0x00,0x00}},
+ {0xBB,5,{0x53,0x00,0x42,0x00,0x00}},
+ {0xC4,1,{0x40}},
+
+ // gate CLK EQ
+ // gate STV EQ
+
+ // PAGE5 :
+ {0xF0,5,{0x55,0xAA,0x52,0x08,0x05}},
+ {0xB0,2,{0x17,0x06}},
+ {0xB8,1,{0x00}},
+ {0xBD,5,{0x03,0x01,0x01,0x00,0x01}},
+ {0xB1,2,{0x17,0x06}},
+ {0xB9,2,{0x00,0x01}},
+ {0xB2,2,{0x17,0x06}},
+ {0xBA,2,{0x00,0x01}},
+ {0xB3,2,{0x17,0x06}},
+ {0xBB,2,{0x0A,0x00}},
+ {0xB4,2,{0x17,0x06}},
+ {0xB5,2,{0x17,0x06}},
+ {0xB6,2,{0x14,0x03}},
+ {0xB7,2,{0x00,0x00}},
+ {0xBC,2,{0x02,0x01}},
+ {0xC0,1,{0x05}},
+ {0xC4,1,{0xA5}},
+ {0xC8,2,{0x03,0x30}},
+ {0xC9,2,{0x03,0x51}},
+ {0xD1,5,{0x00,0x05,0x03,0x00,0x00}},
+ {0xD2,5,{0x00,0x05,0x09,0x00,0x00}},
+ {0xE5,1,{0x02}},
+ {0xE6,1,{0x02}},
+ {0xE7,1,{0x02}},
+ {0xE9,1,{0x02}},
+ {0xED,1,{0x33}},
+
+ /* bist test mode
+ {0xF0,5,{0x55,0xAA,0x52,0x08,0x00}},
+ {0xEF,2,{0x07,0xFF}},
+ {0xEE,4,{0x87,0x78,0x02,0x40}},
+ */
+
+ {0x11,0,{0x00}},
+ {REGFLAG_DELAY, 120, {}},
+ {0x29,0,{0x00}},
+ {REGFLAG_DELAY, 20, {}},
+};
+
+static int hikey_panel_write_cmds(struct mipi_dsi_device *dsi,
+ struct dsi_panel_cmd *cmds,
+ u32 count)
+{
+ struct dsi_panel_cmd *cmd;
+ int ret = 0;
+ u32 i;
+
+ for(i = 0; i < count; i++) {
+ cmd = &cmds[i];
+ switch (cmd->cmd) {
+ case REGFLAG_DELAY:
+ msleep(cmd->len);
+ break;
+ default:
+ ret = mipi_dsi_dcs_write(dsi, cmd->cmd, cmd->data,
+ cmd->len);
+ }
+ }
+
+ return ret;
+}
+
+static inline struct hikey_panel *to_hikey_panel(struct drm_panel *panel)
+{
+ return container_of(panel, struct hikey_panel, base);
+}
+
+static int hikey_panel_disable(struct drm_panel *p)
+{
+ struct hikey_panel *panel = to_hikey_panel(p);
+
+ if (!panel->enabled)
+ return 0;
+
+ /* TODO: send panel off seq cmds */
+ panel->enabled = false;
+
+ gpiod_set_value(panel->gpio_bl_en, 0);
+ return 0;
+}
+
+static int hikey_panel_unprepare(struct drm_panel *p)
+{
+ struct hikey_panel *panel = to_hikey_panel(p);
+
+ if (!panel->prepared)
+ return 0;
+
+ panel->prepared = false;
+
+ return 0;
+}
+
+static int hikey_panel_prepare(struct drm_panel *p)
+{
+ struct hikey_panel *panel = to_hikey_panel(p);
+ int ret;
+
+ if (panel->prepared)
+ return 0;
+
+ /*
+ * A minimum delay of 250ms is required after power-up until commands
+ * can be sent
+ */
+ msleep(250);
+
+#ifndef CONFIG_DRM_ICN_6201
+ /* init the panel */
+ ret = hikey_panel_write_cmds(panel->dsi, n070icn_init_cmds,
+ ARRAY_SIZE(n070icn_init_cmds));
+ if (ret < 0)
+ return ret;
+#endif
+
+ panel->prepared = true;
+
+ return 0;
+}
+
+static int hikey_panel_enable(struct drm_panel *p)
+{
+ struct hikey_panel *panel = to_hikey_panel(p);
+
+ if (panel->enabled)
+ return 0;
+
+ msleep(200);
+ gpiod_set_value(panel->gpio_bl_en, 1);
+ gpiod_set_value(panel->gpio_pwm, 0);
+
+ panel->enabled = true;
+
+ return 0;
+}
+
+static const struct drm_display_mode default_mode = {
+#ifndef CONFIG_DRM_ICN_6201
+ .clock = 66800,
+
+ .hdisplay = 800,
+ .hsync_start = 800 + 40,
+ .hsync_end = 800 + 40 + 4,
+ .htotal = 800 + 40 + 4 + 40,
+
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 10,
+ .vsync_end = 1280 + 10 + 4,
+ .vtotal = 1280 + 10 + 4 + 12,
+#else
+ .clock = 55000,/*56000,//60000,*/
+
+ .hdisplay = 1024,
+ .hsync_start = 1024 + 140,
+ .hsync_end = 1024 + 140 + 40,
+ .htotal = 1024 + 140 + 40 + 140,
+
+ .vdisplay = 600,
+ .vsync_start = 600 + 15,
+ .vsync_end = 600 + 75 + 5,
+ .vtotal = 600 + 15 + 5 + 75,
+#endif
+};
+
+static int hikey_panel_get_modes(struct drm_panel *panel)
+{
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(panel->drm, &default_mode);
+ if (!mode) {
+ DRM_ERROR("failed to add mode %ux%ux@%u\n",
+ default_mode.hdisplay, default_mode.vdisplay,
+ default_mode.vrefresh);
+ return -ENOMEM;
+ }
+
+ drm_mode_set_name(mode);
+
+ drm_mode_probed_add(panel->connector, mode);
+
+ panel->connector->display_info.width_mm = 94;
+ panel->connector->display_info.height_mm = 150;
+
+ return 1;
+}
+
+static const struct drm_panel_funcs hikey_panel_funcs = {
+ .get_modes = hikey_panel_get_modes,
+ .enable = hikey_panel_enable,
+ .disable = hikey_panel_disable,
+ .prepare = hikey_panel_prepare,
+ .unprepare = hikey_panel_unprepare,
+};
+
+static int hikey_panel_add(struct hikey_panel *panel)
+{
+ struct device *dev = &panel->dsi->dev;
+ int ret;
+
+ drm_panel_init(&panel->base);
+ panel->base.funcs = &hikey_panel_funcs;
+ panel->base.dev = dev;
+
+ ret = drm_panel_add(&panel->base);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void hikey_panel_del(struct hikey_panel *panel)
+{
+ if (panel->base.dev)
+ drm_panel_remove(&panel->base);
+}
+
+static int hikey_panel_parse_dt(struct hikey_panel *panel)
+{
+ struct device *dev = &panel->dsi->dev;
+
+ panel->gpio_pwr_en =
+ devm_gpiod_get_optional(dev, "pwr-en", GPIOD_OUT_HIGH);
+ if (IS_ERR(panel->gpio_pwr_en))
+ return PTR_ERR(panel->gpio_pwr_en);
+
+ panel->gpio_bl_en =
+ devm_gpiod_get_optional(dev, "bl-en", GPIOD_OUT_LOW);
+ if (IS_ERR(panel->gpio_bl_en))
+ return PTR_ERR(panel->gpio_bl_en);
+
+ panel->gpio_pwm =
+ devm_gpiod_get_optional(dev, "pwm", GPIOD_OUT_LOW);
+ if (IS_ERR(panel->gpio_pwm))
+ return PTR_ERR(panel->gpio_pwm);
+
+ return 0;
+}
+
+static int hikey_panel_attach_dsi(struct mipi_dsi_device *dsi)
+{
+ int ret;
+
+ dsi->phy_clock = 480000; /* in kHz */
+ dsi->lanes = 4;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+ MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_VIDEO_HSE |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret) {
+ DRM_ERROR("failed to attach dsi to host\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int hikey_panel_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct hikey_panel *panel;
+ int ret;
+
+ DRM_INFO("hikey_panel_probe enter\n");
+
+ panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
+ if (!panel)
+ return -ENOMEM;
+
+ panel->dsi = dsi;
+ ret = hikey_panel_parse_dt(panel);
+ if (ret)
+ return ret;
+
+ ret = hikey_panel_add(panel);
+ if (ret)
+ return ret;
+
+ ret = hikey_panel_attach_dsi(dsi);
+ if (ret){
+ hikey_panel_del(panel);
+ return ret;
+ }
+
+ mipi_dsi_set_drvdata(dsi, panel);
+
+ DRM_INFO("hikey_panel_probe exit\n");
+ return 0;
+}
+
+static int hikey_panel_remove(struct mipi_dsi_device *dsi)
+{
+ struct hikey_panel *panel = mipi_dsi_get_drvdata(dsi);
+ int ret;
+
+ ret = hikey_panel_disable(&panel->base);
+ if (ret < 0)
+ DRM_ERROR("failed to disable panel: %d\n", ret);
+
+ ret = mipi_dsi_detach(dsi);
+ if (ret < 0)
+ DRM_ERROR("failed to detach from DSI host: %d\n", ret);
+
+ drm_panel_detach(&panel->base);
+ hikey_panel_del(panel);
+
+ return 0;
+}
+
+static void hikey_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+ struct hikey_panel *panel = mipi_dsi_get_drvdata(dsi);
+
+ hikey_panel_disable(&panel->base);
+}
+
+static const struct of_device_id panel_of_match[] = {
+ { .compatible = "innolux,n070icn-pb1", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, panel_of_match);
+
+static struct mipi_dsi_driver hikey_panel_driver = {
+ .driver = {
+ .name = "hikey-lcd-panel",
+ .of_match_table = panel_of_match,
+ },
+ .probe = hikey_panel_probe,
+ .remove = hikey_panel_remove,
+ .shutdown = hikey_panel_shutdown,
+};
+module_mipi_dsi_driver(hikey_panel_driver);
+
+MODULE_AUTHOR("support@lemaker.org");
+MODULE_DESCRIPTION("INNOLUX N070ICN-PB1 (800x1280) video mode panel driver");
+MODULE_LICENSE("GPL v2");