drm/panel: support transmit DSI packet
authorWeiYong Bi <bivvy.bi@rock-chips.com>
Tue, 11 Apr 2017 07:58:33 +0000 (15:58 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Mon, 17 Apr 2017 03:08:04 +0000 (11:08 +0800)
Change-Id: I6115479eebc05b44a8c01cd72919db0e5a6cb1f9
Signed-off-by: WeiYong Bi <bivvy.bi@rock-chips.com>
Documentation/devicetree/bindings/display/panel/simple-panel.txt
drivers/gpu/drm/panel/panel-simple.c

index bc66970..346b80b 100644 (file)
@@ -19,6 +19,18 @@ Optional properties when compatible is a dsi devices:
 - dsi,flags: dsi operation mode related flags
 - dsi,format: pixel format for video mode
 - dsi,lanes: number of active data lanes
+- reset-gpios: GPIO pin to reset the panel
+- reset-delay-ms: reset pulse width
+- panel-init-sequence: A byte stream formed by simple multiple dcs packets.
+       byte 0: dcs data type
+       byte 1: wait number of specified ms after dcs command transmitted
+       byte 2, 3: 16 bits length in network byte order
+       byte 4 and beyond: number byte of payload
+- panel-exit-sequence: A byte stream formed by simple multiple dcs packets.
+       byte 0: dcs data type
+       byte 1: wait number of specified ms after dcs command transmitted
+       byte 2, 3: 16 bits length in network byte order
+       byte 4 and beyond: number byte of payload
 
 Example:
 
@@ -40,6 +52,9 @@ Or:
                power-supply = <&vdd_pnl_reg>;
                enable-gpios = <&gpio 90 0>;
 
+               reset-gpios = <&gpio 91 0>;
+               reset-delay-ms = <20>;
+
                backlight = <&backlight>;
 
                dsi,flags = <MIPI_DSI_MODE_VIDEO |
@@ -48,6 +63,19 @@ Or:
                dsi,format = <MIPI_DSI_FMT_RGB888>;
                dsi,lanes = <4>;
 
+               panel-init-sequence = [
+                       39 00 00 10 b1 6c 15 15 24 E4 11 f1 80 e4
+                                   d7 23 80 c0 d2 58
+                       ...
+                       05 78 00 01 11
+                       05 00 00 01 29
+               ];
+
+               panel-exit-sequence = [
+                       05 00 00 01 28
+                       05 78 00 01 10
+               ];
+
                display-timings {
                        native-mode = <&timing0>;
                        timing0: timing0 {
index 7ed53fc..25b6d9a 100644 (file)
 #include <drm/drm_panel.h>
 
 #include <video/display_timing.h>
+#include <video/mipi_display.h>
 #include <video/of_display_timing.h>
 #include <video/videomode.h>
 
+struct dsi_ctrl_hdr {
+       u8 dtype;       /* data type */
+       u8 wait;        /* ms */
+       u16 dlen;       /* payload len */
+} __packed;
+
+struct dsi_cmd_desc {
+       struct dsi_ctrl_hdr dchdr;
+       u8 *payload;
+};
+
+struct dsi_panel_cmds {
+       u8 *buf;
+       int blen;
+       struct dsi_cmd_desc *cmds;
+       int cmd_cnt;
+};
+
 struct panel_desc {
        const struct drm_display_mode *modes;
        unsigned int num_modes;
@@ -73,6 +92,7 @@ struct panel_desc {
 
 struct panel_simple {
        struct drm_panel base;
+       struct mipi_dsi_device *dsi;
        bool prepared;
        bool enabled;
 
@@ -84,6 +104,11 @@ struct panel_simple {
        struct i2c_adapter *ddc;
 
        struct gpio_desc *enable_gpio;
+       struct gpio_desc *reset_gpio;
+       unsigned int reset_delay;
+
+       struct dsi_panel_cmds *on_cmds;
+       struct dsi_panel_cmds *off_cmds;
 };
 
 static inline struct panel_simple *to_panel_simple(struct drm_panel *panel)
@@ -91,6 +116,131 @@ static inline struct panel_simple *to_panel_simple(struct drm_panel *panel)
        return container_of(panel, struct panel_simple, base);
 }
 
+static void panel_simple_dsi_cmds_cleanup(struct panel_simple *p)
+{
+       if (p->on_cmds) {
+               kfree(p->on_cmds->buf);
+               kfree(p->on_cmds->cmds);
+       }
+
+       if (p->off_cmds) {
+               kfree(p->off_cmds->buf);
+               kfree(p->off_cmds->cmds);
+       }
+}
+
+static int panel_simple_dsi_parse_dcs_cmds(struct device *dev,
+                                          const u8 *data, int blen,
+                                          struct dsi_panel_cmds *pcmds)
+{
+       int len;
+       char *buf, *bp;
+       struct dsi_ctrl_hdr *dchdr;
+       int i, cnt;
+
+       if (!pcmds)
+               return -EINVAL;
+
+       buf = kmemdup(data, blen, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       /* scan dcs commands */
+       bp = buf;
+       len = blen;
+       cnt = 0;
+       while (len > sizeof(*dchdr)) {
+               dchdr = (struct dsi_ctrl_hdr *)bp;
+               dchdr->dlen = ntohs(dchdr->dlen);
+
+               if (dchdr->dlen > len) {
+                       dev_err(dev, "%s: error, len=%d", __func__,
+                               dchdr->dlen);
+                       return -EINVAL;
+               }
+
+               bp += sizeof(*dchdr);
+               len -= sizeof(*dchdr);
+               bp += dchdr->dlen;
+               len -= dchdr->dlen;
+               cnt++;
+       }
+
+       if (len != 0) {
+               dev_err(dev, "%s: dcs_cmd=%x len=%d error!",
+                       __func__, buf[0], blen);
+               kfree(buf);
+               return -EINVAL;
+       }
+
+       pcmds->cmds = kcalloc(cnt, sizeof(struct dsi_cmd_desc), GFP_KERNEL);
+       if (!pcmds->cmds) {
+               kfree(buf);
+               return -ENOMEM;
+       }
+
+       pcmds->cmd_cnt = cnt;
+       pcmds->buf = buf;
+       pcmds->blen = blen;
+
+       bp = buf;
+       len = blen;
+       for (i = 0; i < cnt; i++) {
+               dchdr = (struct dsi_ctrl_hdr *)bp;
+               len -= sizeof(*dchdr);
+               bp += sizeof(*dchdr);
+               pcmds->cmds[i].dchdr = *dchdr;
+               pcmds->cmds[i].payload = bp;
+               bp += dchdr->dlen;
+               len -= dchdr->dlen;
+       }
+
+       dev_info(dev, "%s: dcs_cmd=%x len=%d, cmd_cnt=%d\n", __func__,
+                pcmds->buf[0], pcmds->blen, pcmds->cmd_cnt);
+       return 0;
+}
+
+static int panel_simple_dsi_send_cmds(struct panel_simple *panel,
+                                     struct dsi_panel_cmds *cmds)
+{
+       struct mipi_dsi_device *dsi = panel->dsi;
+       int i, err;
+
+       if (!cmds)
+               return -EINVAL;
+
+       for (i = 0; i < cmds->cmd_cnt; i++) {
+               struct dsi_cmd_desc *cmd = &cmds->cmds[i];
+
+               switch (cmd->dchdr.dtype) {
+               case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
+               case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
+               case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
+               case MIPI_DSI_GENERIC_LONG_WRITE:
+                       err = mipi_dsi_generic_write(dsi, cmd->payload,
+                                                    cmd->dchdr.dlen);
+                       break;
+               case MIPI_DSI_DCS_SHORT_WRITE:
+               case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+               case MIPI_DSI_DCS_LONG_WRITE:
+                       err = mipi_dsi_dcs_write_buffer(dsi, cmd->payload,
+                                                       cmd->dchdr.dlen);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               if (err)
+                       dev_err(panel->dev, "failed to write dcs cmd: %d\n",
+                               err);
+
+               if (cmd->dchdr.wait)
+                       msleep(cmd->dchdr.wait);
+       }
+
+       return 0;
+}
+
 static int panel_simple_get_fixed_modes(struct panel_simple *panel)
 {
        struct drm_connector *connector = panel->base.connector;
@@ -204,10 +354,20 @@ static int panel_simple_disable(struct drm_panel *panel)
 static int panel_simple_unprepare(struct drm_panel *panel)
 {
        struct panel_simple *p = to_panel_simple(panel);
+       int err;
 
        if (!p->prepared)
                return 0;
 
+       if (p->off_cmds) {
+               err = panel_simple_dsi_send_cmds(p, p->off_cmds);
+               if (err)
+                       dev_err(p->dev, "failed to send off cmds\n");
+       }
+
+       if (p->reset_gpio)
+               gpiod_direction_output(p->reset_gpio, 1);
+
        if (p->enable_gpio)
                gpiod_direction_output(p->enable_gpio, 0);
 
@@ -241,6 +401,15 @@ static int panel_simple_prepare(struct drm_panel *panel)
        if (p->desc && p->desc->delay.prepare)
                msleep(p->desc->delay.prepare);
 
+       if (p->reset_gpio)
+               gpiod_direction_output(p->reset_gpio, 1);
+
+       if (p->reset_delay)
+               msleep(p->reset_delay);
+
+       if (p->reset_gpio)
+               gpiod_direction_output(p->reset_gpio, 0);
+
        p->prepared = true;
 
        return 0;
@@ -249,10 +418,17 @@ static int panel_simple_prepare(struct drm_panel *panel)
 static int panel_simple_enable(struct drm_panel *panel)
 {
        struct panel_simple *p = to_panel_simple(panel);
+       int err;
 
        if (p->enabled)
                return 0;
 
+       if (p->on_cmds) {
+               err = panel_simple_dsi_send_cmds(p, p->on_cmds);
+               if (err)
+                       dev_err(p->dev, "failed to send on cmds\n");
+       }
+
        if (p->desc && p->desc->delay.enable)
                msleep(p->desc->delay.enable);
 
@@ -359,7 +535,14 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
        panel->enable_gpio = devm_gpiod_get_optional(dev, "enable", 0);
        if (IS_ERR(panel->enable_gpio)) {
                err = PTR_ERR(panel->enable_gpio);
-               dev_err(dev, "failed to request GPIO: %d\n", err);
+               dev_err(dev, "failed to request enable GPIO: %d\n", err);
+               return err;
+       }
+
+       panel->reset_gpio = devm_gpiod_get_optional(dev, "reset", 0);
+       if (IS_ERR(panel->reset_gpio)) {
+               err = PTR_ERR(panel->reset_gpio);
+               dev_err(dev, "failed to request reset GPIO: %d\n", err);
                return err;
        }
 
@@ -420,6 +603,8 @@ static int panel_simple_remove(struct device *dev)
        if (panel->backlight)
                put_device(&panel->backlight->dev);
 
+       panel_simple_dsi_cmds_cleanup(panel);
+
        return 0;
 }
 
@@ -1718,9 +1903,12 @@ MODULE_DEVICE_TABLE(of, dsi_of_match);
 
 static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi)
 {
+       struct panel_simple *panel;
        const struct panel_desc_dsi *desc;
        const struct of_device_id *id;
        const struct panel_desc *pdesc;
+       const void *data;
+       int len;
        u32 val;
        int err;
 
@@ -1743,6 +1931,9 @@ static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi)
        if (err < 0)
                return err;
 
+       panel = dev_get_drvdata(&dsi->dev);
+       panel->dsi = dsi;
+
        if (!of_property_read_u32(dsi->dev.of_node, "dsi,flags", &val))
                dsi->mode_flags = val;
 
@@ -1752,6 +1943,41 @@ static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi)
        if (!of_property_read_u32(dsi->dev.of_node, "dsi,lanes", &val))
                dsi->lanes = val;
 
+       if (!of_property_read_u32(dsi->dev.of_node, "reset-delay-ms", &val))
+               panel->reset_delay = val;
+
+       data = of_get_property(dsi->dev.of_node, "panel-init-sequence", &len);
+       if (data) {
+               panel->on_cmds = devm_kzalloc(&dsi->dev,
+                                             sizeof(*panel->on_cmds),
+                                             GFP_KERNEL);
+               if (!panel->on_cmds)
+                       return -ENOMEM;
+
+               err = panel_simple_dsi_parse_dcs_cmds(&dsi->dev, data, len,
+                                                     panel->on_cmds);
+               if (err) {
+                       dev_err(&dsi->dev, "failed to parse panel init sequence\n");
+                       return err;
+               }
+       }
+
+       data = of_get_property(dsi->dev.of_node, "panel-exit-sequence", &len);
+       if (data) {
+               panel->off_cmds = devm_kzalloc(&dsi->dev,
+                                              sizeof(*panel->off_cmds),
+                                              GFP_KERNEL);
+               if (!panel->off_cmds)
+                       return -ENOMEM;
+
+               err = panel_simple_dsi_parse_dcs_cmds(&dsi->dev, data, len,
+                                                     panel->off_cmds);
+               if (err) {
+                       dev_err(&dsi->dev, "failed to parse panel exit sequence\n");
+                       return err;
+               }
+       }
+
        return mipi_dsi_attach(dsi);
 }