Skip to content

Commit

Permalink
WIP: drm: vc4: Fixup DSI driver to enable video and then retry transfers
Browse files Browse the repository at this point in the history
The DSI block appears to be able to come up stuck in a condition where
it leaves the lanes in HS mode or just jabbering. This stops LP
transfers from completing as there is no LP time available. This is
signalled via the LP1 contention error.

Enabling video briefly clears that condition, so if we detect the
error condition, enable video mode and then retry.
  • Loading branch information
6by9 committed Sep 19, 2024
1 parent 43cd81c commit 1f4c3f6
Showing 1 changed file with 79 additions and 30 deletions.
109 changes: 79 additions & 30 deletions drivers/gpu/drm/vc4/vc4_dsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,8 @@
DSI1_INT_PR_TO)

#define DSI0_STAT 0x2c
# define DSI0_STAT_ERR_CONT_LP1 BIT(6)
# define DSI0_STAT_ERR_CONT_LP0 BIT(5)
#define DSI0_HSTX_TO_CNT 0x30
#define DSI0_LPRX_TO_CNT 0x34
#define DSI0_TA_TO_CNT 0x38
Expand Down Expand Up @@ -586,6 +588,8 @@ struct vc4_dsi {
u32 divider;
u32 mode_flags;

struct drm_display_mode mode;

/* Input clock from CPRMAN to the digital PHY, for the DSI
* escape clock.
*/
Expand Down Expand Up @@ -820,10 +824,8 @@ static void vc4_dsi_bridge_disable(struct drm_bridge *bridge,
DSI_PORT_WRITE(DISP0_CTRL, disp0_ctrl);
}

static void vc4_dsi_bridge_post_disable(struct drm_bridge *bridge,
struct drm_bridge_state *state)
static void vc4_dsi_power_down(struct vc4_dsi *dsi)
{
struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
struct device *dev = &dsi->pdev->dev;

/* Reset the DSI and all its fifos. */
Expand All @@ -842,6 +844,14 @@ static void vc4_dsi_bridge_post_disable(struct drm_bridge *bridge,
pm_runtime_put(dev);
}

static void vc4_dsi_bridge_post_disable(struct drm_bridge *bridge,
struct drm_bridge_state *state)
{
struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);

vc4_dsi_power_down(dsi);
}

/* Extends the mode's blank intervals to handle BCM2835's integer-only
* DSI PLL divider.
*
Expand Down Expand Up @@ -907,18 +917,12 @@ static bool vc4_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
return true;
}

static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge,
struct drm_bridge_state *old_state)
static void vc4_dsi_power_up(struct vc4_dsi *dsi)
{
struct drm_atomic_state *state = old_state->base.state;
struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
const struct drm_crtc_state *crtc_state;
struct device *dev = &dsi->pdev->dev;
const struct drm_display_mode *mode;
struct drm_connector *connector;
const struct drm_display_mode *mode = &dsi->mode;
bool debug_dump_regs = false;
unsigned long hs_clock;
struct drm_crtc *crtc;
u32 ui_ns;
/* Minimum LP state duration in escape clock cycles. */
u32 lpx = dsi_esc_timing(60);
Expand All @@ -939,23 +943,14 @@ static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge,
drm_print_regset32(&p, &dsi->regset);
}

/*
* Retrieve the CRTC adjusted mode. This requires a little dance to go
* from the bridge to the encoder, to the connector and to the CRTC.
*/
connector = drm_atomic_get_new_connector_for_encoder(state,
bridge->encoder);
crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
mode = &crtc_state->adjusted_mode;

pixel_clock_hz = mode->clock * 1000;

/* Round up the clk_set_rate() request slightly, since
* PLLD_DSI1 is an integer divider and its rate selection will
* never round up.
*/
phy_clock = (pixel_clock_hz + 1000) * dsi->divider;

