Message ID | 20190810231048.1921-5-laurent.pinchart@ideasonboard.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | DRM panel drivers for omapdrm | expand |
Sam, Den 11.08.2019 01.10, skrev Laurent Pinchart: > This panel is used on the Gumstix Overo Palo35. > > The code is based on the omapdrm-specific panel-lgphilips-lb035q02 > driver. > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > Reviewed-by: Sam Ravnborg <sam@ravnborg.org> > --- <snip> > diff --git a/drivers/gpu/drm/panel/panel-lg-lb035q02.c b/drivers/gpu/drm/panel/panel-lg-lb035q02.c <snip> > +static int lb035q02_write(struct lb035q02_device *lcd, u16 reg, u16 val) > +{ > + struct spi_message msg; > + struct spi_transfer index_xfer = { > + .len = 3, > + .cs_change = 1, > + }; > + struct spi_transfer value_xfer = { > + .len = 3, > + }; > + u8 buffer[16]; > + > + spi_message_init(&msg); > + > + /* register index */ > + buffer[0] = 0x70; > + buffer[1] = 0x00; > + buffer[2] = reg & 0x7f; > + index_xfer.tx_buf = buffer; > + spi_message_add_tail(&index_xfer, &msg); > + > + /* register value */ > + buffer[4] = 0x72; > + buffer[5] = val >> 8; > + buffer[6] = val; > + value_xfer.tx_buf = buffer + 4; > + spi_message_add_tail(&value_xfer, &msg); > + > + return spi_sync(lcd->spi, &msg); > +} Just a note to Sam: This is the same spi protocol that the ili9325 controller on the hy28b panel uses. I remembered that I have experimented with a regmap implementation: https://github.com/notro/tinydrm/blob/master/tinydrm-ili9325.c#L191 Noralf.
Hi Noralf, On Sun, Aug 11, 2019 at 03:19:13PM +0200, Noralf Trønnes wrote: > Sam, > > Den 11.08.2019 01.10, skrev Laurent Pinchart: > > This panel is used on the Gumstix Overo Palo35. > > > > The code is based on the omapdrm-specific panel-lgphilips-lb035q02 > > driver. > > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > > Reviewed-by: Sam Ravnborg <sam@ravnborg.org> > > --- > > <snip> > > > diff --git a/drivers/gpu/drm/panel/panel-lg-lb035q02.c b/drivers/gpu/drm/panel/panel-lg-lb035q02.c > > <snip> > > > +static int lb035q02_write(struct lb035q02_device *lcd, u16 reg, u16 val) > > +{ > > + struct spi_message msg; > > + struct spi_transfer index_xfer = { > > + .len = 3, > > + .cs_change = 1, > > + }; > > + struct spi_transfer value_xfer = { > > + .len = 3, > > + }; > > + u8 buffer[16]; > > + > > + spi_message_init(&msg); > > + > > + /* register index */ > > + buffer[0] = 0x70; > > + buffer[1] = 0x00; > > + buffer[2] = reg & 0x7f; > > + index_xfer.tx_buf = buffer; > > + spi_message_add_tail(&index_xfer, &msg); > > + > > + /* register value */ > > + buffer[4] = 0x72; > > + buffer[5] = val >> 8; > > + buffer[6] = val; > > + value_xfer.tx_buf = buffer + 4; > > + spi_message_add_tail(&value_xfer, &msg); > > + > > + return spi_sync(lcd->spi, &msg); > > +} > > Just a note to Sam: > This is the same spi protocol that the ili9325 controller on the hy28b > panel uses. > > I remembered that I have experimented with a regmap implementation: > https://github.com/notro/tinydrm/blob/master/tinydrm-ili9325.c#L191 That's useful information, thanks. The controller seems different though, the limited information available in https://www.beyondinfinite.com/lcd/Library/LG-Philips/LB035Q02.pdf doesn't match the registers from https://github.com/notro/tinydrm/blob/master/fb_ili9325.c.
Hi Laurent, Den 11.08.2019 15.35, skrev Laurent Pinchart: > Hi Noralf, > > On Sun, Aug 11, 2019 at 03:19:13PM +0200, Noralf Trønnes wrote: >> Sam, >> >> Den 11.08.2019 01.10, skrev Laurent Pinchart: >>> This panel is used on the Gumstix Overo Palo35. >>> >>> The code is based on the omapdrm-specific panel-lgphilips-lb035q02 >>> driver. >>> >>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> >>> Reviewed-by: Sam Ravnborg <sam@ravnborg.org> >>> --- >> >> <snip> >> >>> diff --git a/drivers/gpu/drm/panel/panel-lg-lb035q02.c b/drivers/gpu/drm/panel/panel-lg-lb035q02.c >> >> <snip> >> >>> +static int lb035q02_write(struct lb035q02_device *lcd, u16 reg, u16 val) >>> +{ >>> + struct spi_message msg; >>> + struct spi_transfer index_xfer = { >>> + .len = 3, >>> + .cs_change = 1, >>> + }; >>> + struct spi_transfer value_xfer = { >>> + .len = 3, >>> + }; >>> + u8 buffer[16]; >>> + >>> + spi_message_init(&msg); >>> + >>> + /* register index */ >>> + buffer[0] = 0x70; >>> + buffer[1] = 0x00; >>> + buffer[2] = reg & 0x7f; >>> + index_xfer.tx_buf = buffer; >>> + spi_message_add_tail(&index_xfer, &msg); >>> + >>> + /* register value */ >>> + buffer[4] = 0x72; >>> + buffer[5] = val >> 8; >>> + buffer[6] = val; >>> + value_xfer.tx_buf = buffer + 4; >>> + spi_message_add_tail(&value_xfer, &msg); >>> + >>> + return spi_sync(lcd->spi, &msg); >>> +} >> >> Just a note to Sam: >> This is the same spi protocol that the ili9325 controller on the hy28b >> panel uses. >> >> I remembered that I have experimented with a regmap implementation: >> https://github.com/notro/tinydrm/blob/master/tinydrm-ili9325.c#L191 > > That's useful information, thanks. The controller seems different > though, the limited information available in > https://www.beyondinfinite.com/lcd/Library/LG-Philips/LB035Q02.pdf > doesn't match the registers from > https://github.com/notro/tinydrm/blob/master/fb_ili9325.c. > I've seen several controllers that use this protocol, and one that had a different start byte configuration. The details unfortunately is lost in time, it's been a while since I worked on these controllers. Noralf.
Hi Noralf. > > +static int lb035q02_write(struct lb035q02_device *lcd, u16 reg, u16 val) > > +{ > > + struct spi_message msg; > > + struct spi_transfer index_xfer = { > > + .len = 3, > > + .cs_change = 1, > > + }; > > + struct spi_transfer value_xfer = { > > + .len = 3, > > + }; > > + u8 buffer[16]; > > + > > + spi_message_init(&msg); > > + > > + /* register index */ > > + buffer[0] = 0x70; > > + buffer[1] = 0x00; > > + buffer[2] = reg & 0x7f; > > + index_xfer.tx_buf = buffer; > > + spi_message_add_tail(&index_xfer, &msg); > > + > > + /* register value */ > > + buffer[4] = 0x72; > > + buffer[5] = val >> 8; > > + buffer[6] = val; > > + value_xfer.tx_buf = buffer + 4; > > + spi_message_add_tail(&value_xfer, &msg); > > + > > + return spi_sync(lcd->spi, &msg); > > +} > > Just a note to Sam: > This is the same spi protocol that the ili9325 controller on the hy28b > panel uses. > > I remembered that I have experimented with a regmap implementation: > https://github.com/notro/tinydrm/blob/master/tinydrm-ili9325.c#L191 regmap seems a too limited interface to use when trying to generalize this. We should rather go for a ili-lib or something that can be used in all the present and future ili based drivers. Obviously it depends on how similar the ili based chips are. I did a quick look and this driver did not match the ili9325 datasheet as different bits was are in register 0x1. So it smeels like another, similar. ili varaint. So for this driver we would just use the hardcoded varaint already present. Then we may re-visit ili-lib idea later if we see too much similar code. This is as I see it for now... Sam
Den 11.08.2019 18.49, skrev Sam Ravnborg: > Hi Noralf. > >>> +static int lb035q02_write(struct lb035q02_device *lcd, u16 reg, u16 val) >>> +{ >>> + struct spi_message msg; >>> + struct spi_transfer index_xfer = { >>> + .len = 3, >>> + .cs_change = 1, >>> + }; >>> + struct spi_transfer value_xfer = { >>> + .len = 3, >>> + }; >>> + u8 buffer[16]; >>> + >>> + spi_message_init(&msg); >>> + >>> + /* register index */ >>> + buffer[0] = 0x70; >>> + buffer[1] = 0x00; >>> + buffer[2] = reg & 0x7f; >>> + index_xfer.tx_buf = buffer; >>> + spi_message_add_tail(&index_xfer, &msg); >>> + >>> + /* register value */ >>> + buffer[4] = 0x72; >>> + buffer[5] = val >> 8; >>> + buffer[6] = val; >>> + value_xfer.tx_buf = buffer + 4; >>> + spi_message_add_tail(&value_xfer, &msg); >>> + >>> + return spi_sync(lcd->spi, &msg); >>> +} >> >> Just a note to Sam: >> This is the same spi protocol that the ili9325 controller on the hy28b >> panel uses. >> >> I remembered that I have experimented with a regmap implementation: >> https://github.com/notro/tinydrm/blob/master/tinydrm-ili9325.c#L191 > > regmap seems a too limited interface to use when trying to generalize > this. > We should rather go for a ili-lib or something that can be used in all > the present and future ili based drivers. > Obviously it depends on how similar the ili based chips are. > > I did a quick look and this driver did not match the ili9325 datasheet > as different bits was are in register 0x1. > So it smeels like another, similar. ili varaint. > > So for this driver we would just use the hardcoded varaint already > present. Then we may re-visit ili-lib idea later if we see too much > similar code. > This is as I see it for now... > Yeah, no point in changing this driver until there are more similar controllers. Just wanted to point out that the hy28b panel you ordered uses the same protocol. A web search helped my memory, these 3 supported by staging/fbtft use a startbyte: ili9320, ili9325 and hx8347d. The ili chips are almost identical. The search revealed many more including: st7793, st7781r, S6E63D6, +many ilitek. Noralf.
Hi Noralf. > >>> + /* register value */ > >>> + buffer[4] = 0x72; > >>> + buffer[5] = val >> 8; > >>> + buffer[6] = val; > >>> + value_xfer.tx_buf = buffer + 4; > >>> + spi_message_add_tail(&value_xfer, &msg); > >>> + > >>> + return spi_sync(lcd->spi, &msg); > >>> +} > >> > >> Just a note to Sam: > >> This is the same spi protocol that the ili9325 controller on the hy28b > >> panel uses. > >> > >> I remembered that I have experimented with a regmap implementation: > >> https://github.com/notro/tinydrm/blob/master/tinydrm-ili9325.c#L191 > > > > regmap seems a too limited interface to use when trying to generalize > > this. > > We should rather go for a ili-lib or something that can be used in all > > the present and future ili based drivers. > > Obviously it depends on how similar the ili based chips are. > > > > I did a quick look and this driver did not match the ili9325 datasheet > > as different bits was are in register 0x1. > > So it smeels like another, similar. ili varaint. > > > > So for this driver we would just use the hardcoded varaint already > > present. Then we may re-visit ili-lib idea later if we see too much > > similar code. > > This is as I see it for now... > > > > Yeah, no point in changing this driver until there are more similar > controllers. Just wanted to point out that the hy28b panel you ordered > uses the same protocol. Ohh, that was your point. Thanks! The display is still in the mail somewhere from China.. Right now I do not have time to hack on it so not a big deal. For the fun of it I ordred a few other displays. One was ssd1306 based, and do not recall the rest. Sam > > A web search helped my memory, these 3 supported by staging/fbtft use a > startbyte: ili9320, ili9325 and hx8347d. The ili chips are almost > identical. The search revealed many more including: > st7793, st7781r, S6E63D6, +many ilitek. > > Noralf.
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index eaecd40cc32e..87cdc330672b 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -103,6 +103,14 @@ config DRM_PANEL_SAMSUNG_LD9040 depends on OF && SPI select VIDEOMODE_HELPERS +config DRM_PANEL_LG_LB035Q02 + tristate "LG LB035Q024573 RGB panel" + depends on GPIOLIB && OF && SPI + help + Say Y here if you want to enable support for the LB035Q02 RGB panel + (found on the Gumstix Overo Palo35 board). To compile this driver as + a module, choose M here. + config DRM_PANEL_LG_LG4573 tristate "LG4573 RGB/SPI panel" depends on OF && SPI diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 62dae45f8f74..9bc6f3c5bf96 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o obj-$(CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04) += panel-kingdisplay-kd097d04.o +obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o obj-$(CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO) += panel-olimex-lcd-olinuxino.o diff --git a/drivers/gpu/drm/panel/panel-lg-lb035q02.c b/drivers/gpu/drm/panel/panel-lg-lb035q02.c new file mode 100644 index 000000000000..694489fb91b2 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-lg-lb035q02.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * LG.Philips LB035Q02 LCD Panel Driver + * + * Copyright (C) 2019 Texas Instruments Incorporated + * + * Based on the omapdrm-specific panel-lgphilips-lb035q02 driver + * + * Copyright (C) 2013 Texas Instruments Incorporated + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + * + * Based on a driver by: Steve Sakoman <steve@sakoman.com> + */ + +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/spi/spi.h> + +#include <drm/drm_connector.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> + +struct lb035q02_device { + struct drm_panel panel; + + struct spi_device *spi; + struct gpio_desc *enable_gpio; +}; + +#define to_lb035q02_device(p) container_of(p, struct lb035q02_device, panel) + +static int lb035q02_write(struct lb035q02_device *lcd, u16 reg, u16 val) +{ + struct spi_message msg; + struct spi_transfer index_xfer = { + .len = 3, + .cs_change = 1, + }; + struct spi_transfer value_xfer = { + .len = 3, + }; + u8 buffer[16]; + + spi_message_init(&msg); + + /* register index */ + buffer[0] = 0x70; + buffer[1] = 0x00; + buffer[2] = reg & 0x7f; + index_xfer.tx_buf = buffer; + spi_message_add_tail(&index_xfer, &msg); + + /* register value */ + buffer[4] = 0x72; + buffer[5] = val >> 8; + buffer[6] = val; + value_xfer.tx_buf = buffer + 4; + spi_message_add_tail(&value_xfer, &msg); + + return spi_sync(lcd->spi, &msg); +} + +static int lb035q02_init(struct lb035q02_device *lcd) +{ + /* Init sequence from page 28 of the lb035q02 spec. */ + static const struct { + u16 index; + u16 value; + } init_data[] = { + { 0x01, 0x6300 }, + { 0x02, 0x0200 }, + { 0x03, 0x0177 }, + { 0x04, 0x04c7 }, + { 0x05, 0xffc0 }, + { 0x06, 0xe806 }, + { 0x0a, 0x4008 }, + { 0x0b, 0x0000 }, + { 0x0d, 0x0030 }, + { 0x0e, 0x2800 }, + { 0x0f, 0x0000 }, + { 0x16, 0x9f80 }, + { 0x17, 0x0a0f }, + { 0x1e, 0x00c1 }, + { 0x30, 0x0300 }, + { 0x31, 0x0007 }, + { 0x32, 0x0000 }, + { 0x33, 0x0000 }, + { 0x34, 0x0707 }, + { 0x35, 0x0004 }, + { 0x36, 0x0302 }, + { 0x37, 0x0202 }, + { 0x3a, 0x0a0d }, + { 0x3b, 0x0806 }, + }; + + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(init_data); ++i) { + ret = lb035q02_write(lcd, init_data[i].index, + init_data[i].value); + if (ret < 0) + return ret; + } + + return 0; +} + +static int lb035q02_disable(struct drm_panel *panel) +{ + struct lb035q02_device *lcd = to_lb035q02_device(panel); + + gpiod_set_value_cansleep(lcd->enable_gpio, 0); + + return 0; +} + +static int lb035q02_enable(struct drm_panel *panel) +{ + struct lb035q02_device *lcd = to_lb035q02_device(panel); + + gpiod_set_value_cansleep(lcd->enable_gpio, 1); + + return 0; +} + +static const struct drm_display_mode lb035q02_mode = { + .clock = 6500, + .hdisplay = 320, + .hsync_start = 320 + 20, + .hsync_end = 320 + 20 + 2, + .htotal = 320 + 20 + 2 + 68, + .vdisplay = 240, + .vsync_start = 240 + 4, + .vsync_end = 240 + 4 + 2, + .vtotal = 240 + 4 + 2 + 18, + .vrefresh = 60, + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + .width_mm = 70, + .height_mm = 53, +}; + +static int lb035q02_get_modes(struct drm_panel *panel) +{ + struct drm_connector *connector = panel->connector; + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(panel->drm, &lb035q02_mode); + if (!mode) + return -ENOMEM; + + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + + connector->display_info.width_mm = lb035q02_mode.width_mm; + connector->display_info.height_mm = lb035q02_mode.height_mm; + /* + * FIXME: According to the datasheet pixel data is sampled on the + * rising edge of the clock, but the code running on the Gumstix Overo + * Palo35 indicates sampling on the negative edge. This should be + * tested on a real device. + */ + connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH + | DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE + | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE; + + return 1; +} + +static const struct drm_panel_funcs lb035q02_funcs = { + .disable = lb035q02_disable, + .enable = lb035q02_enable, + .get_modes = lb035q02_get_modes, +}; + +static int lb035q02_probe(struct spi_device *spi) +{ + struct lb035q02_device *lcd; + int ret; + + lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL); + if (lcd == NULL) + return -ENOMEM; + + spi_set_drvdata(spi, lcd); + lcd->spi = spi; + + lcd->enable_gpio = devm_gpiod_get(&spi->dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(lcd->enable_gpio)) { + dev_err(&spi->dev, "failed to parse enable gpio\n"); + return PTR_ERR(lcd->enable_gpio); + } + + ret = lb035q02_init(lcd); + if (ret < 0) + return ret; + + drm_panel_init(&lcd->panel); + lcd->panel.dev = &lcd->spi->dev; + lcd->panel.funcs = &lb035q02_funcs; + + return drm_panel_add(&lcd->panel); +} + +static int lb035q02_remove(struct spi_device *spi) +{ + struct lb035q02_device *lcd = spi_get_drvdata(spi); + + drm_panel_remove(&lcd->panel); + drm_panel_disable(&lcd->panel); + + return 0; +} + +static const struct of_device_id lb035q02_of_match[] = { + { .compatible = "lgphilips,lb035q02", }, + { /* sentinel */ }, +}; + +MODULE_DEVICE_TABLE(of, lb035q02_of_match); + +static struct spi_driver lb035q02_driver = { + .probe = lb035q02_probe, + .remove = lb035q02_remove, + .driver = { + .name = "panel-lg-lb035q02", + .of_match_table = lb035q02_of_match, + }, +}; + +module_spi_driver(lb035q02_driver); + +MODULE_ALIAS("spi:lgphilips,lb035q02"); +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); +MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver"); +MODULE_LICENSE("GPL");