aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/mcde/mcde_dsi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/mcde/mcde_dsi.c')
-rw-r--r--drivers/gpu/drm/mcde/mcde_dsi.c422
1 files changed, 278 insertions, 144 deletions
diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
index d6214d3c8b33..bb6528b01cd0 100644
--- a/drivers/gpu/drm/mcde/mcde_dsi.c
+++ b/drivers/gpu/drm/mcde/mcde_dsi.c
@@ -39,7 +39,6 @@ struct mcde_dsi {
struct device *dev;
struct mcde *mcde;
struct drm_bridge bridge;
- struct drm_connector connector;
struct drm_panel *panel;
struct drm_bridge *bridge_out;
struct mipi_dsi_host dsi_host;
@@ -64,11 +63,6 @@ static inline struct mcde_dsi *host_to_mcde_dsi(struct mipi_dsi_host *h)
return container_of(h, struct mcde_dsi, dsi_host);
}
-static inline struct mcde_dsi *connector_to_mcde_dsi(struct drm_connector *c)
-{
- return container_of(c, struct mcde_dsi, connector);
-}
-
bool mcde_dsi_irq(struct mipi_dsi_device *mdsi)
{
struct mcde_dsi *d;
@@ -124,12 +118,41 @@ bool mcde_dsi_irq(struct mipi_dsi_device *mdsi)
val = readl(d->regs + DSI_VID_MODE_STS_FLAG);
if (val)
- dev_err(d->dev, "some video mode error status\n");
+ dev_dbg(d->dev, "DSI_VID_MODE_STS_FLAG = %08x\n", val);
+ if (val & DSI_VID_MODE_STS_VSG_RUNNING)
+ dev_dbg(d->dev, "VID mode VSG running\n");
+ if (val & DSI_VID_MODE_STS_ERR_MISSING_DATA)
+ dev_err(d->dev, "VID mode missing data\n");
+ if (val & DSI_VID_MODE_STS_ERR_MISSING_HSYNC)
+ dev_err(d->dev, "VID mode missing HSYNC\n");
+ if (val & DSI_VID_MODE_STS_ERR_MISSING_VSYNC)
+ dev_err(d->dev, "VID mode missing VSYNC\n");
+ if (val & DSI_VID_MODE_STS_REG_ERR_SMALL_LENGTH)
+ dev_err(d->dev, "VID mode less bytes than expected between two HSYNC\n");
+ if (val & DSI_VID_MODE_STS_REG_ERR_SMALL_HEIGHT)
+ dev_err(d->dev, "VID mode less lines than expected between two VSYNC\n");
+ if (val & (DSI_VID_MODE_STS_ERR_BURSTWRITE |
+ DSI_VID_MODE_STS_ERR_LINEWRITE |
+ DSI_VID_MODE_STS_ERR_LONGREAD))
+ dev_err(d->dev, "VID mode read/write error\n");
+ if (val & DSI_VID_MODE_STS_ERR_VRS_WRONG_LENGTH)
+ dev_err(d->dev, "VID mode received packets differ from expected size\n");
+ if (val & DSI_VID_MODE_STS_VSG_RECOVERY)
+ dev_err(d->dev, "VID mode VSG in recovery mode\n");
writel(val, d->regs + DSI_VID_MODE_STS_CLR);
return te_received;
}
+static void mcde_dsi_attach_to_mcde(struct mcde_dsi *d)
+{
+ d->mcde->mdsi = d->mdsi;
+
+ d->mcde->video_mode = !!(d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO);
+ /* Enable use of the TE signal for all command mode panels */
+ d->mcde->te_sync = !d->mcde->video_mode;
+}
+
static int mcde_dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *mdsi)
{
@@ -148,7 +171,7 @@ static int mcde_dsi_host_attach(struct mipi_dsi_host *host,
d->mdsi = mdsi;
if (d->mcde)
- d->mcde->mdsi = mdsi;
+ mcde_dsi_attach_to_mcde(d);
return 0;
}
@@ -223,25 +246,25 @@ static ssize_t mcde_dsi_host_transfer(struct mipi_dsi_host *host,
if (txlen > 0) {
val = 0;
for (i = 0; i < 4 && i < txlen; i++)
- val |= tx[i] << (i & 3) * 8;
+ val |= tx[i] << (i * 8);
}
writel(val, d->regs + DSI_DIRECT_CMD_WRDAT0);
if (txlen > 4) {
val = 0;
for (i = 0; i < 4 && (i + 4) < txlen; i++)
- val |= tx[i + 4] << (i & 3) * 8;
+ val |= tx[i + 4] << (i * 8);
writel(val, d->regs + DSI_DIRECT_CMD_WRDAT1);
}
if (txlen > 8) {
val = 0;
for (i = 0; i < 4 && (i + 8) < txlen; i++)
- val |= tx[i + 8] << (i & 3) * 8;
+ val |= tx[i + 8] << (i * 8);
writel(val, d->regs + DSI_DIRECT_CMD_WRDAT2);
}
if (txlen > 12) {
val = 0;
for (i = 0; i < 4 && (i + 12) < txlen; i++)
- val |= tx[i + 12] << (i & 3) * 8;
+ val |= tx[i + 12] << (i * 8);
writel(val, d->regs + DSI_DIRECT_CMD_WRDAT3);
}
@@ -336,7 +359,7 @@ void mcde_dsi_te_request(struct mipi_dsi_device *mdsi)
val |= 0 << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID_SHIFT;
val |= 2 << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE_SHIFT;
val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN;
- val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_DCS_SHORT_WRITE_1 <<
+ val |= MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM <<
DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_SHIFT;
writel(val, d->regs + DSI_DIRECT_CMD_MAIN_SETTINGS);
@@ -365,13 +388,14 @@ void mcde_dsi_te_request(struct mipi_dsi_device *mdsi)
static void mcde_dsi_setup_video_mode(struct mcde_dsi *d,
const struct drm_display_mode *mode)
{
- u8 bpp = mipi_dsi_pixel_format_to_bpp(d->mdsi->format);
+ /* cpp, characters per pixel, number of bytes per pixel */
+ u8 cpp = mipi_dsi_pixel_format_to_bpp(d->mdsi->format) / 8;
+ u64 pclk;
u64 bpl;
- u32 hfp;
- u32 hbp;
- u32 hsa;
+ int hfp;
+ int hbp;
+ int hsa;
u32 blkline_pck, line_duration;
- u32 blkeol_pck, blkeol_duration;
u32 val;
val = 0;
@@ -408,11 +432,21 @@ static void mcde_dsi_setup_video_mode(struct mcde_dsi *d,
return;
}
- /* TODO: TVG could be enabled here */
+ /* TODO: TVG (test video generator) could be enabled here */
- /* Send blanking packet */
+ /*
+ * During vertical blanking: go to LP mode
+ * Like with the EOL setting, if this is not set, the EOL area will be
+ * filled with NULL or blanking packets in the vblank area.
+ * FIXME: some Samsung phones and display panels such as s6e63m0 use
+ * DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_BLANKING here instead,
+ * figure out how to properly configure that from the panel.
+ */
val |= DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_LP_0;
- /* Send EOL packet */
+ /*
+ * During EOL: go to LP mode. If this is not set, the EOL area will be
+ * filled with NULL or blanking packets.
+ */
val |= DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_LP_0;
/* Recovery mode 1 */
val |= 1 << DSI_VID_MAIN_CTL_RECOVERY_MODE_SHIFT;
@@ -420,13 +454,13 @@ static void mcde_dsi_setup_video_mode(struct mcde_dsi *d,
writel(val, d->regs + DSI_VID_MAIN_CTL);
/* Vertical frame parameters are pretty straight-forward */
- val = mode->vdisplay << DSI_VID_VSIZE_VSA_LENGTH_SHIFT;
+ val = mode->vdisplay << DSI_VID_VSIZE_VACT_LENGTH_SHIFT;
/* vertical front porch */
val |= (mode->vsync_start - mode->vdisplay)
<< DSI_VID_VSIZE_VFP_LENGTH_SHIFT;
/* vertical sync active */
val |= (mode->vsync_end - mode->vsync_start)
- << DSI_VID_VSIZE_VACT_LENGTH_SHIFT;
+ << DSI_VID_VSIZE_VSA_LENGTH_SHIFT;
/* vertical back porch */
val |= (mode->vtotal - mode->vsync_end)
<< DSI_VID_VSIZE_VBP_LENGTH_SHIFT;
@@ -434,36 +468,54 @@ static void mcde_dsi_setup_video_mode(struct mcde_dsi *d,
/*
* Horizontal frame parameters:
- * horizontal resolution is given in pixels and must be re-calculated
- * into bytes since this is what the hardware expects.
+ * horizontal resolution is given in pixels but must be re-calculated
+ * into bytes since this is what the hardware expects, these registers
+ * define the payload size of the packet.
+ *
+ * hfp = horizontal front porch in bytes
+ * hbp = horizontal back porch in bytes
+ * hsa = horizontal sync active in bytes
*
* 6 + 2 is HFP header + checksum
*/
- hfp = (mode->hsync_start - mode->hdisplay) * bpp - 6 - 2;
+ hfp = (mode->hsync_start - mode->hdisplay) * cpp - 6 - 2;
if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
/*
+ * Use sync pulse for sync: explicit HSA time
* 6 is HBP header + checksum
* 4 is RGB header + checksum
*/
- hbp = (mode->htotal - mode->hsync_end) * bpp - 4 - 6;
+ hbp = (mode->htotal - mode->hsync_end) * cpp - 4 - 6;
/*
* 6 is HBP header + checksum
* 4 is HSW packet bytes
* 4 is RGB header + checksum
*/
- hsa = (mode->hsync_end - mode->hsync_start) * bpp - 4 - 4 - 6;
+ hsa = (mode->hsync_end - mode->hsync_start) * cpp - 4 - 4 - 6;
} else {
/*
- * HBP includes both back porch and sync
+ * Use event for sync: HBP includes both back porch and sync
* 6 is HBP header + checksum
* 4 is HSW packet bytes
* 4 is RGB header + checksum
*/
- hbp = (mode->htotal - mode->hsync_start) * bpp - 4 - 4 - 6;
- /* HSA is not considered in this mode and set to 0 */
+ hbp = (mode->htotal - mode->hsync_start) * cpp - 4 - 4 - 6;
+ /* HSA is not present in this mode and set to 0 */
hsa = 0;
}
- dev_dbg(d->dev, "hfp: %u, hbp: %u, hsa: %u\n",
+ if (hfp < 0) {
+ dev_info(d->dev, "hfp negative, set to 0\n");
+ hfp = 0;
+ }
+ if (hbp < 0) {
+ dev_info(d->dev, "hbp negative, set to 0\n");
+ hbp = 0;
+ }
+ if (hsa < 0) {
+ dev_info(d->dev, "hsa negative, set to 0\n");
+ hsa = 0;
+ }
+ dev_dbg(d->dev, "hfp: %u, hbp: %u, hsa: %u bytes\n",
hfp, hbp, hsa);
/* Frame parameters: horizontal sync active */
@@ -474,91 +526,185 @@ static void mcde_dsi_setup_video_mode(struct mcde_dsi *d,
val |= hfp << DSI_VID_HSIZE1_HFP_LENGTH_SHIFT;
writel(val, d->regs + DSI_VID_HSIZE1);
- /* RGB data length (bytes on one scanline) */
- val = mode->hdisplay * (bpp / 8);
+ /* RGB data length (visible bytes on one scanline) */
+ val = mode->hdisplay * cpp;
writel(val, d->regs + DSI_VID_HSIZE2);
+ dev_dbg(d->dev, "RGB length, visible area on a line: %u bytes\n", val);
- /* TODO: further adjustments for TVG mode here */
+ /*
+ * Calculate the time between two pixels in picoseconds using
+ * the supplied refresh rate and total resolution including
+ * porches and sync.
+ */
+ /* (ps/s) / (pixels/s) = ps/pixels */
+ pclk = DIV_ROUND_UP_ULL(1000000000000,
+ (mode->vrefresh * mode->htotal * mode->vtotal));
+ dev_dbg(d->dev, "picoseconds between two pixels: %llu\n",
+ pclk);
/*
- * EOL packet length from bits per line calculations: pixel clock
- * is given in kHz, calculate the time between two pixels in
- * picoseconds.
+ * How many bytes per line will this update frequency yield?
+ *
+ * Calculate the number of picoseconds for one scanline (1), then
+ * divide by 1000000000000 (2) to get in pixels per second we
+ * want to output.
+ *
+ * Multiply with number of bytes per second at this video display
+ * frequency (3) to get number of bytes transferred during this
+ * time. Notice that we use the frequency the display wants,
+ * not what we actually get from the DSI PLL, which is hs_freq.
+ *
+ * These arithmetics are done in a different order to avoid
+ * overflow.
*/
- bpl = mode->clock * mode->htotal;
- bpl *= (d->hs_freq / 8);
- do_div(bpl, 1000000); /* microseconds */
- do_div(bpl, 1000000); /* seconds */
+ bpl = pclk * mode->htotal; /* (1) picoseconds per line */
+ dev_dbg(d->dev, "picoseconds per line: %llu\n", bpl);
+ /* Multiply with bytes per second (3) */
+ bpl *= (d->mdsi->hs_rate / 8);
+ /* Pixels per second (2) */
+ bpl = DIV_ROUND_DOWN_ULL(bpl, 1000000); /* microseconds */
+ bpl = DIV_ROUND_DOWN_ULL(bpl, 1000000); /* seconds */
+ /* parallel transactions in all lanes */
bpl *= d->mdsi->lanes;
- dev_dbg(d->dev, "calculated bytes per line: %llu\n", bpl);
+ dev_dbg(d->dev,
+ "calculated bytes per line: %llu @ %d Hz with HS %lu Hz\n",
+ bpl, mode->vrefresh, d->mdsi->hs_rate);
+
/*
* 6 is header + checksum, header = 4 bytes, checksum = 2 bytes
* 4 is short packet for vsync/hsync
*/
if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
- /* Fixme: isn't the hsync width in pixels? */
+ /* Set the event packet size to 0 (not used) */
+ writel(0, d->regs + DSI_VID_BLKSIZE1);
+ /*
+ * FIXME: isn't the hsync width in pixels? The porch and
+ * sync area size is in pixels here, but this -6
+ * seems to be for bytes. It looks like this in the vendor
+ * code though. Is it completely untested?
+ */
blkline_pck = bpl - (mode->hsync_end - mode->hsync_start) - 6;
val = blkline_pck << DSI_VID_BLKSIZE2_BLKLINE_PULSE_PCK_SHIFT;
writel(val, d->regs + DSI_VID_BLKSIZE2);
} else {
+ /* Set the sync pulse packet size to 0 (not used) */
+ writel(0, d->regs + DSI_VID_BLKSIZE2);
+ /* Specifying payload size in bytes (-4-6 from manual) */
blkline_pck = bpl - 4 - 6;
+ if (blkline_pck > 0x1FFF)
+ dev_err(d->dev, "blkline_pck too big %d bytes\n",
+ blkline_pck);
val = blkline_pck << DSI_VID_BLKSIZE1_BLKLINE_EVENT_PCK_SHIFT;
+ val &= DSI_VID_BLKSIZE1_BLKLINE_EVENT_PCK_MASK;
writel(val, d->regs + DSI_VID_BLKSIZE1);
}
- line_duration = (blkline_pck + 6) / d->mdsi->lanes;
- dev_dbg(d->dev, "line duration %u\n", line_duration);
+ /*
+ * The line duration is used to scale back the frequency from
+ * the max frequency supported by the HS clock to the desired
+ * update frequency in vrefresh.
+ */
+ line_duration = blkline_pck + 6;
+ /*
+ * The datasheet contains this complex condition to decreasing
+ * the line duration by 1 under very specific circumstances.
+ * Here we also imply that LP is used during burst EOL.
+ */
+ if (d->mdsi->lanes == 2 && (hsa & 0x01) && (hfp & 0x01)
+ && (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST))
+ line_duration--;
+ line_duration = DIV_ROUND_CLOSEST(line_duration, d->mdsi->lanes);
+ dev_dbg(d->dev, "line duration %u bytes\n", line_duration);
val = line_duration << DSI_VID_DPHY_TIME_REG_LINE_DURATION_SHIFT;
/*
* This is the time to perform LP->HS on D-PHY
* FIXME: nowhere to get this from: DT property on the DSI?
+ * The manual says this is "system dependent".
+ * values like 48 and 72 seen in the vendor code.
*/
- val |= 0 << DSI_VID_DPHY_TIME_REG_WAKEUP_TIME_SHIFT;
+ val |= 48 << DSI_VID_DPHY_TIME_REG_WAKEUP_TIME_SHIFT;
writel(val, d->regs + DSI_VID_DPHY_TIME);
- /* Calculate block end of line */
- blkeol_pck = bpl - mode->hdisplay * bpp - 6;
- blkeol_duration = (blkeol_pck + 6) / d->mdsi->lanes;
- dev_dbg(d->dev, "blkeol pck: %u, duration: %u\n",
- blkeol_pck, blkeol_duration);
-
+ /*
+ * See the manual figure 657 page 2203 for understanding the impact
+ * of the different burst mode settings.
+ */
if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
- /* Set up EOL clock for burst mode */
+ int blkeol_pck, blkeol_duration;
+ /*
+ * Packet size at EOL for burst mode, this is only used
+ * if DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_LP_0 is NOT set,
+ * but we instead send NULL or blanking packets at EOL.
+ * This is given in number of bytes.
+ *
+ * See the manual page 2198 for the 13 reg_blkeol_pck bits.
+ */
+ blkeol_pck = bpl - (mode->htotal * cpp) - 6;
+ if (blkeol_pck < 0) {
+ dev_err(d->dev, "video block does not fit on line!\n");
+ dev_err(d->dev,
+ "calculated bytes per line: %llu @ %d Hz\n",
+ bpl, mode->vrefresh);
+ dev_err(d->dev,
+ "bytes per line (blkline_pck) %u bytes\n",
+ blkline_pck);
+ dev_err(d->dev,
+ "blkeol_pck becomes %d bytes\n", blkeol_pck);
+ return;
+ }
+ dev_dbg(d->dev, "BLKEOL packet: %d bytes\n", blkeol_pck);
+
val = readl(d->regs + DSI_VID_BLKSIZE1);
+ val &= ~DSI_VID_BLKSIZE1_BLKEOL_PCK_MASK;
val |= blkeol_pck << DSI_VID_BLKSIZE1_BLKEOL_PCK_SHIFT;
writel(val, d->regs + DSI_VID_BLKSIZE1);
- writel(blkeol_pck, d->regs + DSI_VID_VCA_SETTING2);
-
- writel(blkeol_duration, d->regs + DSI_VID_PCK_TIME);
- writel(blkeol_duration - 6, d->regs + DSI_VID_VCA_SETTING1);
+ /* Use the same value for exact burst limit */
+ val = blkeol_pck <<
+ DSI_VID_VCA_SETTING2_EXACT_BURST_LIMIT_SHIFT;
+ val &= DSI_VID_VCA_SETTING2_EXACT_BURST_LIMIT_MASK;
+ writel(val, d->regs + DSI_VID_VCA_SETTING2);
+ /*
+ * This BLKEOL duration is claimed to be the duration in clock
+ * cycles of the BLLP end-of-line (EOL) period for each line if
+ * DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_LP_0 is set.
+ *
+ * It is hard to trust the manuals' claim that this is in clock
+ * cycles as we mimic the behaviour of the vendor code, which
+ * appears to write a number of bytes that would have been
+ * transferred on a single lane.
+ *
+ * See the manual figure 657 page 2203 and page 2198 for the 13
+ * reg_blkeol_duration bits.
+ *
+ * FIXME: should this also be set up also for non-burst mode
+ * according to figure 565 page 2202?
+ */
+ blkeol_duration = DIV_ROUND_CLOSEST(blkeol_pck + 6,
+ d->mdsi->lanes);
+ dev_dbg(d->dev, "BLKEOL duration: %d clock cycles\n",
+ blkeol_duration);
+
+ val = readl(d->regs + DSI_VID_PCK_TIME);
+ val &= ~DSI_VID_PCK_TIME_BLKEOL_DURATION_MASK;
+ val |= blkeol_duration <<
+ DSI_VID_PCK_TIME_BLKEOL_DURATION_SHIFT;
+ writel(val, d->regs + DSI_VID_PCK_TIME);
+
+ /* Max burst limit, this is given in bytes */
+ val = readl(d->regs + DSI_VID_VCA_SETTING1);
+ val &= ~DSI_VID_VCA_SETTING1_MAX_BURST_LIMIT_MASK;
+ val |= (blkeol_pck - 6) <<
+ DSI_VID_VCA_SETTING1_MAX_BURST_LIMIT_SHIFT;
+ writel(val, d->regs + DSI_VID_VCA_SETTING1);
}
/* Maximum line limit */
val = readl(d->regs + DSI_VID_VCA_SETTING2);
- val |= blkline_pck <<
- DSI_VID_VCA_SETTING2_EXACT_BURST_LIMIT_SHIFT;
+ val &= ~DSI_VID_VCA_SETTING2_MAX_LINE_LIMIT_MASK;
+ val |= (blkline_pck - 6) <<
+ DSI_VID_VCA_SETTING2_MAX_LINE_LIMIT_SHIFT;
writel(val, d->regs + DSI_VID_VCA_SETTING2);
-
- /* Put IF1 into video mode */
- val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
- val |= DSI_MCTL_MAIN_DATA_CTL_IF1_MODE;
- writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
-
- /* Disable command mode on IF1 */
- val = readl(d->regs + DSI_CMD_MODE_CTL);
- val &= ~DSI_CMD_MODE_CTL_IF1_LP_EN;
- writel(val, d->regs + DSI_CMD_MODE_CTL);
-
- /* Enable some error interrupts */
- val = readl(d->regs + DSI_VID_MODE_STS_CTL);
- val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC;
- val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA;
- writel(val, d->regs + DSI_VID_MODE_STS_CTL);
-
- /* Enable video mode */
- val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
- val |= DSI_MCTL_MAIN_DATA_CTL_VID_EN;
- writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
+ dev_dbg(d->dev, "blkline pck: %d bytes\n", blkline_pck - 6);
}
static void mcde_dsi_start(struct mcde_dsi *d)
@@ -670,30 +816,25 @@ static void mcde_dsi_start(struct mcde_dsi *d)
static void mcde_dsi_bridge_enable(struct drm_bridge *bridge)
{
struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
+ u32 val;
+
+ if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
+ /* Enable video mode */
+ val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
+ val |= DSI_MCTL_MAIN_DATA_CTL_VID_EN;
+ writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
+ }
dev_info(d->dev, "enable DSI master\n");
};
-static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge,
- const struct drm_display_mode *mode,
- const struct drm_display_mode *adj)
+static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
{
struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
- unsigned long pixel_clock_hz = mode->clock * 1000;
unsigned long hs_freq, lp_freq;
u32 val;
int ret;
- if (!d->mdsi) {
- dev_err(d->dev, "no DSI device attached to encoder!\n");
- return;
- }
-
- dev_info(d->dev, "set DSI master to %dx%d %lu Hz %s mode\n",
- mode->hdisplay, mode->vdisplay, pixel_clock_hz,
- (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ? "VIDEO" : "CMD"
- );
-
/* Copy maximum clock frequencies */
if (d->mdsi->lp_rate)
lp_freq = d->mdsi->lp_rate;
@@ -732,7 +873,21 @@ static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge,
d->hs_freq);
if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
- mcde_dsi_setup_video_mode(d, mode);
+ /* Put IF1 into video mode */
+ val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
+ val |= DSI_MCTL_MAIN_DATA_CTL_IF1_MODE;
+ writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
+
+ /* Disable command mode on IF1 */
+ val = readl(d->regs + DSI_CMD_MODE_CTL);
+ val &= ~DSI_CMD_MODE_CTL_IF1_LP_EN;
+ writel(val, d->regs + DSI_CMD_MODE_CTL);
+
+ /* Enable some error interrupts */
+ val = readl(d->regs + DSI_VID_MODE_STS_CTL);
+ val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC;
+ val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA;
+ writel(val, d->regs + DSI_VID_MODE_STS_CTL);
} else {
/* Command mode, clear IF1 ID */
val = readl(d->regs + DSI_CMD_MODE_CTL);
@@ -746,6 +901,26 @@ static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge,
}
}
+static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ const struct drm_display_mode *adj)
+{
+ struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
+
+ if (!d->mdsi) {
+ dev_err(d->dev, "no DSI device attached to encoder!\n");
+ return;
+ }
+
+ dev_info(d->dev, "set DSI master to %dx%d %u Hz %s mode\n",
+ mode->hdisplay, mode->vdisplay, mode->clock * 1000,
+ (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ? "VIDEO" : "CMD"
+ );
+
+ if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO)
+ mcde_dsi_setup_video_mode(d, mode);
+}
+
static void mcde_dsi_wait_for_command_mode_stop(struct mcde_dsi *d)
{
u32 val;
@@ -811,67 +986,23 @@ static void mcde_dsi_bridge_disable(struct drm_bridge *bridge)
clk_disable_unprepare(d->lp_clk);
}
-/*
- * This connector needs no special handling, just use the default
- * helpers for everything. It's pretty dummy.
- */
-static const struct drm_connector_funcs mcde_dsi_connector_funcs = {
- .reset = drm_atomic_helper_connector_reset,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = drm_connector_cleanup,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
-static int mcde_dsi_get_modes(struct drm_connector *connector)
-{
- struct mcde_dsi *d = connector_to_mcde_dsi(connector);
-
- /* Just pass the question to the panel */
- if (d->panel)
- return drm_panel_get_modes(d->panel);
-
- /* TODO: deal with bridges */
-
- return 0;
-}
-
-static const struct drm_connector_helper_funcs
-mcde_dsi_connector_helper_funcs = {
- .get_modes = mcde_dsi_get_modes,
-};
-
static int mcde_dsi_bridge_attach(struct drm_bridge *bridge)
{
struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
struct drm_device *drm = bridge->dev;
int ret;
- drm_connector_helper_add(&d->connector,
- &mcde_dsi_connector_helper_funcs);
-
if (!drm_core_check_feature(drm, DRIVER_ATOMIC)) {
dev_err(d->dev, "we need atomic updates\n");
return -ENOTSUPP;
}
- ret = drm_connector_init(drm, &d->connector,
- &mcde_dsi_connector_funcs,
- DRM_MODE_CONNECTOR_DSI);
- if (ret) {
- dev_err(d->dev, "failed to initialize DSI bridge connector\n");
- return ret;
- }
- d->connector.polled = DRM_CONNECTOR_POLL_CONNECT;
- /* The encoder in the bridge attached to the DSI bridge */
- drm_connector_attach_encoder(&d->connector, bridge->encoder);
- /* Then we attach the DSI bridge to the output (panel etc) bridge */
+ /* Attach the DSI bridge to the output (panel etc) bridge */
ret = drm_bridge_attach(bridge->encoder, d->bridge_out, bridge);
if (ret) {
dev_err(d->dev, "failed to attach the DSI bridge\n");
return ret;
}
- d->connector.status = connector_status_connected;
return 0;
}
@@ -881,6 +1012,7 @@ static const struct drm_bridge_funcs mcde_dsi_bridge_funcs = {
.mode_set = mcde_dsi_bridge_mode_set,
.disable = mcde_dsi_bridge_disable,
.enable = mcde_dsi_bridge_enable,
+ .pre_enable = mcde_dsi_bridge_pre_enable,
};
static int mcde_dsi_bind(struct device *dev, struct device *master,
@@ -901,7 +1033,7 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
d->mcde = mcde;
/* If the display attached before binding, set this up */
if (d->mdsi)
- d->mcde->mdsi = d->mdsi;
+ mcde_dsi_attach_to_mcde(d);
/* Obtain the clocks */
d->hs_clk = devm_clk_get(dev, "hs");
@@ -935,11 +1067,13 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
for_each_available_child_of_node(dev->of_node, child) {
panel = of_drm_find_panel(child);
if (IS_ERR(panel)) {
- dev_err(dev, "failed to find panel try bridge (%lu)\n",
+ dev_err(dev, "failed to find panel try bridge (%ld)\n",
PTR_ERR(panel));
+ panel = NULL;
+
bridge = of_drm_find_bridge(child);
if (IS_ERR(bridge)) {
- dev_err(dev, "failed to find bridge (%lu)\n",
+ dev_err(dev, "failed to find bridge (%ld)\n",
PTR_ERR(bridge));
return PTR_ERR(bridge);
}