mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-08 03:24:08 +08:00
drm/mcde: Some fixes to handling video mode
The video DSI mode had not really been tested. These fixes makes
it more likely to work on real hardware:
- Put the active width (x width) in the right bits and the VSA
(vertical sync active) in the right bits (those were swapped).
- Calculate the packet sizes in bytes as in the vendor driver,
rather than in bits. Test the calculations agains a
spreadsheet and confirmed by debug prints to be reasonable.
- Also verified the register values with relative confidence
to register dumps from the Samsung GT-I8190 boot loader
graphics. We are not identical but not off by far either.
- Error out if the current mode and refresh frequency doesn't
work out. (In the future we may simply want to scale down
the vrefresh.)
- Handle negative result in front/back/sync packages and fall
back to zero like in the vendor driver.
- Put in lots of clarifying comments and references to the
documentation where the code is hard to understand.
- Set the DSI_VID_VCA_SETTING2 field
DSI_VID_VCA_SETTING2_MAX_LINE_LIMIT to blkline_pck - 6 as in
the vendor driver and mask the field properly.
- Set the DSI_VID_VCA_SETTING1 field
DSI_VID_VCA_SETTING1_MAX_BURST_LIMIT to blkeol_pck - 6 to
blkeol_duration - 6 as in the vendor driver.
Cc: Stephan Gerhold <stephan@gerhold.net>
Fixes: 5fc537bfd0
("drm/mcde: Add new driver for ST-Ericsson MCDE")
Link: https://patchwork.freedesktop.org/patch/msgid/20191217150959.17215-1-linus.walleij@linaro.org
Reviewed-by: Stephan Gerhold <stephan@gerhold.net>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
parent
4a34a9dcec
commit
c8d4a56082
@ -388,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;
|
||||
@ -431,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;
|
||||
@ -443,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;
|
||||
@ -457,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 */
|
||||
@ -497,71 +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);
|
||||
|
||||
/* TODO: further adjustments for TVG mode here */
|
||||
dev_dbg(d->dev, "RGB length, visible area on a line: %u bytes\n", val);
|
||||
|
||||
/*
|
||||
* EOL packet length from bits per line calculations: pixel clock
|
||||
* is given in kHz, calculate the time between two pixels in
|
||||
* picoseconds.
|
||||
* Calculate the time between two pixels in picoseconds using
|
||||
* the supplied refresh rate and total resolution including
|
||||
* porches and sync.
|
||||
*/
|
||||
bpl = mode->clock * mode->htotal;
|
||||
bpl *= (d->hs_freq / 8);
|
||||
do_div(bpl, 1000000); /* microseconds */
|
||||
do_div(bpl, 1000000); /* seconds */
|
||||
/* (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);
|
||||
|
||||
/*
|
||||
* 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 = 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);
|
||||
/* 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);
|
||||
|
||||
writel(blkeol_duration, d->regs + DSI_VID_PCK_TIME);
|
||||
writel(blkeol_duration - 6, d->regs + DSI_VID_VCA_SETTING1);
|
||||
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);
|
||||
|
||||
dev_dbg(d->dev, "blkline pck: %d bytes\n", blkline_pck - 6);
|
||||
}
|
||||
|
||||
static void mcde_dsi_start(struct mcde_dsi *d)
|
||||
|
@ -228,6 +228,7 @@
|
||||
|
||||
#define DSI_VID_PCK_TIME 0x000000A8
|
||||
#define DSI_VID_PCK_TIME_BLKEOL_DURATION_SHIFT 0
|
||||
#define DSI_VID_PCK_TIME_BLKEOL_DURATION_MASK 0x00000FFF
|
||||
|
||||
#define DSI_VID_DPHY_TIME 0x000000AC
|
||||
#define DSI_VID_DPHY_TIME_REG_LINE_DURATION_SHIFT 0
|
||||
|
Loading…
Reference in New Issue
Block a user