The git branch bellow is updated.
Changes since v3: - "drm/tilcdc: Enable sync lost error and recovery handling for rev 1 LCDC" - disable sync-lost irq also for rev1 LCDC - LCDC_V1_SYNC_LOST_ENA to LCDC_V1_SYNC_LOST_INT_ENA - "drm/tilcdc: Enable palette loading for revision 2 LCDC too" - disable palette loaded interrupt after receiving it - "drm/tilcdc: Enable frame done irq and functionality for LCDC rev 1" - disable FRAME_DONE interrupt for rev 1 lcdc in interrupt routine - LCDC_V1_FRAME_DONE_ENA to LCDC_V1_FRAME_DONE_INT_ENA - Add: "drm/tilcdc: Configure video mode to HW in enable() not in mode_set_nofb()" - Drop "drm/tilcdc: Use complete_all() to indicate completed palette loading" - Drop "drm/tilcdc: Call reset() before loading the palette" - "drm/tilcdc: Load palette at the end of mode_set_nofb()" - Just load the palette every time, no runtime_resume hook
Changes since v2: - Add: "drm/tilcdc: Fix load mode bit-field setting in tilcdc_crtc_enable()" - Drop: "drm/tilcdc: Free palette dma memory in tilcdc_crtc_destroy()" - Add: "drm/tilcdc: Add timeout wait for palette loading to complete" - Add: "drm/tilcdc: Call reset() before loading the palette" - Add: "drm/tilcdc: Use complete_all() to indicate completed palette loading" - Add "drm/tilcdc: Enable frame done irq and functionality for LCDC rev 1" - Bartosz: Please test if this works! The symptom for not working is "timeout waiting for framedone" message when screen is blanked.
Changes since first version of the series:
- Move tilcdc_regs.h changes from "drm/tilcdc: Enable palette loading for revision 2 LCDC too" to "drm/tilcdc: Add tilcdc_write_mask() to tilcdc_regs.h"
These patches are inspired by this series form Bartosz Golaszewski: https://www.spinics.net/lists/arm-kernel/msg539629.html
The patches are based on drm-next plus the earlier patches that I plan to send in a pull request for 4.10. The base + these patches are pushed here:
https://github.com/jsarha/linux drm-next-tilcdc-for-4.10-wip
Bartosz, please test if this branch works for rev1 LCDC, with your dts file!
Bartosz Golaszewski (1): drm/tilcdc: implement palette loading for rev1
Jyri Sarha (9): drm/tilcdc: Enable sync lost error and recovery handling for rev 1 LCDC drm/tilcdc: Fix tilcdc_crtc_create() return value handling drm/tilcdc: Add tilcdc_write_mask() to tilcdc_regs.h drm/tilcdc: Fix load mode bit-field setting in tilcdc_crtc_enable() drm/tilcdc: Enable palette loading for revision 2 LCDC too drm/tilcdc: Add timeout wait for palette loading to complete drm/tilcdc: Load palette at the end of mode_set_nofb() drm/tilcdc: Configure video mode to HW in enable() not in mode_set_nofb() drm/tilcdc: Enable frame done irq and functionality for LCDC rev 1
drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 558 ++++++++++++++++++++--------------- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 17 +- drivers/gpu/drm/tilcdc/tilcdc_drv.h | 2 +- drivers/gpu/drm/tilcdc/tilcdc_regs.h | 15 + 4 files changed, 344 insertions(+), 248 deletions(-)
Revision 1 LCDC support also sync lost errors and can benefit from sync lost recovery routine.
Signed-off-by: Jyri Sarha jsarha@ti.com --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 35 ++++++++++++++++++++--------------- drivers/gpu/drm/tilcdc/tilcdc_regs.h | 1 + 2 files changed, 21 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index c787349..0169240 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -113,6 +113,7 @@ static void tilcdc_crtc_enable_irqs(struct drm_device *dev)
if (priv->rev == 1) { tilcdc_set(dev, LCDC_RASTER_CTRL_REG, + LCDC_V1_SYNC_LOST_INT_ENA | LCDC_V1_UNDERFLOW_INT_ENA); tilcdc_set(dev, LCDC_DMA_CTRL_REG, LCDC_V1_END_OF_FRAME_INT_ENA); @@ -131,6 +132,7 @@ static void tilcdc_crtc_disable_irqs(struct drm_device *dev) /* disable irqs that we might have enabled: */ if (priv->rev == 1) { tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, + LCDC_V1_SYNC_LOST_INT_ENA | LCDC_V1_UNDERFLOW_INT_ENA | LCDC_V1_PL_INT_ENA); tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_V1_END_OF_FRAME_INT_ENA); @@ -845,6 +847,24 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underflow", __func__, stat);
+ if (stat & LCDC_SYNC_LOST) { + dev_err_ratelimited(dev->dev, "%s(0x%08x): Sync lost", + __func__, stat); + tilcdc_crtc->frame_intact = false; + if (tilcdc_crtc->sync_lost_count++ > + SYNC_LOST_COUNT_LIMIT) { + dev_err(dev->dev, "%s(0x%08x): Sync lost flood detected, recovering", __func__, stat); + queue_work(system_wq, &tilcdc_crtc->recover_work); + if (priv->rev == 1) + tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, + LCDC_V1_SYNC_LOST_INT_ENA); + else + tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, + LCDC_SYNC_LOST); + tilcdc_crtc->sync_lost_count = 0; + } + } + /* For revision 2 only */ if (priv->rev == 2) { if (stat & LCDC_FRAME_DONE) { @@ -852,21 +872,6 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) wake_up(&tilcdc_crtc->frame_done_wq); }
- if (stat & LCDC_SYNC_LOST) { - dev_err_ratelimited(dev->dev, "%s(0x%08x): Sync lost", - __func__, stat); - tilcdc_crtc->frame_intact = false; - if (tilcdc_crtc->sync_lost_count++ > - SYNC_LOST_COUNT_LIMIT) { - dev_err(dev->dev, "%s(0x%08x): Sync lost flood detected, recovering", __func__, stat); - queue_work(system_wq, - &tilcdc_crtc->recover_work); - tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, - LCDC_SYNC_LOST); - tilcdc_crtc->sync_lost_count = 0; - } - } - /* Indicate to LCDC that the interrupt service routine has * completed, see 13.3.6.1.6 in AM335x TRM. */ diff --git a/drivers/gpu/drm/tilcdc/tilcdc_regs.h b/drivers/gpu/drm/tilcdc/tilcdc_regs.h index f57c0d6..d195b65 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_regs.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_regs.h @@ -61,6 +61,7 @@ #define LCDC_V2_UNDERFLOW_INT_ENA BIT(5) #define LCDC_V1_PL_INT_ENA BIT(4) #define LCDC_V2_PL_INT_ENA BIT(6) +#define LCDC_V1_SYNC_LOST_INT_ENA BIT(5) #define LCDC_MONOCHROME_MODE BIT(1) #define LCDC_RASTER_ENABLE BIT(0) #define LCDC_TFT_ALT_ENABLE BIT(23)
From: Bartosz Golaszewski bgolaszewski@baylibre.com
Revision 1 of the IP doesn't work if we don't load the palette (even if it's not used, which is the case for the RGB565 format).
Add a function called from tilcdc_crtc_enable() which performs all required actions if we're dealing with a rev1 chip.
Signed-off-by: Bartosz Golaszewski bgolaszewski@baylibre.com Signed-off-by: Jyri Sarha jsarha@ti.com --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 88 +++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 0169240..7ea34c2 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -21,11 +21,15 @@ #include <drm/drm_flip_work.h> #include <drm/drm_plane_helper.h> #include <linux/workqueue.h> +#include <linux/completion.h> +#include <linux/dma-mapping.h>
#include "tilcdc_drv.h" #include "tilcdc_regs.h"
-#define TILCDC_VBLANK_SAFETY_THRESHOLD_US 1000 +#define TILCDC_VBLANK_SAFETY_THRESHOLD_US 1000 +#define TILCDC_REV1_PALETTE_SIZE 32 +#define TILCDC_REV1_PALETTE_FIRST_ENTRY 0x4000
struct tilcdc_crtc { struct drm_crtc base; @@ -56,6 +60,10 @@ struct tilcdc_crtc { int sync_lost_count; bool frame_intact; struct work_struct recover_work; + + dma_addr_t palette_dma_handle; + void *palette_base; + struct completion palette_loaded; }; #define to_tilcdc_crtc(x) container_of(x, struct tilcdc_crtc, base)
@@ -105,6 +113,55 @@ static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) tilcdc_crtc->curr_fb = fb; }
+/* + * The driver currently only supports the RGB565 format for revision 1. For + * 16 bits-per-pixel the palette block is bypassed, but the first 32 bytes of + * the framebuffer are still considered palette. The first 16-bit entry must + * be 0x4000 while all other entries must be zeroed. + */ +static void tilcdc_crtc_load_palette(struct drm_crtc *crtc) +{ + u32 dma_fb_base, dma_fb_ceiling, raster_ctl; + struct tilcdc_crtc *tilcdc_crtc; + struct drm_device *dev; + u16 *first_entry; + + dev = crtc->dev; + tilcdc_crtc = to_tilcdc_crtc(crtc); + first_entry = tilcdc_crtc->palette_base; + + *first_entry = TILCDC_REV1_PALETTE_FIRST_ENTRY; + + dma_fb_base = tilcdc_read(dev, LCDC_DMA_FB_BASE_ADDR_0_REG); + dma_fb_ceiling = tilcdc_read(dev, LCDC_DMA_FB_CEILING_ADDR_0_REG); + raster_ctl = tilcdc_read(dev, LCDC_RASTER_CTRL_REG); + + /* Tell the LCDC where the palette is located. */ + tilcdc_write(dev, LCDC_DMA_FB_BASE_ADDR_0_REG, + tilcdc_crtc->palette_dma_handle); + tilcdc_write(dev, LCDC_DMA_FB_CEILING_ADDR_0_REG, + (u32)tilcdc_crtc->palette_dma_handle + + TILCDC_REV1_PALETTE_SIZE - 1); + + /* Load it. */ + tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, + LCDC_PALETTE_LOAD_MODE(DATA_ONLY)); + tilcdc_set(dev, LCDC_RASTER_CTRL_REG, + LCDC_PALETTE_LOAD_MODE(PALETTE_ONLY)); + + /* Enable the LCDC and wait for palette to be loaded. */ + tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_PL_INT_ENA); + tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); + + wait_for_completion(&tilcdc_crtc->palette_loaded); + + /* Restore the registers. */ + tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); + tilcdc_write(dev, LCDC_DMA_FB_BASE_ADDR_0_REG, dma_fb_base); + tilcdc_write(dev, LCDC_DMA_FB_CEILING_ADDR_0_REG, dma_fb_ceiling); + tilcdc_write(dev, LCDC_RASTER_CTRL_REG, raster_ctl); +} + static void tilcdc_crtc_enable_irqs(struct drm_device *dev) { struct tilcdc_drm_private *priv = dev->dev_private; @@ -161,6 +218,7 @@ static void tilcdc_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + struct tilcdc_drm_private *priv = dev->dev_private;
WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); mutex_lock(&tilcdc_crtc->enable_lock); @@ -173,6 +231,9 @@ static void tilcdc_crtc_enable(struct drm_crtc *crtc)
reset(crtc);
+ if (priv->rev == 1 && !completion_done(&tilcdc_crtc->palette_loaded)) + tilcdc_crtc_load_palette(crtc); + tilcdc_crtc_enable_irqs(dev);
tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE); @@ -214,6 +275,13 @@ static void tilcdc_crtc_off(struct drm_crtc *crtc, bool shutdown) __func__); }
+ /* + * LCDC will not retain the palette when reset. Make sure it gets + * reloaded on tilcdc_crtc_enable(). + */ + if (priv->rev == 1) + reinit_completion(&tilcdc_crtc->palette_loaded); + drm_crtc_vblank_off(crtc);
tilcdc_crtc_disable_irqs(dev); @@ -847,6 +915,14 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underflow", __func__, stat);
+ if (priv->rev == 1) { + if (stat & LCDC_PL_LOAD_DONE) { + complete(&tilcdc_crtc->palette_loaded); + tilcdc_clear(dev, + LCDC_RASTER_CTRL_REG, LCDC_V1_PL_INT_ENA); + } + } + if (stat & LCDC_SYNC_LOST) { dev_err_ratelimited(dev->dev, "%s(0x%08x): Sync lost", __func__, stat); @@ -894,6 +970,16 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) return NULL; }
+ if (priv->rev == 1) { + init_completion(&tilcdc_crtc->palette_loaded); + tilcdc_crtc->palette_base = dmam_alloc_coherent(dev->dev, + TILCDC_REV1_PALETTE_SIZE, + &tilcdc_crtc->palette_dma_handle, + GFP_KERNEL | __GFP_ZERO); + if (!tilcdc_crtc->palette_base) + return ERR_PTR(-ENOMEM); + } + crtc = &tilcdc_crtc->base;
ret = tilcdc_plane_init(dev, &tilcdc_crtc->primary);
Failed tilcdc_crtc_create() error handling was broken, this patch should fix it.
Signed-off-by: Jyri Sarha jsarha@ti.com --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 12 +++++++----- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 11 ++++------- drivers/gpu/drm/tilcdc/tilcdc_drv.h | 2 +- 3 files changed, 12 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 7ea34c2..4605942 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -957,7 +957,7 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) return IRQ_HANDLED; }
-struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) +int tilcdc_crtc_create(struct drm_device *dev) { struct tilcdc_drm_private *priv = dev->dev_private; struct tilcdc_crtc *tilcdc_crtc; @@ -967,7 +967,7 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) tilcdc_crtc = devm_kzalloc(dev->dev, sizeof(*tilcdc_crtc), GFP_KERNEL); if (!tilcdc_crtc) { dev_err(dev->dev, "allocation failed\n"); - return NULL; + return -ENOMEM; }
if (priv->rev == 1) { @@ -977,7 +977,7 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) &tilcdc_crtc->palette_dma_handle, GFP_KERNEL | __GFP_ZERO); if (!tilcdc_crtc->palette_base) - return ERR_PTR(-ENOMEM); + return -ENOMEM; }
crtc = &tilcdc_crtc->base; @@ -1020,13 +1020,15 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) if (!crtc->port) { /* This should never happen */ dev_err(dev->dev, "Port node not found in %s\n", dev->dev->of_node->full_name); + ret = -EINVAL; goto fail; } }
- return crtc; + priv->crtc = crtc; + return 0;
fail: tilcdc_crtc_destroy(crtc); - return NULL; + return -ENOMEM; } diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 7f4d3bc..b1bbbfe 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -153,13 +153,11 @@ static int tilcdc_commit(struct drm_device *dev, .atomic_commit = tilcdc_commit, };
-static int modeset_init(struct drm_device *dev) +static void modeset_init(struct drm_device *dev) { struct tilcdc_drm_private *priv = dev->dev_private; struct tilcdc_module *mod;
- priv->crtc = tilcdc_crtc_create(dev); - list_for_each_entry(mod, &module_list, list) { DBG("loading module: %s", mod->name); mod->funcs->modeset_init(mod, dev); @@ -170,8 +168,6 @@ static int modeset_init(struct drm_device *dev) dev->mode_config.max_width = tilcdc_crtc_max_width(priv->crtc); dev->mode_config.max_height = 2048; dev->mode_config.funcs = &mode_config_funcs; - - return 0; }
#ifdef CONFIG_CPU_FREQ @@ -370,11 +366,12 @@ static int tilcdc_init(struct drm_driver *ddrv, struct device *dev) } }
- ret = modeset_init(ddev); + ret = tilcdc_crtc_create(ddev); if (ret < 0) { - dev_err(dev, "failed to initialize mode setting\n"); + dev_err(dev, "failed to create crtc\n"); goto init_failed; } + modeset_init(ddev);
if (priv->is_componentized) { ret = component_bind_all(dev, ddev); diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.h b/drivers/gpu/drm/tilcdc/tilcdc_drv.h index 411f8a8..0e71daf 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.h @@ -168,7 +168,7 @@ struct tilcdc_panel_info {
#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
-struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev); +int tilcdc_crtc_create(struct drm_device *dev); irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc); void tilcdc_crtc_update_clk(struct drm_crtc *crtc); void tilcdc_crtc_set_panel_info(struct drm_crtc *crtc,
Add tilcdc_write_mask() for handling register field wider than one bit and mask values for those fields.
Signed-off-by: Jyri Sarha jsarha@ti.com --- drivers/gpu/drm/tilcdc/tilcdc_regs.h | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_regs.h b/drivers/gpu/drm/tilcdc/tilcdc_regs.h index d195b65..d69a940 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_regs.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_regs.h @@ -34,11 +34,14 @@
/* LCDC DMA Control Register */ #define LCDC_DMA_BURST_SIZE(x) ((x) << 4) +#define LCDC_DMA_BURST_SIZE_MASK ((0x7) << 4) #define LCDC_DMA_BURST_1 0x0 #define LCDC_DMA_BURST_2 0x1 #define LCDC_DMA_BURST_4 0x2 #define LCDC_DMA_BURST_8 0x3 #define LCDC_DMA_BURST_16 0x4 +#define LCDC_DMA_FIFO_THRESHOLD(x) ((x) << 8) +#define LCDC_DMA_FIFO_THRESHOLD_MASK ((0x3) << 8) #define LCDC_V1_END_OF_FRAME_INT_ENA BIT(2) #define LCDC_V2_END_OF_FRAME0_INT_ENA BIT(8) #define LCDC_V2_END_OF_FRAME1_INT_ENA BIT(9) @@ -46,10 +49,12 @@
/* LCDC Control Register */ #define LCDC_CLK_DIVISOR(x) ((x) << 8) +#define LCDC_CLK_DIVISOR_MASK ((0xFF) << 8) #define LCDC_RASTER_MODE 0x01
/* LCDC Raster Control Register */ #define LCDC_PALETTE_LOAD_MODE(x) ((x) << 20) +#define LCDC_PALETTE_LOAD_MODE_MASK ((0x3) << 20) #define PALETTE_AND_DATA 0x00 #define PALETTE_ONLY 0x01 #define DATA_ONLY 0x02 @@ -75,7 +80,9 @@
/* LCDC Raster Timing 2 Register */ #define LCDC_AC_BIAS_TRANSITIONS_PER_INT(x) ((x) << 16) +#define LCDC_AC_BIAS_TRANSITIONS_PER_INT_MASK ((0xF) << 16) #define LCDC_AC_BIAS_FREQUENCY(x) ((x) << 8) +#define LCDC_AC_BIAS_FREQUENCY_MASK ((0xFF) << 8) #define LCDC_SYNC_CTRL BIT(25) #define LCDC_SYNC_EDGE BIT(24) #define LCDC_INVERT_PIXEL_CLOCK BIT(22) @@ -140,6 +147,12 @@ static inline u32 tilcdc_read(struct drm_device *dev, u32 reg) return ioread32(priv->mmio + reg); }
+static inline void tilcdc_write_mask(struct drm_device *dev, u32 reg, + u32 val, u32 mask) +{ + tilcdc_write(dev, reg, (tilcdc_read(dev, reg) & ~mask) | (val & mask)); +} + static inline void tilcdc_set(struct drm_device *dev, u32 reg, u32 mask) { tilcdc_write(dev, reg, tilcdc_read(dev, reg) | mask);
Set LCDC_PALETTE_LOAD_MODE bit-field with new tilcdc_write_mask() instead of tilcdc_set(). Setting a bit-fields with tilcdc_set() is fundamentally broken.
Signed-off-by: Jyri Sarha jsarha@ti.com --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 4605942..1ed65dd 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -237,7 +237,9 @@ static void tilcdc_crtc_enable(struct drm_crtc *crtc) tilcdc_crtc_enable_irqs(dev);
tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE); - tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_PALETTE_LOAD_MODE(DATA_ONLY)); + tilcdc_write_mask(dev, LCDC_RASTER_CTRL_REG, + LCDC_PALETTE_LOAD_MODE(DATA_ONLY), + LCDC_PALETTE_LOAD_MODE_MASK); tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE);
drm_crtc_vblank_on(crtc);
The LCDC revision 2 documentation also mentions the mandatory palette for true color modes. Even if the rev 2 LCDC appears to work just fine without the palette being loaded loading it helps in testing the feature.
Signed-off-by: Jyri Sarha jsarha@ti.com --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 90 +++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 43 deletions(-)
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 1ed65dd..b3edc6d 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -28,8 +28,8 @@ #include "tilcdc_regs.h"
#define TILCDC_VBLANK_SAFETY_THRESHOLD_US 1000 -#define TILCDC_REV1_PALETTE_SIZE 32 -#define TILCDC_REV1_PALETTE_FIRST_ENTRY 0x4000 +#define TILCDC_PALETTE_SIZE 32 +#define TILCDC_PALETTE_FIRST_ENTRY 0x4000
struct tilcdc_crtc { struct drm_crtc base; @@ -62,7 +62,7 @@ struct tilcdc_crtc { struct work_struct recover_work;
dma_addr_t palette_dma_handle; - void *palette_base; + u16 *palette_base; struct completion palette_loaded; }; #define to_tilcdc_crtc(x) container_of(x, struct tilcdc_crtc, base) @@ -114,23 +114,17 @@ static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) }
/* - * The driver currently only supports the RGB565 format for revision 1. For - * 16 bits-per-pixel the palette block is bypassed, but the first 32 bytes of - * the framebuffer are still considered palette. The first 16-bit entry must - * be 0x4000 while all other entries must be zeroed. + * The driver currently only supports only true color formats. For + * true color the palette block is bypassed, but a 32 byte palette + * should still be loaded. The first 16-bit entry must be 0x4000 while + * all other entries must be zeroed. */ static void tilcdc_crtc_load_palette(struct drm_crtc *crtc) { u32 dma_fb_base, dma_fb_ceiling, raster_ctl; - struct tilcdc_crtc *tilcdc_crtc; - struct drm_device *dev; - u16 *first_entry; - - dev = crtc->dev; - tilcdc_crtc = to_tilcdc_crtc(crtc); - first_entry = tilcdc_crtc->palette_base; - - *first_entry = TILCDC_REV1_PALETTE_FIRST_ENTRY; + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct tilcdc_drm_private *priv = dev->dev_private;
dma_fb_base = tilcdc_read(dev, LCDC_DMA_FB_BASE_ADDR_0_REG); dma_fb_ceiling = tilcdc_read(dev, LCDC_DMA_FB_CEILING_ADDR_0_REG); @@ -140,23 +134,34 @@ static void tilcdc_crtc_load_palette(struct drm_crtc *crtc) tilcdc_write(dev, LCDC_DMA_FB_BASE_ADDR_0_REG, tilcdc_crtc->palette_dma_handle); tilcdc_write(dev, LCDC_DMA_FB_CEILING_ADDR_0_REG, - (u32)tilcdc_crtc->palette_dma_handle - + TILCDC_REV1_PALETTE_SIZE - 1); + (u32) tilcdc_crtc->palette_dma_handle + + TILCDC_PALETTE_SIZE - 1);
- /* Load it. */ - tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, - LCDC_PALETTE_LOAD_MODE(DATA_ONLY)); - tilcdc_set(dev, LCDC_RASTER_CTRL_REG, - LCDC_PALETTE_LOAD_MODE(PALETTE_ONLY)); + /* Set dma load mode for palette loading only. */ + tilcdc_write_mask(dev, LCDC_RASTER_CTRL_REG, + LCDC_PALETTE_LOAD_MODE(PALETTE_ONLY), + LCDC_PALETTE_LOAD_MODE_MASK); + + /* Enable DMA Palette Loaded Interrupt */ + if (priv->rev == 1) + tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_PL_INT_ENA); + else + tilcdc_write(dev, LCDC_INT_ENABLE_SET_REG, LCDC_V2_PL_INT_ENA);
- /* Enable the LCDC and wait for palette to be loaded. */ - tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_PL_INT_ENA); + /* Enable LCDC DMA and wait for palette to be loaded. */ + tilcdc_clear_irqstatus(dev, 0xffffffff); tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE);
wait_for_completion(&tilcdc_crtc->palette_loaded);
- /* Restore the registers. */ + /* Disable LCDC DMA and DMA Palette Loaded Interrupt. */ tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); + if (priv->rev == 1) + tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_PL_INT_ENA); + else + tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, LCDC_V2_PL_INT_ENA); + + /* Restore the registers. */ tilcdc_write(dev, LCDC_DMA_FB_BASE_ADDR_0_REG, dma_fb_base); tilcdc_write(dev, LCDC_DMA_FB_CEILING_ADDR_0_REG, dma_fb_ceiling); tilcdc_write(dev, LCDC_RASTER_CTRL_REG, raster_ctl); @@ -218,7 +223,6 @@ static void tilcdc_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); - struct tilcdc_drm_private *priv = dev->dev_private;
WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); mutex_lock(&tilcdc_crtc->enable_lock); @@ -231,7 +235,7 @@ static void tilcdc_crtc_enable(struct drm_crtc *crtc)
reset(crtc);
- if (priv->rev == 1 && !completion_done(&tilcdc_crtc->palette_loaded)) + if (!completion_done(&tilcdc_crtc->palette_loaded)) tilcdc_crtc_load_palette(crtc);
tilcdc_crtc_enable_irqs(dev); @@ -281,8 +285,7 @@ static void tilcdc_crtc_off(struct drm_crtc *crtc, bool shutdown) * LCDC will not retain the palette when reset. Make sure it gets * reloaded on tilcdc_crtc_enable(). */ - if (priv->rev == 1) - reinit_completion(&tilcdc_crtc->palette_loaded); + reinit_completion(&tilcdc_crtc->palette_loaded);
drm_crtc_vblank_off(crtc);
@@ -917,12 +920,14 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underflow", __func__, stat);
- if (priv->rev == 1) { - if (stat & LCDC_PL_LOAD_DONE) { - complete(&tilcdc_crtc->palette_loaded); - tilcdc_clear(dev, - LCDC_RASTER_CTRL_REG, LCDC_V1_PL_INT_ENA); - } + if (stat & LCDC_PL_LOAD_DONE) { + complete(&tilcdc_crtc->palette_loaded); + if (priv->rev == 1) + tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, + LCDC_V1_PL_INT_ENA); + else + tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, + LCDC_V2_PL_INT_ENA); }
if (stat & LCDC_SYNC_LOST) { @@ -972,15 +977,14 @@ int tilcdc_crtc_create(struct drm_device *dev) return -ENOMEM; }
- if (priv->rev == 1) { - init_completion(&tilcdc_crtc->palette_loaded); - tilcdc_crtc->palette_base = dmam_alloc_coherent(dev->dev, - TILCDC_REV1_PALETTE_SIZE, + init_completion(&tilcdc_crtc->palette_loaded); + tilcdc_crtc->palette_base = dmam_alloc_coherent(dev->dev, + TILCDC_PALETTE_SIZE, &tilcdc_crtc->palette_dma_handle, GFP_KERNEL | __GFP_ZERO); - if (!tilcdc_crtc->palette_base) - return -ENOMEM; - } + if (!tilcdc_crtc->palette_base) + return -ENOMEM; + *tilcdc_crtc->palette_base = TILCDC_PALETTE_FIRST_ENTRY;
crtc = &tilcdc_crtc->base;
Add timeout wait for palette loadind to complete. We do not want to hang forever if palette loaded interrupt does not arrive for some reason.
Signed-off-by: Jyri Sarha jsarha@ti.com --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index b3edc6d..f736a89 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -125,6 +125,7 @@ static void tilcdc_crtc_load_palette(struct drm_crtc *crtc) struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; struct tilcdc_drm_private *priv = dev->dev_private; + int ret;
dma_fb_base = tilcdc_read(dev, LCDC_DMA_FB_BASE_ADDR_0_REG); dma_fb_ceiling = tilcdc_read(dev, LCDC_DMA_FB_CEILING_ADDR_0_REG); @@ -152,7 +153,10 @@ static void tilcdc_crtc_load_palette(struct drm_crtc *crtc) tilcdc_clear_irqstatus(dev, 0xffffffff); tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE);
- wait_for_completion(&tilcdc_crtc->palette_loaded); + ret = wait_for_completion_timeout(&tilcdc_crtc->palette_loaded, + msecs_to_jiffies(50)); + if (ret == 0) + dev_err(dev->dev, "%s: Palette loading timeout", __func__);
/* Disable LCDC DMA and DMA Palette Loaded Interrupt. */ tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE);
Load palette at the end of mode_set_nofb(). Moving the palette loading to mode_set_nofb() saves us from storing and restoring of framebuffer addresses in dma registers that were just recently written there.
Signed-off-by: Jyri Sarha jsarha@ti.com --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index f736a89..4472540 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -121,15 +121,12 @@ static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) */ static void tilcdc_crtc_load_palette(struct drm_crtc *crtc) { - u32 dma_fb_base, dma_fb_ceiling, raster_ctl; struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; struct tilcdc_drm_private *priv = dev->dev_private; int ret;
- dma_fb_base = tilcdc_read(dev, LCDC_DMA_FB_BASE_ADDR_0_REG); - dma_fb_ceiling = tilcdc_read(dev, LCDC_DMA_FB_CEILING_ADDR_0_REG); - raster_ctl = tilcdc_read(dev, LCDC_RASTER_CTRL_REG); + reinit_completion(&tilcdc_crtc->palette_loaded);
/* Tell the LCDC where the palette is located. */ tilcdc_write(dev, LCDC_DMA_FB_BASE_ADDR_0_REG, @@ -164,11 +161,6 @@ static void tilcdc_crtc_load_palette(struct drm_crtc *crtc) tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_PL_INT_ENA); else tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, LCDC_V2_PL_INT_ENA); - - /* Restore the registers. */ - tilcdc_write(dev, LCDC_DMA_FB_BASE_ADDR_0_REG, dma_fb_base); - tilcdc_write(dev, LCDC_DMA_FB_CEILING_ADDR_0_REG, dma_fb_ceiling); - tilcdc_write(dev, LCDC_RASTER_CTRL_REG, raster_ctl); }
static void tilcdc_crtc_enable_irqs(struct drm_device *dev) @@ -239,9 +231,6 @@ static void tilcdc_crtc_enable(struct drm_crtc *crtc)
reset(crtc);
- if (!completion_done(&tilcdc_crtc->palette_loaded)) - tilcdc_crtc_load_palette(crtc); - tilcdc_crtc_enable_irqs(dev);
tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE); @@ -285,12 +274,6 @@ static void tilcdc_crtc_off(struct drm_crtc *crtc, bool shutdown) __func__); }
- /* - * LCDC will not retain the palette when reset. Make sure it gets - * reloaded on tilcdc_crtc_enable(). - */ - reinit_completion(&tilcdc_crtc->palette_loaded); - drm_crtc_vblank_off(crtc);
tilcdc_crtc_disable_irqs(dev); @@ -678,10 +661,12 @@ static void tilcdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
drm_framebuffer_reference(fb);
- set_scanout(crtc, fb); - tilcdc_crtc_set_clk(crtc);
+ tilcdc_crtc_load_palette(crtc); + + set_scanout(crtc, fb); + crtc->hwmode = crtc->state->adjusted_mode; }
Configure video mode to HW in enable() call back. There is no reason to do it before that. This makes PM functions way easier because there is no HW context to save when screen is for instance blanked. This patch removes mode_set_nofb() call back from tilcdc.
Signed-off-by: Jyri Sarha jsarha@ti.com --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 426 +++++++++++++++++------------------ drivers/gpu/drm/tilcdc/tilcdc_drv.c | 6 - 2 files changed, 212 insertions(+), 220 deletions(-)
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 4472540..fb24422 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -215,214 +215,6 @@ static void reset(struct drm_crtc *crtc) tilcdc_clear(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); }
-static void tilcdc_crtc_enable(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); - - WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); - mutex_lock(&tilcdc_crtc->enable_lock); - if (tilcdc_crtc->enabled || tilcdc_crtc->shutdown) { - mutex_unlock(&tilcdc_crtc->enable_lock); - return; - } - - pm_runtime_get_sync(dev->dev); - - reset(crtc); - - tilcdc_crtc_enable_irqs(dev); - - tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE); - tilcdc_write_mask(dev, LCDC_RASTER_CTRL_REG, - LCDC_PALETTE_LOAD_MODE(DATA_ONLY), - LCDC_PALETTE_LOAD_MODE_MASK); - tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); - - drm_crtc_vblank_on(crtc); - - tilcdc_crtc->enabled = true; - mutex_unlock(&tilcdc_crtc->enable_lock); -} - -static void tilcdc_crtc_off(struct drm_crtc *crtc, bool shutdown) -{ - struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); - struct drm_device *dev = crtc->dev; - struct tilcdc_drm_private *priv = dev->dev_private; - - mutex_lock(&tilcdc_crtc->enable_lock); - if (shutdown) - tilcdc_crtc->shutdown = true; - if (!tilcdc_crtc->enabled) { - mutex_unlock(&tilcdc_crtc->enable_lock); - return; - } - tilcdc_crtc->frame_done = false; - tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); - - /* - * if necessary wait for framedone irq which will still come - * before putting things to sleep.. - */ - if (priv->rev == 2) { - int ret = wait_event_timeout(tilcdc_crtc->frame_done_wq, - tilcdc_crtc->frame_done, - msecs_to_jiffies(500)); - if (ret == 0) - dev_err(dev->dev, "%s: timeout waiting for framedone\n", - __func__); - } - - drm_crtc_vblank_off(crtc); - - tilcdc_crtc_disable_irqs(dev); - - pm_runtime_put_sync(dev->dev); - - if (tilcdc_crtc->next_fb) { - drm_flip_work_queue(&tilcdc_crtc->unref_work, - tilcdc_crtc->next_fb); - tilcdc_crtc->next_fb = NULL; - } - - if (tilcdc_crtc->curr_fb) { - drm_flip_work_queue(&tilcdc_crtc->unref_work, - tilcdc_crtc->curr_fb); - tilcdc_crtc->curr_fb = NULL; - } - - drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq); - tilcdc_crtc->last_vblank = ktime_set(0, 0); - - tilcdc_crtc->enabled = false; - mutex_unlock(&tilcdc_crtc->enable_lock); -} - -static void tilcdc_crtc_disable(struct drm_crtc *crtc) -{ - WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); - tilcdc_crtc_off(crtc, false); -} - -void tilcdc_crtc_shutdown(struct drm_crtc *crtc) -{ - tilcdc_crtc_off(crtc, true); -} - -static bool tilcdc_crtc_is_on(struct drm_crtc *crtc) -{ - return crtc->state && crtc->state->enable && crtc->state->active; -} - -static void tilcdc_crtc_recover_work(struct work_struct *work) -{ - struct tilcdc_crtc *tilcdc_crtc = - container_of(work, struct tilcdc_crtc, recover_work); - struct drm_crtc *crtc = &tilcdc_crtc->base; - - dev_info(crtc->dev->dev, "%s: Reset CRTC", __func__); - - drm_modeset_lock_crtc(crtc, NULL); - - if (!tilcdc_crtc_is_on(crtc)) - goto out; - - tilcdc_crtc_disable(crtc); - tilcdc_crtc_enable(crtc); -out: - drm_modeset_unlock_crtc(crtc); -} - -static void tilcdc_crtc_destroy(struct drm_crtc *crtc) -{ - struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); - struct tilcdc_drm_private *priv = crtc->dev->dev_private; - - drm_modeset_lock_crtc(crtc, NULL); - tilcdc_crtc_disable(crtc); - drm_modeset_unlock_crtc(crtc); - - flush_workqueue(priv->wq); - - of_node_put(crtc->port); - drm_crtc_cleanup(crtc); - drm_flip_work_cleanup(&tilcdc_crtc->unref_work); -} - -int tilcdc_crtc_update_fb(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_pending_vblank_event *event) -{ - struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); - struct drm_device *dev = crtc->dev; - unsigned long flags; - - WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); - - if (tilcdc_crtc->event) { - dev_err(dev->dev, "already pending page flip!\n"); - return -EBUSY; - } - - drm_framebuffer_reference(fb); - - crtc->primary->fb = fb; - - spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags); - - if (crtc->hwmode.vrefresh && ktime_to_ns(tilcdc_crtc->last_vblank)) { - ktime_t next_vblank; - s64 tdiff; - - next_vblank = ktime_add_us(tilcdc_crtc->last_vblank, - 1000000 / crtc->hwmode.vrefresh); - - tdiff = ktime_to_us(ktime_sub(next_vblank, ktime_get())); - - if (tdiff < TILCDC_VBLANK_SAFETY_THRESHOLD_US) - tilcdc_crtc->next_fb = fb; - } - - if (tilcdc_crtc->next_fb != fb) - set_scanout(crtc, fb); - - tilcdc_crtc->event = event; - - spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags); - - return 0; -} - -static bool tilcdc_crtc_mode_fixup(struct drm_crtc *crtc, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); - - if (!tilcdc_crtc->simulate_vesa_sync) - return true; - - /* - * tilcdc does not generate VESA-compliant sync but aligns - * VS on the second edge of HS instead of first edge. - * We use adjusted_mode, to fixup sync by aligning both rising - * edges and add HSKEW offset to fix the sync. - */ - adjusted_mode->hskew = mode->hsync_end - mode->hsync_start; - adjusted_mode->flags |= DRM_MODE_FLAG_HSKEW; - - if (mode->flags & DRM_MODE_FLAG_NHSYNC) { - adjusted_mode->flags |= DRM_MODE_FLAG_PHSYNC; - adjusted_mode->flags &= ~DRM_MODE_FLAG_NHSYNC; - } else { - adjusted_mode->flags |= DRM_MODE_FLAG_NHSYNC; - adjusted_mode->flags &= ~DRM_MODE_FLAG_PHSYNC; - } - - return true; -} - /* * Calculate the percentage difference between the requested pixel clock rate * and the effective rate resulting from calculating the clock divider value. @@ -499,7 +291,7 @@ static void tilcdc_crtc_set_clk(struct drm_crtc *crtc) LCDC_V2_CORE_CLK_EN); }
-static void tilcdc_crtc_mode_set_nofb(struct drm_crtc *crtc) +static void tilcdc_crtc_set_mode(struct drm_crtc *crtc) { struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; @@ -509,8 +301,6 @@ static void tilcdc_crtc_mode_set_nofb(struct drm_crtc *crtc) struct drm_display_mode *mode = &crtc->state->adjusted_mode; struct drm_framebuffer *fb = crtc->primary->state->fb;
- WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); - if (WARN_ON(!info)) return;
@@ -659,17 +449,226 @@ static void tilcdc_crtc_mode_set_nofb(struct drm_crtc *crtc) else tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ORDER);
- drm_framebuffer_reference(fb); - tilcdc_crtc_set_clk(crtc);
tilcdc_crtc_load_palette(crtc);
set_scanout(crtc, fb);
+ drm_framebuffer_reference(fb); + crtc->hwmode = crtc->state->adjusted_mode; }
+static void tilcdc_crtc_enable(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + + WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); + mutex_lock(&tilcdc_crtc->enable_lock); + if (tilcdc_crtc->enabled || tilcdc_crtc->shutdown) { + mutex_unlock(&tilcdc_crtc->enable_lock); + return; + } + + pm_runtime_get_sync(dev->dev); + + reset(crtc); + + tilcdc_crtc_set_mode(crtc); + + tilcdc_crtc_enable_irqs(dev); + + tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE); + tilcdc_write_mask(dev, LCDC_RASTER_CTRL_REG, + LCDC_PALETTE_LOAD_MODE(DATA_ONLY), + LCDC_PALETTE_LOAD_MODE_MASK); + tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); + + drm_crtc_vblank_on(crtc); + + tilcdc_crtc->enabled = true; + mutex_unlock(&tilcdc_crtc->enable_lock); +} + +static void tilcdc_crtc_off(struct drm_crtc *crtc, bool shutdown) +{ + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct tilcdc_drm_private *priv = dev->dev_private; + int ret; + + mutex_lock(&tilcdc_crtc->enable_lock); + if (shutdown) + tilcdc_crtc->shutdown = true; + if (!tilcdc_crtc->enabled) { + mutex_unlock(&tilcdc_crtc->enable_lock); + return; + } + tilcdc_crtc->frame_done = false; + tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); + + /* + * Wait for framedone irq which will still come before putting + * things to sleep.. + */ + ret = wait_event_timeout(tilcdc_crtc->frame_done_wq, + tilcdc_crtc->frame_done, + msecs_to_jiffies(500)); + if (ret == 0) + dev_err(dev->dev, "%s: timeout waiting for framedone\n", + __func__); + + drm_crtc_vblank_off(crtc); + + tilcdc_crtc_disable_irqs(dev); + + pm_runtime_put_sync(dev->dev); + + if (tilcdc_crtc->next_fb) { + drm_flip_work_queue(&tilcdc_crtc->unref_work, + tilcdc_crtc->next_fb); + tilcdc_crtc->next_fb = NULL; + } + + if (tilcdc_crtc->curr_fb) { + drm_flip_work_queue(&tilcdc_crtc->unref_work, + tilcdc_crtc->curr_fb); + tilcdc_crtc->curr_fb = NULL; + } + + drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq); + tilcdc_crtc->last_vblank = ktime_set(0, 0); + + tilcdc_crtc->enabled = false; + mutex_unlock(&tilcdc_crtc->enable_lock); +} + +static void tilcdc_crtc_disable(struct drm_crtc *crtc) +{ + WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); + tilcdc_crtc_off(crtc, false); +} + +void tilcdc_crtc_shutdown(struct drm_crtc *crtc) +{ + tilcdc_crtc_off(crtc, true); +} + +static bool tilcdc_crtc_is_on(struct drm_crtc *crtc) +{ + return crtc->state && crtc->state->enable && crtc->state->active; +} + +static void tilcdc_crtc_recover_work(struct work_struct *work) +{ + struct tilcdc_crtc *tilcdc_crtc = + container_of(work, struct tilcdc_crtc, recover_work); + struct drm_crtc *crtc = &tilcdc_crtc->base; + + dev_info(crtc->dev->dev, "%s: Reset CRTC", __func__); + + drm_modeset_lock_crtc(crtc, NULL); + + if (!tilcdc_crtc_is_on(crtc)) + goto out; + + tilcdc_crtc_disable(crtc); + tilcdc_crtc_enable(crtc); +out: + drm_modeset_unlock_crtc(crtc); +} + +static void tilcdc_crtc_destroy(struct drm_crtc *crtc) +{ + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + struct tilcdc_drm_private *priv = crtc->dev->dev_private; + + drm_modeset_lock_crtc(crtc, NULL); + tilcdc_crtc_disable(crtc); + drm_modeset_unlock_crtc(crtc); + + flush_workqueue(priv->wq); + + of_node_put(crtc->port); + drm_crtc_cleanup(crtc); + drm_flip_work_cleanup(&tilcdc_crtc->unref_work); +} + +int tilcdc_crtc_update_fb(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event) +{ + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + struct drm_device *dev = crtc->dev; + unsigned long flags; + + WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); + + if (tilcdc_crtc->event) { + dev_err(dev->dev, "already pending page flip!\n"); + return -EBUSY; + } + + drm_framebuffer_reference(fb); + + crtc->primary->fb = fb; + + spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags); + + if (crtc->hwmode.vrefresh && ktime_to_ns(tilcdc_crtc->last_vblank)) { + ktime_t next_vblank; + s64 tdiff; + + next_vblank = ktime_add_us(tilcdc_crtc->last_vblank, + 1000000 / crtc->hwmode.vrefresh); + + tdiff = ktime_to_us(ktime_sub(next_vblank, ktime_get())); + + if (tdiff < TILCDC_VBLANK_SAFETY_THRESHOLD_US) + tilcdc_crtc->next_fb = fb; + } + + if (tilcdc_crtc->next_fb != fb) + set_scanout(crtc, fb); + + tilcdc_crtc->event = event; + + spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags); + + return 0; +} + +static bool tilcdc_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + + if (!tilcdc_crtc->simulate_vesa_sync) + return true; + + /* + * tilcdc does not generate VESA-compliant sync but aligns + * VS on the second edge of HS instead of first edge. + * We use adjusted_mode, to fixup sync by aligning both rising + * edges and add HSKEW offset to fix the sync. + */ + adjusted_mode->hskew = mode->hsync_end - mode->hsync_start; + adjusted_mode->flags |= DRM_MODE_FLAG_HSKEW; + + if (mode->flags & DRM_MODE_FLAG_NHSYNC) { + adjusted_mode->flags |= DRM_MODE_FLAG_PHSYNC; + adjusted_mode->flags &= ~DRM_MODE_FLAG_NHSYNC; + } else { + adjusted_mode->flags |= DRM_MODE_FLAG_NHSYNC; + adjusted_mode->flags &= ~DRM_MODE_FLAG_PHSYNC; + } + + return true; +} + static int tilcdc_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { @@ -710,7 +709,6 @@ static int tilcdc_crtc_atomic_check(struct drm_crtc *crtc, .enable = tilcdc_crtc_enable, .disable = tilcdc_crtc_disable, .atomic_check = tilcdc_crtc_atomic_check, - .mode_set_nofb = tilcdc_crtc_mode_set_nofb, };
int tilcdc_crtc_max_width(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index b1bbbfe..5efb369 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -127,18 +127,12 @@ static int tilcdc_commit(struct drm_device *dev, * current layout. */
- /* Keep HW on while we commit the state. */ - pm_runtime_get_sync(dev->dev); - drm_atomic_helper_commit_modeset_disables(dev, state);
drm_atomic_helper_commit_planes(dev, state, 0);
drm_atomic_helper_commit_modeset_enables(dev, state);
- /* Now HW should remain on if need becase the crtc is enabled */ - pm_runtime_put_sync(dev->dev); - drm_atomic_helper_wait_for_vblanks(dev, state);
drm_atomic_helper_cleanup_planes(dev, state);
We should wait for the last frame to complete before shutting things down also on LCDC rev 1.
Signed-off-by: Jyri Sarha jsarha@ti.com --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 18 +++++++++++------- drivers/gpu/drm/tilcdc/tilcdc_regs.h | 1 + 2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index fb24422..9942b05 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -171,7 +171,7 @@ static void tilcdc_crtc_enable_irqs(struct drm_device *dev)
if (priv->rev == 1) { tilcdc_set(dev, LCDC_RASTER_CTRL_REG, - LCDC_V1_SYNC_LOST_INT_ENA | + LCDC_V1_SYNC_LOST_INT_ENA | LCDC_V1_FRAME_DONE_INT_ENA | LCDC_V1_UNDERFLOW_INT_ENA); tilcdc_set(dev, LCDC_DMA_CTRL_REG, LCDC_V1_END_OF_FRAME_INT_ENA); @@ -190,7 +190,7 @@ static void tilcdc_crtc_disable_irqs(struct drm_device *dev) /* disable irqs that we might have enabled: */ if (priv->rev == 1) { tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, - LCDC_V1_SYNC_LOST_INT_ENA | + LCDC_V1_SYNC_LOST_INT_ENA | LCDC_V1_FRAME_DONE_INT_ENA | LCDC_V1_UNDERFLOW_INT_ENA | LCDC_V1_PL_INT_ENA); tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_V1_END_OF_FRAME_INT_ENA); @@ -935,13 +935,17 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) } }
+ if (stat & LCDC_FRAME_DONE) { + tilcdc_crtc->frame_done = true; + wake_up(&tilcdc_crtc->frame_done_wq); + /* rev 1 lcdc appears to hang if irq is not disbaled here */ + if (priv->rev == 1) + tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, + LCDC_V1_FRAME_DONE_INT_ENA); + } + /* For revision 2 only */ if (priv->rev == 2) { - if (stat & LCDC_FRAME_DONE) { - tilcdc_crtc->frame_done = true; - wake_up(&tilcdc_crtc->frame_done_wq); - } - /* Indicate to LCDC that the interrupt service routine has * completed, see 13.3.6.1.6 in AM335x TRM. */ diff --git a/drivers/gpu/drm/tilcdc/tilcdc_regs.h b/drivers/gpu/drm/tilcdc/tilcdc_regs.h index d69a940..9d528c0 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_regs.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_regs.h @@ -67,6 +67,7 @@ #define LCDC_V1_PL_INT_ENA BIT(4) #define LCDC_V2_PL_INT_ENA BIT(6) #define LCDC_V1_SYNC_LOST_INT_ENA BIT(5) +#define LCDC_V1_FRAME_DONE_INT_ENA BIT(3) #define LCDC_MONOCHROME_MODE BIT(1) #define LCDC_RASTER_ENABLE BIT(0) #define LCDC_TFT_ALT_ENABLE BIT(23)
2016-11-25 10:09 GMT+01:00 Jyri Sarha jsarha@ti.com:
The git branch bellow is updated.
Changes since v3:
- "drm/tilcdc: Enable sync lost error and recovery handling for rev 1 LCDC"
- disable sync-lost irq also for rev1 LCDC
- LCDC_V1_SYNC_LOST_ENA to LCDC_V1_SYNC_LOST_INT_ENA
- "drm/tilcdc: Enable palette loading for revision 2 LCDC too"
- disable palette loaded interrupt after receiving it
- "drm/tilcdc: Enable frame done irq and functionality for LCDC rev 1"
- disable FRAME_DONE interrupt for rev 1 lcdc in interrupt routine
- LCDC_V1_FRAME_DONE_ENA to LCDC_V1_FRAME_DONE_INT_ENA
- Add: "drm/tilcdc: Configure video mode to HW in enable() not in mode_set_nofb()"
- Drop "drm/tilcdc: Use complete_all() to indicate completed palette loading"
- Drop "drm/tilcdc: Call reset() before loading the palette"
- "drm/tilcdc: Load palette at the end of mode_set_nofb()"
- Just load the palette every time, no runtime_resume hook
Changes since v2:
- Add: "drm/tilcdc: Fix load mode bit-field setting in tilcdc_crtc_enable()"
- Drop: "drm/tilcdc: Free palette dma memory in tilcdc_crtc_destroy()"
- Add: "drm/tilcdc: Add timeout wait for palette loading to complete"
- Add: "drm/tilcdc: Call reset() before loading the palette"
- Add: "drm/tilcdc: Use complete_all() to indicate completed palette loading"
- Add "drm/tilcdc: Enable frame done irq and functionality for LCDC rev 1"
- Bartosz: Please test if this works! The symptom for not working is "timeout waiting for framedone" message when screen is blanked.
Changes since first version of the series:
- Move tilcdc_regs.h changes from "drm/tilcdc: Enable palette loading for revision 2 LCDC too" to "drm/tilcdc: Add tilcdc_write_mask() to tilcdc_regs.h"
These patches are inspired by this series form Bartosz Golaszewski: https://www.spinics.net/lists/arm-kernel/msg539629.html
The patches are based on drm-next plus the earlier patches that I plan to send in a pull request for 4.10. The base + these patches are pushed here:
https://github.com/jsarha/linux drm-next-tilcdc-for-4.10-wip
Bartosz, please test if this branch works for rev1 LCDC, with your dts file!
Bartosz Golaszewski (1): drm/tilcdc: implement palette loading for rev1
Jyri Sarha (9): drm/tilcdc: Enable sync lost error and recovery handling for rev 1 LCDC drm/tilcdc: Fix tilcdc_crtc_create() return value handling drm/tilcdc: Add tilcdc_write_mask() to tilcdc_regs.h drm/tilcdc: Fix load mode bit-field setting in tilcdc_crtc_enable() drm/tilcdc: Enable palette loading for revision 2 LCDC too drm/tilcdc: Add timeout wait for palette loading to complete drm/tilcdc: Load palette at the end of mode_set_nofb() drm/tilcdc: Configure video mode to HW in enable() not in mode_set_nofb() drm/tilcdc: Enable frame done irq and functionality for LCDC rev 1
drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 558 ++++++++++++++++++++--------------- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 17 +- drivers/gpu/drm/tilcdc/tilcdc_drv.h | 2 +- drivers/gpu/drm/tilcdc/tilcdc_regs.h | 15 + 4 files changed, 344 insertions(+), 248 deletions(-)
-- 1.9.1
Tested-by: Bartosz Golaszewski bgolaszewski@baylibre.com
dri-devel@lists.freedesktop.org