Discussion:
[PATCH v2 07/12] drm/sun4i: sun6i_mipi_dsi: Setup burst mode
Jagan Teki
2018-11-16 16:39:11 UTC
Permalink
Setting up burst mode display would require to compute
- Horizontal timing edge values to fill burst drq register
- Line, sync values to fill burst line register

Since there is no direct documentation for these computations
the edge and line formulas are taken from BSP code (from linux-sunxi/
drivers/video/sunxi/disp2/disp/de/lowlevel_sun50iw1/de_dsi.c)

line_num = panel->lcd_ht*dsi_pixel_bits[panel->lcd_dsi_format]/
(8*panel->lcd_dsi_lane);
edge1 = sync_point+(panel->lcd_x+panel->lcd_hbp+20)*
dsi_pixel_bits[panel->lcd_dsi_format] /(8*panel->lcd_dsi_lane);
edge1 = (edge1>line_num)?line_num:edge1;
edge0 = edge1+(panel->lcd_x+40)*tcon_div/8;
edge0 = (edge0>line_num)?(edge0-line_num):1;

Signed-off-by: Jagan Teki <***@amarulasolutions.com>
---
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 72 ++++++++++++++++++++++++--
1 file changed, 68 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index 66a01061e51d..0182408f8932 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -364,6 +364,41 @@ static void sun6i_dsi_inst_init(struct sun6i_dsi *dsi,
SUN6I_DSI_INST_JUMP_CFG_NUM(1));
};

+static u32 sun6i_dsi_get_edge1(struct sun6i_dsi *dsi,
+ struct drm_display_mode *mode, u32 sync_point)
+{
+ struct mipi_dsi_device *device = dsi->device;
+ unsigned int bpp = mipi_dsi_pixel_format_to_bpp(device->format);
+ u32 hact_sync_bp;
+
+ /* Horizontal timings duration excluding front porch */
+ hact_sync_bp = (mode->hdisplay + mode->htotal - mode->hsync_start);
+
+ return (sync_point + ((hact_sync_bp + 20) * bpp / (8 * device->lanes)));
+}
+
+static u32 sun6i_dsi_get_edge0(struct sun6i_dsi *dsi,
+ struct drm_display_mode *mode, u32 edge1)
+{
+ struct sun4i_tcon *tcon = dsi->tcon;
+ unsigned long dclk_rate, dclk_parent_rate, tcon0_div;
+
+ dclk_rate = clk_get_rate(tcon->dclk);
+ dclk_parent_rate = clk_get_rate(clk_get_parent(tcon->dclk));
+ tcon0_div = dclk_parent_rate / dclk_rate;
+
+ return (edge1 + (mode->hdisplay + 40) * tcon0_div / 8);
+}
+
+static u32 sun6i_dsi_get_line_num(struct sun6i_dsi *dsi,
+ struct drm_display_mode *mode)
+{
+ struct mipi_dsi_device *device = dsi->device;
+ unsigned int bpp = mipi_dsi_pixel_format_to_bpp(device->format);
+
+ return (mode->htotal * bpp / (8 * device->lanes));
+}
+
static int sun6i_dsi_get_drq(struct sun6i_dsi *dsi,
struct drm_display_mode *mode)
{
@@ -499,9 +534,32 @@ static u16 sun6i_dsi_get_video_start_delay(struct sun6i_dsi *dsi,
static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi,
struct drm_display_mode *mode)
{
- regmap_write(dsi->regs, SUN6I_DSI_TCON_DRQ_REG,
- SUN6I_DSI_TCON_DRQ_ENABLE_MODE |
- SUN6I_DSI_TCON_DRQ_SET(sun6i_dsi_get_drq(dsi, mode)));
+ struct mipi_dsi_device *device = dsi->device;
+ u32 sync_point = 40;
+ u32 line_num = sun6i_dsi_get_line_num(dsi, mode);
+ u32 edge1 = sun6i_dsi_get_edge1(dsi, mode, sync_point);
+ u32 edge0 = sun6i_dsi_get_edge0(dsi, mode, edge1);
+ u32 val;
+
+ if (edge1 > line_num)
+ edge1 = line_num;
+
+ if (edge0 > line_num)
+ edge0 -= line_num;
+ else
+ edge0 = 1;
+
+ regmap_write(dsi->regs, SUN6I_DSI_BURST_DRQ_REG,
+ SUN6I_DSI_BURST_DRQ_EDGE1(edge1) |
+ SUN6I_DSI_BURST_DRQ_EDGE0(edge0));
+ regmap_write(dsi->regs, SUN6I_DSI_BURST_LINE_REG,
+ SUN6I_DSI_BURST_LINE_NUM(line_num) |
+ SUN6I_DSI_BURST_LINE_SYNC_POINT(sync_point));
+
+ /* enable burst mode */
+ regmap_read(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, &val);
+ val |= SUN6I_DSI_BASIC_CTL_VIDEO_BURST;
+ regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, val);
}

static void sun6i_dsi_setup_inst_loop(struct sun6i_dsi *dsi,
@@ -726,7 +784,13 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
SUN6I_DSI_BASIC_CTL1_VIDEO_PRECISION |
SUN6I_DSI_BASIC_CTL1_VIDEO_MODE);

- sun6i_dsi_setup_burst(dsi, mode);
+ regmap_write(dsi->regs, SUN6I_DSI_TCON_DRQ_REG,
+ SUN6I_DSI_TCON_DRQ_ENABLE_MODE |
+ SUN6I_DSI_TCON_DRQ_SET(sun6i_dsi_get_drq(dsi, mode)));
+
+ if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ sun6i_dsi_setup_burst(dsi, mode);
+
sun6i_dsi_setup_inst_loop(dsi, mode);
sun6i_dsi_setup_format(dsi, mode);
sun6i_dsi_setup_timings(dsi, mode);
--
2.18.0.321.gffc6fa0e3
Jagan Teki
2018-11-16 16:39:14 UTC
Permalink
Feiyang FY07024DI26A30-D is 1024x600, 4-lane MIPI-DSI LCD panel.

Add dt-bingings for it.

Signed-off-by: Jagan Teki <***@amarulasolutions.com>
Reviewed-by: Rob Herring <***@kernel.org>
---
.../display/panel/feiyang,fy07024di26a30d.txt | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/panel/feiyang,fy07024di26a30d.txt

diff --git a/Documentation/devicetree/bindings/display/panel/feiyang,fy07024di26a30d.txt b/Documentation/devicetree/bindings/display/panel/feiyang,fy07024di26a30d.txt
new file mode 100644
index 000000000000..82caa7b65ae8
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/feiyang,fy07024di26a30d.txt
@@ -0,0 +1,20 @@
+Feiyang FY07024DI26A30-D 7" MIPI-DSI LCD Panel
+
+Required properties:
+- compatible: must be "feiyang,fy07024di26a30d"
+- reg: DSI virtual channel used by that screen
+- avdd-supply: analog regulator dc1 switch
+- dvdd-supply: 3v3 digital regulator
+- reset-gpios: a GPIO phandle for the reset pin
+
+Optional properties:
+- backlight: phandle for the backlight control.
+
+***@0 {
+ compatible = "feiyang,fy07024di26a30d";
+ reg = <0>;
+ avdd-supply = <&reg_dc1sw>;
+ dvdd-supply = <&reg_dldo2>;
+ reset-gpios = <&pio 3 24 GPIO_ACTIVE_HIGH>; /* LCD-RST: PD24 */
+ backlight = <&backlight>;
+};
--
2.18.0.321.gffc6fa0e3
Jagan Teki
2018-11-16 16:39:07 UTC
Permalink
Burst mode display timings are different from convectional
video mode so update the horizontal and vertical timings.

Reference code taken from BSP (from linux-sunxi/
drivers/video/sunxi/disp2/disp/de/lowlevel_sun50iw1/de_dsi.c)

dsi_hsa = 0;
dsi_hbp = 0;
dsi_hact = x*dsi_pixel_bits[format]/8;
dsi_hblk = dsi_hact;
dsi_hfp = 0;
dsi_vblk = 0;

Signed-off-by: Jagan Teki <***@amarulasolutions.com>
---
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 128 +++++++++++++++----------
1 file changed, 80 insertions(+), 48 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index 3ac002c4d8b3..efd08bfd0cb8 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -153,6 +153,14 @@

#define SUN6I_DSI_CMD_TX_REG(n) (0x300 + (n) * 0x04)

