mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-27 17:18:59 +00:00
538a1d740c
The patches were generated from the RPi repo with the following command: git format-patch v6.6.58..rpi-6.6.y Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
243 lines
9.9 KiB
Diff
243 lines
9.9 KiB
Diff
From e596d70725ca70113d39d9366d7b4d3e492f6449 Mon Sep 17 00:00:00 2001
|
|
From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
|
|
Date: Wed, 31 Jul 2024 19:05:29 +0100
|
|
Subject: [PATCH 1233/1350] drivers: drm: rp1-dsi: Implement more DSI options
|
|
and flags
|
|
|
|
Now implementing:
|
|
- Per-command selection of LP or HS for commands (previously LP)
|
|
- EoTp transmission option (previously EoTp was always disabled)
|
|
- Non-continuous clock option (previously always continuous)
|
|
- Per-command enabling of ACK request (in command mode only)
|
|
|
|
Make a plausible (and possibly correct) attempt to measure the
|
|
longest LP command that will fit into vertical blanking lines.
|
|
|
|
DON'T set both "Burst Mode" and "Sync Events" flags together.
|
|
This is redundant in the standard IP; in this RP1 variant it
|
|
would enable Sync Pulses but may break with some video timings.
|
|
|
|
Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
|
|
---
|
|
drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.c | 5 +-
|
|
drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.h | 3 +-
|
|
drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dsi.c | 106 +++++++++++++++++-----
|
|
3 files changed, 91 insertions(+), 23 deletions(-)
|
|
|
|
--- a/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.c
|
|
+++ b/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.c
|
|
@@ -396,7 +396,10 @@ ssize_t rp1dsi_host_transfer(struct mipi
|
|
return ret;
|
|
}
|
|
|
|
- rp1dsi_dsi_send(dsi, *(u32 *)(&packet.header), packet.payload_length, packet.payload);
|
|
+ rp1dsi_dsi_send(dsi, *(u32 *)(&packet.header),
|
|
+ packet.payload_length, packet.payload,
|
|
+ !!(msg->flags & MIPI_DSI_MSG_USE_LPM),
|
|
+ !!(msg->flags & MIPI_DSI_MSG_REQ_ACK));
|
|
|
|
/* Optional read back */
|
|
if (msg->rx_len && msg->rx_buf)
|
|
--- a/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.h
|
|
+++ b/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.h
|
|
@@ -86,7 +86,8 @@ void rp1dsi_mipicfg_setup(struct rp1_dsi
|
|
/* Functions to control the SNPS D-PHY and DSI block setup */
|
|
|
|
void rp1dsi_dsi_setup(struct rp1_dsi *dsi, struct drm_display_mode const *mode);
|
|
-void rp1dsi_dsi_send(struct rp1_dsi *dsi, u32 header, int len, const u8 *buf);
|
|
+void rp1dsi_dsi_send(struct rp1_dsi *dsi, u32 header, int len, const u8 *buf,
|
|
+ bool use_lpm, bool req_ack);
|
|
int rp1dsi_dsi_recv(struct rp1_dsi *dsi, int len, u8 *buf);
|
|
void rp1dsi_dsi_set_cmdmode(struct rp1_dsi *dsi, int cmd_mode);
|
|
void rp1dsi_dsi_stop(struct rp1_dsi *dsi);
|
|
--- a/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dsi.c
|
|
+++ b/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dsi.c
|
|
@@ -103,6 +103,24 @@
|
|
|
|
/* And some bitfield definitions */
|
|
|
|
+#define DSI_PCKHDL_EOTP_TX_EN BIT(0)
|
|
+#define DSI_PCKHDL_BTA_EN BIT(2)
|
|
+
|
|
+#define DSI_VID_MODE_LP_CMD_EN BIT(15)
|
|
+#define DSI_VID_MODE_FRAME_BTA_ACK_EN BIT(14)
|
|
+#define DSI_VID_MODE_LP_HFP_EN BIT(13)
|
|
+#define DSI_VID_MODE_LP_HBP_EN BIT(12)
|
|
+#define DSI_VID_MODE_LP_VACT_EN BIT(11)
|
|
+#define DSI_VID_MODE_LP_VFP_EN BIT(10)
|
|
+#define DSI_VID_MODE_LP_VBP_EN BIT(9)
|
|
+#define DSI_VID_MODE_LP_VSA_EN BIT(8)
|
|
+#define DSI_VID_MODE_SYNC_PULSES 0
|
|
+#define DSI_VID_MODE_SYNC_EVENTS 1
|
|
+#define DSI_VID_MODE_BURST 2
|
|
+
|
|
+#define DSI_CMD_MODE_ALL_LP 0x10f7f00
|
|
+#define DSI_CMD_MODE_ACK_RQST_EN BIT(1)
|
|
+
|
|
#define DPHY_PWR_UP_SHUTDOWNZ_LSB 0
|
|
#define DPHY_PWR_UP_SHUTDOWNZ_BITS BIT(DPHY_PWR_UP_SHUTDOWNZ_LSB)
|
|
|
|
@@ -1252,8 +1270,8 @@ static u32 dphy_configure_pll(struct rp1
|
|
vco_freq, actual_vco_freq, m, refclk, n,
|
|
hsfreq_table[dsi->hsfreq_index].hsfreqrange);
|
|
} else {
|
|
- drm_warn(dsi->drm,
|
|
- "rp1dsi: Error configuring DPHY PLL %uHz\n", vco_freq);
|
|
+ drm_err(dsi->drm,
|
|
+ "rp1dsi: Error configuring DPHY PLL %uHz\n", vco_freq);
|
|
}
|
|
|
|
return actual_vco_freq;
|
|
@@ -1321,7 +1339,7 @@ static void rp1dsi_dpiclk_start(struct r
|
|
clk_set_rate(dsi->clocks[RP1DSI_CLOCK_DPI], (4 * lanes * byte_clock) / (bpp >> 1));
|
|
clk_prepare_enable(dsi->clocks[RP1DSI_CLOCK_DPI]);
|
|
drm_info(dsi->drm,
|
|
- "rp1dsi: Nominal Byte clock %u DPI clock %lu (parent rate %lu)",
|
|
+ "rp1dsi: Nominal Byte clock %u DPI clock %lu (parent rate %lu)\n",
|
|
byte_clock,
|
|
clk_get_rate(dsi->clocks[RP1DSI_CLOCK_DPI]),
|
|
clk_get_rate(clk_get_parent(dsi->clocks[RP1DSI_CLOCK_DPI])));
|
|
@@ -1365,7 +1383,8 @@ static u32 get_colorcode(enum mipi_dsi_p
|
|
|
|
void rp1dsi_dsi_setup(struct rp1_dsi *dsi, struct drm_display_mode const *mode)
|
|
{
|
|
- u32 timeout, mask, vid_mode_cfg;
|
|
+ int cmdtim;
|
|
+ u32 timeout, mask, clkdiv;
|
|
unsigned int bpp = mipi_dsi_pixel_format_to_bpp(dsi->display_format);
|
|
u32 byte_clock = clamp((bpp * 125 * min(mode->clock, RP1DSI_DPI_MAX_KHZ)) / dsi->lanes,
|
|
RP1DSI_BYTE_CLK_MIN, RP1DSI_BYTE_CLK_MAX);
|
|
@@ -1374,19 +1393,31 @@ void rp1dsi_dsi_setup(struct rp1_dsi *ds
|
|
DSI_WRITE(DSI_DPI_CFG_POL, 0);
|
|
DSI_WRITE(DSI_GEN_VCID, dsi->vc);
|
|
DSI_WRITE(DSI_DPI_COLOR_CODING, get_colorcode(dsi->display_format));
|
|
- /* a conservative guess (LP escape is slow!) */
|
|
- DSI_WRITE(DSI_DPI_LP_CMD_TIM, 0x00100000);
|
|
|
|
- /* Drop to LP where possible; use LP Escape for all commands */
|
|
- vid_mode_cfg = 0xbf00;
|
|
- if (!(dsi->display_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE))
|
|
- vid_mode_cfg |= 0x01;
|
|
- else if (8 * dsi->lanes > bpp)
|
|
- vid_mode_cfg &= ~0x400; /* PULSE && inexact DPICLK => fix HBP time */
|
|
+ /*
|
|
+ * Flags to configure use of LP, EoTp, Burst Mode, Sync Events/Pulses.
|
|
+ * Note that Burst Mode implies Sync Events; the two flags need not be
|
|
+ * set concurrently, and in this RP1 variant *should not* both be set:
|
|
+ * doing so would (counter-intuitively) enable Sync Pulses and may fail
|
|
+ * if there is not sufficient time to return to LP11 state during HBP.
|
|
+ */
|
|
+ mask = DSI_VID_MODE_LP_HFP_EN | DSI_VID_MODE_LP_HBP_EN |
|
|
+ DSI_VID_MODE_LP_VACT_EN | DSI_VID_MODE_LP_VFP_EN |
|
|
+ DSI_VID_MODE_LP_VBP_EN | DSI_VID_MODE_LP_VSA_EN;
|
|
+ if (dsi->display_flags & MIPI_DSI_MODE_LPM)
|
|
+ mask |= DSI_VID_MODE_LP_CMD_EN;
|
|
if (dsi->display_flags & MIPI_DSI_MODE_VIDEO_BURST)
|
|
- vid_mode_cfg |= 0x02;
|
|
- DSI_WRITE(DSI_VID_MODE_CFG, vid_mode_cfg);
|
|
- DSI_WRITE(DSI_CMD_MODE_CFG, 0x10F7F00);
|
|
+ mask |= DSI_VID_MODE_BURST;
|
|
+ else if (!(dsi->display_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE))
|
|
+ mask |= DSI_VID_MODE_SYNC_EVENTS;
|
|
+ else if (8 * dsi->lanes > bpp)
|
|
+ mask &= ~DSI_VID_MODE_LP_HBP_EN; /* PULSE && inexact DPICLK => fix HBP time */
|
|
+ DSI_WRITE(DSI_VID_MODE_CFG, mask);
|
|
+ DSI_WRITE(DSI_CMD_MODE_CFG,
|
|
+ (dsi->display_flags & MIPI_DSI_MODE_LPM) ? DSI_CMD_MODE_ALL_LP : 0);
|
|
+ DSI_WRITE(DSI_PCKHDL_CFG,
|
|
+ DSI_PCKHDL_BTA_EN |
|
|
+ ((dsi->display_flags & MIPI_DSI_MODE_NO_EOT_PACKET) ? 0 : DSI_PCKHDL_EOTP_TX_EN));
|
|
|
|
/* Select Command Mode */
|
|
DSI_WRITE(DSI_MODE_CFG, 1);
|
|
@@ -1397,9 +1428,9 @@ void rp1dsi_dsi_setup(struct rp1_dsi *ds
|
|
timeout = 0;
|
|
DSI_WRITE(DSI_TO_CNT_CFG, (timeout << 16) | RP1DSI_LPRX_TO_VAL);
|
|
DSI_WRITE(DSI_BTA_TO_CNT, RP1DSI_BTA_TO_VAL);
|
|
+ clkdiv = max(2u, 1u + byte_clock / RP1DSI_ESC_CLK_MAX); /* byte clocks per escape clock */
|
|
DSI_WRITE(DSI_CLKMGR_CFG,
|
|
- (RP1DSI_TO_CLK_DIV << 8) |
|
|
- max(2u, 1u + byte_clock / RP1DSI_ESC_CLK_MAX));
|
|
+ (RP1DSI_TO_CLK_DIV << 8) | clkdiv);
|
|
|
|
/* Configure video timings */
|
|
DSI_WRITE(DSI_VID_PKT_SIZE, mode->hdisplay);
|
|
@@ -1425,6 +1456,18 @@ void rp1dsi_dsi_setup(struct rp1_dsi *ds
|
|
(hsfreq_table[dsi->hsfreq_index].data_lp2hs << DSI_PHY_TMR_LP2HS_LSB) |
|
|
(hsfreq_table[dsi->hsfreq_index].data_hs2lp << DSI_PHY_TMR_HS2LP_LSB));
|
|
|
|
+ /* Estimate how many LP bytes can be sent during vertical blanking (Databook 3.6.2.1) */
|
|
+ cmdtim = mode->htotal;
|
|
+ if (dsi->display_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
|
|
+ cmdtim -= mode->hsync_end - mode->hsync_start;
|
|
+ cmdtim = (bpp * cmdtim - 64) / (8 * dsi->lanes); /* byte clocks after HSS and EoTp */
|
|
+ cmdtim -= hsfreq_table[dsi->hsfreq_index].data_hs2lp;
|
|
+ cmdtim -= hsfreq_table[dsi->hsfreq_index].data_lp2hs;
|
|
+ cmdtim = (cmdtim / clkdiv) - 24; /* escape clocks for commands */
|
|
+ cmdtim = max(0, cmdtim >> 4); /* bytes (at 2 clocks per bit) */
|
|
+ drm_info(dsi->drm, "rp1dsi: Command time (outvact): %d\n", cmdtim);
|
|
+ DSI_WRITE(DSI_DPI_LP_CMD_TIM, cmdtim << 16);
|
|
+
|
|
/* Wait for PLL lock */
|
|
for (timeout = (1 << 14); timeout != 0; --timeout) {
|
|
usleep_range(10, 50);
|
|
@@ -1434,9 +1477,9 @@ void rp1dsi_dsi_setup(struct rp1_dsi *ds
|
|
if (timeout == 0)
|
|
drm_err(dsi->drm, "RP1DSI: Time out waiting for PLL\n");
|
|
|
|
- DSI_WRITE(DSI_LPCLK_CTRL, 0x1); /* configure the requesthsclk */
|
|
+ DSI_WRITE(DSI_LPCLK_CTRL,
|
|
+ (dsi->display_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) ? 0x3 : 0x1);
|
|
DSI_WRITE(DSI_PHY_TST_CTRL0, 0x2);
|
|
- DSI_WRITE(DSI_PCKHDL_CFG, 1 << 2); /* allow bus turnaround */
|
|
DSI_WRITE(DSI_PWR_UP, 0x1); /* power up */
|
|
|
|
/* Now it should be safe to start the external DPI clock divider */
|
|
@@ -1460,7 +1503,8 @@ void rp1dsi_dsi_setup(struct rp1_dsi *ds
|
|
mask, DSI_READ(DSI_PHY_STATUS));
|
|
}
|
|
|
|
-void rp1dsi_dsi_send(struct rp1_dsi *dsi, u32 hdr, int len, const u8 *buf)
|
|
+void rp1dsi_dsi_send(struct rp1_dsi *dsi, u32 hdr, int len, const u8 *buf,
|
|
+ bool use_lpm, bool req_ack)
|
|
{
|
|
u32 val;
|
|
|
|
@@ -1471,6 +1515,24 @@ void rp1dsi_dsi_send(struct rp1_dsi *dsi
|
|
usleep_range(100, 150);
|
|
}
|
|
|
|
+ /*
|
|
+ * Update global configuration flags for LP/HS and ACK options.
|
|
+ * XXX It's not clear if having empty FIFOs (checked above and below) guarantees that
|
|
+ * the last command has completed and been ACKed, or how closely these control registers
|
|
+ * align with command/payload FIFO writes (as each is an independent clock-crossing)?
|
|
+ */
|
|
+ val = DSI_READ(DSI_VID_MODE_CFG);
|
|
+ if (use_lpm)
|
|
+ val |= DSI_VID_MODE_LP_CMD_EN;
|
|
+ else
|
|
+ val &= ~DSI_VID_MODE_LP_CMD_EN;
|
|
+ DSI_WRITE(DSI_VID_MODE_CFG, val);
|
|
+ val = (use_lpm) ? DSI_CMD_MODE_ALL_LP : 0;
|
|
+ if (req_ack)
|
|
+ val |= DSI_CMD_MODE_ACK_RQST_EN;
|
|
+ DSI_WRITE(DSI_CMD_MODE_CFG, val);
|
|
+ (void)DSI_READ(DSI_CMD_MODE_CFG);
|
|
+
|
|
/* Write payload (in 32-bit words) and header */
|
|
for (; len > 0; len -= 4) {
|
|
val = *buf++;
|
|
@@ -1504,8 +1566,10 @@ int rp1dsi_dsi_recv(struct rp1_dsi *dsi,
|
|
break;
|
|
usleep_range(100, 150);
|
|
}
|
|
- if (i == 0)
|
|
+ if (!i) {
|
|
+ drm_warn(dsi->drm, "Receive failed\n");
|
|
return -EIO;
|
|
+ }
|
|
|
|
for (i = 0; i < len; i += 4) {
|
|
/* Read fifo must not be empty before all bytes are read */
|