diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-30 08:04:01 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-30 08:04:01 -0800 |
commit | 9f68e3655aae6d49d6ba05dd263f99f33c2567af (patch) | |
tree | 42c2c4579c4acbbb456695326af4f4ad8f402813 /drivers/gpu/drm/mcde | |
parent | 4cadc60d6bcfee9c626d4b55e9dc1475d21ad3bb (diff) | |
parent | d47c7f06268082bc0082a15297a07c0da59b0fc4 (diff) | |
download | linux-9f68e3655aae6d49d6ba05dd263f99f33c2567af.tar.bz2 |
Merge tag 'drm-next-2020-01-30' of git://anongit.freedesktop.org/drm/drm
Pull drm updates from Davbe Airlie:
"This is the main pull request for graphics for 5.6. Usual selection of
changes all over.
I've got one outstanding vmwgfx pull that touches mm so kept it
separate until after all of this lands. I'll try and get it to you
soon after this, but it might be early next week (nothing wrong with
code, just my schedule is messy)
This also hits a lot of fbdev drivers with some cleanups.
Other notables:
- vulkan timeline semaphore support added to syncobjs
- nouveau turing secureboot/graphics support
- Displayport MST display stream compression support
Detailed summary:
uapi:
- dma-buf heaps added (and fixed)
- command line add support for panel oreientation
- command line allow overriding penguin count
drm:
- mipi dsi definition updates
- lockdep annotations for dma_resv
- remove dma-buf kmap/kunmap support
- constify fb_ops in all fbdev drivers
- MST fix for daisy chained hotplug-
- CTA-861-G modes with VIC >= 193 added
- fix drm_panel_of_backlight export
- LVDS decoder support
- more device based logging support
- scanline alighment for dumb buffers
- MST DSC helpers
scheduler:
- documentation fixes
- job distribution improvements
panel:
- Logic PD type 28 panel support
- Jimax8729d MIPI-DSI
- igenic JZ4770
- generic DSI devicetree bindings
- sony acx424AKP panel
- Leadtek LTK500HD1829
- xinpeng XPP055C272
- AUO B116XAK01
- GiantPlus GPM940B0
- BOE NV140FHM-N49
- Satoz SAT050AT40H12R2
- Sharp LS020B1DD01D panels.
ttm:
- use blocking WW lock
i915:
- hw/uapi state separation
- Lock annotation improvements
- selftest improvements
- ICL/TGL DSI VDSC support
- VBT parsing improvments
- Display refactoring
- DSI updates + fixes
- HDCP 2.2 for CFL
- CML PCI ID fixes
- GLK+ fbc fix
- PSR fixes
- GEN/GT refactor improvments
- DP MST fixes
- switch context id alloc to xarray
- workaround updates
- LMEM debugfs support
- tiled monitor fixes
- ICL+ clock gating programming removed
- DP MST disable sequence fixed
- LMEM discontiguous object maps
- prefaulting for discontiguous objects
- use LMEM for dumb buffers if possible
- add LMEM mmap support
amdgpu:
- enable sync object timelines for vulkan
- MST atomic routines
- enable MST DSC support
- add DMCUB display microengine support
- DC OEM i2c support
- Renoir DC fixes
- Initial HDCP 2.x support
- BACO support for Arcturus
- Use BACO for runtime PM power save
- gfxoff on navi10
- gfx10 golden updates and fixes
- DCN support on POWER
- GFXOFF for raven1 refresh
- MM engine idle handlers cleanup
- 10bpc EDP panel fixes
- renoir watermark fixes
- SR-IOV fixes
- Arcturus VCN fixes
- GDDR6 training fixes
- freesync fixes
- Pollock support
amdkfd:
- unify more codepath with amdgpu
- use KIQ to setup HIQ rather than MMIO
radeon:
- fix vma fault handler race
- PPC DMA fix
- register check fixes for r100/r200
nouveau:
- mmap_sem vs dma_resv fix
- rewrite the ACR secure boot code for Turing
- TU10x graphics engine support (TU11x pending)
- Page kind mapping for turing
- 10-bit LUT support
- GP10B Tegra fixes
- HD audio regression fix
hisilicon/hibmc:
- use generic fbdev code and helpers
rockchip:
- dsi/px30 support
virtio:
- fb damage support
- static some functions
vc4:
- use dma_resv lock wrappers
msm:
- use dma_resv lock wrappers
- sc7180 display + DSI support
- a618 support
- UBWC support improvements
vmwgfx:
- updates + new logging uapi
exynos:
- enable/disable callback cleanups
etnaviv:
- use dma_resv lock wrappers
atmel-hlcdc:
- clock fixes
mediatek:
- cmdq support
- non-smooth cursor fixes
- ctm property support
sun4i:
- suspend support
- A64 mipi dsi support
rcar-du:
- Color management module support
- LVDS encoder dual-link support
- R8A77980 support
analogic:
- add support for an6345
ast:
- atomic modeset support
- primary plane garbage fix
arcgpu:
- fixes for fourcc handling
tegra:
- minor fixes and improvments
mcde:
- vblank support
meson:
- OSD1 plane AFBC commit
gma500:
- add pageflip support
- reomve global drm_dev
komeda:
- tweak debugfs output
- d32 support
- runtime PM suppotr
udl:
- use generic shmem helpers
- cleanup and fixes"
* tag 'drm-next-2020-01-30' of git://anongit.freedesktop.org/drm/drm: (1998 commits)
drm/nouveau/fb/gp102-: allow module to load even when scrubber binary is missing
drm/nouveau/acr: return error when registering LSF if ACR not supported
drm/nouveau/disp/gv100-: not all channel types support reporting error codes
drm/nouveau/disp/nv50-: prevent oops when no channel method map provided
drm/nouveau: support synchronous pushbuf submission
drm/nouveau: signal pending fences when channel has been killed
drm/nouveau: reject attempts to submit to dead channels
drm/nouveau: zero vma pointer even if we only unreference it rather than free
drm/nouveau: Add HD-audio component notifier support
drm/nouveau: fix build error without CONFIG_IOMMU_API
drm/nouveau/kms/nv04: remove set but not used variable 'width'
drm/nouveau/kms/nv50: remove set but not unused variable 'nv_connector'
drm/nouveau/mmu: fix comptag memory leak
drm/nouveau/gr/gp10b: Use gp100_grctx and gp100_gr_zbc
drm/nouveau/pmu/gm20b,gp10b: Fix Falcon bootstrapping
drm/exynos: Rename Exynos to lowercase
drm/exynos: change callback names
drm/mst: Don't do atomic checks over disabled managers
drm/amdgpu: add the lost mutex_init back
drm/amd/display: skip opp blank or unblank if test pattern enabled
...
Diffstat (limited to 'drivers/gpu/drm/mcde')
-rw-r--r-- | drivers/gpu/drm/mcde/mcde_display.c | 57 | ||||
-rw-r--r-- | drivers/gpu/drm/mcde/mcde_drm.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/mcde/mcde_drv.c | 18 | ||||
-rw-r--r-- | drivers/gpu/drm/mcde/mcde_dsi.c | 416 | ||||
-rw-r--r-- | drivers/gpu/drm/mcde/mcde_dsi_regs.h | 22 |
5 files changed, 323 insertions, 191 deletions
diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c index 751454ae3cd1..e59907e68854 100644 --- a/drivers/gpu/drm/mcde/mcde_display.c +++ b/drivers/gpu/drm/mcde/mcde_display.c @@ -498,24 +498,20 @@ static void mcde_configure_channel(struct mcde *mcde, enum mcde_channel ch, } /* Set up channel 0 sync (based on chnl_update_registers()) */ - if (mcde->te_sync) { - /* - * Turn on hardware TE0 synchronization - */ + if (mcde->video_mode || mcde->te_sync) val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE << MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT; - val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE0 - << MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT; - } else { - /* - * Set up sync source to software, out sync formatter - * Code mostly from mcde_hw.c chnl_update_registers() - */ + else val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SOFTWARE << MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT; + + if (mcde->te_sync) + val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE0 + << MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT; + else val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_FORMATTER << MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT; - } + writel(val, mcde->regs + sync); /* Set up pixels per line and lines per frame */ @@ -934,10 +930,17 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe, val = readl(mcde->regs + MCDE_CRC); val |= MCDE_CRC_SYCEN0; writel(val, mcde->regs + MCDE_CRC); - - drm_crtc_vblank_on(crtc); } + drm_crtc_vblank_on(crtc); + + if (mcde->video_mode) + /* + * Keep FIFO permanently enabled in video mode, + * otherwise MCDE will stop feeding data to the panel. + */ + mcde_enable_fifo(mcde, MCDE_FIFO_A); + dev_info(drm->dev, "MCDE display is enabled\n"); } @@ -946,13 +949,22 @@ static void mcde_display_disable(struct drm_simple_display_pipe *pipe) struct drm_crtc *crtc = &pipe->crtc; struct drm_device *drm = crtc->dev; struct mcde *mcde = drm->dev_private; + struct drm_pending_vblank_event *event; - if (mcde->te_sync) - drm_crtc_vblank_off(crtc); + drm_crtc_vblank_off(crtc); /* Disable FIFO A flow */ mcde_disable_fifo(mcde, MCDE_FIFO_A, true); + event = crtc->state->event; + if (event) { + crtc->state->event = NULL; + + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); + } + dev_info(drm->dev, "MCDE display is disabled\n"); } @@ -1048,8 +1060,9 @@ static void mcde_display_update(struct drm_simple_display_pipe *pipe, */ if (fb) { mcde_set_extsrc(mcde, drm_fb_cma_get_gem_addr(fb, pstate, 0)); - /* Send a single frame using software sync */ - mcde_display_send_one_frame(mcde); + if (!mcde->video_mode) + /* Send a single frame using software sync */ + mcde_display_send_one_frame(mcde); dev_info_once(mcde->dev, "sent first display update\n"); } else { /* @@ -1097,6 +1110,8 @@ static struct drm_simple_display_pipe_funcs mcde_display_funcs = { .enable = mcde_display_enable, .disable = mcde_display_disable, .update = mcde_display_update, + .enable_vblank = mcde_display_enable_vblank, + .disable_vblank = mcde_display_disable_vblank, .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, }; @@ -1123,12 +1138,6 @@ int mcde_display_init(struct drm_device *drm) DRM_FORMAT_YUV422, }; - /* Provide vblank only when we have TE enabled */ - if (mcde->te_sync) { - mcde_display_funcs.enable_vblank = mcde_display_enable_vblank; - mcde_display_funcs.disable_vblank = mcde_display_disable_vblank; - } - ret = drm_simple_display_pipe_init(drm, &mcde->pipe, &mcde_display_funcs, formats, ARRAY_SIZE(formats), diff --git a/drivers/gpu/drm/mcde/mcde_drm.h b/drivers/gpu/drm/mcde/mcde_drm.h index dab4db021231..80edd6628979 100644 --- a/drivers/gpu/drm/mcde/mcde_drm.h +++ b/drivers/gpu/drm/mcde/mcde_drm.h @@ -19,6 +19,7 @@ struct mcde { struct mipi_dsi_device *mdsi; s16 stride; bool te_sync; + bool video_mode; bool oneshot_mode; unsigned int flow_active; spinlock_t flow_lock; /* Locks the channel flow control */ diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c index 5649887d2b90..9008ddcfc528 100644 --- a/drivers/gpu/drm/mcde/mcde_drv.c +++ b/drivers/gpu/drm/mcde/mcde_drv.c @@ -179,18 +179,10 @@ static int mcde_modeset_init(struct drm_device *drm) mode_config->min_height = 1; mode_config->max_height = 1080; - /* - * Currently we only support vblank handling on the DSI bridge, using - * TE synchronization. If TE sync is not set up, it is still possible - * to push out a single update on demand, but this is hard for DRM to - * exploit. - */ - if (mcde->te_sync) { - ret = drm_vblank_init(drm, 1); - if (ret) { - dev_err(drm->dev, "failed to init vblank\n"); - goto out_config; - } + ret = drm_vblank_init(drm, 1); + if (ret) { + dev_err(drm->dev, "failed to init vblank\n"); + goto out_config; } ret = mcde_display_init(drm); @@ -339,8 +331,6 @@ static int mcde_probe(struct platform_device *pdev) drm->dev_private = mcde; platform_set_drvdata(pdev, drm); - /* Enable use of the TE signal and interrupt */ - mcde->te_sync = true; /* Enable continuous updates: this is what Linux' framebuffer expects */ mcde->oneshot_mode = false; drm->dev_private = mcde; diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c index ef4c630afe3f..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"); diff --git a/drivers/gpu/drm/mcde/mcde_dsi_regs.h b/drivers/gpu/drm/mcde/mcde_dsi_regs.h index c9253321a3be..16551af1089e 100644 --- a/drivers/gpu/drm/mcde/mcde_dsi_regs.h +++ b/drivers/gpu/drm/mcde/mcde_dsi_regs.h @@ -123,17 +123,6 @@ #define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LONGNOTSHORT BIT(3) #define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_SHIFT 8 #define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_MASK 0x00003F00 -#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_TURN_ON_PERIPHERAL 50 -#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_SHUT_DOWN_PERIPHERAL 34 -#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_GENERIC_SHORT_WRITE_0 3 -#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_GENERIC_SHORT_WRITE_1 19 -#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_GENERIC_SHORT_WRITE_2 35 -#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_GENERIC_LONG_WRITE 41 -#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_DCS_SHORT_WRITE_0 5 -#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_DCS_SHORT_WRITE_1 21 -#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_DCS_LONG_WRITE 57 -#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_DCS_READ 6 -#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_SET_MAX_PKT_SIZE 55 #define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID_SHIFT 14 #define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE_SHIFT 16 #define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN BIT(21) @@ -239,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 @@ -248,6 +238,16 @@ #define DSI_VID_MODE_STS 0x000000BC #define DSI_VID_MODE_STS_VSG_RUNNING BIT(0) +#define DSI_VID_MODE_STS_ERR_MISSING_DATA BIT(1) +#define DSI_VID_MODE_STS_ERR_MISSING_HSYNC BIT(2) +#define DSI_VID_MODE_STS_ERR_MISSING_VSYNC BIT(3) +#define DSI_VID_MODE_STS_REG_ERR_SMALL_LENGTH BIT(4) +#define DSI_VID_MODE_STS_REG_ERR_SMALL_HEIGHT BIT(5) +#define DSI_VID_MODE_STS_ERR_BURSTWRITE BIT(6) +#define DSI_VID_MODE_STS_ERR_LINEWRITE BIT(7) +#define DSI_VID_MODE_STS_ERR_LONGREAD BIT(8) +#define DSI_VID_MODE_STS_ERR_VRS_WRONG_LENGTH BIT(9) +#define DSI_VID_MODE_STS_VSG_RECOVERY BIT(10) #define DSI_VID_VCA_SETTING1 0x000000C0 #define DSI_VID_VCA_SETTING1_MAX_BURST_LIMIT_SHIFT 0 |