+struct sun6i_dsi_timings {
+ u16 hsa;
+ u16 hbp;
+ u16 hblk;
+ u16 hfp;
+ u16 vblk;
+};
+
enum sun6i_dsi_start_inst {
DSI_START_LPRX,
DSI_START_LPTX,
@@ -379,6 +387,60 @@ static u16 sun6i_dsi_get_timings_vblk(struct sun6i_dsi *dsi,
return vblk;
}

+static void sun6i_dsi_get_timings(struct sun6i_dsi *dsi,
+ struct drm_display_mode *mode,
+ struct sun6i_dsi_timings *timings)
+{
+ struct mipi_dsi_device *device = dsi->device;
+ unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;
+ u16 hsa, hbp, hblk, hfp, vblk;
+
+ /*
+ * A sync period is composed of a blanking packet (4 bytes +
+ * payload + 2 bytes) and a sync event packet (4 bytes).
+ * Its minimal size is therefore 10 bytes
+ */
+#define HSA_PACKET_OVERHEAD 10
+ hsa = max((unsigned int)HSA_PACKET_OVERHEAD,
+ (mode->hsync_end - mode->hsync_start) * Bpp -
+ HSA_PACKET_OVERHEAD);
+
+ /*
+ * The backporch is set using a blanking packet (4 bytes +
+ * payload + 2 bytes). Its minimal size is therefore 6 bytes
+ */
+#define HBP_PACKET_OVERHEAD 6
+ hbp = max((unsigned int)HBP_PACKET_OVERHEAD,
+ (mode->htotal - mode->hsync_end) * Bpp - HBP_PACKET_OVERHEAD);
+
+ /*
+ * hblk seems to be the line + porches length.
+ * The blank is set using a blanking packet (4 bytes + 4 bytes
+ * + payload + 2 bytes). So minimal size is 10 bytes
+ */
+#define HBLK_PACKET_OVERHEAD 10
+ hblk = max((unsigned int)HBLK_PACKET_OVERHEAD,
+ (mode->htotal - (mode->hsync_end - mode->hsync_start)) *
+ Bpp - HBLK_PACKET_OVERHEAD);
+
+ /*
+ * The frontporch is set using a blanking packet (4 bytes +
+ * payload + 2 bytes). Its minimal size is therefore 6 bytes
+ */
+#define HFP_PACKET_OVERHEAD 6
+ hfp = max((unsigned int)HFP_PACKET_OVERHEAD,
+ (mode->hsync_start - mode->hdisplay) * Bpp -
+ HFP_PACKET_OVERHEAD);
+
+ vblk = sun6i_dsi_get_timings_vblk(dsi, mode, hblk);
+
+ timings->hsa = hsa;
+ timings->hbp = hbp;
+ timings->hblk = hblk;
+ timings->hfp = hfp;
+ timings->vblk = vblk;
+}
+
static u16 sun6i_dsi_setup_inst_delay(struct sun6i_dsi *dsi,
struct drm_display_mode *mode)
{
@@ -506,52 +568,22 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi,
{
struct mipi_dsi_device *device = dsi->device;
unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;
- u16 hbp, hfp, hsa, hblk, vblk;
+ struct sun6i_dsi_timings timings;
size_t bytes;
u8 *buffer;

/* Do all timing calculations up front to allocate buffer space */

- /*
- * A sync period is composed of a blanking packet (4 bytes +
- * payload + 2 bytes) and a sync event packet (4 bytes). Its
- * minimal size is therefore 10 bytes
- */
-#define HSA_PACKET_OVERHEAD 10
- hsa = max((unsigned int)HSA_PACKET_OVERHEAD,
- (mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD);
+ memset(&timings, 0, sizeof(timings));

- /*
- * The backporch is set using a blanking packet (4 bytes +
- * payload + 2 bytes). Its minimal size is therefore 6 bytes
- */
-#define HBP_PACKET_OVERHEAD 6
- hbp = max((unsigned int)HBP_PACKET_OVERHEAD,
- (mode->htotal - mode->hsync_end) * Bpp - HBP_PACKET_OVERHEAD);
-
- /*
- * The frontporch is set using a blanking packet (4 bytes +
- * payload + 2 bytes). Its minimal size is therefore 6 bytes
- */
-#define HFP_PACKET_OVERHEAD 6
- hfp = max((unsigned int)HFP_PACKET_OVERHEAD,
- (mode->hsync_start - mode->hdisplay) * Bpp -
- HFP_PACKET_OVERHEAD);
-
- /*
- * hblk seems to be the line + porches length.
- * The blank is set using a blanking packet (4 bytes + 4 bytes +
- * payload + 2 bytes). So minimal size is 10 bytes
- */
-#define HBLK_PACKET_OVERHEAD 10
- hblk = max((unsigned int)HBLK_PACKET_OVERHEAD,
- (mode->htotal - (mode->hsync_end - mode->hsync_start)) *
- Bpp - HBLK_PACKET_OVERHEAD);
-
- vblk = sun6i_dsi_get_timings_vblk(dsi, mode, hblk);
+ if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ timings.hblk = (mode->hdisplay * Bpp);
+ else
+ sun6i_dsi_get_timings(dsi, mode, &timings);

/* How many bytes do we need to send all payloads? */
- bytes = max_t(size_t, max(max(hfp, hblk), max(hsa, hbp)), vblk);
+ bytes = max_t(size_t, max(max(timings.hfp, timings.hblk),
+ max(timings.hsa, timings.hbp)), timings.vblk);
buffer = kmalloc(bytes, GFP_KERNEL);
if (WARN_ON(!buffer))
return;
@@ -590,33 +622,33 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi,

/* sync */
regmap_write(dsi->regs, SUN6I_DSI_BLK_HSA0_REG,
- sun6i_dsi_build_blk0_pkt(device->channel, hsa));
+ sun6i_dsi_build_blk0_pkt(device->channel, timings.hsa));
regmap_write(dsi->regs, SUN6I_DSI_BLK_HSA1_REG,
- sun6i_dsi_build_blk1_pkt(0, buffer, hsa));
+ sun6i_dsi_build_blk1_pkt(0, buffer, timings.hsa));

/* backporch */
regmap_write(dsi->regs, SUN6I_DSI_BLK_HBP0_REG,
- sun6i_dsi_build_blk0_pkt(device->channel, hbp));
+ sun6i_dsi_build_blk0_pkt(device->channel, timings.hbp));
regmap_write(dsi->regs, SUN6I_DSI_BLK_HBP1_REG,
- sun6i_dsi_build_blk1_pkt(0, buffer, hbp));
+ sun6i_dsi_build_blk1_pkt(0, buffer, timings.hbp));

/* frontporch */
regmap_write(dsi->regs, SUN6I_DSI_BLK_HFP0_REG,
- sun6i_dsi_build_blk0_pkt(device->channel, hfp));
+ sun6i_dsi_build_blk0_pkt(device->channel, timings.hfp));
regmap_write(dsi->regs, SUN6I_DSI_BLK_HFP1_REG,
- sun6i_dsi_build_blk1_pkt(0, buffer, hfp));
+ sun6i_dsi_build_blk1_pkt(0, buffer, timings.hfp));

/* hblk */
regmap_write(dsi->regs, SUN6I_DSI_BLK_HBLK0_REG,
- sun6i_dsi_build_blk0_pkt(device->channel, hblk));
+ sun6i_dsi_build_blk0_pkt(device->channel, timings.hblk));
regmap_write(dsi->regs, SUN6I_DSI_BLK_HBLK1_REG,
- sun6i_dsi_build_blk1_pkt(0, buffer, hblk));
+ sun6i_dsi_build_blk1_pkt(0, buffer, timings.hblk));

/* vblk */
regmap_write(dsi->regs, SUN6I_DSI_BLK_VBLK0_REG,
- sun6i_dsi_build_blk0_pkt(device->channel, vblk));
+ sun6i_dsi_build_blk0_pkt(device->channel, timings.vblk));
regmap_write(dsi->regs, SUN6I_DSI_BLK_VBLK1_REG,
- sun6i_dsi_build_blk1_pkt(0, buffer, vblk));
+ sun6i_dsi_build_blk1_pkt(0, buffer, timings.vblk));

kfree(buffer);
}
--
2.18.0.321.gffc6fa0e3
Maxime Ripard
2018-11-19 08:30:36 UTC
Permalink
Post by Jagan Teki
Burst mode display timings are different from convectional
video mode so update the horizontal and vertical timings.
Reference code taken from BSP (from linux-sunxi/
drivers/video/sunxi/disp2/disp/de/lowlevel_sun50iw1/de_dsi.c)
dsi_hsa = 0;
dsi_hbp = 0;
dsi_hact = x*dsi_pixel_bits[format]/8;
dsi_hblk = dsi_hact;
dsi_hfp = 0;
dsi_vblk = 0;
How is that matching the code you have in the rest of your patch?

Maxime
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Jagan Teki
2018-11-19 11:00:37 UTC
Permalink
Post by Maxime Ripard
Post by Jagan Teki
Burst mode display timings are different from convectional
video mode so update the horizontal and vertical timings.
Reference code taken from BSP (from linux-sunxi/
drivers/video/sunxi/disp2/disp/de/lowlevel_sun50iw1/de_dsi.c)
dsi_hsa = 0;
dsi_hbp = 0;
dsi_hact = x*dsi_pixel_bits[format]/8;
dsi_hblk = dsi_hact;
dsi_hfp = 0;
dsi_vblk = 0;
How is that matching the code you have in the rest of your patch?
+ if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ timings.hblk = (mode->hdisplay * Bpp);
+ else
+ sun6i_dsi_get_timings(dsi, mode, &timings);

