From beea6214d1cc5bb08ce25ffe06b57fb948445bed Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 13 Jun 2017 12:02:09 +0300 Subject: [PATCH 1/4] drm/omap: fix analog tv-out modecheck omapdrm rejects all venc (analog tv-out) videomodes, due to somewhat strict checking of the values, making tv-out unusable. We only support two videomodes, one for PAL and one for NTSC, so instead of trying to check every field in the videomode struct, this patch makes the driver check only the pixel clock and the size of the display. Signed-off-by: Tomi Valkeinen --- drivers/gpu/drm/omapdrm/dss/venc.c | 65 +++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/venc.c b/drivers/gpu/drm/omapdrm/dss/venc.c index 5bd7788357b2..d58da6f32693 100644 --- a/drivers/gpu/drm/omapdrm/dss/venc.c +++ b/drivers/gpu/drm/omapdrm/dss/venc.c @@ -263,6 +263,12 @@ static const struct venc_config venc_config_pal_bdghi = { .fid_ext_start_y__fid_ext_offset_y = 0x01380005, }; +enum venc_videomode { + VENC_MODE_UNKNOWN, + VENC_MODE_PAL, + VENC_MODE_NTSC, +}; + static const struct videomode omap_dss_pal_vm = { .hactive = 720, .vactive = 574, @@ -297,6 +303,24 @@ static const struct videomode omap_dss_ntsc_vm = { DISPLAY_FLAGS_SYNC_NEGEDGE, }; +static enum venc_videomode venc_get_videomode(const struct videomode *vm) +{ + if (!(vm->flags & DISPLAY_FLAGS_INTERLACED)) + return VENC_MODE_UNKNOWN; + + if (vm->pixelclock == omap_dss_pal_vm.pixelclock && + vm->hactive == omap_dss_pal_vm.hactive && + vm->vactive == omap_dss_pal_vm.vactive) + return VENC_MODE_PAL; + + if (vm->pixelclock == omap_dss_ntsc_vm.pixelclock && + vm->hactive == omap_dss_ntsc_vm.hactive && + vm->vactive == omap_dss_ntsc_vm.vactive) + return VENC_MODE_NTSC; + + return VENC_MODE_UNKNOWN; +} + static struct { struct platform_device *pdev; void __iomem *base; @@ -423,14 +447,14 @@ static void venc_runtime_put(void) static const struct venc_config *venc_timings_to_config(struct videomode *vm) { - if (memcmp(&omap_dss_pal_vm, vm, sizeof(*vm)) == 0) + switch (venc_get_videomode(vm)) { + default: + WARN_ON_ONCE(1); + case VENC_MODE_PAL: return &venc_config_pal_trm; - - if (memcmp(&omap_dss_ntsc_vm, vm, sizeof(*vm)) == 0) + case VENC_MODE_NTSC: return &venc_config_ntsc_trm; - - BUG(); - return NULL; + } } static int venc_power_on(struct omap_dss_device *dssdev) @@ -541,15 +565,28 @@ static void venc_display_disable(struct omap_dss_device *dssdev) static void venc_set_timings(struct omap_dss_device *dssdev, struct videomode *vm) { + struct videomode actual_vm; + DSSDBG("venc_set_timings\n"); mutex_lock(&venc.venc_lock); + switch (venc_get_videomode(vm)) { + default: + WARN_ON_ONCE(1); + case VENC_MODE_PAL: + actual_vm = omap_dss_pal_vm; + break; + case VENC_MODE_NTSC: + actual_vm = omap_dss_ntsc_vm; + break; + } + /* Reset WSS data when the TV standard changes. */ - if (memcmp(&venc.vm, vm, sizeof(*vm))) + if (memcmp(&venc.vm, &actual_vm, sizeof(actual_vm))) venc.wss_data = 0; - venc.vm = *vm; + venc.vm = actual_vm; dispc_set_tv_pclk(13500000); @@ -561,13 +598,13 @@ static int venc_check_timings(struct omap_dss_device *dssdev, { DSSDBG("venc_check_timings\n"); - if (memcmp(&omap_dss_pal_vm, vm, sizeof(*vm)) == 0) + switch (venc_get_videomode(vm)) { + case VENC_MODE_PAL: + case VENC_MODE_NTSC: return 0; - - if (memcmp(&omap_dss_ntsc_vm, vm, sizeof(*vm)) == 0) - return 0; - - return -EINVAL; + default: + return -EINVAL; + } } static void venc_get_timings(struct omap_dss_device *dssdev, From 0c43f1e02598d304d4cfb06187305445c8207675 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 13 Jun 2017 12:02:10 +0300 Subject: [PATCH 2/4] drm/omap: fix i886 work-around 7d267f068a8b4944d52e8b0ae4c8fcc1c1c5c5ba ("drm/omap: work-around for errata i886") changed how the PLL dividers and multipliers are calculated. While the new way should work fine for all the PLLs, it breaks omap5 PLLs. The issues seen are rather odd: seemed that the output clock rate is half of what we asked. It is unclear what's causing there issues. As a work-around this patch adds a "errata_i886" flag, which is set only for DRA7's PLLs, and the PLL setup is done according to that flag. Signed-off-by: Tomi Valkeinen Tested-by: H. Nikolaus Schaller --- drivers/gpu/drm/omapdrm/dss/dss.h | 3 +++ drivers/gpu/drm/omapdrm/dss/pll.c | 29 +++++++++++++++++-------- drivers/gpu/drm/omapdrm/dss/video-pll.c | 2 ++ 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/dss.h b/drivers/gpu/drm/omapdrm/dss/dss.h index 085486024089..ed465572491e 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss.h +++ b/drivers/gpu/drm/omapdrm/dss/dss.h @@ -185,6 +185,9 @@ struct dss_pll_hw { bool has_freqsel; bool has_selfreqdco; bool has_refsel; + + /* DRA7 errata i886: use high N & M to avoid jitter */ + bool errata_i886; }; struct dss_pll { diff --git a/drivers/gpu/drm/omapdrm/dss/pll.c b/drivers/gpu/drm/omapdrm/dss/pll.c index 5e221302768b..9d9d9d42009b 100644 --- a/drivers/gpu/drm/omapdrm/dss/pll.c +++ b/drivers/gpu/drm/omapdrm/dss/pll.c @@ -215,8 +215,8 @@ bool dss_pll_calc_a(const struct dss_pll *pll, unsigned long clkin, dss_pll_calc_func func, void *data) { const struct dss_pll_hw *hw = pll->hw; - int n, n_min, n_max; - int m, m_min, m_max; + int n, n_start, n_stop, n_inc; + int m, m_start, m_stop, m_inc; unsigned long fint, clkdco; unsigned long pll_hw_max; unsigned long fint_hw_min, fint_hw_max; @@ -226,22 +226,33 @@ bool dss_pll_calc_a(const struct dss_pll *pll, unsigned long clkin, fint_hw_min = hw->fint_min; fint_hw_max = hw->fint_max; - n_min = max(DIV_ROUND_UP(clkin, fint_hw_max), 1ul); - n_max = min((unsigned)(clkin / fint_hw_min), hw->n_max); + n_start = max(DIV_ROUND_UP(clkin, fint_hw_max), 1ul); + n_stop = min((unsigned)(clkin / fint_hw_min), hw->n_max); + n_inc = 1; + + if (hw->errata_i886) { + swap(n_start, n_stop); + n_inc = -1; + } pll_max = pll_max ? pll_max : ULONG_MAX; - /* Try to find high N & M to avoid jitter (DRA7 errata i886) */ - for (n = n_max; n >= n_min; --n) { + for (n = n_start; n != n_stop; n += n_inc) { fint = clkin / n; - m_min = max(DIV_ROUND_UP(DIV_ROUND_UP(pll_min, fint), 2), + m_start = max(DIV_ROUND_UP(DIV_ROUND_UP(pll_min, fint), 2), 1ul); - m_max = min3((unsigned)(pll_max / fint / 2), + m_stop = min3((unsigned)(pll_max / fint / 2), (unsigned)(pll_hw_max / fint / 2), hw->m_max); + m_inc = 1; - for (m = m_max; m >= m_min; --m) { + if (hw->errata_i886) { + swap(m_start, m_stop); + m_inc = -1; + } + + for (m = m_start; m != m_stop; m += m_inc) { clkdco = 2 * m * fint; if (func(n, m, fint, clkdco, data)) diff --git a/drivers/gpu/drm/omapdrm/dss/video-pll.c b/drivers/gpu/drm/omapdrm/dss/video-pll.c index f7ea02a88b1a..38a239cc5e04 100644 --- a/drivers/gpu/drm/omapdrm/dss/video-pll.c +++ b/drivers/gpu/drm/omapdrm/dss/video-pll.c @@ -130,6 +130,8 @@ static const struct dss_pll_hw dss_dra7_video_pll_hw = { .mX_lsb[3] = 5, .has_refsel = true, + + .errata_i886 = true, }; struct dss_pll *dss_video_pll_init(struct platform_device *pdev, int id, From fc5cc9678e130196012c17b37e555d53d3d3476b Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 23 Aug 2017 12:19:02 +0300 Subject: [PATCH 3/4] drm/omap: work-around for omap3 display enable Seems that on omap3 enabling a crtc without any planes causes a sync lost flood. This only happens on the first enable, and after that it works. This looks like an HW issue and it's unclear why this is happening or how to fix it. This started happening after 897145d0c7010b4e07fa9bc674b1dfb9a2c6fff9 ("drm/omapdrm: Move commit_modeset_enables() before commit_planes()") which, as a work-around, changed omapdrm first to do the modeset enable, and plane set only after that. This WA should be fine on all DSS versions, but apparently OMAP3 DSS is an exception. This patch reverts that work-around for OMAP3 DSS. Signed-off-by: Tomi Valkeinen --- drivers/gpu/drm/omapdrm/omap_drv.c | 43 +++++++++++++++++++----------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 9b3c36b48356..cdf5b0601eba 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -84,23 +84,36 @@ static void omap_atomic_commit_tail(struct drm_atomic_state *old_state) /* Apply the atomic update. */ drm_atomic_helper_commit_modeset_disables(dev, old_state); - /* With the current dss dispc implementation we have to enable - * the new modeset before we can commit planes. The dispc ovl - * configuration relies on the video mode configuration been - * written into the HW when the ovl configuration is - * calculated. - * - * This approach is not ideal because after a mode change the - * plane update is executed only after the first vblank - * interrupt. The dispc implementation should be fixed so that - * it is able use uncommitted drm state information. - */ - drm_atomic_helper_commit_modeset_enables(dev, old_state); - omap_atomic_wait_for_completion(dev, old_state); + if (priv->omaprev != 0x3430) { + /* With the current dss dispc implementation we have to enable + * the new modeset before we can commit planes. The dispc ovl + * configuration relies on the video mode configuration been + * written into the HW when the ovl configuration is + * calculated. + * + * This approach is not ideal because after a mode change the + * plane update is executed only after the first vblank + * interrupt. The dispc implementation should be fixed so that + * it is able use uncommitted drm state information. + */ + drm_atomic_helper_commit_modeset_enables(dev, old_state); + omap_atomic_wait_for_completion(dev, old_state); - drm_atomic_helper_commit_planes(dev, old_state, 0); + drm_atomic_helper_commit_planes(dev, old_state, 0); - drm_atomic_helper_commit_hw_done(old_state); + drm_atomic_helper_commit_hw_done(old_state); + } else { + /* + * OMAP3 DSS seems to have issues with the work-around above, + * resulting in endless sync losts if a crtc is enabled without + * a plane. For now, skip the WA for OMAP3. + */ + drm_atomic_helper_commit_planes(dev, old_state, 0); + + drm_atomic_helper_commit_modeset_enables(dev, old_state); + + drm_atomic_helper_commit_hw_done(old_state); + } /* * Wait for completion of the page flips to ensure that old buffers From d1bbc823781f9b325fb25b4af83cf1afd314e6d5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 23 Aug 2017 15:28:19 +0200 Subject: [PATCH 4/4] ARM: OMAP2+: fix missing variable declaration The function that was added doesn't actually build: arch/arm/mach-omap2/display.c: In function 'omapdss_init_fbdev': arch/arm/mach-omap2/display.c:184:2: error: 'r' undeclared (first use in this function) This adds a declaration for 'r' to fix it. Fixes: 5ce783025c82 ("ARM: OMAP2+: Don't register omapdss device for omapdrm") Signed-off-by: Arnd Bergmann Reviewed-by: Laurent Pinchart Acked-by: Tony Lindgren Signed-off-by: Tomi Valkeinen --- arch/arm/mach-omap2/display.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c index 798fc718fffe..b3f6eb5d04a2 100644 --- a/arch/arm/mach-omap2/display.c +++ b/arch/arm/mach-omap2/display.c @@ -172,6 +172,7 @@ static int __init omapdss_init_fbdev(void) .set_min_bus_tput = omap_dss_set_min_bus_tput, }; struct device_node *node; + int r; board_data.version = omap_display_get_version(); if (board_data.version == OMAPDSS_VER_UNKNOWN) {