ret = clk_set_rate(dsi->pll_phy_clock, phy_clock);
if (ret) {
dev_err(&dsi->pdev->dev,
Expand Down Expand Up @@ -1033,10 +1028,10 @@ static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge,

DSI_PORT_WRITE(PHY_AFEC0, afec0);

DSI_PORT_WRITE(PHY_AFEC1, 0);

/* AFEC reset hold time */
mdelay(1);
mdelay(5);

DSI_PORT_WRITE(PHY_AFEC1, 0x00001806);
}

/* Yes, we set the DSI0P/DSI1P pixel clock to the byte rate,
Expand Down Expand Up @@ -1175,6 +1170,29 @@ static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge,
}
}

static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge,
struct drm_bridge_state *old_state)
{
struct drm_atomic_state *state = old_state->base.state;
struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
const struct drm_crtc_state *crtc_state;
struct drm_connector *connector;
struct drm_crtc *crtc;

/*
* Retrieve the CRTC adjusted mode. This requires a little dance to go
* from the bridge to the encoder, to the connector and to the CRTC.
*/
connector = drm_atomic_get_new_connector_for_encoder(state,
bridge->encoder);
crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);

drm_mode_copy(&dsi->mode, &crtc_state->adjusted_mode);

vc4_dsi_power_up(dsi);
}

static void vc4_dsi_bridge_enable(struct drm_bridge *bridge,
struct drm_bridge_state *old_state)
{
Expand Down Expand Up @@ -1203,10 +1221,9 @@ static int vc4_dsi_bridge_attach(struct drm_bridge *bridge,
&dsi->bridge, flags);
}

static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host,
const struct mipi_dsi_msg *msg)
static ssize_t vc4_dsi_transfer(struct vc4_dsi *dsi,
const struct mipi_dsi_msg *msg)
{
struct vc4_dsi *dsi = host_to_dsi(host);
struct mipi_dsi_packet packet;
u32 pkth = 0, pktc = 0;
int i, ret;
Expand Down Expand Up @@ -1315,10 +1332,10 @@ static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host,
DSI_PORT_WRITE(TXPKT1C, pktc);

if (!wait_for_completion_timeout(&dsi->xfer_completion,
msecs_to_jiffies(1000))) {
msecs_to_jiffies(500))) {
dev_err(&dsi->pdev->dev, "transfer interrupt wait timeout");
dev_err(&dsi->pdev->dev, "instat: 0x%08x\n",
DSI_PORT_READ(INT_STAT));
dev_err(&dsi->pdev->dev, "instat: 0x%08x stat: 0x%08x\n",
DSI_PORT_READ(INT_STAT), DSI_PORT_READ(STAT));
ret = -ETIMEDOUT;
} else {
ret = dsi->xfer_result;
Expand Down Expand Up @@ -1374,6 +1391,38 @@ static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host,
return ret;
}

static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host,
const struct mipi_dsi_msg *msg)
{
struct vc4_dsi *dsi = host_to_dsi(host);
u32 stat, disp0_ctrl;
int ret;

ret = vc4_dsi_transfer(dsi, msg);

if (ret == -ETIMEDOUT) {
stat = DSI_PORT_READ(STAT);
if (stat & DSI_PORT_BIT(STAT_ERR_CONT_LP1)) {
DSI_PORT_WRITE(STAT, DSI_PORT_BIT(STAT_ERR_CONT_LP1));

disp0_ctrl = DSI_PORT_READ(DISP0_CTRL);
disp0_ctrl |= DSI_DISP0_ENABLE;
DSI_PORT_WRITE(DISP0_CTRL, disp0_ctrl);
msleep(20);
disp0_ctrl &= ~DSI_DISP0_ENABLE;
DSI_PORT_WRITE(DISP0_CTRL, disp0_ctrl);
msleep(20);

/* vc4_dsi_power_down(dsi);
msleep(200);
vc4_dsi_power_up(dsi);
msleep(200);*/
ret = vc4_dsi_transfer(dsi, msg);
}
}
return ret;
}

static const struct component_ops vc4_dsi_ops;
static int vc4_dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *device)
Expand Down

0 comments on commit 1f4c3f6

Please sign in to comment.