It's again your request to "keep the couple of function to make more readable"
Maxime Ripard
2018-11-20 15:45:32 UTC
Permalink
Post by Jagan Teki
Post by Maxime Ripard
Post by Jagan Teki
Burst mode display timings are different from convectional
video mode so update the horizontal and vertical timings.
Reference code taken from BSP (from linux-sunxi/
drivers/video/sunxi/disp2/disp/de/lowlevel_sun50iw1/de_dsi.c)
dsi_hsa = 0;
dsi_hbp = 0;
dsi_hact = x*dsi_pixel_bits[format]/8;
dsi_hblk = dsi_hact;
dsi_hfp = 0;
dsi_vblk = 0;
How is that matching the code you have in the rest of your patch?
+ if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ timings.hblk = (mode->hdisplay * Bpp);
+ else
+ sun6i_dsi_get_timings(dsi, mode, &timings);
It's again your request to "keep the couple of function to make more readable"
That function in particular doesn't make it much more readable, but
more importantly, your commit log doesn't match what you code does.

Maxime
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Jagan Teki
2018-11-20 16:22:23 UTC
Permalink
Post by Maxime Ripard
Post by Jagan Teki
Post by Maxime Ripard
Post by Jagan Teki
Burst mode display timings are different from convectional
video mode so update the horizontal and vertical timings.
Reference code taken from BSP (from linux-sunxi/
drivers/video/sunxi/disp2/disp/de/lowlevel_sun50iw1/de_dsi.c)
dsi_hsa = 0;
dsi_hbp = 0;
dsi_hact = x*dsi_pixel_bits[format]/8;
dsi_hblk = dsi_hact;
dsi_hfp = 0;
dsi_vblk = 0;
How is that matching the code you have in the rest of your patch?
+ if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ timings.hblk = (mode->hdisplay * Bpp);
+ else
+ sun6i_dsi_get_timings(dsi, mode, &timings);
It's again your request to "keep the couple of function to make more readable"
That function in particular doesn't make it much more readable, but
more importantly, your commit log doesn't match what you code does.
May be I can update the commit message if the function is OK. for
burst most of the timings are 0 so I used structure with memset to
keep not assigning 0's explicitly. otherwise do you have any
suggestions, please let me know.
Maxime Ripard
2018-11-27 10:07:24 UTC
Permalink
Post by Jagan Teki
Post by Maxime Ripard
Post by Jagan Teki
Post by Maxime Ripard
Post by Jagan Teki
Burst mode display timings are different from convectional
video mode so update the horizontal and vertical timings.
Reference code taken from BSP (from linux-sunxi/
drivers/video/sunxi/disp2/disp/de/lowlevel_sun50iw1/de_dsi.c)
dsi_hsa = 0;
dsi_hbp = 0;
dsi_hact = x*dsi_pixel_bits[format]/8;
dsi_hblk = dsi_hact;
dsi_hfp = 0;
dsi_vblk = 0;
How is that matching the code you have in the rest of your patch?
+ if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ timings.hblk = (mode->hdisplay * Bpp);
+ else
+ sun6i_dsi_get_timings(dsi, mode, &timings);
It's again your request to "keep the couple of function to make more readable"
That function in particular doesn't make it much more readable, but
more importantly, your commit log doesn't match what you code does.
May be I can update the commit message if the function is OK. for
burst most of the timings are 0 so I used structure with memset to
keep not assigning 0's explicitly. otherwise do you have any
suggestions, please let me know.
Drop the part that move the timings setup to another function in this
patch.

Thanks!
Maxime
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Jagan Teki
2018-11-16 16:39:05 UTC
Permalink
Loop N1 instruction delay for burst mode lcd panel are
computed as per BSP code.

Reference code is available in BSP (from linux-sunxi
drivers/video/sunxi/disp2/disp/de/lowlevel_sun50iw1/de_dsi.c)

dsi_dev[sel]->dsi_inst_loop_num.bits.loop_n1=
(panel->lcd_ht-panel->lcd_x)*(150)/(panel->lcd_dclk_freq*8) - 50;
=> (((mode->htotal - mode->hdisplay) * 150) /
((mode->clock / 1000) * 8)) - 50;

So use the similar computation for loop N1 delay.

Signed-off-by: Jagan Teki <***@amarulasolutions.com>
---
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index def145086a5c..43ab7127d428 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -379,6 +379,24 @@ static u16 sun6i_dsi_get_timings_vblk(struct sun6i_dsi *dsi,
return vblk;
}

