Skip to content

Commit

Permalink
drm/sun4i: dsi: Add burst support
Browse files Browse the repository at this point in the history
The current driver doesn't support the DSI burst operation mode.

Let's add the needed quirks to make it work.

Signed-off-by: Konstantin Sudakov <k.sudakov@integrasources.com>
Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
Reviewed-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
Link: https://patchwork.freedesktop.org/patch/msgid/1dcabf2b38d3f0d3387b1cf02575e3d14e3ecd4e.1549896081.git-series.maxime.ripard@bootlin.com
  • Loading branch information
Konstantin Sudakov authored and Maxime Ripard committed Feb 19, 2019
1 parent 62e7511 commit 1c1a7aa
Showing 1 changed file with 129 additions and 40 deletions.
169 changes: 129 additions & 40 deletions drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>

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

#include <video/mipi_display.h>
Expand All @@ -33,6 +35,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)
Expand Down Expand Up @@ -153,6 +157,8 @@

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

#define SUN6I_DSI_SYNC_POINT 40

enum sun6i_dsi_start_inst {
DSI_START_LPRX,
DSI_START_LPTX,
Expand Down Expand Up @@ -367,13 +373,70 @@ static u16 sun6i_dsi_get_video_start_delay(struct sun6i_dsi *dsi,
return max_t(u16, delay, 1);
}

static u16 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) / 8;

return mode->htotal * Bpp / device->lanes;
}

static u16 sun6i_dsi_get_drq_edge0(struct sun6i_dsi *dsi,
struct drm_display_mode *mode,
u16 line_num, u16 edge1)
{
u16 edge0 = edge1;

edge0 += (mode->hdisplay + 40) * SUN6I_DSI_TCON_DIV / 8;

if (edge0 > line_num)
return edge0 - line_num;

return 1;
}

static u16 sun6i_dsi_get_drq_edge1(struct sun6i_dsi *dsi,
struct drm_display_mode *mode,
u16 line_num)
{
struct mipi_dsi_device *device = dsi->device;
unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;
unsigned int hbp = mode->htotal - mode->hsync_end;
u16 edge1;

edge1 = SUN6I_DSI_SYNC_POINT;
edge1 += (mode->hdisplay + hbp + 20) * Bpp / device->lanes;

if (edge1 > line_num)
return line_num;

return edge1;
}

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_end - mode->hdisplay) > 20) {
if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
u16 line_num = sun6i_dsi_get_line_num(dsi, mode);
u16 edge0, edge1;

edge1 = sun6i_dsi_get_drq_edge1(dsi, mode, line_num);
edge0 = sun6i_dsi_get_drq_edge0(dsi, mode, line_num, edge1);

regmap_write(dsi->regs, SUN6I_DSI_BURST_DRQ_REG,
SUN6I_DSI_BURST_DRQ_EDGE0(edge0) |
SUN6I_DSI_BURST_DRQ_EDGE1(edge1));

regmap_write(dsi->regs, SUN6I_DSI_BURST_LINE_REG,
SUN6I_DSI_BURST_LINE_NUM(line_num) |
SUN6I_DSI_BURST_LINE_SYNC_POINT(SUN6I_DSI_SYNC_POINT));

val = SUN6I_DSI_TCON_DRQ_ENABLE_MODE;
} else if ((mode->hsync_end - mode->hdisplay) > 20) {
/* Maaaaaagic */
u16 drq = (mode->hsync_end - mode->hdisplay) - 20;

Expand All @@ -390,8 +453,19 @@ 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)
{
struct mipi_dsi_device *device = dsi->device;
u16 delay = 50 - 1;

if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
delay = (mode->htotal - mode->hdisplay) * 150;
delay /= (mode->clock / 1000) * 8;
delay -= 50;
}

regmap_write(dsi->regs, SUN6I_DSI_INST_LOOP_SEL_REG,
2 << (4 * DSI_INST_ID_LP11) |
3 << (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));
Expand Down Expand Up @@ -457,61 +531,76 @@ 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;
u16 hbp = 0, hfp = 0, hsa = 0, hblk = 0, vblk = 0;
u32 basic_ctl = 0;
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);
if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
hblk = mode->hdisplay * Bpp;
basic_ctl = SUN6I_DSI_BASIC_CTL_VIDEO_BURST |
SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS |
SUN6I_DSI_BASIC_CTL_HBP_DIS;

/*
* The backporch is set using a blanking packet (4 bytes +
* payload + 2 bytes). Its minimal size is therefore 6 bytes
*/
if (device->lanes == 4)
basic_ctl |= SUN6I_DSI_BASIC_CTL_TRAIL_FILL |
SUN6I_DSI_BASIC_CTL_TRAIL_INV(0xc);
} else {
/*
* 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);

/*
* The frontporch is set using a blanking packet (4 bytes +
* payload + 2 bytes). Its minimal size is therefore 6 bytes
*/
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);

/*
* The blanking is set using a sync event (4 bytes) and a
* blanking packet (4 bytes + payload + 2 bytes). Its minimal
* size is therefore 10 bytes.
*/
hfp = max((unsigned int)HFP_PACKET_OVERHEAD,
(mode->hsync_start - mode->hdisplay) * Bpp - HFP_PACKET_OVERHEAD);

/*
* The blanking is set using a sync event (4 bytes)
* and a blanking packet (4 bytes + payload + 2
* bytes). Its minimal size is therefore 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);

/*
* And I'm not entirely sure what vblk is about. The driver in
* Allwinner BSP is using a rather convoluted calculation
* there only for 4 lanes. However, using 0 (the !4 lanes
* case) even with a 4 lanes screen seems to work...
*/
vblk = 0;
hblk = max((unsigned int)HBLK_PACKET_OVERHEAD,
(mode->htotal - (mode->hsync_end - mode->hsync_start)) * Bpp -
HBLK_PACKET_OVERHEAD);

/*
* And I'm not entirely sure what vblk is about. The driver in
* Allwinner BSP is using a rather convoluted calculation
* there only for 4 lanes. However, using 0 (the !4 lanes
* case) even with a 4 lanes screen seems to work...
*/
vblk = 0;
}

/* How many bytes do we need to send all payloads? */
bytes = max_t(size_t, max(max(hfp, hblk), max(hsa, hbp)), vblk);
buffer = kmalloc(bytes, GFP_KERNEL);
if (WARN_ON(!buffer))
return;

regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, 0);
regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, basic_ctl);

regmap_write(dsi->regs, SUN6I_DSI_SYNC_HSS_REG,
sun6i_dsi_build_sync_pkt(MIPI_DSI_H_SYNC_START,
Expand Down

0 comments on commit 1c1a7aa

Please sign in to comment.