Hi Tomi, 4.20-rc1 seems to have broken our DSI panel driver for the OMAP5 Pyra.
There were some omapdrm API changes which I have tried to fix by inspecting panel-dsi-cm.c and also comparing with panel-tpo-td028ttec1.c (which is still working on our omap3 systems).
I also have removed the tiler patches by Matthijs to make sure that they do not have an influence.
The strange thing is that the panel driver module is not even loaded during probe time, although we have defined the display alias and not changed that compared to 4.19.
The other strange thing is that I can manually modprobe the driver and it is loaded and shown by lsmod, but the probe function is never called.
Any hints what we should check and compare and verify?
Did the panel driver compatible matching mechanism change for dsi panels without a bus (dpi panels are usually loaded as an spi bus client)?
Or is there a new CONFIG that must be set?
BR and thanks, Nikolaus
Hi,
* H. Nikolaus Schaller hns@goldelico.com [181112 17:35]:
Hi Tomi, 4.20-rc1 seems to have broken our DSI panel driver for the OMAP5 Pyra.
There were some omapdrm API changes which I have tried to fix by inspecting panel-dsi-cm.c and also comparing with panel-tpo-td028ttec1.c (which is still working on our omap3 systems).
I also have removed the tiler patches by Matthijs to make sure that they do not have an influence.
The strange thing is that the panel driver module is not even loaded during probe time, although we have defined the display alias and not changed that compared to 4.19.
The other strange thing is that I can manually modprobe the driver and it is loaded and shown by lsmod, but the probe function is never called.
Any hints what we should check and compare and verify?
Did the panel driver compatible matching mechanism change for dsi panels without a bus (dpi panels are usually loaded as an spi bus client)?
Or is there a new CONFIG that must be set?
Test with Laurent's fixes, and also looks like Sebastian also has some recent yet to be posted probe order changes on top of Laurent's fixes in his linux-n900 branch if Laurent's fixes alone don't help.
Laurent's patches are "[PATCH v3 0/4] omapdrm: Fix runtime PM issues at module load and unload time" and Sebastian's git tree is at https://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-n900.git/.
Adding Laurent and Sebastian to Cc too.
Regards,
Tony
Hi,
On Mon, Nov 12, 2018 at 10:30:21AM -0800, Tony Lindgren wrote:
- H. Nikolaus Schaller hns@goldelico.com [181112 17:35]:
4.20-rc1 seems to have broken our DSI panel driver for the OMAP5 Pyra.
Laurent's patches break omapdrm DSI support in 4.20 (please read as factual information, not as insult). Note, that there is no working mainline user of this, so technically its not a user visible regression.
There were some omapdrm API changes which I have tried to fix by inspecting panel-dsi-cm.c and also comparing with panel-tpo-td028ttec1.c (which is still working on our omap3 systems).
I also have removed the tiler patches by Matthijs to make sure that they do not have an influence.
The strange thing is that the panel driver module is not even loaded during probe time, although we have defined the display alias and not changed that compared to 4.19.
The other strange thing is that I can manually modprobe the driver and it is loaded and shown by lsmod, but the probe function is never called.
Any hints what we should check and compare and verify?
This is probably fixed by the following patch, which depends on Laurent's fixes from a few days ago:
https://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-n900.git/commit/?h...
Did the panel driver compatible matching mechanism change for dsi panels without a bus (dpi panels are usually loaded as an spi bus client)?
Or is there a new CONFIG that must be set?
Test with Laurent's fixes, and also looks like Sebastian also has some recent yet to be posted probe order changes on top of Laurent's fixes in his linux-n900 branch if Laurent's fixes alone don't help.
Laurent's patches are "[PATCH v3 0/4] omapdrm: Fix runtime PM issues at module load and unload time" and Sebastian's git tree is at https://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-n900.git/.
Adding Laurent and Sebastian to Cc too.
I hope to send a new patchset fixing panel-dsi-cm support in omapdrm later this week (assuming, that nothing unexpected happens).
-- Sebastian
Hi,
Am 12.11.2018 um 21:09 schrieb Sebastian Reichel sre@kernel.org:
Hi,
On Mon, Nov 12, 2018 at 10:30:21AM -0800, Tony Lindgren wrote:
- H. Nikolaus Schaller hns@goldelico.com [181112 17:35]:
4.20-rc1 seems to have broken our DSI panel driver for the OMAP5 Pyra.
Laurent's patches break omapdrm DSI support in 4.20 (please read as factual information, not as insult). Note, that there is no working mainline user of this, so technically its not a user visible regression.
Yes, but makes the same problems for out-of tree users :)
The Pyra is planned to become a mainline user of omapdrm.dsi, but as long as there are only a handful of prototypes it is still too early to submit (parts of) the DTS. I think it is more convincing for inclusion if mass production of the hardware is already running.
There were some omapdrm API changes which I have tried to fix by inspecting panel-dsi-cm.c and also comparing with panel-tpo-td028ttec1.c (which is still working on our omap3 systems).
I also have removed the tiler patches by Matthijs to make sure that they do not have an influence.
The strange thing is that the panel driver module is not even loaded during probe time, although we have defined the display alias and not changed that compared to 4.19.
The other strange thing is that I can manually modprobe the driver and it is loaded and shown by lsmod, but the probe function is never called.
Any hints what we should check and compare and verify?
This is probably fixed by the following patch, which depends on Laurent's fixes from a few days ago:
https://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-n900.git/commit/?h...
Yeah! It now loads the panel driver again:
[ 10.344961] dsi: w677l_probe_of() [ 10.628239] dsi: w677l_connect()
The latter ends in a NULL pointer deref, but this is obviously my fault when converting the driver to the new API. And I can debug that now.
Did the panel driver compatible matching mechanism change for dsi panels without a bus (dpi panels are usually loaded as an spi bus client)?
Or is there a new CONFIG that must be set?
Test with Laurent's fixes, and also looks like Sebastian also has some recent yet to be posted probe order changes on top of Laurent's fixes in his linux-n900 branch if Laurent's fixes alone don't help.
Laurent's patches are "[PATCH v3 0/4] omapdrm: Fix runtime PM issues at module load and unload time" and Sebastian's git tree is at https://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-n900.git/.
Adding Laurent and Sebastian to Cc too.
I hope to send a new patchset fixing panel-dsi-cm support in omapdrm later this week (assuming, that nothing unexpected happens).
-- Sebastian
BR and thanks, Nikolaus
Hi Tomi,
Am 12.11.2018 um 19:30 schrieb Tony Lindgren tony@atomide.com:
Hi,
- H. Nikolaus Schaller hns@goldelico.com [181112 17:35]:
Hi Tomi, 4.20-rc1 seems to have broken our DSI panel driver for the OMAP5 Pyra.
There were some omapdrm API changes which I have tried to fix by inspecting panel-dsi-cm.c and also comparing with panel-tpo-td028ttec1.c (which is still working on our omap3 systems).
I also have removed the tiler patches by Matthijs to make sure that they do not have an influence.
The strange thing is that the panel driver module is not even loaded during probe time, although we have defined the display alias and not changed that compared to 4.19.
The other strange thing is that I can manually modprobe the driver and it is loaded and shown by lsmod, but the probe function is never called.
Any hints what we should check and compare and verify?
Did the panel driver compatible matching mechanism change for dsi panels without a bus (dpi panels are usually loaded as an spi bus client)?
Or is there a new CONFIG that must be set?
Test with Laurent's fixes, and also looks like Sebastian also has some recent yet to be posted probe order changes on top of Laurent's fixes in his linux-n900 branch if Laurent's fixes alone don't help.
Laurent's patches are "[PATCH v3 0/4] omapdrm: Fix runtime PM issues at module load and unload time" and Sebastian's git tree is at https://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-n900.git/.
Adding Laurent and Sebastian to Cc too.
now as the panel-dpi device is working and Sebastian's patches make our Pyra dsi panel at least load successfully, I have tried to find out more what is going on.
It does probe(), connect() but seems to never call enable(). Hence it is not sending any dcs commands to the panel.
Attached is the current driver code and a boot log. Device tree is the same as for earlier kernels, so it should be ok (unless dsi panels need to define new properties).
Maybe you can spot something which is missing. For example, I have seen that there is a new propery dssdev->caps which we did not touch before.
Or there is .suppress_bind_attrs = true which I simply did copy from panel-dsi-cm.c assuming it is needed for all dsi panels.
It is probably just one or two lines which are missing...
BR and thanks, Nikolaus
diff --git a/drivers/gpu/drm/omapdrm/displays/panel-boe-w677l.c b/drivers/gpu/drm/omapdrm/displays/panel-boe-w677l.c new file mode 100644 index 000000000000..39c93fdf957e --- /dev/null +++ b/drivers/gpu/drm/omapdrm/displays/panel-boe-w677l.c +/* + * Driver for BOE W677L panel with Orise OTM1283A controller + * + * Copyright 2011 Texas Instruments, Inc. + * Author: Archit Taneja archit@ti.com + * Author: Tomi Valkeinen tomi.valkeinen@ti.com + * + * based on d2l panel driver by Jerry Alexander x0135174@ti.com + * + * Copyright (C) 2014-2018 Golden Delicious Computers + * by H. Nikolaus Schaller hns@goldelico.com + * based on lg4591 panel driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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/. + */ + +#define LOG 1 +#define OPTIONAL 0 + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/sched/signal.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> + +#include <video/mipi_display.h> +#include <video/of_display_timing.h> + +#include "../dss/omapdss.h" + +#undef dev_dbg +#define dev_dbg dev_err + +/* extended DCS commands (not defined in mipi_display.h) */ +#define DCS_READ_DDB_START 0x02 +#define DCS_READ_NUM_ERRORS 0x05 +#define DCS_BRIGHTNESS 0x51 /* write brightness */ +#define DCS_READ_BRIGHTNESS 0x52 /* read brightness */ +#define DCS_CTRL_DISPLAY 0x53 /* enable backlight etc. */ +#define DCS_READ_CTRL_DISPLAY 0x53 /* read control */ +#define DCS_WRITE_CABC 0x55 +#define DCS_READ_CABC 0x56 +#define MCS_READID1 0xda +#define MCS_READID2 0xdb +#define MCS_READID3 0xdc + +/* DCS NOP is 1-byte 0x00 while 2-byte 0x00 is address shift function of this controller */ + +#define IS_MCS(CMD, LEN) ((((CMD) == 0) && (LEN) == 2) || (((CMD) >= 0xb0 && (CMD) < 0xda)) || ((CMD) > 0xdc)) + +/* horizontal * vertical * refresh */ +#define w677l_W (720) +#define w677l_H (1280) +#define w677l_WIDTH (w677l_W+80+88) +#define w677l_HEIGHT (w677l_H+160) +#define w677l_FPS (60ll) +#define w677l_PIXELCLOCK (w677l_WIDTH * w677l_HEIGHT * w677l_FPS) /* Full HD * 60 fps */ +/* panel has 16.7M colors = RGB888 = 3*8 bit per pixel */ +#define w677l_PIXELFORMAT OMAP_DSS_DSI_FMT_RGB888 /* 16.7M color = RGB888 */ +#define w677l_BIT_PER_PIXEL (3*8) +/* the panel can handle 4 lanes */ +#define w677l_LANES 4 +/* high speed clock is running at double data rate, i.e. half speed + * (take care of integer overflows!) + * hsck = bit/pixel * 110% * pixel clock / lanes / 2 clock edges + * real clock rate may be rounded up or down depending on divisors + */ +#define w677l_HS_CLOCK (w677l_BIT_PER_PIXEL * (w677l_PIXELCLOCK / (w677l_LANES * 2))) +/* low power clock is quite arbitrarily choosen to be roughly 10 MHz */ +#define w677l_LP_CLOCK 9200000 /* low power clock */ + +static struct videomode w677l_timings = { + .hactive = w677l_W, + .vactive = w677l_H, + .pixelclock = w677l_PIXELCLOCK, + .hfront_porch = 5, + .hsync_len = 5, + .hback_porch = w677l_WIDTH-w677l_W-5-5, + .vfront_porch = 50, + .vsync_len = w677l_HEIGHT-w677l_H-50-50, + .vback_porch = 50, +}; + +struct panel_drv_data { + struct omap_dss_device dssdev; + + struct videomode vm; + + struct platform_device *pdev; + + struct mutex lock; + + int bl; + + int reset_gpio; + int regulator_gpio; + + struct omap_dsi_pin_config pin_config; + + bool enabled; + + int config_channel; + int pixel_channel; + +}; + +#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) + +struct w677l_reg { + u8 data[20]; /* data[0] is length, data[1] is first byte */ +}; + +static struct w677l_reg init_seq[] = { + { 2, 0x00, 0x00, }, + { 4, 0xff, 0x12, 0x83, 0x01, }, //EXTC=1 + { 2, 0x00, 0x80, }, //Orise mode enable + { 3, 0xff, 0x12, 0x83, }, + + +//-------------------- panel setting --------------------// + { 2, 0x00, 0x80, }, //TCON Setting + { 10, 0xc0, 0x00, 0x64, 0x00, 0x0f, 0x11, 0x00, 0x64, 0x0f, 0x11, }, + + { 2, 0x00, 0x90, }, //Panel Timing Setting + { 7, 0xc0, 0x00, 0x5c, 0x00, 0x01, 0x00, 0x04, }, + + { 2, 0x00, 0x87, }, + { 2, 0xc4, 0x18, }, + + { 2, 0x00, 0xb3, }, //Interval Scan Frame: 0 frame, column inversion + { 3, 0xc0, 0x00, 0x50, }, + + { 2, 0x00, 0x81, }, //frame rate:60Hz + { 2, 0xc1, 0x66, }, + + { 2, 0x00, 0x81, }, + { 3, 0xc4, 0x82, 0x02, }, + + { 2, 0x00, 0x90, }, + { 2, 0xc4, 0x49, }, + + { 2, 0x00, 0xc6, }, + { 2, 0xb0, 0x03, }, + + { 2, 0x00, 0x90, }, //Mode-3 + { 5, 0xf5, 0x02, 0x11, 0x02, 0x11, }, + + { 2, 0x00, 0x90, }, //2xVPNL, 1.5*=00, 2*=50, 3*=a0 + { 2, 0xc5, 0x50, }, + + { 2, 0x00, 0x94, }, //Frequency + { 2, 0xc5, 0x66, }, + + { 2, 0x00, 0xb2, }, //VGLO1 setting + { 3, 0xf5, 0x00, 0x00, }, + + { 2, 0x00, 0xb4, }, //VGLO1_S setting + { 3, 0xf5, 0x00, 0x00, }, + + { 2, 0x00, 0xb6, }, //VGLO2 setting + { 3, 0xf5, 0x00, 0x00, }, + + { 2, 0x00, 0xb8, }, //VGLO2_S setting + { 3, 0xf5, 0x00, 0x00, }, + + { 2, 0x00, 0x94, }, //VCL ON + { 2, 0xf5, 0x02, }, + + { 2, 0x00, 0xBA, }, //VSP ON + { 2, 0xf5, 0x03, }, + + { 2, 0x00, 0xb2, }, //VGHO Option + { 2, 0xc5, 0x40, }, + + { 2, 0x00, 0xb4, }, //VGLO Option + { 2, 0xc5, 0xC0, }, + +//-------------------- power setting --------------------// + { 2, 0x00, 0xa0, }, //dcdc setting + { 15, 0xc4, 0x05, 0x10, 0x06, 0x02, 0x05, 0x15, 0x10, 0x05, 0x10, 0x07, 0x02, 0x05, 0x15, 0x10, }, + + { 2, 0x00, 0xb0, }, //clamp voltage setting + { 3, 0xc4, 0x00, 0x00, }, + + { 2, 0x00, 0x91, }, //VGH=13V, VGL=-12V, pump ratio:VGH=6x, VGL=-5x + { 3, 0xc5, 0x19, 0x50, }, + + { 2, 0x00, 0x00, }, //GVDD=4.87V, NGVDD=-4.87V + { 3, 0xd8, 0xbc, 0xbc, }, + + { 2, 0x00, 0x00, }, //VCOMDC=-1.1 + { 2, 0xd9, 0x5a, }, //5d 6f + + { 2, 0x00, 0x00, }, + { 17, 0xE1, 0x01, 0x07, 0x0b, 0x0d, 0x06, 0x0d, 0x0b, 0x0a, 0x04, 0x07, 0x10, 0x08, 0x0f, 0x11, 0x0a, 0x01, }, + + { 2, 0x00, 0x00, }, + { 17, 0xE2, 0x01, 0x07, 0x0b, 0x0d, 0x06, 0x0d, 0x0b, 0x0a, 0x04, 0x07, 0x10, 0x08, 0x0f, 0x11, 0x0a, 0x01, }, + + { 2, 0x00, 0xb0, }, //VDD_18V=1.7V, LVDSVDD=1.55V + { 3, 0xc5, 0x04, 0xB8, }, + + { 2, 0x00, 0xbb, }, //LVD voltage level setting + { 2, 0xc5, 0x80, }, + +//-------------------- panel timing state control --------------------// + { 2, 0x00, 0x80, }, //panel timing state control + { 12, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0x90, }, //panel timing state control + { 16, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xa0, }, //panel timing state control + { 16, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xb0, }, //panel timing state control + { 16, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xc0, }, //panel timing state control + { 16, 0xcb, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xd0, }, //panel timing state control + { 16, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x00, 0x00, }, + + { 2, 0x00, 0xe0, }, //panel timing state control + { 15, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x05, }, + + { 2, 0x00, 0xf0, }, //panel timing state control + { 12, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }, + +//-------------------- panel pad mapping control --------------------// + { 2, 0x00, 0x80, }, //panel pad mapping control + { 16, 0xcc, 0x0a, 0x0c, 0x0e, 0x10, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0x90, }, //panel pad mapping control + { 16, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x2d, 0x09, 0x0b, 0x0d, 0x0f, 0x01, 0x03, 0x00, 0x00, }, + + { 2, 0x00, 0xa0, }, //panel pad mapping control + { 15, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x2d, }, + + { 2, 0x00, 0xb0, }, //panel pad mapping control + { 16, 0xcc, 0x0F, 0x0D, 0x0B, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xc0, }, //panel pad mapping control + { 16, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x2e, 0x10, 0x0E, 0x0C, 0x0A, 0x04, 0x02, 0x00, 0x00, }, + + { 2, 0x00, 0xd0, }, //panel pad mapping control + { 15, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x2e, }, + +//-------------------- panel timing setting --------------------// + { 2, 0x00, 0x80, }, //panel VST setting + { 13, 0xce, 0x8D, 0x03, 0x00, 0x8C, 0x03, 0x00, 0x8B, 0x03, 0x00, 0x8A, 0x03, 0x00, }, + + { 2, 0x00, 0x90, }, //panel VEND setting + { 15, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xa0, }, //panel CLKA1/2 setting + { 15, 0xce, 0x38, 0x0B, 0x04, 0xFC, 0x00, 0x00, 0x00, 0x38, 0x0A, 0x04, 0xFD, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xb0, }, //panel CLKA3/4 setting + { 15, 0xce, 0x38, 0x09, 0x04, 0xFE, 0x00, 0x00, 0x00, 0x38, 0x08, 0x04, 0xFF, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xc0, }, //panel CLKb1/2 setting + { 15, 0xce, 0x38, 0x07, 0x05, 0x00, 0x00, 0x00, 0x00, 0x38, 0x06, 0x05, 0x01, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xd0, }, //panel CLKb3/4 setting + { 15, 0xce, 0x38, 0x05, 0x05, 0x02, 0x00, 0x00, 0x00, 0x38, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0x80, }, //panel CLKc1/2 setting + { 15, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0x90, }, //panel CLKc3/4 setting + { 15, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xa0, }, //panel CLKd1/2 setting + { 15, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xb0, }, //panel CLKd3/4 setting + { 15, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xc0, }, //panel ECLK setting + { 12, 0xcf, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x08, }, + + { 2, 0x00, 0xb5, }, //TCON_GOA_OUT Setting + { 7, 0xc5, 0x33, 0xf1, 0xff, 0x33, 0xf1, 0xff, }, //normal output with VGH/VGL + + { 2, 0x00, 0xa0, }, + { 2, 0xc1, 0x02, }, + + { 2, 0x00, 0xb1, }, + { 2, 0xc6, 0x04, }, + + { 2, 0x00, 0x00, }, //Orise mode disable + /* this might not go through the SSD2858! */ + { 3, 0xff, 0xff, 0xff, 0xff, }, + +#if 0 + { 2, { DCS_CTRL_DISPLAY, 0x24}, }, // LEDPWM ON + { 2, { DCS_WRITE_CABC, 0x00}, }, // CABC off +#endif +}; + +static struct w677l_reg sleep_out[] = { +// { 1, { MIPI_DCS_SET_DISPLAY_ON, }, }, + { 1, { MIPI_DCS_EXIT_SLEEP_MODE, }, }, +}; + +static struct w677l_reg display_on[] = { + { 1, { MIPI_DCS_SET_DISPLAY_ON, }, }, +// { 1, { MIPI_DCS_EXIT_SLEEP_MODE, }, }, +}; + +static int w677l_write(struct omap_dss_device *dssdev, u8 *buf, int len) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->dssdev.src; + int r; + +#if LOG + int i; + printk("dsi: w677l_write(%s", IS_MCS(buf[0], len)?"g":""); for(i=0; i<len; i++) printk("%02x%s", buf[i], i+1 == len?")\n":" "); +#endif + + if(IS_MCS(buf[0], len)) + { /* this is a "manufacturer command" that must be sent as a "generic write command" */ + r = in->ops->dsi.gen_write(in, ddata->config_channel, buf, len); + } + else + { /* this is a "user command" that must be sent as "DCS command" */ + r = in->ops->dsi.dcs_write_nosync(in, ddata->config_channel, buf, len); + } + + if (r) + dev_err(&ddata->pdev->dev, "write cmd/reg(%x) failed: %d\n", + buf[0], r); + + return r; +} + +static int w677l_read(struct omap_dss_device *dssdev, u8 dcs_cmd, u8 *buf, int len) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->dssdev.src; + int r; + + r = in->ops->dsi.set_max_rx_packet_size(in, ddata->config_channel, len); /* tell panel how much we expect */ + if (r) { + dev_err(&ddata->pdev->dev, "can't set max rx packet size\n"); + return -EIO; + } + + if(IS_MCS(dcs_cmd, len)) + { /* this is a "manufacturer command" that must be sent as a "generic read command" */ + r = in->ops->dsi.gen_read(in, ddata->config_channel, &dcs_cmd, 1, buf, len); + } + else + { /* this is a "user command" that must be sent as "DCS command" */ + r = in->ops->dsi.dcs_read(in, ddata->config_channel, dcs_cmd, buf, len); + } + + if (r) + dev_err(&ddata->pdev->dev, "read cmd/reg(%02x, %d) failed: %d\n", + dcs_cmd, len, r); + +#if LOG + { + int i; + printk("dsi: w677l_read(%02x,", dcs_cmd); for(i=0; i<len; i++) printk(" %02x", buf[i]); + printk(") -> %d\n", r); + } +#endif + + return r; +} + +static int w677l_write_sequence(struct omap_dss_device *dssdev, + struct w677l_reg *seq, int len) +{ + int r, i; + struct panel_drv_data *ddata = to_panel_data(dssdev); + + for (i = 0; i < len; i++) { + r = w677l_write(dssdev, &seq[i].data[1], seq[i].data[0]); + if (r) { + dev_err(&ddata->pdev->dev, "sequence failed: %d\n", i); + return -EINVAL; + } + } + + return 0; +} + +static int w677l_reset(struct omap_dss_device *dssdev, int activate) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + dev_dbg(&ddata->pdev->dev, "reset(%s)\n", activate?"active":"inactive"); + + if (gpio_is_valid(ddata->reset_gpio)) + gpio_set_value(ddata->reset_gpio, !activate); + return 0; +} + +static int w677l_regulator(struct omap_dss_device *dssdev, int state) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + dev_dbg(&ddata->pdev->dev, "regulator(%d)\n", state); + + if (gpio_is_valid(ddata->regulator_gpio)) + gpio_set_value(ddata->regulator_gpio, state); /* switch regulator */ + return 0; +} + +static int w677l_update_brightness(struct omap_dss_device *dssdev, int level) +{ + int r; +#if 1 + u8 buf[2]; + buf[0] = DCS_BRIGHTNESS; + buf[1] = level; +#else + u8 buf[3]; + buf[0] = DCS_BRIGHTNESS; + buf[1] = level >> 4; /* 12 bit mode */ + buf[2] = buf[1] + ((level & 0x0f) << 4); +#endif + r = w677l_write(dssdev, buf, sizeof(buf)); + if (r) + return r; + return 0; +} + +static int w677l_set_brightness(struct backlight_device *bd) +{ + struct omap_dss_device *dssdev = dev_get_drvdata(&bd->dev); + struct panel_drv_data *ddata = to_panel_data(dssdev); +// struct omap_dss_device *in = ddata->dssdev.src; + int bl = bd->props.brightness; + int r = 0; + + dev_dbg(&ddata->pdev->dev, "set_brightness(%d)\n", bl); + + if (bl == ddata->bl) + return 0; + +#if 0 + + mutex_lock(&ddata->lock); + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + in->ops->dsi.bus_lock(in); + + r = w677l_update_brightness(dssdev, bl); + if (!r) + ddata->bl = bl; + + in->ops->dsi.bus_unlock(in); + } + + mutex_unlock(&ddata->lock); +#endif + + return r; +} + +static int w677l_get_brightness(struct backlight_device *bd) +{ + struct omap_dss_device *dssdev = dev_get_drvdata(&bd->dev); + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->dssdev.src; + u8 data[16]; + u16 brightness = 0; + int r = 0; + + dev_dbg(&ddata->pdev->dev, "get_brightness()\n"); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { + dev_err(&ddata->pdev->dev, "get_brightness: display is not active\n"); + return 0; + } + + mutex_lock(&ddata->lock); + + if (ddata->enabled) { + in->ops->dsi.bus_lock(in); + r = w677l_read(dssdev, DCS_READ_BRIGHTNESS, data, 2); + brightness = (data[0]<<4) + (data[1]>>4); + + in->ops->dsi.bus_unlock(in); + } + + mutex_unlock(&ddata->lock); + + if(r < 0) { + dev_err(&ddata->pdev->dev, "get_brightness: read error\n"); + return bd->props.brightness; + } + + dev_dbg(&ddata->pdev->dev, "get_brightness -> %d\n", brightness); + + return brightness>>4; /* get into range 0..255 */ +} + +static const struct backlight_ops w677l_backlight_ops = { + .get_brightness = w677l_get_brightness, + .update_status = w677l_set_brightness, +}; + +static int w677l_connect(struct omap_dss_device *in, struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct device *dev = &ddata->pdev->dev; + int r; + + dev_dbg(&ddata->pdev->dev, "connect()\n"); + + /* channel0 used for video packets */ + r = in->ops->dsi.request_vc(in, &ddata->pixel_channel); + if (r) { + dev_err(dev, "failed to get virtual channel\n"); + goto err_req_vc0; + } + + r = in->ops->dsi.set_vc_id(in, ddata->pixel_channel, 0); + if (r) { + dev_err(dev, "failed to set VC_ID\n"); + goto err_vc_id0; + } + + /* channel1 used for registers access in LP mode */ + r = in->ops->dsi.request_vc(in, &ddata->config_channel); + if (r) { + dev_err(dev, "failed to get virtual channel\n"); + goto err_req_vc1; + } + + r = in->ops->dsi.set_vc_id(in, ddata->config_channel, 0); + if (r) { + dev_err(dev, "failed to set VC_ID\n"); + goto err_vc_id1; + } + + dev_dbg(&ddata->pdev->dev, "connect() ok\n"); + + return 0; + +err_vc_id1: + in->ops->dsi.release_vc(in, ddata->config_channel); +err_req_vc1: +err_vc_id0: + in->ops->dsi.release_vc(in, ddata->pixel_channel); +err_req_vc0: + in->ops->disconnect(in, dssdev); + return r; +} + +static void w677l_disconnect(struct omap_dss_device *in, struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + dev_dbg(&ddata->pdev->dev, "disconnect()\n"); + + if (!omapdss_device_is_connected(dssdev)) + return; + + in->ops->dsi.release_vc(in, ddata->pixel_channel); + in->ops->dsi.release_vc(in, ddata->config_channel); +} + +static void w677l_get_timings(struct omap_dss_device *dssdev, + struct videomode *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->dssdev.src; + + dev_dbg(&ddata->pdev->dev, "get_timings() in = %s %u %p\n", in->name, in->alias_id, in->driver); + + /* if we are connected to the ssd2858 driver in->driver provides get_timings() */ + + if (in->driver && in->ops->get_timings) { + dev_dbg(&ddata->pdev->dev, "get_timings() from source\n"); + in->ops->get_timings(in, timings); + } else + *timings = ddata->vm; +} + +static int w677l_check_timings(struct omap_dss_device *dssdev, + struct videomode *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->dssdev.src; + + dev_dbg(&ddata->pdev->dev, "check_timings() in = %s %u %p\n", in->name, in->alias_id, in->driver); + + /* if we are connected to the ssd2858 driver in->driver provides check_timings() */ + + if (in->driver && in->ops->check_timings) { + dev_dbg(&ddata->pdev->dev, "check_timings with source\n"); + return in->ops->check_timings(in, (struct videomode *) timings); + } + return 0; +} + +static void w677l_get_size(struct omap_dss_device *dssdev, + unsigned int *width, unsigned int *height) +{ + *width = 63; + *height = 112; +} + +static int w677l_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct device *dev = &ddata->pdev->dev; + struct omap_dss_device *in = ddata->dssdev.src; + struct omap_dss_dsi_config w677l_dsi_config = { + .mode = OMAP_DSS_DSI_VIDEO_MODE, + .pixel_format = w677l_PIXELFORMAT, + .vm = &w677l_timings, + .hs_clk_min = 125000000 /*w677l_HS_CLOCK*/, + .hs_clk_max = 450000000 /*(12*w677l_HS_CLOCK)/10*/, + .lp_clk_min = (7*w677l_LP_CLOCK)/10, + .lp_clk_max = w677l_LP_CLOCK, + .ddr_clk_always_on = true, + .trans_mode = OMAP_DSS_DSI_BURST_MODE, + }; + int r = 0; + + dev_dbg(&ddata->pdev->dev, "enable\n"); + + if (!omapdss_device_is_connected(dssdev)) + return -ENODEV; + + if (omapdss_device_is_enabled(dssdev)) + return 0; + + dev_dbg(&ddata->pdev->dev, "start()\n"); + + mutex_lock(&ddata->lock); + + in->ops->dsi.bus_lock(in); + + dev_dbg(&ddata->pdev->dev, "hs_clk_min=%lu\n", w677l_dsi_config.hs_clk_min); + dev_dbg(&ddata->pdev->dev, "power_on()\n"); + + w677l_reset(dssdev, true); /* activate reset */ + +#if 0 + if (ddata->pin_config.num_pins > 0) { + r = in->ops->dsi.configure_pins(in, &ddata->pin_config); + if (r) { + dev_err(&ddata->pdev->dev, + "failed to configure DSI pins\n"); + goto err; + } + } +#endif + + // CHECKME: this might be the first place where timings are really important + // or is this too late? + + r = in->ops->dsi.set_config(in, &w677l_dsi_config); + if (r) { + dev_err(dev, "failed to configure DSI\n"); + goto err; + } + + r = in->ops->enable(in); + if (r) { + dev_err(dev, "failed to enable DSI\n"); + goto err; + } + + w677l_regulator(dssdev, true); /* switch power on */ + msleep(50); + + w677l_reset(dssdev, false); /* release reset */ + msleep(10); + + in->ops->dsi.enable_hs(in, ddata->pixel_channel, true); + + r = w677l_write_sequence(dssdev, sleep_out, ARRAY_SIZE(sleep_out)); + if (r) + goto cleanup; + + msleep(10); + + r = w677l_write_sequence(dssdev, init_seq, ARRAY_SIZE(init_seq)); + if (r) { + dev_err(dev, "failed to configure panel\n"); +// can fail if the ssd can't forward long commands +// goto cleanup; + } + + r = w677l_update_brightness(dssdev, 255); + if (r) + goto cleanup; + +#if LOG + { + u8 ret[8]; + /* read some registers through DCS commands */ + r = w677l_read(dssdev, 0x05, ret, 1); + r = w677l_read(dssdev, 0x0a, ret, 1); // power mode 0x10=sleep off; 0x04=display on + r = w677l_read(dssdev, 0x0b, ret, 1); // address mode + r = w677l_read(dssdev, MIPI_DCS_GET_PIXEL_FORMAT, ret, 1); // pixel format 0x70 = RGB888 + r = w677l_read(dssdev, 0x0d, ret, 1); // display mode 0x80 = command 0x34/0x35 + r = w677l_read(dssdev, 0x0e, ret, 1); // signal mode + r = w677l_read(dssdev, MIPI_DCS_GET_DIAGNOSTIC_RESULT, ret, 1); // diagnostic 0x40 = functional + r = w677l_read(dssdev, 0x45, ret, 2); // get scanline + } +#endif + r = in->ops->dsi.enable_video_output(in, ddata->pixel_channel); + if (r) + goto cleanup; + + msleep(10); + +#if 1 /* this is recommended by the latest data sheet */ + r = w677l_write_sequence(dssdev, display_on, ARRAY_SIZE(display_on)); + if (r) + goto cleanup; +#endif + + ddata->enabled = true; + + dev_dbg(&ddata->pdev->dev, "enable() powered on()\n"); + + goto ok; + +cleanup: + dev_err(dev, "error while enabling panel, issuing HW reset\n"); + + in->ops->disable(in); + mdelay(10); +// w677l_reset(dssdev, true); /* activate reset */ + w677l_regulator(dssdev, false); /* switch power off */ + mdelay(20); + +err: +ok: + + in->ops->dsi.bus_unlock(in); + + if (r) + dev_err(&ddata->pdev->dev, "enable failed\n"); + else + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + mutex_unlock(&ddata->lock); + + return r; +} + +static void w677l_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->dssdev.src; + + dev_dbg(&ddata->pdev->dev, "disable\n"); + + if (!omapdss_device_is_enabled(dssdev)) + return; + + dev_dbg(&ddata->pdev->dev, "stop()\n"); + + mutex_lock(&ddata->lock); + + in->ops->dsi.bus_lock(in); + + dev_dbg(&ddata->pdev->dev, "power_off()\n"); + + ddata->enabled = 0; + in->ops->dsi.disable_video_output(in, ddata->pixel_channel); + in->ops->disable(in); + mdelay(10); + w677l_reset(dssdev, true); /* activate reset + w677l_regulator(dssdev, false); /* switch power off - after stopping video stream */ + mdelay(20); + /* here we can also power off IOVCC */ + + in->ops->dsi.bus_unlock(in); + + mutex_unlock(&ddata->lock); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +} + +static struct omap_dss_device_ops w677l_dss_ops = { + .connect = w677l_connect, + .disconnect = w677l_disconnect, + + .enable = w677l_enable, + .disable = w677l_disable, + + .get_timings = w677l_get_timings, + .check_timings = w677l_check_timings, +}; + +static struct omap_dss_driver w677l_dss_driver = { + .get_size = w677l_get_size, +}; + +static int w677l_probe_of(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + int gpio; + + dev_dbg(&ddata->pdev->dev, "probe_of()\n"); + + if (!node) + return -ENODEV; + + gpio = of_get_gpio(node, 0); + if (gpio == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (!gpio_is_valid(gpio)) { + dev_err(&pdev->dev, "failed to parse reset gpio (err=%d)\n", gpio); + return gpio; + } + ddata->reset_gpio = gpio; + +#if OPTIONAL + gpio = of_get_gpio(node, 1); + if (!gpio_is_valid(gpio)) { + dev_err(&pdev->dev, "failed to parse regulator gpio (err=%d)\n", gpio); + return gpio; + } + + if (gpio == -EPROBE_DEFER) + return -EPROBE_DEFER; + ddata->regulator_gpio = gpio; +#endif + + return 0; +} + +static int w677l_probe(struct platform_device *pdev) +{ + struct panel_drv_data *ddata; + struct device *dev = &pdev->dev; + struct omap_dss_device *dssdev; + int r; + + dev_dbg(dev, "%s\n", __func__); + + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + platform_set_drvdata(pdev, ddata); + ddata->pdev = pdev; + + ddata->vm = w677l_timings; + + r = w677l_probe_of(pdev); + if (r) { + dev_err(dev, "Failed to probe %d\n", r); + return r; + } + + dssdev = &ddata->dssdev; + dssdev->dev = dev; + dssdev->ops = &w677l_dss_ops; + dssdev->driver = &w677l_dss_driver; + dssdev->type = OMAP_DISPLAY_TYPE_DSI; + dssdev->owner = THIS_MODULE; + dssdev->of_ports = BIT(0); + +#if 0 + dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE | + OMAP_DSS_DISPLAY_CAP_TEAR_ELIM; +#endif + + omapdss_display_init(dssdev); + omapdss_device_register(dssdev); + + mutex_init(&ddata->lock); + + if (gpio_is_valid(ddata->reset_gpio)) { + r = devm_gpio_request_one(dev, ddata->reset_gpio, + GPIOF_DIR_OUT, "panel reset"); + if (r) { + dev_err(dev, "failed to request reset gpio (%d err=%d)\n", ddata->reset_gpio, r); + return r; + } + } + +#if OPTIONAL + if (gpio_is_valid(ddata->regulator_gpio)) { + r = devm_gpio_request_one(dev, ddata->regulator_gpio, + GPIOF_DIR_OUT, "panel DC/DC regulator"); + if (r) { + dev_err(dev, "failed to request regulator gpio (%d err=%d)\n", ddata->regulator_gpio, r); + return r; + } + } +#endif + + dev_dbg(dev, "probe ok\n"); + + return 0; +} + + +static int __exit w677l_remove(struct platform_device *pdev) +{ + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *dssdev = &ddata->dssdev; + + dev_dbg(&pdev->dev, "%s\n", __func__); + + omapdss_device_unregister(dssdev); + + w677l_disable(dssdev); + omapdss_device_disconnect(dssdev->src, dssdev); + + mutex_destroy(&ddata->lock); + + return 0; +} + +static const struct of_device_id w677l_of_match[] = { + { .compatible = "omapdss,boe,btl507212-w677l", }, + { .compatible = "boe,btl507212-w677l", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, w677l_of_match); + +static struct platform_driver w677l_driver = { + .probe = w677l_probe, + .remove = __exit_p(w677l_remove), + .driver = { + .name = "panel-btl507212-w677l", + .of_match_table = w677l_of_match, + .suppress_bind_attrs = true, + }, +}; + +module_platform_driver(w677l_driver); + +MODULE_AUTHOR("H. Nikolaus Schaller hns@goldelico.com"); +MODULE_DESCRIPTION("btl507212-w677l driver"); +MODULE_LICENSE("GPL");
(resent - because I did have you on CC only)
Hi Tomi,
Am 12.11.2018 um 19:30 schrieb Tony Lindgren tony@atomide.com:
Hi,
- H. Nikolaus Schaller hns@goldelico.com [181112 17:35]:
Hi Tomi, 4.20-rc1 seems to have broken our DSI panel driver for the OMAP5 Pyra.
There were some omapdrm API changes which I have tried to fix by inspecting panel-dsi-cm.c and also comparing with panel-tpo-td028ttec1.c (which is still working on our omap3 systems).
I also have removed the tiler patches by Matthijs to make sure that they do not have an influence.
The strange thing is that the panel driver module is not even loaded during probe time, although we have defined the display alias and not changed that compared to 4.19.
The other strange thing is that I can manually modprobe the driver and it is loaded and shown by lsmod, but the probe function is never called.
Any hints what we should check and compare and verify?
Did the panel driver compatible matching mechanism change for dsi panels without a bus (dpi panels are usually loaded as an spi bus client)?
Or is there a new CONFIG that must be set?
Test with Laurent's fixes, and also looks like Sebastian also has some recent yet to be posted probe order changes on top of Laurent's fixes in his linux-n900 branch if Laurent's fixes alone don't help.
Laurent's patches are "[PATCH v3 0/4] omapdrm: Fix runtime PM issues at module load and unload time" and Sebastian's git tree is at https://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-n900.git/.
Adding Laurent and Sebastian to Cc too.
now as the panel-dpi device is working and Sebastian's patches make our Pyra dsi panel driver at least load successfully, I have tried to find out more about what is going on.
It does probe(), connect() but seems to never call enable(). Hence it is not sending any dcs commands to the panel.
Attached is the current driver code and a boot log. Device tree is the same as for earlier kernels, so it should be ok (unless dsi panels need to define new properties).
Maybe you can spot something which is missing. For example, I have seen that there is a new propery dssdev->caps which we did not touch before.
Or there is .suppress_bind_attrs = true which I simply did copy from panel-dsi-cm.c assuming it is needed for all dsi panels.
It is probably just one or two lines which are missing...
BR and thanks, Nikolaus
diff --git a/drivers/gpu/drm/omapdrm/displays/panel-boe-w677l.c b/drivers/gpu/drm/omapdrm/displays/panel-boe-w677l.c new file mode 100644 index 000000000000..39c93fdf957e --- /dev/null +++ b/drivers/gpu/drm/omapdrm/displays/panel-boe-w677l.c +/* + * Driver for BOE W677L panel with Orise OTM1283A controller + * + * Copyright 2011 Texas Instruments, Inc. + * Author: Archit Taneja archit@ti.com + * Author: Tomi Valkeinen tomi.valkeinen@ti.com + * + * based on d2l panel driver by Jerry Alexander x0135174@ti.com + * + * Copyright (C) 2014-2018 Golden Delicious Computers + * by H. Nikolaus Schaller hns@goldelico.com + * based on lg4591 panel driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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/. + */ + +#define LOG 1 +#define OPTIONAL 0 + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/sched/signal.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> + +#include <video/mipi_display.h> +#include <video/of_display_timing.h> + +#include "../dss/omapdss.h" + +#undef dev_dbg +#define dev_dbg dev_err + +/* extended DCS commands (not defined in mipi_display.h) */ +#define DCS_READ_DDB_START 0x02 +#define DCS_READ_NUM_ERRORS 0x05 +#define DCS_BRIGHTNESS 0x51 /* write brightness */ +#define DCS_READ_BRIGHTNESS 0x52 /* read brightness */ +#define DCS_CTRL_DISPLAY 0x53 /* enable backlight etc. */ +#define DCS_READ_CTRL_DISPLAY 0x53 /* read control */ +#define DCS_WRITE_CABC 0x55 +#define DCS_READ_CABC 0x56 +#define MCS_READID1 0xda +#define MCS_READID2 0xdb +#define MCS_READID3 0xdc + +/* DCS NOP is 1-byte 0x00 while 2-byte 0x00 is address shift function of this controller */ + +#define IS_MCS(CMD, LEN) ((((CMD) == 0) && (LEN) == 2) || (((CMD) >= 0xb0 && (CMD) < 0xda)) || ((CMD) > 0xdc)) + +/* horizontal * vertical * refresh */ +#define w677l_W (720) +#define w677l_H (1280) +#define w677l_WIDTH (w677l_W+80+88) +#define w677l_HEIGHT (w677l_H+160) +#define w677l_FPS (60ll) +#define w677l_PIXELCLOCK (w677l_WIDTH * w677l_HEIGHT * w677l_FPS) /* Full HD * 60 fps */ +/* panel has 16.7M colors = RGB888 = 3*8 bit per pixel */ +#define w677l_PIXELFORMAT OMAP_DSS_DSI_FMT_RGB888 /* 16.7M color = RGB888 */ +#define w677l_BIT_PER_PIXEL (3*8) +/* the panel can handle 4 lanes */ +#define w677l_LANES 4 +/* high speed clock is running at double data rate, i.e. half speed + * (take care of integer overflows!) + * hsck = bit/pixel * 110% * pixel clock / lanes / 2 clock edges + * real clock rate may be rounded up or down depending on divisors + */ +#define w677l_HS_CLOCK (w677l_BIT_PER_PIXEL * (w677l_PIXELCLOCK / (w677l_LANES * 2))) +/* low power clock is quite arbitrarily choosen to be roughly 10 MHz */ +#define w677l_LP_CLOCK 9200000 /* low power clock */ + +static struct videomode w677l_timings = { + .hactive = w677l_W, + .vactive = w677l_H, + .pixelclock = w677l_PIXELCLOCK, + .hfront_porch = 5, + .hsync_len = 5, + .hback_porch = w677l_WIDTH-w677l_W-5-5, + .vfront_porch = 50, + .vsync_len = w677l_HEIGHT-w677l_H-50-50, + .vback_porch = 50, +}; + +struct panel_drv_data { + struct omap_dss_device dssdev; + + struct videomode vm; + + struct platform_device *pdev; + + struct mutex lock; + + int bl; + + int reset_gpio; + int regulator_gpio; + + struct omap_dsi_pin_config pin_config; + + bool enabled; + + int config_channel; + int pixel_channel; + +}; + +#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) + +struct w677l_reg { + u8 data[20]; /* data[0] is length, data[1] is first byte */ +}; + +static struct w677l_reg init_seq[] = { + { 2, 0x00, 0x00, }, + { 4, 0xff, 0x12, 0x83, 0x01, }, //EXTC=1 + { 2, 0x00, 0x80, }, //Orise mode enable + { 3, 0xff, 0x12, 0x83, }, + + +//-------------------- panel setting --------------------// + { 2, 0x00, 0x80, }, //TCON Setting + { 10, 0xc0, 0x00, 0x64, 0x00, 0x0f, 0x11, 0x00, 0x64, 0x0f, 0x11, }, + + { 2, 0x00, 0x90, }, //Panel Timing Setting + { 7, 0xc0, 0x00, 0x5c, 0x00, 0x01, 0x00, 0x04, }, + + { 2, 0x00, 0x87, }, + { 2, 0xc4, 0x18, }, + + { 2, 0x00, 0xb3, }, //Interval Scan Frame: 0 frame, column inversion + { 3, 0xc0, 0x00, 0x50, }, + + { 2, 0x00, 0x81, }, //frame rate:60Hz + { 2, 0xc1, 0x66, }, + + { 2, 0x00, 0x81, }, + { 3, 0xc4, 0x82, 0x02, }, + + { 2, 0x00, 0x90, }, + { 2, 0xc4, 0x49, }, + + { 2, 0x00, 0xc6, }, + { 2, 0xb0, 0x03, }, + + { 2, 0x00, 0x90, }, //Mode-3 + { 5, 0xf5, 0x02, 0x11, 0x02, 0x11, }, + + { 2, 0x00, 0x90, }, //2xVPNL, 1.5*=00, 2*=50, 3*=a0 + { 2, 0xc5, 0x50, }, + + { 2, 0x00, 0x94, }, //Frequency + { 2, 0xc5, 0x66, }, + + { 2, 0x00, 0xb2, }, //VGLO1 setting + { 3, 0xf5, 0x00, 0x00, }, + + { 2, 0x00, 0xb4, }, //VGLO1_S setting + { 3, 0xf5, 0x00, 0x00, }, + + { 2, 0x00, 0xb6, }, //VGLO2 setting + { 3, 0xf5, 0x00, 0x00, }, + + { 2, 0x00, 0xb8, }, //VGLO2_S setting + { 3, 0xf5, 0x00, 0x00, }, + + { 2, 0x00, 0x94, }, //VCL ON + { 2, 0xf5, 0x02, }, + + { 2, 0x00, 0xBA, }, //VSP ON + { 2, 0xf5, 0x03, }, + + { 2, 0x00, 0xb2, }, //VGHO Option + { 2, 0xc5, 0x40, }, + + { 2, 0x00, 0xb4, }, //VGLO Option + { 2, 0xc5, 0xC0, }, + +//-------------------- power setting --------------------// + { 2, 0x00, 0xa0, }, //dcdc setting + { 15, 0xc4, 0x05, 0x10, 0x06, 0x02, 0x05, 0x15, 0x10, 0x05, 0x10, 0x07, 0x02, 0x05, 0x15, 0x10, }, + + { 2, 0x00, 0xb0, }, //clamp voltage setting + { 3, 0xc4, 0x00, 0x00, }, + + { 2, 0x00, 0x91, }, //VGH=13V, VGL=-12V, pump ratio:VGH=6x, VGL=-5x + { 3, 0xc5, 0x19, 0x50, }, + + { 2, 0x00, 0x00, }, //GVDD=4.87V, NGVDD=-4.87V + { 3, 0xd8, 0xbc, 0xbc, }, + + { 2, 0x00, 0x00, }, //VCOMDC=-1.1 + { 2, 0xd9, 0x5a, }, //5d 6f + + { 2, 0x00, 0x00, }, + { 17, 0xE1, 0x01, 0x07, 0x0b, 0x0d, 0x06, 0x0d, 0x0b, 0x0a, 0x04, 0x07, 0x10, 0x08, 0x0f, 0x11, 0x0a, 0x01, }, + + { 2, 0x00, 0x00, }, + { 17, 0xE2, 0x01, 0x07, 0x0b, 0x0d, 0x06, 0x0d, 0x0b, 0x0a, 0x04, 0x07, 0x10, 0x08, 0x0f, 0x11, 0x0a, 0x01, }, + + { 2, 0x00, 0xb0, }, //VDD_18V=1.7V, LVDSVDD=1.55V + { 3, 0xc5, 0x04, 0xB8, }, + + { 2, 0x00, 0xbb, }, //LVD voltage level setting + { 2, 0xc5, 0x80, }, + +//-------------------- panel timing state control --------------------// + { 2, 0x00, 0x80, }, //panel timing state control + { 12, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0x90, }, //panel timing state control + { 16, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xa0, }, //panel timing state control + { 16, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xb0, }, //panel timing state control + { 16, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xc0, }, //panel timing state control + { 16, 0xcb, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xd0, }, //panel timing state control + { 16, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x00, 0x00, }, + + { 2, 0x00, 0xe0, }, //panel timing state control + { 15, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x05, }, + + { 2, 0x00, 0xf0, }, //panel timing state control + { 12, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }, + +//-------------------- panel pad mapping control --------------------// + { 2, 0x00, 0x80, }, //panel pad mapping control + { 16, 0xcc, 0x0a, 0x0c, 0x0e, 0x10, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0x90, }, //panel pad mapping control + { 16, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x2d, 0x09, 0x0b, 0x0d, 0x0f, 0x01, 0x03, 0x00, 0x00, }, + + { 2, 0x00, 0xa0, }, //panel pad mapping control + { 15, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x2d, }, + + { 2, 0x00, 0xb0, }, //panel pad mapping control + { 16, 0xcc, 0x0F, 0x0D, 0x0B, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xc0, }, //panel pad mapping control + { 16, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x2e, 0x10, 0x0E, 0x0C, 0x0A, 0x04, 0x02, 0x00, 0x00, }, + + { 2, 0x00, 0xd0, }, //panel pad mapping control + { 15, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x2e, }, + +//-------------------- panel timing setting --------------------// + { 2, 0x00, 0x80, }, //panel VST setting + { 13, 0xce, 0x8D, 0x03, 0x00, 0x8C, 0x03, 0x00, 0x8B, 0x03, 0x00, 0x8A, 0x03, 0x00, }, + + { 2, 0x00, 0x90, }, //panel VEND setting + { 15, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xa0, }, //panel CLKA1/2 setting + { 15, 0xce, 0x38, 0x0B, 0x04, 0xFC, 0x00, 0x00, 0x00, 0x38, 0x0A, 0x04, 0xFD, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xb0, }, //panel CLKA3/4 setting + { 15, 0xce, 0x38, 0x09, 0x04, 0xFE, 0x00, 0x00, 0x00, 0x38, 0x08, 0x04, 0xFF, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xc0, }, //panel CLKb1/2 setting + { 15, 0xce, 0x38, 0x07, 0x05, 0x00, 0x00, 0x00, 0x00, 0x38, 0x06, 0x05, 0x01, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xd0, }, //panel CLKb3/4 setting + { 15, 0xce, 0x38, 0x05, 0x05, 0x02, 0x00, 0x00, 0x00, 0x38, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0x80, }, //panel CLKc1/2 setting + { 15, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0x90, }, //panel CLKc3/4 setting + { 15, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xa0, }, //panel CLKd1/2 setting + { 15, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xb0, }, //panel CLKd3/4 setting + { 15, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + + { 2, 0x00, 0xc0, }, //panel ECLK setting + { 12, 0xcf, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x08, }, + + { 2, 0x00, 0xb5, }, //TCON_GOA_OUT Setting + { 7, 0xc5, 0x33, 0xf1, 0xff, 0x33, 0xf1, 0xff, }, //normal output with VGH/VGL + + { 2, 0x00, 0xa0, }, + { 2, 0xc1, 0x02, }, + + { 2, 0x00, 0xb1, }, + { 2, 0xc6, 0x04, }, + + { 2, 0x00, 0x00, }, //Orise mode disable + /* this might not go through the SSD2858! */ + { 3, 0xff, 0xff, 0xff, 0xff, }, + +#if 0 + { 2, { DCS_CTRL_DISPLAY, 0x24}, }, // LEDPWM ON + { 2, { DCS_WRITE_CABC, 0x00}, }, // CABC off +#endif +}; + +static struct w677l_reg sleep_out[] = { +// { 1, { MIPI_DCS_SET_DISPLAY_ON, }, }, + { 1, { MIPI_DCS_EXIT_SLEEP_MODE, }, }, +}; + +static struct w677l_reg display_on[] = { + { 1, { MIPI_DCS_SET_DISPLAY_ON, }, }, +// { 1, { MIPI_DCS_EXIT_SLEEP_MODE, }, }, +}; + +static int w677l_write(struct omap_dss_device *dssdev, u8 *buf, int len) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->dssdev.src; + int r; + +#if LOG + int i; + printk("dsi: w677l_write(%s", IS_MCS(buf[0], len)?"g":""); for(i=0; i<len; i++) printk("%02x%s", buf[i], i+1 == len?")\n":" "); +#endif + + if(IS_MCS(buf[0], len)) + { /* this is a "manufacturer command" that must be sent as a "generic write command" */ + r = in->ops->dsi.gen_write(in, ddata->config_channel, buf, len); + } + else + { /* this is a "user command" that must be sent as "DCS command" */ + r = in->ops->dsi.dcs_write_nosync(in, ddata->config_channel, buf, len); + } + + if (r) + dev_err(&ddata->pdev->dev, "write cmd/reg(%x) failed: %d\n", + buf[0], r); + + return r; +} + +static int w677l_read(struct omap_dss_device *dssdev, u8 dcs_cmd, u8 *buf, int len) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->dssdev.src; + int r; + + r = in->ops->dsi.set_max_rx_packet_size(in, ddata->config_channel, len); /* tell panel how much we expect */ + if (r) { + dev_err(&ddata->pdev->dev, "can't set max rx packet size\n"); + return -EIO; + } + + if(IS_MCS(dcs_cmd, len)) + { /* this is a "manufacturer command" that must be sent as a "generic read command" */ + r = in->ops->dsi.gen_read(in, ddata->config_channel, &dcs_cmd, 1, buf, len); + } + else + { /* this is a "user command" that must be sent as "DCS command" */ + r = in->ops->dsi.dcs_read(in, ddata->config_channel, dcs_cmd, buf, len); + } + + if (r) + dev_err(&ddata->pdev->dev, "read cmd/reg(%02x, %d) failed: %d\n", + dcs_cmd, len, r); + +#if LOG + { + int i; + printk("dsi: w677l_read(%02x,", dcs_cmd); for(i=0; i<len; i++) printk(" %02x", buf[i]); + printk(") -> %d\n", r); + } +#endif + + return r; +} + +static int w677l_write_sequence(struct omap_dss_device *dssdev, + struct w677l_reg *seq, int len) +{ + int r, i; + struct panel_drv_data *ddata = to_panel_data(dssdev); + + for (i = 0; i < len; i++) { + r = w677l_write(dssdev, &seq[i].data[1], seq[i].data[0]); + if (r) { + dev_err(&ddata->pdev->dev, "sequence failed: %d\n", i); + return -EINVAL; + } + } + + return 0; +} + +static int w677l_reset(struct omap_dss_device *dssdev, int activate) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + dev_dbg(&ddata->pdev->dev, "reset(%s)\n", activate?"active":"inactive"); + + if (gpio_is_valid(ddata->reset_gpio)) + gpio_set_value(ddata->reset_gpio, !activate); + return 0; +} + +static int w677l_regulator(struct omap_dss_device *dssdev, int state) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + dev_dbg(&ddata->pdev->dev, "regulator(%d)\n", state); + + if (gpio_is_valid(ddata->regulator_gpio)) + gpio_set_value(ddata->regulator_gpio, state); /* switch regulator */ + return 0; +} + +static int w677l_update_brightness(struct omap_dss_device *dssdev, int level) +{ + int r; +#if 1 + u8 buf[2]; + buf[0] = DCS_BRIGHTNESS; + buf[1] = level; +#else + u8 buf[3]; + buf[0] = DCS_BRIGHTNESS; + buf[1] = level >> 4; /* 12 bit mode */ + buf[2] = buf[1] + ((level & 0x0f) << 4); +#endif + r = w677l_write(dssdev, buf, sizeof(buf)); + if (r) + return r; + return 0; +} + +static int w677l_set_brightness(struct backlight_device *bd) +{ + struct omap_dss_device *dssdev = dev_get_drvdata(&bd->dev); + struct panel_drv_data *ddata = to_panel_data(dssdev); +// struct omap_dss_device *in = ddata->dssdev.src; + int bl = bd->props.brightness; + int r = 0; + + dev_dbg(&ddata->pdev->dev, "set_brightness(%d)\n", bl); + + if (bl == ddata->bl) + return 0; + +#if 0 + + mutex_lock(&ddata->lock); + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + in->ops->dsi.bus_lock(in); + + r = w677l_update_brightness(dssdev, bl); + if (!r) + ddata->bl = bl; + + in->ops->dsi.bus_unlock(in); + } + + mutex_unlock(&ddata->lock); +#endif + + return r; +} + +static int w677l_get_brightness(struct backlight_device *bd) +{ + struct omap_dss_device *dssdev = dev_get_drvdata(&bd->dev); + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->dssdev.src; + u8 data[16]; + u16 brightness = 0; + int r = 0; + + dev_dbg(&ddata->pdev->dev, "get_brightness()\n"); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { + dev_err(&ddata->pdev->dev, "get_brightness: display is not active\n"); + return 0; + } + + mutex_lock(&ddata->lock); + + if (ddata->enabled) { + in->ops->dsi.bus_lock(in); + r = w677l_read(dssdev, DCS_READ_BRIGHTNESS, data, 2); + brightness = (data[0]<<4) + (data[1]>>4); + + in->ops->dsi.bus_unlock(in); + } + + mutex_unlock(&ddata->lock); + + if(r < 0) { + dev_err(&ddata->pdev->dev, "get_brightness: read error\n"); + return bd->props.brightness; + } + + dev_dbg(&ddata->pdev->dev, "get_brightness -> %d\n", brightness); + + return brightness>>4; /* get into range 0..255 */ +} + +static const struct backlight_ops w677l_backlight_ops = { + .get_brightness = w677l_get_brightness, + .update_status = w677l_set_brightness, +}; + +static int w677l_connect(struct omap_dss_device *in, struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct device *dev = &ddata->pdev->dev; + int r; + + dev_dbg(&ddata->pdev->dev, "connect()\n"); + + /* channel0 used for video packets */ + r = in->ops->dsi.request_vc(in, &ddata->pixel_channel); + if (r) { + dev_err(dev, "failed to get virtual channel\n"); + goto err_req_vc0; + } + + r = in->ops->dsi.set_vc_id(in, ddata->pixel_channel, 0); + if (r) { + dev_err(dev, "failed to set VC_ID\n"); + goto err_vc_id0; + } + + /* channel1 used for registers access in LP mode */ + r = in->ops->dsi.request_vc(in, &ddata->config_channel); + if (r) { + dev_err(dev, "failed to get virtual channel\n"); + goto err_req_vc1; + } + + r = in->ops->dsi.set_vc_id(in, ddata->config_channel, 0); + if (r) { + dev_err(dev, "failed to set VC_ID\n"); + goto err_vc_id1; + } + + dev_dbg(&ddata->pdev->dev, "connect() ok\n"); + + return 0; + +err_vc_id1: + in->ops->dsi.release_vc(in, ddata->config_channel); +err_req_vc1: +err_vc_id0: + in->ops->dsi.release_vc(in, ddata->pixel_channel); +err_req_vc0: + in->ops->disconnect(in, dssdev); + return r; +} + +static void w677l_disconnect(struct omap_dss_device *in, struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + dev_dbg(&ddata->pdev->dev, "disconnect()\n"); + + if (!omapdss_device_is_connected(dssdev)) + return; + + in->ops->dsi.release_vc(in, ddata->pixel_channel); + in->ops->dsi.release_vc(in, ddata->config_channel); +} + +static void w677l_get_timings(struct omap_dss_device *dssdev, + struct videomode *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->dssdev.src; + + dev_dbg(&ddata->pdev->dev, "get_timings() in = %s %u %p\n", in->name, in->alias_id, in->driver); + + /* if we are connected to the ssd2858 driver in->driver provides get_timings() */ + + if (in->driver && in->ops->get_timings) { + dev_dbg(&ddata->pdev->dev, "get_timings() from source\n"); + in->ops->get_timings(in, timings); + } else + *timings = ddata->vm; +} + +static int w677l_check_timings(struct omap_dss_device *dssdev, + struct videomode *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->dssdev.src; + + dev_dbg(&ddata->pdev->dev, "check_timings() in = %s %u %p\n", in->name, in->alias_id, in->driver); + + /* if we are connected to the ssd2858 driver in->driver provides check_timings() */ + + if (in->driver && in->ops->check_timings) { + dev_dbg(&ddata->pdev->dev, "check_timings with source\n"); + return in->ops->check_timings(in, (struct videomode *) timings); + } + return 0; +} + +static void w677l_get_size(struct omap_dss_device *dssdev, + unsigned int *width, unsigned int *height) +{ + *width = 63; + *height = 112; +} + +static int w677l_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct device *dev = &ddata->pdev->dev; + struct omap_dss_device *in = ddata->dssdev.src; + struct omap_dss_dsi_config w677l_dsi_config = { + .mode = OMAP_DSS_DSI_VIDEO_MODE, + .pixel_format = w677l_PIXELFORMAT, + .vm = &w677l_timings, + .hs_clk_min = 125000000 /*w677l_HS_CLOCK*/, + .hs_clk_max = 450000000 /*(12*w677l_HS_CLOCK)/10*/, + .lp_clk_min = (7*w677l_LP_CLOCK)/10, + .lp_clk_max = w677l_LP_CLOCK, + .ddr_clk_always_on = true, + .trans_mode = OMAP_DSS_DSI_BURST_MODE, + }; + int r = 0; + + dev_dbg(&ddata->pdev->dev, "enable\n"); + + if (!omapdss_device_is_connected(dssdev)) + return -ENODEV; + + if (omapdss_device_is_enabled(dssdev)) + return 0; + + dev_dbg(&ddata->pdev->dev, "start()\n"); + + mutex_lock(&ddata->lock); + + in->ops->dsi.bus_lock(in); + + dev_dbg(&ddata->pdev->dev, "hs_clk_min=%lu\n", w677l_dsi_config.hs_clk_min); + dev_dbg(&ddata->pdev->dev, "power_on()\n"); + + w677l_reset(dssdev, true); /* activate reset */ + +#if 0 + if (ddata->pin_config.num_pins > 0) { + r = in->ops->dsi.configure_pins(in, &ddata->pin_config); + if (r) { + dev_err(&ddata->pdev->dev, + "failed to configure DSI pins\n"); + goto err; + } + } +#endif + + // CHECKME: this might be the first place where timings are really important + // or is this too late? + + r = in->ops->dsi.set_config(in, &w677l_dsi_config); + if (r) { + dev_err(dev, "failed to configure DSI\n"); + goto err; + } + + r = in->ops->enable(in); + if (r) { + dev_err(dev, "failed to enable DSI\n"); + goto err; + } + + w677l_regulator(dssdev, true); /* switch power on */ + msleep(50); + + w677l_reset(dssdev, false); /* release reset */ + msleep(10); + + in->ops->dsi.enable_hs(in, ddata->pixel_channel, true); + + r = w677l_write_sequence(dssdev, sleep_out, ARRAY_SIZE(sleep_out)); + if (r) + goto cleanup; + + msleep(10); + + r = w677l_write_sequence(dssdev, init_seq, ARRAY_SIZE(init_seq)); + if (r) { + dev_err(dev, "failed to configure panel\n"); +// can fail if the ssd can't forward long commands +// goto cleanup; + } + + r = w677l_update_brightness(dssdev, 255); + if (r) + goto cleanup; + +#if LOG + { + u8 ret[8]; + /* read some registers through DCS commands */ + r = w677l_read(dssdev, 0x05, ret, 1); + r = w677l_read(dssdev, 0x0a, ret, 1); // power mode 0x10=sleep off; 0x04=display on + r = w677l_read(dssdev, 0x0b, ret, 1); // address mode + r = w677l_read(dssdev, MIPI_DCS_GET_PIXEL_FORMAT, ret, 1); // pixel format 0x70 = RGB888 + r = w677l_read(dssdev, 0x0d, ret, 1); // display mode 0x80 = command 0x34/0x35 + r = w677l_read(dssdev, 0x0e, ret, 1); // signal mode + r = w677l_read(dssdev, MIPI_DCS_GET_DIAGNOSTIC_RESULT, ret, 1); // diagnostic 0x40 = functional + r = w677l_read(dssdev, 0x45, ret, 2); // get scanline + } +#endif + r = in->ops->dsi.enable_video_output(in, ddata->pixel_channel); + if (r) + goto cleanup; + + msleep(10); + +#if 1 /* this is recommended by the latest data sheet */ + r = w677l_write_sequence(dssdev, display_on, ARRAY_SIZE(display_on)); + if (r) + goto cleanup; +#endif + + ddata->enabled = true; + + dev_dbg(&ddata->pdev->dev, "enable() powered on()\n"); + + goto ok; + +cleanup: + dev_err(dev, "error while enabling panel, issuing HW reset\n"); + + in->ops->disable(in); + mdelay(10); +// w677l_reset(dssdev, true); /* activate reset */ + w677l_regulator(dssdev, false); /* switch power off */ + mdelay(20); + +err: +ok: + + in->ops->dsi.bus_unlock(in); + + if (r) + dev_err(&ddata->pdev->dev, "enable failed\n"); + else + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + mutex_unlock(&ddata->lock); + + return r; +} + +static void w677l_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->dssdev.src; + + dev_dbg(&ddata->pdev->dev, "disable\n"); + + if (!omapdss_device_is_enabled(dssdev)) + return; + + dev_dbg(&ddata->pdev->dev, "stop()\n"); + + mutex_lock(&ddata->lock); + + in->ops->dsi.bus_lock(in); + + dev_dbg(&ddata->pdev->dev, "power_off()\n"); + + ddata->enabled = 0; + in->ops->dsi.disable_video_output(in, ddata->pixel_channel); + in->ops->disable(in); + mdelay(10); + w677l_reset(dssdev, true); /* activate reset + w677l_regulator(dssdev, false); /* switch power off - after stopping video stream */ + mdelay(20); + /* here we can also power off IOVCC */ + + in->ops->dsi.bus_unlock(in); + + mutex_unlock(&ddata->lock); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +} + +static struct omap_dss_device_ops w677l_dss_ops = { + .connect = w677l_connect, + .disconnect = w677l_disconnect, + + .enable = w677l_enable, + .disable = w677l_disable, + + .get_timings = w677l_get_timings, + .check_timings = w677l_check_timings, +}; + +static struct omap_dss_driver w677l_dss_driver = { + .get_size = w677l_get_size, +}; + +static int w677l_probe_of(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + int gpio; + + dev_dbg(&ddata->pdev->dev, "probe_of()\n"); + + if (!node) + return -ENODEV; + + gpio = of_get_gpio(node, 0); + if (gpio == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (!gpio_is_valid(gpio)) { + dev_err(&pdev->dev, "failed to parse reset gpio (err=%d)\n", gpio); + return gpio; + } + ddata->reset_gpio = gpio; + +#if OPTIONAL + gpio = of_get_gpio(node, 1); + if (!gpio_is_valid(gpio)) { + dev_err(&pdev->dev, "failed to parse regulator gpio (err=%d)\n", gpio); + return gpio; + } + + if (gpio == -EPROBE_DEFER) + return -EPROBE_DEFER; + ddata->regulator_gpio = gpio; +#endif + + return 0; +} + +static int w677l_probe(struct platform_device *pdev) +{ + struct panel_drv_data *ddata; + struct device *dev = &pdev->dev; + struct omap_dss_device *dssdev; + int r; + + dev_dbg(dev, "%s\n", __func__); + + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + platform_set_drvdata(pdev, ddata); + ddata->pdev = pdev; + + ddata->vm = w677l_timings; + + r = w677l_probe_of(pdev); + if (r) { + dev_err(dev, "Failed to probe %d\n", r); + return r; + } + + dssdev = &ddata->dssdev; + dssdev->dev = dev; + dssdev->ops = &w677l_dss_ops; + dssdev->driver = &w677l_dss_driver; + dssdev->type = OMAP_DISPLAY_TYPE_DSI; + dssdev->owner = THIS_MODULE; + dssdev->of_ports = BIT(0); + +#if 0 + dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE | + OMAP_DSS_DISPLAY_CAP_TEAR_ELIM; +#endif + + omapdss_display_init(dssdev); + omapdss_device_register(dssdev); + + mutex_init(&ddata->lock); + + if (gpio_is_valid(ddata->reset_gpio)) { + r = devm_gpio_request_one(dev, ddata->reset_gpio, + GPIOF_DIR_OUT, "panel reset"); + if (r) { + dev_err(dev, "failed to request reset gpio (%d err=%d)\n", ddata->reset_gpio, r); + return r; + } + } + +#if OPTIONAL + if (gpio_is_valid(ddata->regulator_gpio)) { + r = devm_gpio_request_one(dev, ddata->regulator_gpio, + GPIOF_DIR_OUT, "panel DC/DC regulator"); + if (r) { + dev_err(dev, "failed to request regulator gpio (%d err=%d)\n", ddata->regulator_gpio, r); + return r; + } + } +#endif + + dev_dbg(dev, "probe ok\n"); + + return 0; +} + + +static int __exit w677l_remove(struct platform_device *pdev) +{ + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *dssdev = &ddata->dssdev; + + dev_dbg(&pdev->dev, "%s\n", __func__); + + omapdss_device_unregister(dssdev); + + w677l_disable(dssdev); + omapdss_device_disconnect(dssdev->src, dssdev); + + mutex_destroy(&ddata->lock); + + return 0; +} + +static const struct of_device_id w677l_of_match[] = { + { .compatible = "omapdss,boe,btl507212-w677l", }, + { .compatible = "boe,btl507212-w677l", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, w677l_of_match); + +static struct platform_driver w677l_driver = { + .probe = w677l_probe, + .remove = __exit_p(w677l_remove), + .driver = { + .name = "panel-btl507212-w677l", + .of_match_table = w677l_of_match, + .suppress_bind_attrs = true, + }, +}; + +module_platform_driver(w677l_driver); + +MODULE_AUTHOR("H. Nikolaus Schaller hns@goldelico.com"); +MODULE_DESCRIPTION("btl507212-w677l driver"); +MODULE_LICENSE("GPL");
Hi,
On 19/11/18 14:29, H. Nikolaus Schaller wrote:
now as the panel-dpi device is working and Sebastian's patches make our Pyra dsi panel driver at least load successfully, I have tried to find out more about what is going on.
It does probe(), connect() but seems to never call enable(). Hence it is not sending any dcs commands to the panel.
Attached is the current driver code and a boot log. Device tree is the same as for earlier kernels, so it should be ok (unless dsi panels need to define new properties).
I haven't had a chance to look at this (or Sebastian's patches), but one thing to note is that Sebastian has been working and testing with DSI command mode panels. You're using video mode panels. They work quite differently, and video mode panels should work without Sebastian's work (specifically the work that's for command mode panels). However, there were some general DSI fixes in his series, which are likely needed.
So I suspect that either something got broken for DSI video mode panels because of Laurent's patches, or some of Sebastian's patches enable command mode but at the same time break video mode.
Maybe you can spot something which is missing. For example, I have seen that there is a new propery dssdev->caps which we did not touch before.
No, those shouldn't be needed.
Or there is .suppress_bind_attrs = true which I simply did copy from panel-dsi-cm.c assuming it is needed for all dsi panels.
That shouldn't matter. It's just for preventing the userspace from unbinding the panel driver.
Tomi
Hi,
On Mon, Nov 19, 2018 at 02:38:38PM +0200, Tomi Valkeinen wrote:
On 19/11/18 14:29, H. Nikolaus Schaller wrote:
now as the panel-dpi device is working and Sebastian's patches make our Pyra dsi panel driver at least load successfully, I have tried to find out more about what is going on.
It does probe(), connect() but seems to never call enable(). Hence it is not sending any dcs commands to the panel.
Attached is the current driver code and a boot log. Device tree is the same as for earlier kernels, so it should be ok (unless dsi panels need to define new properties).
I haven't had a chance to look at this (or Sebastian's patches), but one thing to note is that Sebastian has been working and testing with DSI command mode panels. You're using video mode panels. They work quite differently, and video mode panels should work without Sebastian's work (specifically the work that's for command mode panels). However, there were some general DSI fixes in his series, which are likely needed.
My series:
patch 1: Simple cleanup patch 2-4: General DSI fixes for the kernel changes in 4.20-rc1. I think they are also relevant for DSI video mode panels. patch 5-6: Only relevant for DSI command mode. It's not really DSI specific and should not make a difference on any outputs not setting OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE.
-- Sebastian
Hi,
Am 19.11.2018 um 14:11 schrieb Sebastian Reichel sre@kernel.org:
Hi,
On Mon, Nov 19, 2018 at 02:38:38PM +0200, Tomi Valkeinen wrote:
On 19/11/18 14:29, H. Nikolaus Schaller wrote:
now as the panel-dpi device is working and Sebastian's patches make our Pyra dsi panel driver at least load successfully, I have tried to find out more about what is going on.
It does probe(), connect() but seems to never call enable(). Hence it is not sending any dcs commands to the panel.
Attached is the current driver code and a boot log. Device tree is the same as for earlier kernels, so it should be ok (unless dsi panels need to define new properties).
I haven't had a chance to look at this (or Sebastian's patches), but one thing to note is that Sebastian has been working and testing with DSI command mode panels. You're using video mode panels. They work quite differently, and video mode panels should work without Sebastian's work (specifically the work that's for command mode panels). However, there were some general DSI fixes in his series, which are likely needed.
My series:
patch 1: Simple cleanup patch 2-4: General DSI fixes for the kernel changes in 4.20-rc1. I think they are also relevant for DSI video mode panels. patch 5-6: Only relevant for DSI command mode. It's not really DSI specific and should not make a difference on any outputs not setting OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE.
-- Sebastian
I have them all included (I think it doesn't harm if they fix things not related to video mode panels) but the video mode panel doesn't call enable(). It did up to 4.19.0 and stopped in 4.20-rc1.
So how can I debug the panel->enable()? Where should it be called from dss core?
Then I could check the conditions to call enable().
BR and thanks, Nikolaus
Hi,
On Mon, Nov 19, 2018 at 02:55:35PM +0100, H. Nikolaus Schaller wrote:
Am 19.11.2018 um 14:11 schrieb Sebastian Reichel sre@kernel.org: On Mon, Nov 19, 2018 at 02:38:38PM +0200, Tomi Valkeinen wrote:
On 19/11/18 14:29, H. Nikolaus Schaller wrote:
My series:
patch 1: Simple cleanup patch 2-4: General DSI fixes for the kernel changes in 4.20-rc1. I think they are also relevant for DSI video mode panels. patch 5-6: Only relevant for DSI command mode. It's not really DSI specific and should not make a difference on any outputs not setting OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE.
-- Sebastian
I have them all included (I think it doesn't harm if they fix things not related to video mode panels)
Ack, assuming I did not write buggy code :)
but the video mode panel doesn't call enable(). It did up to 4.19.0 and stopped in 4.20-rc1.
Ok.
So how can I debug the panel->enable()? Where should it be called from dss core? Then I could check the conditions to call enable().
This is called in omap_encoder_enable(). For debugging I suggest to add "drm.debug=0xf" kernel argument. "drm/omap: don't check dispc timings for DSI" fixes a similar problem for panel-dsi-cm.
-- Sebastian
Hi,
Am 19.11.2018 um 15:18 schrieb Sebastian Reichel sre@kernel.org:
Hi,
On Mon, Nov 19, 2018 at 02:55:35PM +0100, H. Nikolaus Schaller wrote:
Am 19.11.2018 um 14:11 schrieb Sebastian Reichel sre@kernel.org: On Mon, Nov 19, 2018 at 02:38:38PM +0200, Tomi Valkeinen wrote:
On 19/11/18 14:29, H. Nikolaus Schaller wrote:
My series:
patch 1: Simple cleanup patch 2-4: General DSI fixes for the kernel changes in 4.20-rc1. I think they are also relevant for DSI video mode panels. patch 5-6: Only relevant for DSI command mode. It's not really DSI specific and should not make a difference on any outputs not setting OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE.
-- Sebastian
but the video mode panel doesn't call enable(). It did up to 4.19.0 and stopped in 4.20-rc1.
Ok.
So how can I debug the panel->enable()? Where should it be called from dss core? Then I could check the conditions to call enable().
This is called in omap_encoder_enable(). For debugging I suggest to add "drm.debug=0xf" kernel argument. "drm/omap: don't check dispc timings for DSI" fixes a similar problem for panel-dsi-cm.
I have checked that and it looks all reasonable and I did enable the same on the 4.19 kernel and they are basically the same (some not obviously significant diffs in sequence or connector numbers).
The main difference is that after
[ 10.811910] [drm:omap_fbdev_create [omapdrm]] allocated 720x1280 fb
there is no more drm activity on 4.20-rc3 but on 4.19 there is. And there, the panel enable() is called.
Another diff is
4.20: [drm:pdev_probe [omapdrm]] registered 4 planes, 2 crtcs, 2 encoders and 2 connectors 4.19: [drm:pdev_probe [omapdrm]] registered 4 planes, 2 crtcs/encoders/connectors
but I am not sure if this is just a cosmetic change or if encoders/connectors are missing.
So I have no more ideas how to find out why the panel is not becoming enabled.
BR and thanks, Nikolaus
Hi all, I am still fighting to get our DSI panel on OMAP5 working again with 4.20-rc.
Am 20.11.2018 um 10:17 schrieb H. Nikolaus Schaller hns@goldelico.com:
Hi,
Am 19.11.2018 um 15:18 schrieb Sebastian Reichel sre@kernel.org:
Hi,
So how can I debug the panel->enable()? Where should it be called from dss core? Then I could check the conditions to call enable().
This is called in omap_encoder_enable(). For debugging I suggest to add "drm.debug=0xf" kernel argument. "drm/omap: don't check dispc timings for DSI" fixes a similar problem for panel-dsi-cm.
I have checked that and it looks all reasonable and I did enable the same on the 4.19 kernel and they are basically the same (some not obviously significant diffs in sequence or connector numbers).
We now have added Sebastian's latest patches and found a small bug in our panel driver that prevented it from being enabled in v4.20-rc.
And we also include: * drm/omap: Work around missing DISPC in runtime PM handlers
Now the driver becomes enabled, sends DCS commands to the panel and receives register values.
But panel still stays (almost) black and shows no image.
Somtimes there are errors:
[ 20.852710] DSI: omapdss DSI error: VC(0) busy when trying to configure it! [ 20.852714] DSI: omapdss DSI error: VC(1) busy when trying to configure it! [ 20.852718] DSI: omapdss DSI error: VC(2) busy when trying to configure it! [ 20.852721] DSI: omapdss DSI error: VC(3) busy when trying to configure it!
On other occasions we can see a single vertical line displayed which appears to show some X11 or framebuffer content. At least it changes if we launch e.g. xterm and changes back if we stop it.
Now I have tried to use the debugfs to read out the omapdss registers and compare with 4.19.10 (where the panel works).
There is a strange issue with dsi1.
Firstly, /sys/kernel/debug/omapdss/clk does not include the DSI1 clocks. Therefore I can't check if dividers etc. are correct.
And trying to read dsi1_regs ends in a NULL pointer dereference in dsi_runtime_get() called from dsi_dump_dsi_regs().
I have attached both logs.
Please check if you can see something there.
DSI panel Driver code is here:
http://git.goldelico.com/?p=letux-kernel.git;a=blob;f=drivers/gpu/drm/omapdr...
BR and thanks, Nikolaus
On 18/12/18 13:21, H. Nikolaus Schaller wrote:
Hi all, I am still fighting to get our DSI panel on OMAP5 working again with 4.20-rc.
Am 20.11.2018 um 10:17 schrieb H. Nikolaus Schaller hns@goldelico.com:
Hi,
Am 19.11.2018 um 15:18 schrieb Sebastian Reichel sre@kernel.org:
Hi,
So how can I debug the panel->enable()? Where should it be called from dss core? Then I could check the conditions to call enable().
This is called in omap_encoder_enable(). For debugging I suggest to add "drm.debug=0xf" kernel argument. "drm/omap: don't check dispc timings for DSI" fixes a similar problem for panel-dsi-cm.
I have checked that and it looks all reasonable and I did enable the same on the 4.19 kernel and they are basically the same (some not obviously significant diffs in sequence or connector numbers).
We now have added Sebastian's latest patches and found a small bug in our panel driver that prevented it from being enabled in v4.20-rc.
And we also include:
- drm/omap: Work around missing DISPC in runtime PM handlers
That shouldn't be needed with -rc7.
Firstly, /sys/kernel/debug/omapdss/clk does not include the DSI1 clocks. Therefore I can't check if dividers etc. are correct.
It's "dsi1_clks".
And trying to read dsi1_regs ends in a NULL pointer dereference in dsi_runtime_get() called from dsi_dump_dsi_regs().
Ok... I guess we have broken the debug facilities.
How's it with the attached patch?
Tomi
Hi Tomi,
Am 18.12.2018 um 13:05 schrieb Tomi Valkeinen tomi.valkeinen@ti.com:
On 18/12/18 13:21, H. Nikolaus Schaller wrote:
Hi all, I am still fighting to get our DSI panel on OMAP5 working again with 4.20-rc.
Am 20.11.2018 um 10:17 schrieb H. Nikolaus Schaller hns@goldelico.com:
Hi,
Am 19.11.2018 um 15:18 schrieb Sebastian Reichel sre@kernel.org:
Hi,
So how can I debug the panel->enable()? Where should it be called from dss core? Then I could check the conditions to call enable().
This is called in omap_encoder_enable(). For debugging I suggest to add "drm.debug=0xf" kernel argument. "drm/omap: don't check dispc timings for DSI" fixes a similar problem for panel-dsi-cm.
I have checked that and it looks all reasonable and I did enable the same on the 4.19 kernel and they are basically the same (some not obviously significant diffs in sequence or connector numbers).
We now have added Sebastian's latest patches and found a small bug in our panel driver that prevented it from being enabled in v4.20-rc.
And we also include:
- drm/omap: Work around missing DISPC in runtime PM handlers
That shouldn't be needed with -rc7.
Firstly, /sys/kernel/debug/omapdss/clk does not include the DSI1 clocks. Therefore I can't check if dividers etc. are correct.
It's "dsi1_clks".
Ah, I see. Clocks are now in separate files.
I didn't notice because I reused the same script as for 4.19 and didn't update.
And trying to read dsi1_regs ends in a NULL pointer dereference in dsi_runtime_get() called from dsi_dump_dsi_regs().
Ok... I guess we have broken the debug facilities.
How's it with the attached patch?
Yes, this patch solves the NULL pointer problem!
Now I get the attached debug register sets.
Good news: the clock and dividers are set up 100% the same now. Bad news: panel is still black with blakc background (backlight on).
Good for debugging: there aren't many diffs:
iMac:master hns$ diff 4.19.txt 4.20.txt 1c1 < 4.19-10 ---
4.20-rc7
38c38 < DISPC_IRQSTATUS 000000a2 ---
DISPC_IRQSTATUS 00000000
40c40 < DISPC_CONTROL 00018309 ---
DISPC_CONTROL 00018308
43c43 < DISPC_LINE_STATUS 00000288 ---
DISPC_LINE_STATUS 00000fff
93,94c93,94 < DISPC_OVL_BA0(GFX) d0003440 < DISPC_OVL_BA1(GFX) d0003440 ---
DISPC_OVL_BA0(GFX) 7fb00000 DISPC_OVL_BA1(GFX) 7fb00000
97c97 < DISPC_OVL_ATTRIBUTES(GFX) 220040b1 ---
DISPC_OVL_ATTRIBUTES(GFX) 020040b0
100c100 < DISPC_OVL_ROW_INC(GFX) 000034c1 ---
DISPC_OVL_ROW_INC(GFX) 000004c1
374c374 < DSI_VC_CTRL(0) 20808f91 ---
DSI_VC_CTRL(0) 20808f81
404c404 < DSI_DSIPHY_CFG2 b800000f ---
DSI_DSIPHY_CFG2 b83c000f
iMac:master hns$
I think we can probably ignore the LINE_STATUS and the OVL things and IRQSTATUS. So the remaning diffs are
DISPC_CONTROL bit 0 DSI_VC_CTRL(0) bit 4 which may be the problem behind the VC(x) error (but the error did not appear this time) DSI_DSIPHY_CFG2 bits 16-23
BR and thanks, Nikolaus
On 18/12/18 13:21, H. Nikolaus Schaller wrote:
Now the driver becomes enabled, sends DCS commands to the panel and receives register values.
I have a DSI video mode panel that attaches to OMAP5 uevm (I think you sent it to me long time back), and I made some tests with that.
Now, oddly, I couldn't get even an old branch working with that, and I thought it should have worked fine. I get GFX underflows. But if I use kmstest and just show a plane of 400x400, it works. So I think the DSI side is ok, and DISPC side is mostly ok, but for some odd reason, DSS is not able to fetch pixels fast enough.
So I tried to get to the same stage (400x400) working with latest -rc7.
But panel still stays (almost) black and shows no image.
And I got this same issue.
Somtimes there are errors:
[ 20.852710] DSI: omapdss DSI error: VC(0) busy when trying to configure it! [ 20.852714] DSI: omapdss DSI error: VC(1) busy when trying to configure it! [ 20.852718] DSI: omapdss DSI error: VC(2) busy when trying to configure it! [ 20.852721] DSI: omapdss DSI error: VC(3) busy when trying to configure it!
I did not get these...
On other occasions we can see a single vertical line displayed which appears to show some X11 or framebuffer content. At least it changes if we launch e.g. xterm and changes back if we stop it.
And I saw a similar single line too.
Comparing it to the (mostly) working kernel, I found out that the bus flags are again broken. I attached a quick hack patch that seems to fix them, and with that I get a working 400x400 plane, but underflows with full-screen plane.
I couldn't quite figure out how to fix the bus-flags correctly, though.
Tomi
Am 18.12.2018 um 15:24 schrieb Tomi Valkeinen tomi.valkeinen@ti.com:
On 18/12/18 13:21, H. Nikolaus Schaller wrote:
Now the driver becomes enabled, sends DCS commands to the panel and receives register values.
I have a DSI video mode panel that attaches to OMAP5 uevm (I think you sent it to me long time back), and I made some tests with that.
Now, oddly, I couldn't get even an old branch working with that, and I thought it should have worked fine. I get GFX underflows. But if I use kmstest and just show a plane of 400x400, it works. So I think the DSI side is ok, and DISPC side is mostly ok, but for some odd reason, DSS is not able to fetch pixels fast enough.
So I tried to get to the same stage (400x400) working with latest -rc7.
But panel still stays (almost) black and shows no image.
And I got this same issue.
Somtimes there are errors:
[ 20.852710] DSI: omapdss DSI error: VC(0) busy when trying to configure it! [ 20.852714] DSI: omapdss DSI error: VC(1) busy when trying to configure it! [ 20.852718] DSI: omapdss DSI error: VC(2) busy when trying to configure it! [ 20.852721] DSI: omapdss DSI error: VC(3) busy when trying to configure it!
I did not get these...
On other occasions we can see a single vertical line displayed which appears to show some X11 or framebuffer content. At least it changes if we launch e.g. xterm and changes back if we stop it.
And I saw a similar single line too.
Ok, interesting!
Comparing it to the (mostly) working kernel, I found out that the bus flags are again broken. I attached a quick hack patch that seems to fix them, and with that I get a working 400x400 plane, but underflows with full-screen plane.
I couldn't quite figure out how to fix the bus-flags correctly, though.
Hm. For me this patch does not make a difference.
But I think we are close to a solution.
BR, Nikolaus
On 18/12/18 20:09, H. Nikolaus Schaller wrote:
Comparing it to the (mostly) working kernel, I found out that the bus flags are again broken. I attached a quick hack patch that seems to fix them, and with that I get a working 400x400 plane, but underflows with full-screen plane.
I couldn't quite figure out how to fix the bus-flags correctly, though.
Hm. For me this patch does not make a difference.
But I think we are close to a solution.
Ok... Well, the VC stuff works ok for me, as far as I see.
I pushed my hacky test branch to:
git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux.git 4.20/omap5-video-dsi-test
It has one additional fix "fix dsi depopulate", which is needed if you want to unload modules.
The panel r63311 driver probes fine, and can read something via DSI (I get a proper reply for MIPI_DCS_GET_PIXEL_FORMAT), and everything seems to be ok.
The only problem with my branch are FIFO underflows. Apparently these were present on older kernels too, and is probably somehow related to horizontal blanking. 1080x1920 plane causes underflows, but 1000x1920 does not.
Maybe you can compare your driver to the r63311 driver in my branch (although the r63311 driver is a total mess =).
Tomi
Hi Tomi,
Am 19.12.2018 um 10:30 schrieb Tomi Valkeinen tomi.valkeinen@ti.com:
On 18/12/18 20:09, H. Nikolaus Schaller wrote:
Comparing it to the (mostly) working kernel, I found out that the bus flags are again broken. I attached a quick hack patch that seems to fix them, and with that I get a working 400x400 plane, but underflows with full-screen plane.
I couldn't quite figure out how to fix the bus-flags correctly, though.
Hm. For me this patch does not make a difference.
But I think we are close to a solution.
Ok... Well, the VC stuff works ok for me, as far as I see.
I pushed my hacky test branch to:
git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux.git 4.20/omap5-video-dsi-test
It has one additional fix "fix dsi depopulate", which is needed if you want to unload modules.
It seems as if that one already makes the difference!
Well, we
modprobe -r omapdrm modprobe -r drm_kms_helper
and then reload the modules with different rotation parameter.
The panel r63311 driver probes fine, and can read something via DSI (I get a proper reply for MIPI_DCS_GET_PIXEL_FORMAT), and everything seems to be ok.
Good!
Unfortunately I think I have no working r63311 setup any more since we switched to a different panel in the Pyra project.
The only problem with my branch are FIFO underflows. Apparently these were present on older kernels too, and is probably somehow related to horizontal blanking. 1080x1920 plane causes underflows, but 1000x1920 does not.
I haven't seen that but it might be that if horizontal blanking is too short, it might not be enough to send long DCS commands.
Maybe you can compare your driver to the r63311 driver in my branch (although the r63311 driver is a total mess =).
Yes, it is... Anyways, it works which is a good starting point for optimizations :)
I have compared drivers and only found that I try to set dssdev->driver to a private structure. And there was a spurious omapdss_device_disconnect() in the remove() function. But both do not seem to be critical.
Maybe I should clean up my driver (remove some bells and whistles) and submit it upstream as soon as the Pyra is being mass produced and shipped. Then we would have a good reference implementation and hardware to test for future regressions.
So in summary I have added these patches not yet in v4.20-rc7 (plus the panel driver):
b2b80dbeff3a fix dsi depopulate 2ef5e09c79d9 drm/omap: hackfix dsi bus flags b8aedc2b8295 drm/omap: fix crash in DSI debug dumps 5cceb44e03e3 drm/omap: add support for manually updated displays 87dfb765c3be drm/omap: add framedone interrupt support 50f581c97fbe drm/omap: fix incorrect union usage 977e0c2e641f drm/omap: don't check dispc timings for DSI f62952e37eae drm/omap: use DRM_DEBUG_DRIVER instead of CORE
Do you plan to get these fixes already into v4.20.0?
BR and thanks, Nikolaus
On 19/12/18 18:36, H. Nikolaus Schaller wrote:
Hi Tomi,
Am 19.12.2018 um 10:30 schrieb Tomi Valkeinen tomi.valkeinen@ti.com:
On 18/12/18 20:09, H. Nikolaus Schaller wrote:
Comparing it to the (mostly) working kernel, I found out that the bus flags are again broken. I attached a quick hack patch that seems to fix them, and with that I get a working 400x400 plane, but underflows with full-screen plane.
I couldn't quite figure out how to fix the bus-flags correctly, though.
Hm. For me this patch does not make a difference.
But I think we are close to a solution.
Ok... Well, the VC stuff works ok for me, as far as I see.
I pushed my hacky test branch to:
git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux.git 4.20/omap5-video-dsi-test
It has one additional fix "fix dsi depopulate", which is needed if you want to unload modules.
It seems as if that one already makes the difference!
You mean it works now for you?
The only problem with my branch are FIFO underflows. Apparently these were present on older kernels too, and is probably somehow related to horizontal blanking. 1080x1920 plane causes underflows, but 1000x1920 does not.
I haven't seen that but it might be that if horizontal blanking is too short, it might not be enough to send long DCS commands.
Hmm I don't think it's related to DCS, as this happens when no commands are being sent. And the DSI side shouldn't affect DISPC, where the underflows are coming from.
So in summary I have added these patches not yet in v4.20-rc7 (plus the panel driver):
b2b80dbeff3a fix dsi depopulate 2ef5e09c79d9 drm/omap: hackfix dsi bus flags b8aedc2b8295 drm/omap: fix crash in DSI debug dumps 5cceb44e03e3 drm/omap: add support for manually updated displays 87dfb765c3be drm/omap: add framedone interrupt support 50f581c97fbe drm/omap: fix incorrect union usage 977e0c2e641f drm/omap: don't check dispc timings for DSI f62952e37eae drm/omap: use DRM_DEBUG_DRIVER instead of CORE
Do you plan to get these fixes already into v4.20.0?
Are you sure you're on -rc7? At least "drm/omap: fix incorrect union usage" is in -rc7.
Tomi
Hi Tomi,
Am 20.12.2018 um 09:21 schrieb Tomi Valkeinen tomi.valkeinen@ti.com:
On 19/12/18 18:36, H. Nikolaus Schaller wrote:
Hi Tomi,
Am 19.12.2018 um 10:30 schrieb Tomi Valkeinen tomi.valkeinen@ti.com:
On 18/12/18 20:09, H. Nikolaus Schaller wrote:
Comparing it to the (mostly) working kernel, I found out that the bus flags are again broken. I attached a quick hack patch that seems to fix them, and with that I get a working 400x400 plane, but underflows with full-screen plane.
I couldn't quite figure out how to fix the bus-flags correctly, though.
Hm. For me this patch does not make a difference.
But I think we are close to a solution.
Ok... Well, the VC stuff works ok for me, as far as I see.
I pushed my hacky test branch to:
git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux.git 4.20/omap5-video-dsi-test
It has one additional fix "fix dsi depopulate", which is needed if you want to unload modules.
It seems as if that one already makes the difference!
You mean it works now for you?
Yes.
The only problem with my branch are FIFO underflows. Apparently these were present on older kernels too, and is probably somehow related to horizontal blanking. 1080x1920 plane causes underflows, but 1000x1920 does not.
I haven't seen that but it might be that if horizontal blanking is too short, it might not be enough to send long DCS commands.
Hmm I don't think it's related to DCS, as this happens when no commands are being sent. And the DSI side shouldn't affect DISPC, where the underflows are coming from.
So in summary I have added these patches not yet in v4.20-rc7 (plus the panel driver):
b2b80dbeff3a fix dsi depopulate 2ef5e09c79d9 drm/omap: hackfix dsi bus flags b8aedc2b8295 drm/omap: fix crash in DSI debug dumps 5cceb44e03e3 drm/omap: add support for manually updated displays 87dfb765c3be drm/omap: add framedone interrupt support 50f581c97fbe drm/omap: fix incorrect union usage 977e0c2e641f drm/omap: don't check dispc timings for DSI f62952e37eae drm/omap: use DRM_DEBUG_DRIVER instead of CORE
Do you plan to get these fixes already into v4.20.0?
Are you sure you're on -rc7? At least "drm/omap: fix incorrect union usage" is in -rc7.
Well, I am on v4.20-rc7 but after rebasing a private copy of all the patches collected in the last weeks.
Looks as if I had an older version of "drm/omap: fix incorrect union usage" in my branch so that the rebase to -rc7 did not melt it completely away. So there remained an additional safety test in this (local) patch.
BR and thanks, Nikolaus