drm/panel: support transmit DSI packet

Change-Id: I6115479eebc05b44a8c01cd72919db0e5a6cb1f9
Signed-off-by: WeiYong Bi <bivvy.bi@rock-chips.com>
This commit is contained in:
WeiYong Bi
2017-04-11 15:58:33 +08:00
committed by Huang, Tao
parent 8e8535815e
commit 1616bc22bd
2 changed files with 255 additions and 1 deletions

View 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 {

View File

@@ -34,9 +34,28 @@
#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);
}