The Innolux p097pfg is one of two possible panels used in the gru-scarlet ChromeOS devices.
This v5 mainly addresses the very valuable comments from Emil regarding power-supply and delay handling as well as documenting the (unsatisfying) source of the init commands.
Individual changelogs inside the specific patches.
Tested with the recently resurrected Rockchip dual-dsi patches on a gru-scarlet.
Lin Huang (2): drm/panel: p079zca: add variable unprepare_delay properties drm/panel: p079zca: support Innolux P097PFG panel
huang lin (2): drm/panel: p079zca: refactor panel driver to support multiple panels dt-bindings: Add Innolux P097PFG panel bindings
.../display/panel/innolux,p097pfg.txt | 24 ++ drivers/gpu/drm/panel/panel-innolux-p079zca.c | 353 +++++++++++++++--- 2 files changed, 328 insertions(+), 49 deletions(-) create mode 100644 Documentation/devicetree/bindings/display/panel/innolux,p097pfg.txt
From: huang lin hl@rock-chips.com
Refactor Innolux P079ZCA panel driver, let it support multi panels from Innolux that share similar power sequences.
Panels may require different power supplies so use regulator bulk interfaces and define per panel supply-names.
Changes in v2: - Change regulator property name to meet the panel datasheet Changes in v3: - this patch only refactor P079ZCA panel to support multi panel, support P097PFG panel in another patch Changes in v4: - Modify the patch which suggest by Thierry Changes in v5: - use regulator_bulk to handle different supply number
Signed-off-by: Lin Huang hl@rock-chips.com Signed-off-by: Heiko Stuebner heiko@sntech.de --- drivers/gpu/drm/panel/panel-innolux-p079zca.c | 143 ++++++++++++------ 1 file changed, 100 insertions(+), 43 deletions(-)
diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index bb53e0850764..840ad4a6a6a6 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -20,12 +20,29 @@
#include <video/mipi_display.h>
+struct panel_desc { + const struct drm_display_mode *modes; + unsigned int bpc; + struct { + unsigned int width; + unsigned int height; + } size; + + unsigned long flags; + enum mipi_dsi_pixel_format format; + unsigned int lanes; + const char * const *supply_names; + unsigned int num_supplies; +}; + struct innolux_panel { struct drm_panel base; struct mipi_dsi_device *link; + const struct panel_desc *desc;
struct backlight_device *backlight; - struct regulator *supply; + struct regulator_bulk_data *supplies; + unsigned int num_supplies; struct gpio_desc *enable_gpio;
bool prepared; @@ -77,9 +94,7 @@ static int innolux_panel_unprepare(struct drm_panel *panel) /* T8: 80ms - 1000ms */ msleep(80);
- err = regulator_disable(innolux->supply); - if (err < 0) - return err; + regulator_bulk_disable(innolux->desc->num_supplies, innolux->supplies);
innolux->prepared = false;
@@ -89,14 +104,15 @@ static int innolux_panel_unprepare(struct drm_panel *panel) static int innolux_panel_prepare(struct drm_panel *panel) { struct innolux_panel *innolux = to_innolux_panel(panel); - int err, regulator_err; + int err;
if (innolux->prepared) return 0;
gpiod_set_value_cansleep(innolux->enable_gpio, 0);
- err = regulator_enable(innolux->supply); + err = regulator_bulk_enable(innolux->desc->num_supplies, + innolux->supplies); if (err < 0) return err;
@@ -133,12 +149,9 @@ static int innolux_panel_prepare(struct drm_panel *panel) return 0;
poweroff: - regulator_err = regulator_disable(innolux->supply); - if (regulator_err) - DRM_DEV_ERROR(panel->dev, "failed to disable regulator: %d\n", - regulator_err); - gpiod_set_value_cansleep(innolux->enable_gpio, 0); + regulator_bulk_disable(innolux->desc->num_supplies, innolux->supplies); + return err; }
@@ -162,7 +175,11 @@ static int innolux_panel_enable(struct drm_panel *panel) return 0; }
-static const struct drm_display_mode default_mode = { +static const char * const innolux_p079zca_supply_names[] = { + "power", +}; + +static const struct drm_display_mode innolux_p079zca_mode = { .clock = 56900, .hdisplay = 768, .hsync_start = 768 + 40, @@ -175,15 +192,31 @@ static const struct drm_display_mode default_mode = { .vrefresh = 60, };
+static const struct panel_desc innolux_p079zca_panel_desc = { + .modes = &innolux_p079zca_mode, + .bpc = 8, + .size = { + .width = 120, + .height = 160, + }, + .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_MODE_LPM, + .format = MIPI_DSI_FMT_RGB888, + .lanes = 4, + .supply_names = innolux_p079zca_supply_names, + .num_supplies = ARRAY_SIZE(innolux_p079zca_supply_names), +}; + static int innolux_panel_get_modes(struct drm_panel *panel) { struct drm_display_mode *mode; + struct innolux_panel *innolux = to_innolux_panel(panel); + const struct drm_display_mode *m = innolux->desc->modes;
- mode = drm_mode_duplicate(panel->drm, &default_mode); + mode = drm_mode_duplicate(panel->drm, m); if (!mode) { DRM_DEV_ERROR(panel->drm->dev, "failed to add mode %ux%ux@%u\n", - default_mode.hdisplay, default_mode.vdisplay, - default_mode.vrefresh); + m->hdisplay, m->vdisplay, m->vrefresh); return -ENOMEM; }
@@ -191,9 +224,11 @@ static int innolux_panel_get_modes(struct drm_panel *panel)
drm_mode_probed_add(panel->connector, mode);
- panel->connector->display_info.width_mm = 120; - panel->connector->display_info.height_mm = 160; - panel->connector->display_info.bpc = 8; + panel->connector->display_info.width_mm = + innolux->desc->size.width; + panel->connector->display_info.height_mm = + innolux->desc->size.height; + panel->connector->display_info.bpc = innolux->desc->bpc;
return 1; } @@ -207,19 +242,38 @@ static const struct drm_panel_funcs innolux_panel_funcs = { };
static const struct of_device_id innolux_of_match[] = { - { .compatible = "innolux,p079zca", }, + { .compatible = "innolux,p079zca", + .data = &innolux_p079zca_panel_desc + }, { } }; MODULE_DEVICE_TABLE(of, innolux_of_match);
-static int innolux_panel_add(struct innolux_panel *innolux) +static int innolux_panel_add(struct mipi_dsi_device *dsi, + const struct panel_desc *desc) { - struct device *dev = &innolux->link->dev; - int err; + struct innolux_panel *innolux; + struct device *dev = &dsi->dev; + int err, i;
- innolux->supply = devm_regulator_get(dev, "power"); - if (IS_ERR(innolux->supply)) - return PTR_ERR(innolux->supply); + innolux = devm_kzalloc(dev, sizeof(*innolux), GFP_KERNEL); + if (!innolux) + return -ENOMEM; + + innolux->desc = desc; + + innolux->supplies = devm_kcalloc(dev, desc->num_supplies, + sizeof(*innolux->supplies), GFP_KERNEL); + if (!innolux->supplies) + return -ENOMEM; + + for (i = 0; i < desc->num_supplies; i++) + innolux->supplies[i].supply = desc->supply_names[i]; + + err = devm_regulator_bulk_get(dev, desc->num_supplies, + innolux->supplies); + if (err < 0) + return err;
innolux->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); @@ -230,15 +284,21 @@ static int innolux_panel_add(struct innolux_panel *innolux) }
innolux->backlight = devm_of_find_backlight(dev); - if (IS_ERR(innolux->backlight)) return PTR_ERR(innolux->backlight);
drm_panel_init(&innolux->base); innolux->base.funcs = &innolux_panel_funcs; - innolux->base.dev = &innolux->link->dev; + innolux->base.dev = dev; + + err = drm_panel_add(&innolux->base); + if (err < 0) + return err; + + mipi_dsi_set_drvdata(dsi, innolux); + innolux->link = dsi;
- return drm_panel_add(&innolux->base); + return 0; }
static void innolux_panel_del(struct innolux_panel *innolux) @@ -249,28 +309,24 @@ static void innolux_panel_del(struct innolux_panel *innolux)
static int innolux_panel_probe(struct mipi_dsi_device *dsi) { - struct innolux_panel *innolux; + const struct panel_desc *desc; + const struct of_device_id *id; int err;
- 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_LPM; - - innolux = devm_kzalloc(&dsi->dev, sizeof(*innolux), GFP_KERNEL); - if (!innolux) - return -ENOMEM; - - mipi_dsi_set_drvdata(dsi, innolux); + id = of_match_node(innolux_of_match, dsi->dev.of_node); + if (!id) + return -ENODEV;
- innolux->link = dsi; + desc = id->data; + dsi->mode_flags = desc->flags; + dsi->format = desc->format; + dsi->lanes = desc->lanes;
- err = innolux_panel_add(innolux); + err = innolux_panel_add(dsi, desc); if (err < 0) return err;
- err = mipi_dsi_attach(dsi); - return err; + return mipi_dsi_attach(dsi); }
static int innolux_panel_remove(struct mipi_dsi_device *dsi) @@ -317,5 +373,6 @@ static struct mipi_dsi_driver innolux_panel_driver = { module_mipi_dsi_driver(innolux_panel_driver);
MODULE_AUTHOR("Chris Zhong zyw@rock-chips.com"); +MODULE_AUTHOR("Lin Huang hl@rock-chips.com"); MODULE_DESCRIPTION("Innolux P079ZCA panel driver"); MODULE_LICENSE("GPL v2");
On Mon, Jul 02, 2018 at 12:27:18PM +0200, Heiko Stuebner wrote:
From: huang lin hl@rock-chips.com
Refactor Innolux P079ZCA panel driver, let it support multi panels from Innolux that share similar power sequences.
Panels may require different power supplies so use regulator bulk interfaces and define per panel supply-names.
Changes in v2:
- Change regulator property name to meet the panel datasheet
Changes in v3:
- this patch only refactor P079ZCA panel to support multi panel, support P097PFG panel in another patch
Changes in v4:
- Modify the patch which suggest by Thierry
Changes in v5:
- use regulator_bulk to handle different supply number
Signed-off-by: Lin Huang hl@rock-chips.com Signed-off-by: Heiko Stuebner heiko@sntech.de
drivers/gpu/drm/panel/panel-innolux-p079zca.c | 143 ++++++++++++------ 1 file changed, 100 insertions(+), 43 deletions(-)
Applied with minor changes, see below.
diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index bb53e0850764..840ad4a6a6a6 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -20,12 +20,29 @@
#include <video/mipi_display.h>
+struct panel_desc {
- const struct drm_display_mode *modes;
I renamed this to "mode" because there is always only one.
- unsigned int bpc;
- struct {
unsigned int width;
unsigned int height;
- } size;
- unsigned long flags;
- enum mipi_dsi_pixel_format format;
- unsigned int lanes;
- const char * const *supply_names;
- unsigned int num_supplies;
+};
struct innolux_panel { struct drm_panel base; struct mipi_dsi_device *link;
const struct panel_desc *desc;
struct backlight_device *backlight;
- struct regulator *supply;
struct regulator_bulk_data *supplies;
unsigned int num_supplies; struct gpio_desc *enable_gpio;
bool prepared;
@@ -77,9 +94,7 @@ static int innolux_panel_unprepare(struct drm_panel *panel) /* T8: 80ms - 1000ms */ msleep(80);
- err = regulator_disable(innolux->supply);
- if (err < 0)
return err;
- regulator_bulk_disable(innolux->desc->num_supplies, innolux->supplies);
I kept the error check and return here.
Thierry
From: Lin Huang hl@rock-chips.com
When panel power down, p079zca need delay between reset and disable power supply, but p097pfg does not need it. Similarly p097zca needs a delay after entering panel sleep mode. So add two delay properties, so we can meet these two panel power down sequence.
Signed-off-by: Lin Huang hl@rock-chips.com [add sleep-mode delay] Signed-off-by: Heiko Stuebner heiko@sntech.de --- drivers/gpu/drm/panel/panel-innolux-p079zca.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index 840ad4a6a6a6..630b0c2549ef 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -33,6 +33,8 @@ struct panel_desc { unsigned int lanes; const char * const *supply_names; unsigned int num_supplies; + unsigned int sleep_mode_delay; + unsigned int power_down_delay; };
struct innolux_panel { @@ -89,10 +91,13 @@ static int innolux_panel_unprepare(struct drm_panel *panel) return err; }
+ if (innolux->desc->sleep_mode_delay) + msleep(innolux->desc->sleep_mode_delay); + gpiod_set_value_cansleep(innolux->enable_gpio, 0);
- /* T8: 80ms - 1000ms */ - msleep(80); + if (innolux->desc->power_down_delay) + msleep(innolux->desc->power_down_delay);
regulator_bulk_disable(innolux->desc->num_supplies, innolux->supplies);
@@ -205,6 +210,7 @@ static const struct panel_desc innolux_p079zca_panel_desc = { .lanes = 4, .supply_names = innolux_p079zca_supply_names, .num_supplies = ARRAY_SIZE(innolux_p079zca_supply_names), + .power_down_delay = 80, /* T8: 80ms - 1000ms */ };
static int innolux_panel_get_modes(struct drm_panel *panel)
On Mon, Jul 02, 2018 at 12:27:19PM +0200, Heiko Stuebner wrote:
From: Lin Huang hl@rock-chips.com
When panel power down, p079zca need delay between reset and disable power supply, but p097pfg does not need it. Similarly p097zca needs a delay after entering panel sleep mode. So add two delay properties, so we can meet these two panel power down sequence.
Signed-off-by: Lin Huang hl@rock-chips.com [add sleep-mode delay] Signed-off-by: Heiko Stuebner heiko@sntech.de
drivers/gpu/drm/panel/panel-innolux-p079zca.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
Applied, thanks.
Thierry
From: huang lin hl@rock-chips.com
The Innolux P097PFG panel is 9.7" panel with 1536X2048 resolution, it reuse P079ZCA panel driver, so improve p079ZCA dt-binding to support P097PFG.
Changes in v2: - None Changes in v3: - None Changes in v4: - None Changes in v5: - use separate file for binding - keep power supplies as required
Signed-off-by: Lin Huang hl@rock-chips.com Signed-off-by: Heiko Stuebner heiko@sntech.de --- .../display/panel/innolux,p097pfg.txt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/innolux,p097pfg.txt
diff --git a/Documentation/devicetree/bindings/display/panel/innolux,p097pfg.txt b/Documentation/devicetree/bindings/display/panel/innolux,p097pfg.txt new file mode 100644 index 000000000000..595d9dfeffd3 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/innolux,p097pfg.txt @@ -0,0 +1,24 @@ +Innolux P097PFG 9.7" 1536x2048 TFT LCD panel + +Required properties: +- compatible: should be "innolux,p097pfg" +- reg: DSI virtual channel of the peripheral +- avdd-supply: phandle of the regulator that provides positive voltage +- avee-supply: phandle of the regulator that provides negative voltage +- enable-gpios: panel enable gpio + +Optional properties: +- backlight: phandle of the backlight device attached to the panel + +Example: + + &mipi_dsi { + panel { + compatible = "innolux,p079zca"; + reg = <0>; + avdd-supply = <...>; + avee-supply = <...>; + backlight = <&backlight>; + enable-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + }; + };
On Mon, Jul 02, 2018 at 12:27:20PM +0200, Heiko Stuebner wrote:
From: huang lin hl@rock-chips.com
The Innolux P097PFG panel is 9.7" panel with 1536X2048 resolution, it reuse P079ZCA panel driver, so improve p079ZCA dt-binding to support P097PFG.
Changes in v2:
- None
Changes in v3:
- None
Changes in v4:
- None
Changes in v5:
- use separate file for binding
- keep power supplies as required
Signed-off-by: Lin Huang hl@rock-chips.com Signed-off-by: Heiko Stuebner heiko@sntech.de
.../display/panel/innolux,p097pfg.txt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/innolux,p097pfg.txt
Reviewed-by: Rob Herring robh@kernel.org
On Mon, Jul 02, 2018 at 12:27:20PM +0200, Heiko Stuebner wrote:
From: huang lin hl@rock-chips.com
The Innolux P097PFG panel is 9.7" panel with 1536X2048 resolution, it reuse P079ZCA panel driver, so improve p079ZCA dt-binding to support P097PFG.
Changes in v2:
- None
Changes in v3:
- None
Changes in v4:
- None
Changes in v5:
- use separate file for binding
- keep power supplies as required
Signed-off-by: Lin Huang hl@rock-chips.com Signed-off-by: Heiko Stuebner heiko@sntech.de
.../display/panel/innolux,p097pfg.txt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/innolux,p097pfg.txt
Applied, thanks.
Thierry
From: Lin Huang hl@rock-chips.com
Support Innolux P097PFG 9.7" 1536x2048 TFT LCD panel, it reuse the Innolux P079ZCA panel driver.
Changes in v2: - None Changes in v3: - None Changes in v4: - None Changes in v5: - Document source of init-commands - 4 lanes per DSI interface
Signed-off-by: Lin Huang hl@rock-chips.com Signed-off-by: Heiko Stuebner heiko@sntech.de --- drivers/gpu/drm/panel/panel-innolux-p079zca.c | 200 +++++++++++++++++- 1 file changed, 196 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index 630b0c2549ef..8d25b87bfbd6 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -20,6 +20,15 @@
#include <video/mipi_display.h>
+struct panel_init_cmd { + int len; + const char *data; +}; + +#define _INIT_CMD(...) { \ + .len = sizeof((char[]){__VA_ARGS__}), \ + .data = (char[]){__VA_ARGS__} } + struct panel_desc { const struct drm_display_mode *modes; unsigned int bpc; @@ -30,6 +39,7 @@ struct panel_desc {
unsigned long flags; enum mipi_dsi_pixel_format format; + const struct panel_init_cmd *init_cmds; unsigned int lanes; const char * const *supply_names; unsigned int num_supplies; @@ -121,13 +131,43 @@ static int innolux_panel_prepare(struct drm_panel *panel) if (err < 0) return err;
- /* T2: 15ms - 1000ms */ - usleep_range(15000, 16000); + /* p079zca: t2 (20ms), p097pfg: t4 (15ms) */ + usleep_range(20000, 21000);
gpiod_set_value_cansleep(innolux->enable_gpio, 1);
- /* T4: 15ms - 1000ms */ - usleep_range(15000, 16000); + /* p079zca: t4, p097pfg: t5 */ + usleep_range(20000, 21000); + + if (innolux->desc->init_cmds) { + const struct panel_init_cmd *cmds = + innolux->desc->init_cmds; + int i; + + for (i = 0; cmds[i].len != 0; i++) { + const struct panel_init_cmd *cmd = &cmds[i]; + + err = mipi_dsi_generic_write(innolux->link, cmd->data, + cmd->len); + if (err < 0) { + dev_err(panel->dev, + "failed to write command %d\n", i); + goto poweroff; + } + + /* + * Included by random guessing, because without this + * (or at least, some delay), the panel sometimes + * didn't appear to pick up the command sequence. + */ + err = mipi_dsi_dcs_nop(innolux->link); + if (err < 0) { + dev_err(panel->dev, + "failed to send DCS nop: %d\n", err); + goto poweroff; + } + } + }
err = mipi_dsi_dcs_exit_sleep_mode(innolux->link); if (err < 0) { @@ -213,6 +253,155 @@ static const struct panel_desc innolux_p079zca_panel_desc = { .power_down_delay = 80, /* T8: 80ms - 1000ms */ };
+static const char * const innolux_p097pfg_supply_names[] = { + "avdd", + "avee", +}; + +static const struct drm_display_mode innolux_p097pfg_mode = { + .clock = 229000, + .hdisplay = 1536, + .hsync_start = 1536 + 100, + .hsync_end = 1536 + 100 + 24, + .htotal = 1536 + 100 + 24 + 100, + .vdisplay = 2048, + .vsync_start = 2048 + 100, + .vsync_end = 2048 + 100 + 2, + .vtotal = 2048 + 100 + 2 + 18, + .vrefresh = 60, +}; + +/* + * Display manufacturer failed to provide init sequencing according to + * https://chromium-review.googlesource.com/c/chromiumos/third_party/coreboot/+... + * so the init sequence stems from a register dump of a working panel. + */ +static const struct panel_init_cmd innolux_p097pfg_init_cmds[] = { + /* page 0 */ + _INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x00), + _INIT_CMD(0xB1, 0xE8, 0x11), + _INIT_CMD(0xB2, 0x25, 0x02), + _INIT_CMD(0xB5, 0x08, 0x00), + _INIT_CMD(0xBC, 0x0F, 0x00), + _INIT_CMD(0xB8, 0x03, 0x06, 0x00, 0x00), + _INIT_CMD(0xBD, 0x01, 0x90, 0x14, 0x14), + _INIT_CMD(0x6F, 0x01), + _INIT_CMD(0xC0, 0x03), + _INIT_CMD(0x6F, 0x02), + _INIT_CMD(0xC1, 0x0D), + _INIT_CMD(0xD9, 0x01, 0x09, 0x70), + _INIT_CMD(0xC5, 0x12, 0x21, 0x00), + _INIT_CMD(0xBB, 0x93, 0x93), + + /* page 1 */ + _INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x01), + _INIT_CMD(0xB3, 0x3C, 0x3C), + _INIT_CMD(0xB4, 0x0F, 0x0F), + _INIT_CMD(0xB9, 0x45, 0x45), + _INIT_CMD(0xBA, 0x14, 0x14), + _INIT_CMD(0xCA, 0x02), + _INIT_CMD(0xCE, 0x04), + _INIT_CMD(0xC3, 0x9B, 0x9B), + _INIT_CMD(0xD8, 0xC0, 0x03), + _INIT_CMD(0xBC, 0x82, 0x01), + _INIT_CMD(0xBD, 0x9E, 0x01), + + /* page 2 */ + _INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x02), + _INIT_CMD(0xB0, 0x82), + _INIT_CMD(0xD1, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x82, 0x00, 0xA5, + 0x00, 0xC1, 0x00, 0xEA, 0x01, 0x0D, 0x01, 0x40), + _INIT_CMD(0xD2, 0x01, 0x6A, 0x01, 0xA8, 0x01, 0xDC, 0x02, 0x29, + 0x02, 0x67, 0x02, 0x68, 0x02, 0xA8, 0x02, 0xF0), + _INIT_CMD(0xD3, 0x03, 0x19, 0x03, 0x49, 0x03, 0x67, 0x03, 0x8C, + 0x03, 0xA6, 0x03, 0xC7, 0x03, 0xDE, 0x03, 0xEC), + _INIT_CMD(0xD4, 0x03, 0xFF, 0x03, 0xFF), + _INIT_CMD(0xE0, 0x00, 0x00, 0x00, 0x86, 0x00, 0xC5, 0x00, 0xE5, + 0x00, 0xFF, 0x01, 0x26, 0x01, 0x45, 0x01, 0x75), + _INIT_CMD(0xE1, 0x01, 0x9C, 0x01, 0xD5, 0x02, 0x05, 0x02, 0x4D, + 0x02, 0x86, 0x02, 0x87, 0x02, 0xC3, 0x03, 0x03), + _INIT_CMD(0xE2, 0x03, 0x2A, 0x03, 0x56, 0x03, 0x72, 0x03, 0x94, + 0x03, 0xAC, 0x03, 0xCB, 0x03, 0xE0, 0x03, 0xED), + _INIT_CMD(0xE3, 0x03, 0xFF, 0x03, 0xFF), + + /* page 3 */ + _INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x03), + _INIT_CMD(0xB0, 0x00, 0x00, 0x00, 0x00), + _INIT_CMD(0xB1, 0x00, 0x00, 0x00, 0x00), + _INIT_CMD(0xB2, 0x00, 0x00, 0x06, 0x04, 0x01, 0x40, 0x85), + _INIT_CMD(0xB3, 0x10, 0x07, 0xFC, 0x04, 0x01, 0x40, 0x80), + _INIT_CMD(0xB6, 0xF0, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, + 0x40, 0x80), + _INIT_CMD(0xBA, 0xC5, 0x07, 0x00, 0x04, 0x11, 0x25, 0x8C), + _INIT_CMD(0xBB, 0xC5, 0x07, 0x00, 0x03, 0x11, 0x25, 0x8C), + _INIT_CMD(0xC0, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x80, 0x80), + _INIT_CMD(0xC1, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x80, 0x80), + _INIT_CMD(0xC4, 0x00, 0x00), + _INIT_CMD(0xEF, 0x41), + + /* page 4 */ + _INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x04), + _INIT_CMD(0xEC, 0x4C), + + /* page 5 */ + _INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x05), + _INIT_CMD(0xB0, 0x13, 0x03, 0x03, 0x01), + _INIT_CMD(0xB1, 0x30, 0x00), + _INIT_CMD(0xB2, 0x02, 0x02, 0x00), + _INIT_CMD(0xB3, 0x82, 0x23, 0x82, 0x9D), + _INIT_CMD(0xB4, 0xC5, 0x75, 0x24, 0x57), + _INIT_CMD(0xB5, 0x00, 0xD4, 0x72, 0x11, 0x11, 0xAB, 0x0A), + _INIT_CMD(0xB6, 0x00, 0x00, 0xD5, 0x72, 0x24, 0x56), + _INIT_CMD(0xB7, 0x5C, 0xDC, 0x5C, 0x5C), + _INIT_CMD(0xB9, 0x0C, 0x00, 0x00, 0x01, 0x00), + _INIT_CMD(0xC0, 0x75, 0x11, 0x11, 0x54, 0x05), + _INIT_CMD(0xC6, 0x00, 0x00, 0x00, 0x00), + _INIT_CMD(0xD0, 0x00, 0x48, 0x08, 0x00, 0x00), + _INIT_CMD(0xD1, 0x00, 0x48, 0x09, 0x00, 0x00), + + /* page 6 */ + _INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x06), + _INIT_CMD(0xB0, 0x02, 0x32, 0x32, 0x08, 0x2F), + _INIT_CMD(0xB1, 0x2E, 0x15, 0x14, 0x13, 0x12), + _INIT_CMD(0xB2, 0x11, 0x10, 0x00, 0x3D, 0x3D), + _INIT_CMD(0xB3, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D), + _INIT_CMD(0xB4, 0x3D, 0x32), + _INIT_CMD(0xB5, 0x03, 0x32, 0x32, 0x09, 0x2F), + _INIT_CMD(0xB6, 0x2E, 0x1B, 0x1A, 0x19, 0x18), + _INIT_CMD(0xB7, 0x17, 0x16, 0x01, 0x3D, 0x3D), + _INIT_CMD(0xB8, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D), + _INIT_CMD(0xB9, 0x3D, 0x32), + _INIT_CMD(0xC0, 0x01, 0x32, 0x32, 0x09, 0x2F), + _INIT_CMD(0xC1, 0x2E, 0x1A, 0x1B, 0x16, 0x17), + _INIT_CMD(0xC2, 0x18, 0x19, 0x03, 0x3D, 0x3D), + _INIT_CMD(0xC3, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D), + _INIT_CMD(0xC4, 0x3D, 0x32), + _INIT_CMD(0xC5, 0x00, 0x32, 0x32, 0x08, 0x2F), + _INIT_CMD(0xC6, 0x2E, 0x14, 0x15, 0x10, 0x11), + _INIT_CMD(0xC7, 0x12, 0x13, 0x02, 0x3D, 0x3D), + _INIT_CMD(0xC8, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D), + _INIT_CMD(0xC9, 0x3D, 0x32), + + {}, +}; + +static const struct panel_desc innolux_p097pfg_panel_desc = { + .modes = &innolux_p097pfg_mode, + .bpc = 8, + .size = { + .width = 147, + .height = 196, + }, + .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_MODE_LPM, + .format = MIPI_DSI_FMT_RGB888, + .init_cmds = innolux_p097pfg_init_cmds, + .lanes = 4, + .supply_names = innolux_p097pfg_supply_names, + .num_supplies = ARRAY_SIZE(innolux_p097pfg_supply_names), + .sleep_mode_delay = 100, /* T15 */ +}; + static int innolux_panel_get_modes(struct drm_panel *panel) { struct drm_display_mode *mode; @@ -251,6 +440,9 @@ static const struct of_device_id innolux_of_match[] = { { .compatible = "innolux,p079zca", .data = &innolux_p079zca_panel_desc }, + { .compatible = "innolux,p097pfg", + .data = &innolux_p097pfg_panel_desc + }, { } }; MODULE_DEVICE_TABLE(of, innolux_of_match);
On Mon, Jul 02, 2018 at 12:27:21PM +0200, Heiko Stuebner wrote:
From: Lin Huang hl@rock-chips.com
Support Innolux P097PFG 9.7" 1536x2048 TFT LCD panel, it reuse the Innolux P079ZCA panel driver.
Changes in v2:
- None
Changes in v3:
- None
Changes in v4:
- None
Changes in v5:
- Document source of init-commands
- 4 lanes per DSI interface
Signed-off-by: Lin Huang hl@rock-chips.com Signed-off-by: Heiko Stuebner heiko@sntech.de
drivers/gpu/drm/panel/panel-innolux-p079zca.c | 200 +++++++++++++++++- 1 file changed, 196 insertions(+), 4 deletions(-)
Applied with two small changes, see below.
diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index 630b0c2549ef..8d25b87bfbd6 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -20,6 +20,15 @@
#include <video/mipi_display.h>
+struct panel_init_cmd {
- int len;
I changed the type here to size_t for consistency.
- const char *data;
+};
+#define _INIT_CMD(...) { \
- .len = sizeof((char[]){__VA_ARGS__}), \
- .data = (char[]){__VA_ARGS__} }
struct panel_desc { const struct drm_display_mode *modes; unsigned int bpc; @@ -30,6 +39,7 @@ struct panel_desc {
unsigned long flags; enum mipi_dsi_pixel_format format;
- const struct panel_init_cmd *init_cmds; unsigned int lanes; const char * const *supply_names; unsigned int num_supplies;
@@ -121,13 +131,43 @@ static int innolux_panel_prepare(struct drm_panel *panel) if (err < 0) return err;
- /* T2: 15ms - 1000ms */
- usleep_range(15000, 16000);
/* p079zca: t2 (20ms), p097pfg: t4 (15ms) */
usleep_range(20000, 21000);
gpiod_set_value_cansleep(innolux->enable_gpio, 1);
- /* T4: 15ms - 1000ms */
- usleep_range(15000, 16000);
- /* p079zca: t4, p097pfg: t5 */
- usleep_range(20000, 21000);
- if (innolux->desc->init_cmds) {
const struct panel_init_cmd *cmds =
innolux->desc->init_cmds;
int i;
I made this unsigned.
Thierry
dri-devel@lists.freedesktop.org