+static u16 sun6i_dsi_setup_inst_delay(struct sun6i_dsi *dsi,
+ struct drm_display_mode *mode)
+{
+ struct mipi_dsi_device *device = dsi->device;
+ u32 hsync_porch, dclk;
+ u16 delay;
+
+ hsync_porch = (mode->htotal - mode->hdisplay);
+ dclk = (mode->clock / 1000);
+
+ if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ delay = ((hsync_porch * 150) / (dclk * 8)) - 50;
+ else
+ delay = 50 - 1;
+
+ return delay;
+}
+
static u16 sun6i_dsi_get_video_start_delay(struct sun6i_dsi *dsi,
struct drm_display_mode *mode)
{
@@ -418,7 +436,7 @@ static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi,
static void sun6i_dsi_setup_inst_loop(struct sun6i_dsi *dsi,
struct drm_display_mode *mode)
{
- u16 delay = 50 - 1;
+ u16 delay = sun6i_dsi_setup_inst_delay(dsi, mode);

regmap_write(dsi->regs, SUN6I_DSI_INST_LOOP_NUM_REG(0),
SUN6I_DSI_INST_LOOP_NUM_N0(50 - 1) |
--
2.18.0.321.gffc6fa0e3
Maxime Ripard
2018-11-19 08:27:07 UTC
Permalink
Post by Jagan Teki
Loop N1 instruction delay for burst mode lcd panel are
computed as per BSP code.
Reference code is available in BSP (from linux-sunxi
drivers/video/sunxi/disp2/disp/de/lowlevel_sun50iw1/de_dsi.c)
dsi_dev[sel]->dsi_inst_loop_num.bits.loop_n1=
(panel->lcd_ht-panel->lcd_x)*(150)/(panel->lcd_dclk_freq*8) - 50;
=> (((mode->htotal - mode->hdisplay) * 150) /
((mode->clock / 1000) * 8)) - 50;
So use the similar computation for loop N1 delay.
*why* are you doing this? What is it fixing? on which devices?

Maxime
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Jagan Teki
2018-11-19 10:58:29 UTC
Permalink
Post by Maxime Ripard
Post by Jagan Teki
Loop N1 instruction delay for burst mode lcd panel are
computed as per BSP code.
Reference code is available in BSP (from linux-sunxi
drivers/video/sunxi/disp2/disp/de/lowlevel_sun50iw1/de_dsi.c)
dsi_dev[sel]->dsi_inst_loop_num.bits.loop_n1=
(panel->lcd_ht-panel->lcd_x)*(150)/(panel->lcd_dclk_freq*8) - 50;
=> (((mode->htotal - mode->hdisplay) * 150) /
((mode->clock / 1000) * 8)) - 50;
So use the similar computation for loop N1 delay.
*why* are you doing this? What is it fixing? on which devices?
You mentioned the separate function to compute the delay for all modes
[1], ie what I did. did I missing anything?

[1] https://patchwork.kernel.org/patch/10666599/
Maxime Ripard
2018-11-20 13:23:57 UTC
Permalink
Post by Jagan Teki
Post by Maxime Ripard
Post by Jagan Teki
Loop N1 instruction delay for burst mode lcd panel are
computed as per BSP code.
Reference code is available in BSP (from linux-sunxi
drivers/video/sunxi/disp2/disp/de/lowlevel_sun50iw1/de_dsi.c)
dsi_dev[sel]->dsi_inst_loop_num.bits.loop_n1=
(panel->lcd_ht-panel->lcd_x)*(150)/(panel->lcd_dclk_freq*8) - 50;
=> (((mode->htotal - mode->hdisplay) * 150) /
((mode->clock / 1000) * 8)) - 50;
So use the similar computation for loop N1 delay.
*why* are you doing this? What is it fixing? on which devices?
You mentioned the separate function to compute the delay for all modes
[1], ie what I did. did I missing anything?
You're missing that you are never explaining why that patch is needed
in the first place. Or answering the question I asked a couple of
lines above.

Maxime
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Maxime Ripard
2018-11-20 15:58:26 UTC
Permalink
Post by Maxime Ripard
Post by Jagan Teki
Post by Maxime Ripard
Post by Jagan Teki
Loop N1 instruction delay for burst mode lcd panel are
computed as per BSP code.
Reference code is available in BSP (from linux-sunxi
drivers/video/sunxi/disp2/disp/de/lowlevel_sun50iw1/de_dsi.c)
dsi_dev[sel]->dsi_inst_loop_num.bits.loop_n1=
(panel->lcd_ht-panel->lcd_x)*(150)/(panel->lcd_dclk_freq*8) - 50;
=> (((mode->htotal - mode->hdisplay) * 150) /
((mode->clock / 1000) * 8)) - 50;
So use the similar computation for loop N1 delay.
*why* are you doing this? What is it fixing? on which devices?
You mentioned the separate function to compute the delay for all modes
[1], ie what I did. did I missing anything?
You're missing that you are never explaining why that patch is needed
in the first place. Or answering the question I asked a couple of
lines above.
OK.
The instruction delay varies between video and burst mode. for burst
mode panels it is computed based on the panel clock along with
horizontal sync+porch timings.
You're still stating a fact. What issue, that you experienced, are you
trying to solve here?

Maxime
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Vasily Khoruzhick
2018-11-20 16:01:36 UTC
Permalink
Post by Maxime Ripard
Post by Maxime Ripard
Post by Jagan Teki
Post by Maxime Ripard
Post by Jagan Teki
Loop N1 instruction delay for burst mode lcd panel are
computed as per BSP code.
Reference code is available in BSP (from linux-sunxi
drivers/video/sunxi/disp2/disp/de/lowlevel_sun50iw1/de_dsi.c)
dsi_dev[sel]->dsi_inst_loop_num.bits.loop_n1=
(panel->lcd_ht-panel->lcd_x)*(150)/(panel->lcd_dclk_freq*8) - 50;
=> (((mode->htotal - mode->hdisplay) * 150) /
((mode->clock / 1000) * 8)) - 50;
So use the similar computation for loop N1 delay.
*why* are you doing this? What is it fixing? on which devices?
You mentioned the separate function to compute the delay for all modes
[1], ie what I did. did I missing anything?
You're missing that you are never explaining why that patch is needed
in the first place. Or answering the question I asked a couple of
lines above.
OK.
The instruction delay varies between video and burst mode. for burst
mode panels it is computed based on the panel clock along with
horizontal sync+porch timings.
You're still stating a fact. What issue, that you experienced, are you
trying to solve here?
IIRC that's what BSP driver does. Otherwise panels that use burst mode
just don't work.
Post by Maxime Ripard
Maxime
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Jagan Teki
2018-11-20 16:19:45 UTC
Permalink
Post by Maxime Ripard
Post by Maxime Ripard
Post by Jagan Teki
Post by Maxime Ripard
Post by Jagan Teki
Loop N1 instruction delay for burst mode lcd panel are
computed as per BSP code.
Reference code is available in BSP (from linux-sunxi
drivers/video/sunxi/disp2/disp/de/lowlevel_sun50iw1/de_dsi.c)
dsi_dev[sel]->dsi_inst_loop_num.bits.loop_n1=
(panel->lcd_ht-panel->lcd_x)*(150)/(panel->lcd_dclk_freq*8) - 50;
=> (((mode->htotal - mode->hdisplay) * 150) /
((mode->clock / 1000) * 8)) - 50;
So use the similar computation for loop N1 delay.
*why* are you doing this? What is it fixing? on which devices?
You mentioned the separate function to compute the delay for all modes
[1], ie what I did. did I missing anything?
You're missing that you are never explaining why that patch is needed
in the first place. Or answering the question I asked a couple of
lines above.
OK.
The instruction delay varies between video and burst mode. for burst
mode panels it is computed based on the panel clock along with
horizontal sync+porch timings.
You're still stating a fact. What issue, that you experienced, are you
trying to solve here?
This change is specific for burst mode instruction delay. for
non-burst it is 50 - 1 and for burst mode it is computed as mentioned
in commit message. Both things are available in BSP code. and without
this burst mode panels not working with existing (50 - 1)
Jagan Teki
2018-11-16 16:39:06 UTC
Permalink
Instruction loop selection would require before writing
loop number registers, so enable idle, LP11 bits on
loop selection register.

Reference code available in BSP (from linux-sunxi/
drivers/video/sunxi/disp2/disp/de/lowlevel_sun50iw1/de_dsi.c)

(dsi_dev[sel]->dsi_inst_loop_sel.dwval = 2<<(4*DSI_INST_ID_LP11) |
3<<(4*DSI_INST_ID_DLY);

Signed-off-by: Jagan Teki <***@amarulasolutions.com>
---
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index 43ab7127d428..3ac002c4d8b3 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -438,6 +438,9 @@ static void sun6i_dsi_setup_inst_loop(struct sun6i_dsi *dsi,
{
u16 delay = sun6i_dsi_setup_inst_delay(dsi, mode);

+ regmap_write(dsi->regs, SUN6I_DSI_INST_LOOP_SEL_REG,
+ DSI_INST_ID_HSC << (4 * DSI_INST_ID_LP11) |
+ DSI_INST_ID_HSD << (4 * DSI_INST_ID_DLY));
regmap_write(dsi->regs, SUN6I_DSI_INST_LOOP_NUM_REG(0),
SUN6I_DSI_INST_LOOP_NUM_N0(50 - 1) |
SUN6I_DSI_INST_LOOP_NUM_N1(delay));
--
2.18.0.321.gffc6fa0e3
Jagan Teki
2018-11-16 16:39:12 UTC
Permalink
For 4-lane, burst mode panels would need to enable 2byte trail_fill
along with filling trail_env in dsi base control register.

Similar reference code avialable in BSP (from linux-sunxi/
drivers/video/sunxi/disp2/disp/de/lowlevel_sun50iw1/de_dsi.c)

if (panel->lcd_dsi_lane == 4)
{
dsi_dev[sel]->dsi_basic_ctl.bits.trail_inv = 0xc;
dsi_dev[sel]->dsi_basic_ctl.bits.trail_fill = 1;
}

Signed-off-by: Jagan Teki <***@amarulasolutions.com>
---
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index 0182408f8932..22d2987c3298 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -34,6 +34,8 @@
#define SUN6I_DSI_CTL_EN BIT(0)

#define SUN6I_DSI_BASIC_CTL_REG 0x00c
+#define SUN6I_DSI_BASIC_CTL_TRAIL_INV(n) (((n) & 0xf) << 4)
+#define SUN6I_DSI_BASIC_CTL_TRAIL_FILL BIT(3)
#define SUN6I_DSI_BASIC_CTL_HBP_DIS BIT(2)
#define SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS BIT(1)
#define SUN6I_DSI_BASIC_CTL_VIDEO_BURST BIT(0)
@@ -559,6 +561,10 @@ static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi,
/* enable burst mode */
regmap_read(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, &val);
val |= SUN6I_DSI_BASIC_CTL_VIDEO_BURST;
+ if (device->lanes == 4) {
+ val |= SUN6I_DSI_BASIC_CTL_TRAIL_INV(0xc);
+ val |= SUN6I_DSI_BASIC_CTL_TRAIL_FILL;
+ }
regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, val);
}
--
2.18.0.321.gffc6fa0e3
Jagan Teki
2018-11-16 16:39:16 UTC
Permalink
Feiyang FY07024DI26A30-D MIPI_DSI panel is desiged to attach with
DSI connector on pine64 boards, enable the same for pine64 LTS.

DSI panel connected via board DSI port with,
- DC1SW as AVDD supply
- DLDO2 as DVDD supply
- DLDO1 as VCC-DSI supply
- PD24 gpio for reset pin
- PH10 gpio for backlight enable pin

Signed-off-by: Jagan Teki <***@amarulasolutions.com>
---
.../dts/allwinner/sun50i-a64-pine64-lts.dts | 37 +++++++++++++++++++
1 file changed, 37 insertions(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-lts.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-lts.dts
index 72d6961dc312..9e230c612799 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-lts.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-lts.dts
@@ -5,9 +5,46 @@
*/

#include "sun50i-a64-sopine-baseboard.dts"
+#include <dt-bindings/pwm/pwm.h>

/ {
model = "Pine64 LTS";
compatible = "pine64,pine64-lts", "allwinner,sun50i-r18",
"allwinner,sun50i-a64";
+
+ backlight: backlight {
+ compatible = "pwm-backlight";
+ pwms = <&r_pwm 0 50000 PWM_POLARITY_INVERTED>;
+ brightness-levels = <1 2 4 8 16 32 64 128 512>;
+ default-brightness-level = <8>;
+ enable-gpios = <&pio 7 10 GPIO_ACTIVE_HIGH>; /* LCD-BL-EN: PH10 */
+ };
+};
+
+&de {
+ status = "okay";
+};
+
+&dphy {
+ status = "okay";
+};
+
+&dsi {
+ vcc-dsi-supply = <&reg_dldo1>; /* VCC3V3-DSI */
+ status = "okay";
+
+ ***@0 {
+ compatible = "feiyang,fy07024di26a30d";
+ reg = <0>;
+ avdd-supply = <&reg_dc1sw>; /* VCC-LCD */
+ dvdd-supply = <&reg_dldo2>; /* VCC-MIPI */
+ reset-gpios = <&pio 3 24 GPIO_ACTIVE_HIGH>; /* LCD-RST: PD24 */
+ backlight = <&backlight>;
+ };
+};
+
+&r_pwm {
+ pinctrl-names = "default";
+ pinctrl-0 = <&r_pwm_pin>;
+ status = "okay";
};
--
2.18.0.321.gffc6fa0e3
Jagan Teki
2018-11-16 16:39:13 UTC
Permalink
Horizontal back porch, sync active and sync end bits are
needed to disable for burst mode panel operations.

So, disable them via dsi base control register.

Signed-off-by: Jagan Teki <***@amarulasolutions.com>
---
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index 22d2987c3298..20a1de8493e0 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -644,15 +644,21 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi,
struct sun6i_dsi_timings timings;
size_t bytes;
u8 *buffer;
+ u32 val = 0;

/* Do all timing calculations up front to allocate buffer space */

memset(&timings, 0, sizeof(timings));

- if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
timings.hblk = (mode->hdisplay * Bpp);
- else
+
+ regmap_read(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, &val);
+ val |= SUN6I_DSI_BASIC_CTL_HBP_DIS;
+ val |= SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS;
+ } else {
sun6i_dsi_get_timings(dsi, mode, &timings);
+ }

/* How many bytes do we need to send all payloads? */
bytes = max_t(size_t, max(max(timings.hfp, timings.hblk),
@@ -661,7 +667,7 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi,
if (WARN_ON(!buffer))
return;

- regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, 0);
+ regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, val);

regmap_write(dsi->regs, SUN6I_DSI_SYNC_HSS_REG,
sun6i_dsi_build_sync_pkt(MIPI_DSI_H_SYNC_START,
--
2.18.0.321.gffc6fa0e3
Jagan Teki
2018-11-16 16:39:15 UTC
Permalink
Feiyang FY07024DI26A30-D is 1024x600, 4-lane MIPI-DSI LCD panel.

Add panel driver for it.

Signed-off-by: Jagan Teki <***@amarulasolutions.com>
---
MAINTAINERS | 6 +
drivers/gpu/drm/panel/Kconfig | 9 +
drivers/gpu/drm/panel/Makefile | 1 +
.../drm/panel/panel-feiyang-fy07024di26a30d.c | 286 ++++++++++++++++++
4 files changed, 302 insertions(+)
create mode 100644 drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 3dac08d0b3cb..40c8bfc974f4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4620,6 +4620,12 @@ T: git git://anongit.freedesktop.org/drm/drm-misc
S: Maintained
F: drivers/gpu/drm/tve200/

+DRM DRIVER FOR FEIYANG FY07024DI26A30-D MIPI-DSI LCD PANELS
+M: Jagan Teki <***@amarulasolutions.com>
+S: Maintained
+F: drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
+F: Documentation/devicetree/bindings/display/panel/feiyang,fy07024di26a30d.txt
+
DRM DRIVER FOR ILITEK ILI9225 PANELS
M: David Lechner <***@lechnology.com>
S: Maintained
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index d0d4e60f5153..bc70896fe43c 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -47,6 +47,15 @@ config DRM_PANEL_SIMPLE
that it can be automatically turned off when the panel goes into a
low power state.

+config DRM_PANEL_FEIYANG_FY07024DI26A30D
+ tristate "Feiyang FY07024DI26A30-D MIPI-DSI LCD panel"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y if you want to enable support for panels based on the
+ Feiyang FY07024DI26A30-D MIPI-DSI interface.
+
config DRM_PANEL_ILITEK_IL9322
tristate "Ilitek ILI9322 320x240 QVGA panels"
depends on OF && SPI
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 88011f06edb8..e23c017639c7 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_DRM_PANEL_ARM_VERSATILE) += panel-arm-versatile.o
obj-$(CONFIG_DRM_PANEL_BANANAPI_S070WV20_ICN6211) += panel-bananapi-s070wv20-icn6211.o
obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o
obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
+obj-$(CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D) += panel-feiyang-fy07024di26a30d.o
obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o
obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o
diff --git a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
new file mode 100644
index 000000000000..a4b46bd8fdbe
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Amarula Solutions
+ * Author: Jagan Teki <***@amarulasolutions.com>
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+struct feiyang {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+
+ struct backlight_device *backlight;
+ struct regulator *dvdd;
+ struct regulator *avdd;
+ struct gpio_desc *reset;
+};
+
+static inline struct feiyang *panel_to_feiyang(struct drm_panel *panel)
+{
+ return container_of(panel, struct feiyang, panel);
+}
+
+struct feiyang_init_cmd {
+ size_t len;
+ const char *data;
+};
+
+#define FY07024DI26A30D(...) { \
+ .len = sizeof((char[]){__VA_ARGS__}), \
+ .data = (char[]){__VA_ARGS__} }
+
+static const struct feiyang_init_cmd feiyang_init_cmds[] = {
+ FY07024DI26A30D(0x80, 0x58),
+ FY07024DI26A30D(0x81, 0x47),
+ FY07024DI26A30D(0x82, 0xD4),
+ FY07024DI26A30D(0x83, 0x88),
+ FY07024DI26A30D(0x84, 0xA9),
+ FY07024DI26A30D(0x85, 0xC3),
+ FY07024DI26A30D(0x86, 0x82),
+};
+
+static int feiyang_prepare(struct drm_panel *panel)
+{
+ struct feiyang *ctx = panel_to_feiyang(panel);
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ unsigned int i;
+ int ret;
+
+ ret = regulator_enable(ctx->dvdd);
+ if (ret)
+ return ret;
+
+ msleep(100);
+
+ ret = regulator_enable(ctx->avdd);
+ if (ret)
+ return ret;
+
+ msleep(20);
+
+ gpiod_set_value(ctx->reset, 1);
+ msleep(50);
+
+ gpiod_set_value(ctx->reset, 0);
+ msleep(20);
+
+ gpiod_set_value(ctx->reset, 1);
+ msleep(200);
+
+ for (i = 0; i < ARRAY_SIZE(feiyang_init_cmds); i++) {
+ const struct feiyang_init_cmd *cmd =
+ &feiyang_init_cmds[i];
+
+ ret = mipi_dsi_dcs_write_buffer(dsi, cmd->data, cmd->len);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int feiyang_enable(struct drm_panel *panel)
+{
+ struct feiyang *ctx = panel_to_feiyang(panel);
+
+ msleep(120);
+
+ mipi_dsi_dcs_set_display_on(ctx->dsi);
+ backlight_enable(ctx->backlight);
+
+ return 0;
+}
+
+static int feiyang_disable(struct drm_panel *panel)
+{
+ struct feiyang *ctx = panel_to_feiyang(panel);
+
+ backlight_disable(ctx->backlight);
+ return mipi_dsi_dcs_set_display_on(ctx->dsi);
+}
+
+static int feiyang_unprepare(struct drm_panel *panel)
+{
+ struct feiyang *ctx = panel_to_feiyang(panel);
+ int ret;
+
+ ret = mipi_dsi_dcs_set_display_off(ctx->dsi);
+ if (ret < 0)
+ DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
+ ret);
+
+ ret = mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
+ if (ret < 0)
+ DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n",
+ ret);
+
+ msleep(100);
+
+ regulator_disable(ctx->avdd);
+
+ regulator_disable(ctx->dvdd);
+
+ gpiod_set_value(ctx->reset, 0);
+
+ gpiod_set_value(ctx->reset, 1);
+
+ gpiod_set_value(ctx->reset, 0);
+
+ return 0;
+}
+
+static const struct drm_display_mode feiyang_default_mode = {
+ .clock = 55000,
+ .vrefresh = 60,
+
+ .hdisplay = 1024,
+ .hsync_start = 1024 + 396,
+ .hsync_end = 1024 + 396 + 20,
+ .htotal = 1024 + 396 + 20 + 100,
+
+ .vdisplay = 600,
+ .vsync_start = 600 + 12,
+ .vsync_end = 600 + 12 + 2,
+ .vtotal = 600 + 12 + 2 + 21,
+};
+
+static int feiyang_get_modes(struct drm_panel *panel)
+{
+ struct drm_connector *connector = panel->connector;
+ struct feiyang *ctx = panel_to_feiyang(panel);
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(panel->drm, &feiyang_default_mode);
+ if (!mode) {
+ DRM_DEV_ERROR(&ctx->dsi->dev, "failed to add mode %ux%ux@%u\n",
+ feiyang_default_mode.hdisplay,
+ feiyang_default_mode.vdisplay,
+ feiyang_default_mode.vrefresh);
+ return -ENOMEM;
+ }
+
+ drm_mode_set_name(mode);
+
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(connector, mode);
+
+ return 1;
+}
+
+static const struct drm_panel_funcs feiyang_funcs = {
+ .disable = feiyang_disable,
+ .unprepare = feiyang_unprepare,
+ .prepare = feiyang_prepare,
+ .enable = feiyang_enable,
+ .get_modes = feiyang_get_modes,
+};
+
+static int feiyang_dsi_probe(struct mipi_dsi_device *dsi)
+{
+ struct device_node *np;
+ struct feiyang *ctx;
+ int ret;
+
+ ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ mipi_dsi_set_drvdata(dsi, ctx);
+ ctx->dsi = dsi;
+
+ drm_panel_init(&ctx->panel);
+ ctx->panel.dev = &dsi->dev;
+ ctx->panel.funcs = &feiyang_funcs;
+
+ ctx->dvdd = devm_regulator_get(&dsi->dev, "dvdd");
+ if (IS_ERR(ctx->dvdd)) {
+ DRM_DEV_ERROR(&dsi->dev, "Couldn't get dvdd regulator\n");
+ return PTR_ERR(ctx->dvdd);
+ }
+
+ ctx->avdd = devm_regulator_get(&dsi->dev, "avdd");
+ if (IS_ERR(ctx->avdd)) {
+ DRM_DEV_ERROR(&dsi->dev, "Couldn't get avdd regulator\n");
+ return PTR_ERR(ctx->avdd);
+ }
+
+ ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ctx->reset)) {
+ DRM_DEV_ERROR(&dsi->dev, "Couldn't get our reset GPIO\n");
+ return PTR_ERR(ctx->reset);
+ }
+
+ np = of_parse_phandle(dsi->dev.of_node, "backlight", 0);
+ if (np) {
+ ctx->backlight = of_find_backlight_by_node(np);
+ of_node_put(np);
+
+ if (!ctx->backlight)
+ return -EPROBE_DEFER;
+ }
+
+ ret = drm_panel_add(&ctx->panel);
+ if (ret < 0)
+ goto put_backlight;
+
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->lanes = 4;
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0)
+ goto panel_remove;
+
+ return ret;
+
+panel_remove:
+ drm_panel_remove(&ctx->panel);
+put_backlight:
+ if (ctx->backlight)
+ put_device(&ctx->backlight->dev);
+
+ return ret;
+}
+
+static int feiyang_dsi_remove(struct mipi_dsi_device *dsi)
+{
+ struct feiyang *ctx = mipi_dsi_get_drvdata(dsi);
+
+ mipi_dsi_detach(dsi);
+ drm_panel_remove(&ctx->panel);
+
+ if (ctx->backlight)
+ put_device(&ctx->backlight->dev);
+
+ return 0;
+}
+
+static const struct of_device_id feiyang_of_match[] = {
+ { .compatible = "feiyang,fy07024di26a30d", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, feiyang_of_match);
+
+static struct mipi_dsi_driver feiyang_driver = {
+ .probe = feiyang_dsi_probe,
+ .remove = feiyang_dsi_remove,
+ .driver = {
+ .name = "feiyang-fy07024di26a30d",
+ .of_match_table = feiyang_of_match,
+ },
+};
+module_mipi_dsi_driver(feiyang_driver);
+
+MODULE_AUTHOR("Jagan Teki <***@amarulasolutions.com>");
+MODULE_DESCRIPTION("Feiyang FY07024DI26A30-D MIPI-DSI LCD panel");
+MODULE_LICENSE("GPL");
--
2.18.0.321.gffc6fa0e3
Jagan Teki
2018-12-10 16:12:38 UTC
Permalink
Hi Thierry and David,
Post by Jagan Teki
Feiyang FY07024DI26A30-D is 1024x600, 4-lane MIPI-DSI LCD panel.
Add panel driver for it.
---
MAINTAINERS | 6 +
drivers/gpu/drm/panel/Kconfig | 9 +
drivers/gpu/drm/panel/Makefile | 1 +
.../drm/panel/panel-feiyang-fy07024di26a30d.c | 286 ++++++++++++++++++
4 files changed, 302 insertions(+)
create mode 100644 drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 3dac08d0b3cb..40c8bfc974f4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4620,6 +4620,12 @@ T: git git://anongit.freedesktop.org/drm/drm-misc
S: Maintained
F: drivers/gpu/drm/tve200/
+DRM DRIVER FOR FEIYANG FY07024DI26A30-D MIPI-DSI LCD PANELS
+S: Maintained
+F: drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
+F: Documentation/devicetree/bindings/display/panel/feiyang,fy07024di26a30d.txt
+
DRM DRIVER FOR ILITEK ILI9225 PANELS
S: Maintained
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index d0d4e60f5153..bc70896fe43c 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -47,6 +47,15 @@ config DRM_PANEL_SIMPLE
that it can be automatically turned off when the panel goes into a
low power state.
+config DRM_PANEL_FEIYANG_FY07024DI26A30D
+ tristate "Feiyang FY07024DI26A30-D MIPI-DSI LCD panel"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y if you want to enable support for panels based on the
+ Feiyang FY07024DI26A30-D MIPI-DSI interface.
+
Any comments on this?

Jagan Teki
2018-11-16 16:39:09 UTC
Permalink
Sometimes tcon attributes like tcon divider, clock rate etc are
needed in interface drivers like DSI. So for such cases interface
driver must probe the respective tcon and get the attributes.
Instead of probing tcon explictly, better export the existing
sun5i_get_tcon0 so-that the relevant interface can reuse.

Signed-off-by: Jagan Teki <***@amarulasolutions.com>
---
drivers/gpu/drm/sun4i/sun4i_tcon.c | 3 ++-
drivers/gpu/drm/sun4i/sun4i_tcon.h | 1 +
2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index c78cd35a1294..6e85a33c6828 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -221,7 +221,7 @@ EXPORT_SYMBOL(sun4i_tcon_enable_vblank);
* are located in TCON0. This helper returns a pointer to TCON0's
* sun4i_tcon structure, or NULL if not found.
*/
-static struct sun4i_tcon *sun4i_get_tcon0(struct drm_device *drm)
+struct sun4i_tcon *sun4i_get_tcon0(struct drm_device *drm)
{
struct sun4i_drv *drv = drm->dev_private;
struct sun4i_tcon *tcon;
@@ -235,6 +235,7 @@ static struct sun4i_tcon *sun4i_get_tcon0(struct drm_device *drm)

return NULL;
}
+EXPORT_SYMBOL(sun4i_get_tcon0);

void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel,
const struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h
index 3d492c8be1fc..195deb9db57a 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.h
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h
@@ -273,6 +273,7 @@ struct sun4i_tcon {
struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node);
struct drm_panel *sun4i_tcon_find_panel(struct device_node *node);

+struct sun4i_tcon *sun4i_get_tcon0(struct drm_device *drm);
void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable);
void sun4i_tcon_mode_set(struct sun4i_tcon *tcon,
const struct drm_encoder *encoder,
--
2.18.0.321.gffc6fa0e3
Maxime Ripard
2018-11-19 08:34:36 UTC
Permalink
Post by Jagan Teki
Sometimes tcon attributes like tcon divider, clock rate etc are
needed in interface drivers like DSI. So for such cases interface
driver must probe the respective tcon and get the attributes.
Instead of probing tcon explictly, better export the existing
sun5i_get_tcon0 so-that the relevant interface can reuse.
That's not the name of the function you export.

Maxime
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Jagan Teki
2018-11-16 16:39:10 UTC
Permalink
Probe tcon0 during dsi_bind, so-that the tcon attributes like
divider value, clock rate can get whenever it need.

Signed-off-by: Jagan Teki <***@amarulasolutions.com>
---
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 7 +++++++
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h | 1 +
2 files changed, 8 insertions(+)

diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index d60955880c43..66a01061e51d 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -25,6 +25,7 @@
#include <drm/drm_panel.h>

#include "sun4i_drv.h"
+#include "sun4i_tcon.h"
#include "sun6i_mipi_dsi.h"

#include <video/mipi_display.h>
@@ -1007,6 +1008,7 @@ static int sun6i_dsi_bind(struct device *dev, struct device *master,
struct drm_device *drm = data;
struct sun4i_drv *drv = drm->dev_private;
struct sun6i_dsi *dsi = dev_get_drvdata(dev);
+ struct sun4i_tcon *tcon0 = sun4i_get_tcon0(drm);
int ret;

if (!dsi->panel)
@@ -1014,6 +1016,11 @@ static int sun6i_dsi_bind(struct device *dev, struct device *master,

dsi->drv = drv;

+ if (!tcon0)
+ return -EPROBE_DEFER;
+
+ dsi->tcon = tcon0;
+
drm_encoder_helper_add(&dsi->encoder,
&sun6i_dsi_enc_helper_funcs);
ret = drm_encoder_init(drm,
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
index 0df60f84bab3..3c532e83958d 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
@@ -40,6 +40,7 @@ struct sun6i_dsi {

struct device *dev;
struct sun4i_drv *drv;
+ struct sun4i_tcon *tcon;
struct mipi_dsi_device *device;
struct drm_panel *panel;
const struct sun6i_dsi_variant *variant;
--
2.18.0.321.gffc6fa0e3
Maxime Ripard
2018-11-19 08:38:33 UTC
Permalink
Post by Jagan Teki
Probe tcon0 during dsi_bind, so-that the tcon attributes like
divider value, clock rate can get whenever it need.
---
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 7 +++++++
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h | 1 +
2 files changed, 8 insertions(+)
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index d60955880c43..66a01061e51d 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -25,6 +25,7 @@
#include <drm/drm_panel.h>
#include "sun4i_drv.h"
+#include "sun4i_tcon.h"
#include "sun6i_mipi_dsi.h"
#include <video/mipi_display.h>
@@ -1007,6 +1008,7 @@ static int sun6i_dsi_bind(struct device *dev, struct device *master,
struct drm_device *drm = data;
struct sun4i_drv *drv = drm->dev_private;
struct sun6i_dsi *dsi = dev_get_drvdata(dev);
+ struct sun4i_tcon *tcon0 = sun4i_get_tcon0(drm);
int ret;
if (!dsi->panel)
@@ -1014,6 +1016,11 @@ static int sun6i_dsi_bind(struct device *dev, struct device *master,
dsi->drv = drv;
+ if (!tcon0)
+ return -EPROBE_DEFER;
You can't fall in that condition. The component framework won't call
bind unless every components have been probed.
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Jagan Teki
2018-11-19 11:36:32 UTC
Permalink
Post by Maxime Ripard
Post by Jagan Teki
Probe tcon0 during dsi_bind, so-that the tcon attributes like
divider value, clock rate can get whenever it need.
---
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 7 +++++++
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h | 1 +
2 files changed, 8 insertions(+)
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index d60955880c43..66a01061e51d 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -25,6 +25,7 @@
#include <drm/drm_panel.h>
#include "sun4i_drv.h"
+#include "sun4i_tcon.h"
#include "sun6i_mipi_dsi.h"
#include <video/mipi_display.h>
@@ -1007,6 +1008,7 @@ static int sun6i_dsi_bind(struct device *dev, struct device *master,
struct drm_device *drm = data;
struct sun4i_drv *drv = drm->dev_private;
struct sun6i_dsi *dsi = dev_get_drvdata(dev);
+ struct sun4i_tcon *tcon0 = sun4i_get_tcon0(drm);
int ret;
if (!dsi->panel)
@@ -1014,6 +1016,11 @@ static int sun6i_dsi_bind(struct device *dev, struct device *master,
dsi->drv = drv;
+ if (!tcon0)
+ return -EPROBE_DEFER;
You can't fall in that condition. The component framework won't call
bind unless every components have been probed.
Since tcon0 is already probed is it? if so I can return -EINVAL is it ok?
Maxime Ripard
2018-11-20 15:44:29 UTC
Permalink
Post by Jagan Teki
Post by Maxime Ripard
Post by Jagan Teki
Probe tcon0 during dsi_bind, so-that the tcon attributes like
divider value, clock rate can get whenever it need.
---
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 7 +++++++
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h | 1 +
2 files changed, 8 insertions(+)
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index d60955880c43..66a01061e51d 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -25,6 +25,7 @@
#include <drm/drm_panel.h>
#include "sun4i_drv.h"
+#include "sun4i_tcon.h"
#include "sun6i_mipi_dsi.h"
#include <video/mipi_display.h>
@@ -1007,6 +1008,7 @@ static int sun6i_dsi_bind(struct device *dev, struct device *master,
struct drm_device *drm = data;
struct sun4i_drv *drv = drm->dev_private;
struct sun6i_dsi *dsi = dev_get_drvdata(dev);
+ struct sun4i_tcon *tcon0 = sun4i_get_tcon0(drm);
int ret;
if (!dsi->panel)
@@ -1014,6 +1016,11 @@ static int sun6i_dsi_bind(struct device *dev, struct device *master,
dsi->drv = drv;
+ if (!tcon0)
+ return -EPROBE_DEFER;
You can't fall in that condition. The component framework won't call
bind unless every components have been probed.
Since tcon0 is already probed is it? if so I can return -EINVAL is it ok?
Sounds good yes.

Maxime
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Jagan Teki
2018-11-16 16:39:08 UTC
Permalink
Allwinner MIPI DSI DRQ set value can be varied with respective
video modes.
- burst mode the set value is always 0
- video modes whose front porch greater than 20, the set value
can be computed based front porch and bpp.
- video modes whose front porch is not greater than 20, the set value
is simply 0

This patch simplifies existing drq set value code by grouping
into sun6i_dsi_get_drq and support all video modes.

Signed-off-by: Jagan Teki <***@amarulasolutions.com>
---
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 38 ++++++++++++++++----------
1 file changed, 23 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index efd08bfd0cb8..d60955880c43 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -363,6 +363,26 @@ static void sun6i_dsi_inst_init(struct sun6i_dsi *dsi,
SUN6I_DSI_INST_JUMP_CFG_NUM(1));
};

+static int sun6i_dsi_get_drq(struct sun6i_dsi *dsi,
+ struct drm_display_mode *mode)
+{
+ struct mipi_dsi_device *device = dsi->device;
+ int drq = 0;
+
+ if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ return drq;
+
+ if ((mode->hsync_start - mode->hdisplay) > 20) {
+ /* Maaaaaagic */
+ u16 drq = (mode->hsync_start - mode->hdisplay) - 20;
+
+ drq *= mipi_dsi_pixel_format_to_bpp(device->format);
+ drq /= 32;
+ }
+
+ return drq;
+}
+
static u16 sun6i_dsi_get_timings_vblk(struct sun6i_dsi *dsi,
struct drm_display_mode *mode, u16 hblk)
{
@@ -478,21 +498,9 @@ static u16 sun6i_dsi_get_video_start_delay(struct sun6i_dsi *dsi,
static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi,
struct drm_display_mode *mode)
{
- struct mipi_dsi_device *device = dsi->device;
- u32 val = 0;
-
- if ((mode->hsync_start - mode->hdisplay) > 20) {
- /* Maaaaaagic */
- u16 drq = (mode->hsync_start - mode->hdisplay) - 20;
-
- drq *= mipi_dsi_pixel_format_to_bpp(device->format);
- drq /= 32;
-
- val = (SUN6I_DSI_TCON_DRQ_ENABLE_MODE |
- SUN6I_DSI_TCON_DRQ_SET(drq));
- }
-
- regmap_write(dsi->regs, SUN6I_DSI_TCON_DRQ_REG, val);
+ regmap_write(dsi->regs, SUN6I_DSI_TCON_DRQ_REG,
+ SUN6I_DSI_TCON_DRQ_ENABLE_MODE |
+ SUN6I_DSI_TCON_DRQ_SET(sun6i_dsi_get_drq(dsi, mode)));
}

static void sun6i_dsi_setup_inst_loop(struct sun6i_dsi *dsi,
--
2.18.0.321.gffc6fa0e3
Maxime Ripard
2018-11-19 08:32:43 UTC
Permalink
Post by Jagan Teki
Allwinner MIPI DSI DRQ set value can be varied with respective
video modes.
- burst mode the set value is always 0
- video modes whose front porch greater than 20, the set value
can be computed based front porch and bpp.
- video modes whose front porch is not greater than 20, the set value
is simply 0
This patch simplifies existing drq set value code by grouping
into sun6i_dsi_get_drq and support all video modes.
---
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 38 ++++++++++++++++----------
1 file changed, 23 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index efd08bfd0cb8..d60955880c43 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -363,6 +363,26 @@ static void sun6i_dsi_inst_init(struct sun6i_dsi *dsi,
SUN6I_DSI_INST_JUMP_CFG_NUM(1));
};
+static int sun6i_dsi_get_drq(struct sun6i_dsi *dsi,
+ struct drm_display_mode *mode)
+{
+ struct mipi_dsi_device *device = dsi->device;
+ int drq = 0;
So, here, you declaring a variable called drq, set to 0.
Post by Jagan Teki
+ if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ return drq;
That you return here. You could just return 0, to be clearer.
Post by Jagan Teki
+ if ((mode->hsync_start - mode->hdisplay) > 20) {
+ /* Maaaaaagic */
+ u16 drq = (mode->hsync_start - mode->hdisplay) - 20;
You re-declare a variable with the same name here, but a different
type....
Post by Jagan Teki
+ drq *= mipi_dsi_pixel_format_to_bpp(device->format);
+ drq /= 32;
+ }
+
+ return drq;
And then return the first one? How is that even working?
Post by Jagan Teki
+
static u16 sun6i_dsi_get_timings_vblk(struct sun6i_dsi *dsi,
struct drm_display_mode *mode, u16 hblk)
{
@@ -478,21 +498,9 @@ static u16 sun6i_dsi_get_video_start_delay(struct sun6i_dsi *dsi,
static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi,
struct drm_display_mode *mode)
{
- struct mipi_dsi_device *device = dsi->device;
- u32 val = 0;
-
- if ((mode->hsync_start - mode->hdisplay) > 20) {
- /* Maaaaaagic */
- u16 drq = (mode->hsync_start - mode->hdisplay) - 20;
-
- drq *= mipi_dsi_pixel_format_to_bpp(device->format);
- drq /= 32;
-
- val = (SUN6I_DSI_TCON_DRQ_ENABLE_MODE |
- SUN6I_DSI_TCON_DRQ_SET(drq));
- }
-
- regmap_write(dsi->regs, SUN6I_DSI_TCON_DRQ_REG, val);
+ regmap_write(dsi->regs, SUN6I_DSI_TCON_DRQ_REG,
+ SUN6I_DSI_TCON_DRQ_ENABLE_MODE |
+ SUN6I_DSI_TCON_DRQ_SET(sun6i_dsi_get_drq(dsi, mode)));
On top of that, you now enable the DRQ stuff all the time, while it
was conditional before.
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Jagan Teki
2018-11-19 11:22:17 UTC
Permalink
Post by Maxime Ripard
Post by Jagan Teki
Allwinner MIPI DSI DRQ set value can be varied with respective
video modes.
- burst mode the set value is always 0
- video modes whose front porch greater than 20, the set value
can be computed based front porch and bpp.
- video modes whose front porch is not greater than 20, the set value
is simply 0
This patch simplifies existing drq set value code by grouping
into sun6i_dsi_get_drq and support all video modes.
---
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 38 ++++++++++++++++----------
1 file changed, 23 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index efd08bfd0cb8..d60955880c43 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -363,6 +363,26 @@ static void sun6i_dsi_inst_init(struct sun6i_dsi *dsi,
SUN6I_DSI_INST_JUMP_CFG_NUM(1));
};
+static int sun6i_dsi_get_drq(struct sun6i_dsi *dsi,
+ struct drm_display_mode *mode)
+{
+ struct mipi_dsi_device *device = dsi->device;
+ int drq = 0;
So, here, you declaring a variable called drq, set to 0.
Post by Jagan Teki
+ if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ return drq;
That you return here. You could just return 0, to be clearer.
Post by Jagan Teki
+ if ((mode->hsync_start - mode->hdisplay) > 20) {
+ /* Maaaaaagic */
+ u16 drq = (mode->hsync_start - mode->hdisplay) - 20;
You re-declare a variable with the same name here, but a different
type....
Post by Jagan Teki
+ drq *= mipi_dsi_pixel_format_to_bpp(device->format);
+ drq /= 32;
+ }
+
+ return drq;
And then return the first one? How is that even working?
Will fix this.
Post by Maxime Ripard
Post by Jagan Teki
+
static u16 sun6i_dsi_get_timings_vblk(struct sun6i_dsi *dsi,
struct drm_display_mode *mode, u16 hblk)
{
@@ -478,21 +498,9 @@ static u16 sun6i_dsi_get_video_start_delay(struct sun6i_dsi *dsi,
static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi,
struct drm_display_mode *mode)
{
- struct mipi_dsi_device *device = dsi->device;
- u32 val = 0;
-
- if ((mode->hsync_start - mode->hdisplay) > 20) {
- /* Maaaaaagic */
- u16 drq = (mode->hsync_start - mode->hdisplay) - 20;
-
- drq *= mipi_dsi_pixel_format_to_bpp(device->format);
- drq /= 32;
-
- val = (SUN6I_DSI_TCON_DRQ_ENABLE_MODE |
- SUN6I_DSI_TCON_DRQ_SET(drq));
- }
-
- regmap_write(dsi->regs, SUN6I_DSI_TCON_DRQ_REG, val);
+ regmap_write(dsi->regs, SUN6I_DSI_TCON_DRQ_REG,
+ SUN6I_DSI_TCON_DRQ_ENABLE_MODE |
+ SUN6I_DSI_TCON_DRQ_SET(sun6i_dsi_get_drq(dsi, mode)));
On top of that, you now enable the DRQ stuff all the time, while it
Earlier the val value is ENABLE_MODE ORed with drq value. for the
condition drq is computed in if block otherwise the val is 0.
So as I explained in commit message the drq value is 0
- for video modes whose front porch is not greater than 20 and
- for burst mode the val

ie reason I mode it common.
Maxime Ripard
2018-11-20 14:32:29 UTC
Permalink
Post by Jagan Teki
Post by Maxime Ripard
Post by Jagan Teki
Allwinner MIPI DSI DRQ set value can be varied with respective
video modes.
- burst mode the set value is always 0
- video modes whose front porch greater than 20, the set value
can be computed based front porch and bpp.
- video modes whose front porch is not greater than 20, the set value
is simply 0
This patch simplifies existing drq set value code by grouping
into sun6i_dsi_get_drq and support all video modes.
---
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 38 ++++++++++++++++----------
1 file changed, 23 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index efd08bfd0cb8..d60955880c43 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -363,6 +363,26 @@ static void sun6i_dsi_inst_init(struct sun6i_dsi *dsi,
SUN6I_DSI_INST_JUMP_CFG_NUM(1));
};
+static int sun6i_dsi_get_drq(struct sun6i_dsi *dsi,
+ struct drm_display_mode *mode)
+{
+ struct mipi_dsi_device *device = dsi->device;
+ int drq = 0;
So, here, you declaring a variable called drq, set to 0.
Post by Jagan Teki
+ if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ return drq;
That you return here. You could just return 0, to be clearer.
Post by Jagan Teki
+ if ((mode->hsync_start - mode->hdisplay) > 20) {
+ /* Maaaaaagic */
+ u16 drq = (mode->hsync_start - mode->hdisplay) - 20;
You re-declare a variable with the same name here, but a different
type....
Post by Jagan Teki
+ drq *= mipi_dsi_pixel_format_to_bpp(device->format);
+ drq /= 32;
+ }
+
+ return drq;
And then return the first one? How is that even working?
Will fix this.
I don't want you to only fix this, I also want you to contribute code
that is A) useful, B) tested.
Post by Jagan Teki
Post by Maxime Ripard
Post by Jagan Teki
static u16 sun6i_dsi_get_timings_vblk(struct sun6i_dsi *dsi,
struct drm_display_mode *mode, u16 hblk)
{
@@ -478,21 +498,9 @@ static u16 sun6i_dsi_get_video_start_delay(struct sun6i_dsi *dsi,
static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi,
struct drm_display_mode *mode)
{
- struct mipi_dsi_device *device = dsi->device;
- u32 val = 0;
-
- if ((mode->hsync_start - mode->hdisplay) > 20) {
- /* Maaaaaagic */
- u16 drq = (mode->hsync_start - mode->hdisplay) - 20;
-
- drq *= mipi_dsi_pixel_format_to_bpp(device->format);
- drq /= 32;
-
- val = (SUN6I_DSI_TCON_DRQ_ENABLE_MODE |
- SUN6I_DSI_TCON_DRQ_SET(drq));
- }
-
- regmap_write(dsi->regs, SUN6I_DSI_TCON_DRQ_REG, val);
+ regmap_write(dsi->regs, SUN6I_DSI_TCON_DRQ_REG,
+ SUN6I_DSI_TCON_DRQ_ENABLE_MODE |
+ SUN6I_DSI_TCON_DRQ_SET(sun6i_dsi_get_drq(dsi, mode)));
On top of that, you now enable the DRQ stuff all the time, while it
Earlier the val value is ENABLE_MODE ORed with drq value. for the
condition drq is computed in if block otherwise the val is 0.
So as I explained in commit message the drq value is 0
- for video modes whose front porch is not greater than 20 and
- for burst mode the val
ie reason I mode it common.
The previous code was enabling it if the front porch was larger than
20 only. You enable it all the time now, and you never explain why.

Maxime
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Jagan Teki
2018-11-20 14:48:46 UTC
Permalink
Post by Maxime Ripard
Post by Jagan Teki
Post by Maxime Ripard
Post by Jagan Teki
Allwinner MIPI DSI DRQ set value can be varied with respective
video modes.
- burst mode the set value is always 0
- video modes whose front porch greater than 20, the set value
can be computed based front porch and bpp.
- video modes whose front porch is not greater than 20, the set value
is simply 0
This patch simplifies existing drq set value code by grouping
into sun6i_dsi_get_drq and support all video modes.
---
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 38 ++++++++++++++++----------
1 file changed, 23 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index efd08bfd0cb8..d60955880c43 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -363,6 +363,26 @@ static void sun6i_dsi_inst_init(struct sun6i_dsi *dsi,
SUN6I_DSI_INST_JUMP_CFG_NUM(1));
};
+static int sun6i_dsi_get_drq(struct sun6i_dsi *dsi,
+ struct drm_display_mode *mode)
+{
+ struct mipi_dsi_device *device = dsi->device;
+ int drq = 0;
So, here, you declaring a variable called drq, set to 0.
Post by Jagan Teki
+ if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ return drq;
That you return here. You could just return 0, to be clearer.
Post by Jagan Teki
+ if ((mode->hsync_start - mode->hdisplay) > 20) {
+ /* Maaaaaagic */
+ u16 drq = (mode->hsync_start - mode->hdisplay) - 20;
You re-declare a variable with the same name here, but a different
type....
Post by Jagan Teki
+ drq *= mipi_dsi_pixel_format_to_bpp(device->format);
+ drq /= 32;
+ }
+
+ return drq;
And then return the first one? How is that even working?
Will fix this.
I don't want you to only fix this, I also want you to contribute code
that is A) useful, B) tested.
It was tested in burst mode panel, the same added in the series. It
worked because burst mode need 0 drq rate.
Post by Maxime Ripard
Post by Jagan Teki
Post by Maxime Ripard
Post by Jagan Teki
static u16 sun6i_dsi_get_timings_vblk(struct sun6i_dsi *dsi,
struct drm_display_mode *mode, u16 hblk)
{
@@ -478,21 +498,9 @@ static u16 sun6i_dsi_get_video_start_delay(struct sun6i_dsi *dsi,
static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi,
struct drm_display_mode *mode)
{
- struct mipi_dsi_device *device = dsi->device;
- u32 val = 0;
-
- if ((mode->hsync_start - mode->hdisplay) > 20) {
- /* Maaaaaagic */
- u16 drq = (mode->hsync_start - mode->hdisplay) - 20;
-
- drq *= mipi_dsi_pixel_format_to_bpp(device->format);
- drq /= 32;
-
- val = (SUN6I_DSI_TCON_DRQ_ENABLE_MODE |
- SUN6I_DSI_TCON_DRQ_SET(drq));
- }
-
- regmap_write(dsi->regs, SUN6I_DSI_TCON_DRQ_REG, val);
+ regmap_write(dsi->regs, SUN6I_DSI_TCON_DRQ_REG,
+ SUN6I_DSI_TCON_DRQ_ENABLE_MODE |
+ SUN6I_DSI_TCON_DRQ_SET(sun6i_dsi_get_drq(dsi, mode)));
On top of that, you now enable the DRQ stuff all the time, while it
Earlier the val value is ENABLE_MODE ORed with drq value. for the
condition drq is computed in if block otherwise the val is 0.
So as I explained in commit message the drq value is 0
- for video modes whose front porch is not greater than 20 and
- for burst mode the val
ie reason I mode it common.
The previous code was enabling it if the front porch was larger than
20 only. You enable it all the time now, and you never explain why.
Got it. Loo like I was confused to test these two separate series in
separate panels and missed something. Is it Ok to combine all dsi
changes together and send it in next version.
Loading...