aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQiang Liu <qiangliu@marvell.com>2015-06-26 14:44:49 +0800
committerQing Zhu <qzhu@marvell.com>2015-07-08 21:09:47 +0800
commit1fede8ec2cfa91872f2c09363a0e166b98deb1a2 (patch)
tree5b9959f0ee58cb4f0e449cec17498ac5faa842e0
parente110b5e67e868f8ddaaf3df7520e083c0e1edbcb (diff)
downloadpxa-v3.14-1fede8ec2cfa91872f2c09363a0e166b98deb1a2.tar.gz
video: mmp: add hx8394d_jt panel driver support for pxa1908sl
Add driver of panel hxa8394d_jt. Change-Id: If46dac100482247278d7c1697188255da010514b Signed-off-by: Qiang Liu <qiangliu@marvell.com> Signed-off-by: Andy Luo <yifeiluo@marvell.com>
-rw-r--r--drivers/video/mmp/panel/Kconfig6
-rw-r--r--drivers/video/mmp/panel/Makefile1
-rw-r--r--drivers/video/mmp/panel/mipi_hx8394d_jt.c678
3 files changed, 685 insertions, 0 deletions
diff --git a/drivers/video/mmp/panel/Kconfig b/drivers/video/mmp/panel/Kconfig
index 964a14d9839..ab23d838121 100644
--- a/drivers/video/mmp/panel/Kconfig
+++ b/drivers/video/mmp/panel/Kconfig
@@ -59,6 +59,12 @@ config MMP_PANEL_HX8394
help
MIPI panel hx8394 support
+config MMP_PANEL_HX8394D
+ bool "MIPI panel hx8394D support"
+ default n
+ help
+ MIPI panel hx8394D support
+
config MMP_PANEL_FL10802
bool "MIPI panel fl10802 support"
default n
diff --git a/drivers/video/mmp/panel/Makefile b/drivers/video/mmp/panel/Makefile
index 85b57faf014..21631af16b5 100644
--- a/drivers/video/mmp/panel/Makefile
+++ b/drivers/video/mmp/panel/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_MMP_PANEL_LG4591) += mipi_lg4591.o
obj-$(CONFIG_MMP_PANEL_OTM1281A) += mipi_otm1281a.o
obj-$(CONFIG_MMP_PANEL_TD043MGEA1) += paral_td043mgea1.o
obj-$(CONFIG_MMP_PANEL_HX8394) += mipi_hx8394.o
+obj-$(CONFIG_MMP_PANEL_HX8394D) += mipi_hx8394d_jt.o
obj-$(CONFIG_MMP_PANEL_FL10802) += mipi_fl10802.o
obj-$(CONFIG_MMP_PANEL_OTM8018B) += mipi_otm8018b.o
obj-$(CONFIG_MMP_PANEL_OTM1283A) += mipi_otm1283a.o
diff --git a/drivers/video/mmp/panel/mipi_hx8394d_jt.c b/drivers/video/mmp/panel/mipi_hx8394d_jt.c
new file mode 100644
index 00000000000..7bb404b9c23
--- /dev/null
+++ b/drivers/video/mmp/panel/mipi_hx8394d_jt.c
@@ -0,0 +1,678 @@
+/*
+ * drivers/video/mmp/panel/mipi_hx8394d.c
+ * active panel using DSI interface to do init
+ *
+ * Copyright (C) 2013 Marvell Technology Group Ltd.
+ * Authors: Yonghai Huang <huangyh@marvell.com>
+ * Xiaolong Ye <yexl@marvell.com>
+ * Guoqing Li <ligq@marvell.com>
+ * Lisa Du <cldu@marvell.com>
+ * Zhou Zhu <zzhu3@marvell.com>
+ * Jing Xiang <jxiang@marvell.com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <video/mmp_disp.h>
+#include <video/mipi_display.h>
+#include <video/mmp_esd.h>
+
+struct hx8394d_plat_data {
+ const char *name;
+ struct mmp_panel *panel;
+ u32 esd_enable;
+ void (*plat_onoff)(int status);
+ void (*plat_set_backlight)(struct mmp_panel *panel, int level);
+};
+
+static u8 hx8394dd_720p_video_on_cmd_list0[] = {0xB9, 0xFF, 0x83, 0x94};
+static u8 hx8394dd_720p_video_on_cmd_list1[] = {0xBA, 0x73, 0x83};
+static u8 hx8394dd_720p_video_on_cmd_list2[] = {
+ 0xB1, 0x6c, 0x12, 0x12, 0x26,
+ 0x04, 0x11, 0xF1, 0x81, 0x3a,
+ 0x54, 0x23, 0x80, 0xC0, 0xD2,
+ 0x58
+};
+static u8 hx8394dd_720p_video_on_cmd_list3[] = {
+ 0xB2, 0x00, 0x64, 0x0e, 0x0d,
+ 0x22, 0x1C, 0x08, 0x08, 0x1C,
+ 0x4D, 0x00
+};
+static u8 hx8394dd_720p_video_on_cmd_list4[] = {
+ 0xB4, 0x00, 0xFF, 0x51, 0x5A,
+ 0x59, 0x5A, 0x03, 0x5A, 0x01,
+ 0x70, 0x20, 0x70
+};
+static u8 hx8394dd_720p_video_on_cmd_list5[] = {0xBC, 0x07};
+static u8 hx8394dd_720p_video_on_cmd_list6[] = {0xBF, 0x41, 0x0E, 0x01};
+/* static u8 hx8394dd_720p_video_on_cmd_list7[] = {0xD2, 0x55}; */
+static u8 hx8394dd_720p_video_on_cmd_list7[] = {0xB6, 0x6B, 0x6B};
+static u8 hx8394dd_720p_video_on_cmd_list8[] = {
+ 0xD3, 0x00, 0x0F, 0x00, 0x40,
+ 0x07, 0x10, 0x00, 0x08, 0x10,
+ 0x08, 0x00, 0x08, 0x54, 0x15,
+ 0x0e, 0x05, 0x0e, 0x02, 0x15,
+ 0x06, 0x05, 0x06, 0x47, 0x44,
+ 0x0a, 0x0a, 0x4b, 0x10, 0x07,
+ 0x07
+};
+static u8 hx8394dd_720p_video_on_cmd_list9[] = {
+ 0xD5, 0x1a, 0x1a, 0x1b, 0x1b,
+ 0x00, 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x24, 0x25, 0x18,
+ 0x18, 0x26, 0x27, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x20,
+ 0x21, 0x18, 0x18, 0x18, 0x18
+};
+static u8 hx8394dd_720p_video_on_cmd_list10[] = {
+ 0xD6, 0x1a, 0x1a, 0x1b, 0x1b,
+ 0x0B, 0x0a, 0x09, 0x08, 0x07,
+ 0x06, 0x05, 0x04, 0x03, 0x02,
+ 0x01, 0x00, 0x21, 0x20, 0x58,
+ 0x58, 0x27, 0x26, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x25,
+ 0x24, 0x18, 0x18, 0x18, 0x18
+};
+/* static u8 hx8394dd_720p_video_on_cmd_list11[] = {0xB6, 0x82, 0x82}; */
+/* Gammer = 2.6.. */
+#if 0
+static u8 hx8394dd_720p_video_on_cmd_list12[] = {
+ 0xE0, 0x00, 0x1b, 0x1f, 0x3a,
+ 0x3f, 0x3F, 0x28, 0x41, 0x09,
+ 0x0b, 0x0C, 0x17, 0x0D, 0x10,
+ 0x13, 0x11, 0x10, 0x02, 0x0f,
+ 0x06, 0x11, 0x00, 0x1b, 0x20,
+ 0x3b, 0x3f, 0x3F, 0x28, 0x47,
+ 0x08, 0x0b, 0x0C, 0x17, 0x0c,
+ 0x10, 0x11, 0x10, 0x12, 0x04,
+ 0x0c, 0x0d, 0x0a
+};
+#else /* Gammer = 2.03 */
+static u8 hx8394dd_720p_video_on_cmd_list12[] = {
+ 0xE0, 0x00, 0x0b, 0x10, 0x2c,
+ 0x32, 0x3F, 0x1d, 0x39, 0x06,
+ 0x0b, 0x0d, 0x17, 0x0e, 0x11,
+ 0x14, 0x11, 0x13, 0x08, 0x13,
+ 0x14, 0x16, 0x00, 0x0b, 0x10,
+ 0x2c, 0x32, 0x3F, 0x1d, 0x39,
+ 0x06, 0x0b, 0x0d, 0x17, 0x0e,
+ 0x11, 0x14, 0x11, 0x13, 0x08,
+ 0x13, 0x14, 0x16
+};
+#endif
+static u8 hx8394dd_720p_video_on_cmd_list13[] = {0xC0, 0x30, 0x14};
+static u8 hx8394dd_720p_video_on_cmd_list14[] = {0xC7, 0x00, 0xC0, 0x40, 0xC0};
+static u8 hx8394dd_720p_video_on_cmd_list15[] = {0xCC, 0x09};
+/* static u8 hx8394dd_720p_video_on_cmd_list16[] = {0xDF, 0x88}; */
+static u8 hx8394dd_720p_video_on_cmd_list17[] = {0x11};
+static u8 hx8394dd_720p_video_on_cmd_list18[] = {0x29};
+
+static struct mmp_dsi_cmd_desc hx8394d_display_on_cmds[] = {
+ {MIPI_DSI_DCS_LONG_WRITE, 1, 0,
+ sizeof(hx8394dd_720p_video_on_cmd_list0),
+ hx8394dd_720p_video_on_cmd_list0},
+ {MIPI_DSI_DCS_LONG_WRITE, 1, 0,
+ sizeof(hx8394dd_720p_video_on_cmd_list1),
+ hx8394dd_720p_video_on_cmd_list1},
+ {MIPI_DSI_DCS_LONG_WRITE, 1, 0,
+ sizeof(hx8394dd_720p_video_on_cmd_list2),
+ hx8394dd_720p_video_on_cmd_list2},
+ {MIPI_DSI_DCS_LONG_WRITE, 1, 0,
+ sizeof(hx8394dd_720p_video_on_cmd_list3),
+ hx8394dd_720p_video_on_cmd_list3},
+ {MIPI_DSI_DCS_LONG_WRITE, 1, 0,
+ sizeof(hx8394dd_720p_video_on_cmd_list4),
+ hx8394dd_720p_video_on_cmd_list4},
+ {MIPI_DSI_DCS_LONG_WRITE, 1, 0,
+ sizeof(hx8394dd_720p_video_on_cmd_list5),
+ hx8394dd_720p_video_on_cmd_list5},
+ {MIPI_DSI_DCS_LONG_WRITE, 1, 0,
+ sizeof(hx8394dd_720p_video_on_cmd_list7),
+ hx8394dd_720p_video_on_cmd_list7},
+ {MIPI_DSI_DCS_LONG_WRITE, 1, 0,
+ sizeof(hx8394dd_720p_video_on_cmd_list8),
+ hx8394dd_720p_video_on_cmd_list8},
+ {MIPI_DSI_DCS_LONG_WRITE, 1, 0,
+ sizeof(hx8394dd_720p_video_on_cmd_list9),
+ hx8394dd_720p_video_on_cmd_list9},
+ {MIPI_DSI_DCS_LONG_WRITE, 1, 0,
+ sizeof(hx8394dd_720p_video_on_cmd_list6),
+ hx8394dd_720p_video_on_cmd_list6},
+ {MIPI_DSI_DCS_LONG_WRITE, 1, 0,
+ sizeof(hx8394dd_720p_video_on_cmd_list10),
+ hx8394dd_720p_video_on_cmd_list10},
+ /*
+ * {MIPI_DSI_DCS_LONG_WRITE, 1, 0,
+ * sizeof(hx8394dd_720p_video_on_cmd_list11),
+ * hx8394dd_720p_video_on_cmd_list11},
+ */
+ {MIPI_DSI_DCS_LONG_WRITE, 1, 0,
+ sizeof(hx8394dd_720p_video_on_cmd_list12),
+ hx8394dd_720p_video_on_cmd_list12},
+ {MIPI_DSI_DCS_LONG_WRITE, 1, 0,
+ sizeof(hx8394dd_720p_video_on_cmd_list13),
+ hx8394dd_720p_video_on_cmd_list13},
+ {MIPI_DSI_DCS_LONG_WRITE, 1, 0,
+ sizeof(hx8394dd_720p_video_on_cmd_list14),
+ hx8394dd_720p_video_on_cmd_list14},
+ {MIPI_DSI_DCS_LONG_WRITE, 1, 0,
+ sizeof(hx8394dd_720p_video_on_cmd_list15),
+ hx8394dd_720p_video_on_cmd_list15},
+ /*
+ * {MIPI_DSI_DCS_LONG_WRITE, 1, 0,
+ * sizeof(hx8394dd_720p_video_on_cmd_list16),
+ * hx8394dd_720p_video_on_cmd_list16},
+ */
+ {MIPI_DSI_DCS_SHORT_WRITE, 1, 120,
+ sizeof(hx8394dd_720p_video_on_cmd_list17),
+ hx8394dd_720p_video_on_cmd_list17},
+ {MIPI_DSI_DCS_SHORT_WRITE, 1, 200,
+ sizeof(hx8394dd_720p_video_on_cmd_list18),
+ hx8394dd_720p_video_on_cmd_list18}
+};
+
+static char enter_sleep[] = {0x10};
+static char display_off[] = {0x28};
+
+static struct mmp_dsi_cmd_desc hx8394d_display_off_cmds[] = {
+ {MIPI_DSI_DCS_SHORT_WRITE, 0, 200,
+ sizeof(enter_sleep), enter_sleep},
+ {MIPI_DSI_DCS_SHORT_WRITE, 0, 0,
+ sizeof(display_off), display_off},
+};
+
+static void hx8394d_panel_on(struct mmp_panel *panel, int status)
+{
+ if (status) {
+ mmp_panel_dsi_ulps_set_on(panel, 0);
+ mmp_panel_dsi_tx_cmd_array(panel, hx8394d_display_on_cmds,
+ ARRAY_SIZE(hx8394d_display_on_cmds));
+ } else {
+ mmp_panel_dsi_tx_cmd_array(panel, hx8394d_display_off_cmds,
+ ARRAY_SIZE(hx8394d_display_off_cmds));
+ mmp_panel_dsi_ulps_set_on(panel, 1);
+ }
+}
+
+#ifdef CONFIG_OF
+static void hx8394d_panel_power(struct mmp_panel *panel, int skip_on, int on)
+{
+ static struct regulator *lcd_iovdd, *lcd_avdd;
+ int lcd_rst_n, ret = 0;
+
+ lcd_rst_n = of_get_named_gpio(panel->dev->of_node, "rst_gpio", 0);
+ if (lcd_rst_n < 0) {
+ pr_err("%s: of_get_named_gpio failed\n", __func__);
+ return;
+ }
+
+ if (gpio_request(lcd_rst_n, "lcd reset gpio")) {
+ pr_err("gpio %d request failed\n", lcd_rst_n);
+ return;
+ }
+
+ /* FIXME: LCD_IOVDD, 1.8V from buck2 */
+ if (panel->is_iovdd && (!lcd_iovdd)) {
+ lcd_iovdd = regulator_get(panel->dev, "iovdd");
+ if (IS_ERR(lcd_iovdd)) {
+ pr_err("%s regulator get error!\n", __func__);
+ ret = -EIO;
+ goto regu_lcd_iovdd;
+ }
+ }
+ if (panel->is_avdd && (!lcd_avdd)) {
+ lcd_avdd = regulator_get(panel->dev, "avdd");
+ if (IS_ERR(lcd_avdd)) {
+ pr_err("%s regulator get error!\n", __func__);
+ ret = -EIO;
+ goto regu_lcd_iovdd;
+ }
+ }
+ if (on) {
+ if (panel->is_avdd && lcd_avdd) {
+ regulator_set_voltage(lcd_avdd, 2800000, 2800000);
+ ret = regulator_enable(lcd_avdd);
+ if (ret < 0)
+ goto regu_lcd_iovdd;
+ }
+
+ if (panel->is_iovdd && lcd_iovdd) {
+ regulator_set_voltage(lcd_iovdd, 1800000, 1800000);
+ ret = regulator_enable(lcd_iovdd);
+ if (ret < 0)
+ goto regu_lcd_iovdd;
+ }
+ usleep_range(10000, 12000);
+ if (!skip_on) {
+ gpio_direction_output(lcd_rst_n, 1);
+ usleep_range(10000, 11000);
+ gpio_direction_output(lcd_rst_n, 0);
+ usleep_range(10000, 12000);
+ gpio_direction_output(lcd_rst_n, 1);
+ usleep_range(10000, 12000);
+ }
+ } else {
+ /* set panel reset */
+ gpio_direction_output(lcd_rst_n, 0);
+
+ /* disable LCD_IOVDD 1.8v */
+ if (panel->is_iovdd && lcd_iovdd)
+ regulator_disable(lcd_iovdd);
+ if (panel->is_avdd && lcd_avdd)
+ regulator_disable(lcd_avdd);
+ }
+
+regu_lcd_iovdd:
+ gpio_free(lcd_rst_n);
+ if (ret < 0) {
+ lcd_iovdd = NULL;
+ lcd_avdd = NULL;
+ }
+}
+#else
+static void hx8394d_panel_power(struct mmp_panel *panel, int skip_on, int on) {}
+#endif
+
+static void hx8394d_set_status(struct mmp_panel *panel, int status)
+{
+ struct hx8394d_plat_data *plat = panel->plat_data;
+ int skip_on = (status == MMP_ON_REDUCED);
+
+ if (status_is_on(status)) {
+ /* power on */
+ if (plat->plat_onoff)
+ plat->plat_onoff(1);
+ else
+ hx8394d_panel_power(panel, skip_on, 1);
+ if (!skip_on)
+ hx8394d_panel_on(panel, 1);
+ } else if (status_is_off(status)) {
+ hx8394d_panel_on(panel, 0);
+ /* power off */
+ if (plat->plat_onoff)
+ plat->plat_onoff(0);
+ else
+ hx8394d_panel_power(panel, 0, 0);
+ } else
+ dev_warn(panel->dev, "set status %s not supported\n",
+ status_name(status));
+}
+
+static void hx8394d_esd_onoff(struct mmp_panel *panel, int status)
+{
+ struct hx8394d_plat_data *plat = panel->plat_data;
+
+ if (status) {
+ if (plat->esd_enable)
+ esd_start(&panel->esd);
+ } else {
+ if (plat->esd_enable)
+ esd_stop(&panel->esd);
+ }
+}
+
+static void hx8394d_esd_recover(struct mmp_panel *panel)
+{
+ struct mmp_path *path = mmp_get_path(panel->plat_path_name);
+ static int count = 1;
+ /*
+ * FIXME: skip the first esd_recover
+ * since the first esd check will fail.
+ */
+ if (count++ > 1)
+ esd_panel_recover(path);
+}
+
+/* for panel hx8394d */
+static u8 hx8394d_pkt_size_cmd1[] = {0x01};
+static u8 read_id_0xhx8394d1[] = {0xD9};
+static struct mmp_dsi_cmd_desc hx8394d_read_id_cmds1[] = {
+ {MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, 1, 1,
+ sizeof(hx8394d_pkt_size_cmd1),
+ hx8394d_pkt_size_cmd1},
+ {MIPI_DSI_DCS_READ, 1, 1,
+ sizeof(read_id_0xhx8394d1), read_id_0xhx8394d1},
+};
+
+static u8 hx8394d_pkt_size_cmd2[] = {0x02};
+static u8 read_id_0xhx8394d2[] = {0x09};
+static struct mmp_dsi_cmd_desc hx8394d_read_id_cmds2[] = {
+ {MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, 1, 1,
+ sizeof(hx8394d_pkt_size_cmd2),
+ hx8394d_pkt_size_cmd2},
+ {MIPI_DSI_DCS_READ, 1, 1,
+ sizeof(read_id_0xhx8394d2), read_id_0xhx8394d2},
+};
+
+static int hx8394d_get_status(struct mmp_panel *panel)
+{
+ struct mmp_dsi_buf dbuf;
+ u32 read_id = 0;
+ int ret;
+
+ ret = mmp_panel_dsi_rx_cmd_array(panel, &dbuf,
+ hx8394d_read_id_cmds1,
+ ARRAY_SIZE(hx8394d_read_id_cmds1));
+ if (ret < 0) {
+ pr_err("[ERROR] hx8394d_get_status id_cmds1 DSI receive failure!\n");
+ return 1;
+ }
+
+ read_id |= dbuf.data[0];
+ ret = mmp_panel_dsi_rx_cmd_array(panel, &dbuf,
+ hx8394d_read_id_cmds2,
+ ARRAY_SIZE(hx8394d_read_id_cmds2));
+ if (ret < 0) {
+ pr_err("[ERROR] hx8394d_get_status id_cmds2 DSI receive failure!\n");
+ return 1;
+ }
+
+ if ((read_id != 0x80) || (dbuf.data[0] != 0x80) ||
+ (dbuf.data[1] != 0x73)) {
+ pr_err("[ERROR] panel status is 0x%x\n", read_id);
+ return 1;
+ } else {
+ pr_debug("panel status is 0x%x\n", read_id);
+ }
+
+ return 0;
+}
+static struct mmp_mode mmp_modes_hx8394d[] = {
+ [0] = {
+ .pixclock_freq = 75513600,
+ .refresh = 60,
+ .xres = 720,
+ .yres = 1280,
+ .real_xres = 720,
+ .real_yres = 1280,
+ .hsync_len = 20,
+ .left_margin = 110,
+ .right_margin = 110,
+ .vsync_len = 4,
+ .upper_margin = 12,
+ .lower_margin = 15,
+ .invert_pixclock = 0,
+ .pix_fmt_out = PIXFMT_BGR888PACK,
+ .hsync_invert = 0,
+ .vsync_invert = 0,
+ .height = 110,
+ .width = 62,
+ },
+};
+
+static int hx8394d_get_modelist(struct mmp_panel *panel,
+ struct mmp_mode **modelist)
+{
+ *modelist = mmp_modes_hx8394d;
+ return 1;
+}
+
+static struct mmp_panel panel_hx8394d = {
+ .name = "hx8394d",
+ .panel_type = PANELTYPE_DSI_VIDEO,
+ .is_iovdd = 0,
+ .is_avdd = 0,
+ .get_modelist = hx8394d_get_modelist,
+ .set_status = hx8394d_set_status,
+ .get_status = hx8394d_get_status,
+ .panel_esd_recover = hx8394d_esd_recover,
+ .esd_set_onoff = hx8394d_esd_onoff,
+};
+
+static int hx8394d_bl_update_status(struct backlight_device *bl)
+{
+ struct hx8394d_plat_data *data = dev_get_drvdata(&bl->dev);
+ struct mmp_panel *panel = data->panel;
+ int level;
+
+ if (bl->props.fb_blank == FB_BLANK_UNBLANK &&
+ bl->props.power == FB_BLANK_UNBLANK)
+ level = bl->props.brightness;
+ else
+ level = 0;
+
+ /* If there is backlight function of board, use it */
+ if (data && data->plat_set_backlight) {
+ data->plat_set_backlight(panel, level);
+ return 0;
+ }
+
+ if (panel && panel->set_brightness)
+ panel->set_brightness(panel, level);
+
+ return 0;
+}
+
+static int hx8394d_bl_get_brightness(struct backlight_device *bl)
+{
+ if (bl->props.fb_blank == FB_BLANK_UNBLANK &&
+ bl->props.power == FB_BLANK_UNBLANK)
+ return bl->props.brightness;
+
+ return 0;
+}
+
+static DEFINE_SPINLOCK(bl_lock);
+static void hx8394d_panel_set_bl(struct mmp_panel *panel, int intensity)
+{
+ int gpio_bl, bl_level, p_num;
+ unsigned long flags;
+ /*
+ * FIXME
+ * the initial value of bl_level_last is the
+ * uboot backlight level, it should be aligned.
+ */
+ static int bl_level_last = 17;
+
+ gpio_bl = of_get_named_gpio(panel->dev->of_node, "bl_gpio", 0);
+ if (gpio_bl < 0) {
+ pr_err("%s: of_get_named_gpio failed\n", __func__);
+ return;
+ }
+
+ if (gpio_request(gpio_bl, "lcd backlight")) {
+ pr_err("gpio %d request failed\n", gpio_bl);
+ return;
+ }
+
+ /*
+ * Brightness is controlled by a series of pulses
+ * generated by gpio. It has 32 leves and level 1
+ * is the brightest. Pull low for 3ms makes
+ * backlight shutdown
+ */
+ bl_level = (100 - intensity) * 32 / 100 + 1;
+
+ if (bl_level == bl_level_last)
+ goto set_bl_return;
+
+ if (bl_level == 33) {
+ /* shutdown backlight */
+ gpio_direction_output(gpio_bl, 0);
+ goto set_bl_return;
+ }
+
+ if (bl_level > bl_level_last)
+ p_num = bl_level - bl_level_last;
+ else
+ p_num = bl_level + 32 - bl_level_last;
+
+ while (p_num--) {
+ spin_lock_irqsave(&bl_lock, flags);
+ gpio_direction_output(gpio_bl, 0);
+ udelay(1);
+ gpio_direction_output(gpio_bl, 1);
+ spin_unlock_irqrestore(&bl_lock, flags);
+ udelay(1);
+ }
+
+set_bl_return:
+ if (bl_level == 33)
+ bl_level_last = 0;
+ else
+ bl_level_last = bl_level;
+ gpio_free(gpio_bl);
+}
+static const struct backlight_ops hx8394d_bl_ops = {
+ .get_brightness = hx8394d_bl_get_brightness,
+ .update_status = hx8394d_bl_update_status,
+};
+
+static int hx8394d_probe(struct platform_device *pdev)
+{
+ struct mmp_mach_panel_info *mi;
+ struct hx8394d_plat_data *plat_data;
+ struct device_node *np = pdev->dev.of_node;
+ const char *path_name;
+ struct backlight_properties props;
+ struct backlight_device *bl;
+ int ret;
+ u32 esd_enable;
+
+ plat_data = kzalloc(sizeof(*plat_data), GFP_KERNEL);
+ if (!plat_data)
+ return -ENOMEM;
+
+ if (IS_ENABLED(CONFIG_OF)) {
+ ret = of_property_read_string(np, "marvell,path-name",
+ &path_name);
+ if (ret < 0) {
+ kfree(plat_data);
+ return ret;
+ }
+ panel_hx8394d.plat_path_name = path_name;
+
+ if (of_property_read_string(np, "panel_name", &plat_data->name))
+ return -EINVAL;
+
+ if (of_find_property(np, "iovdd-supply", NULL))
+ panel_hx8394d.is_iovdd = 1;
+
+ if (of_find_property(np, "avdd-supply", NULL))
+ panel_hx8394d.is_avdd = 1;
+ if (of_property_read_u32(np, "panel_esd", &esd_enable))
+ plat_data->esd_enable = 0;
+
+ plat_data->esd_enable = esd_enable;
+ if (of_get_named_gpio(np, "bl_gpio", 0) < 0)
+ pr_debug("%s: get bl_gpio failed\n", __func__);
+ else
+ plat_data->plat_set_backlight = hx8394d_panel_set_bl;
+
+ } else {
+ /* get configs from platform data */
+ mi = pdev->dev.platform_data;
+ if (mi == NULL) {
+ dev_err(&pdev->dev, "no platform data defined\n");
+ kfree(plat_data);
+ return -EINVAL;
+ }
+ plat_data->plat_onoff = mi->plat_set_onoff;
+ panel_hx8394d.plat_path_name = mi->plat_path_name;
+ plat_data->plat_set_backlight = mi->plat_set_backlight;
+ plat_data->esd_enable = mi->esd_enable;
+ }
+
+ plat_data->panel = &panel_hx8394d;
+ panel_hx8394d.plat_data = plat_data;
+ panel_hx8394d.dev = &pdev->dev;
+ mmp_register_panel(&panel_hx8394d);
+
+ if (plat_data->esd_enable)
+ esd_init(&panel_hx8394d);
+
+ /*
+ * if no panel or plat associate backlight control,
+ * don't register backlight device here.
+ */
+ if (!panel_hx8394d.set_brightness && !plat_data->plat_set_backlight)
+ return 0;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.max_brightness = 100;
+ props.type = BACKLIGHT_RAW;
+
+ bl = backlight_device_register("lcd-bl", &pdev->dev, plat_data,
+ &hx8394d_bl_ops, &props);
+ if (IS_ERR(bl)) {
+ ret = PTR_ERR(bl);
+ dev_err(&pdev->dev, "failed to register lcd-backlight\n");
+ return ret;
+ }
+
+ bl->props.fb_blank = FB_BLANK_UNBLANK;
+ bl->props.power = FB_BLANK_UNBLANK;
+ bl->props.brightness = 40;
+
+ return 0;
+}
+
+static int hx8394d_remove(struct platform_device *dev)
+{
+ mmp_unregister_panel(&panel_hx8394d);
+ kfree(panel_hx8394d.plat_data);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mmp_hx8394d_dt_match[] = {
+ { .compatible = "marvell,mmp-hx8394d_jt" },
+ {},
+};
+#endif
+
+static struct platform_driver hx8394d_driver = {
+ .driver = {
+ .name = "mmp-hx8394d_jt",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(mmp_hx8394d_dt_match),
+ },
+ .probe = hx8394d_probe,
+ .remove = hx8394d_remove,
+};
+
+static int hx8394d_init(void)
+{
+ return platform_driver_register(&hx8394d_driver);
+}
+static void hx8394d_exit(void)
+{
+ platform_driver_unregister(&hx8394d_driver);
+}
+module_init(hx8394d_init);
+module_exit(hx8394d_exit);
+
+MODULE_AUTHOR("Yonghai Huang <huangyh@marvell.com>");
+MODULE_DESCRIPTION("Panel driver for MIPI panel HX8394d");
+MODULE_LICENSE("GPL");