summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/stm/ltdc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/stm/ltdc.c')
-rw-r--r--drivers/gpu/drm/stm/ltdc.c294
1 files changed, 213 insertions, 81 deletions
diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
index 6bd45df8f5a7..da7a0a183b27 100644
--- a/drivers/gpu/drm/stm/ltdc.c
+++ b/drivers/gpu/drm/stm/ltdc.c
@@ -12,6 +12,7 @@
#include <linux/component.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/media-bus-format.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_graph.h>
@@ -23,10 +24,13 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
#include <drm/drm_bridge.h>
#include <drm/drm_device.h>
+#include <drm/drm_edid.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_of.h>
@@ -162,16 +166,20 @@
#define BCCR_BCWHITE GENMASK(23, 0) /* Background Color WHITE */
#define IER_LIE BIT(0) /* Line Interrupt Enable */
-#define IER_FUIE BIT(1) /* Fifo Underrun Interrupt Enable */
+#define IER_FUWIE BIT(1) /* Fifo Underrun Warning Interrupt Enable */
#define IER_TERRIE BIT(2) /* Transfer ERRor Interrupt Enable */
-#define IER_RRIE BIT(3) /* Register Reload Interrupt enable */
+#define IER_RRIE BIT(3) /* Register Reload Interrupt Enable */
+#define IER_FUEIE BIT(6) /* Fifo Underrun Error Interrupt Enable */
+#define IER_CRCIE BIT(7) /* CRC Error Interrupt Enable */
#define CPSR_CYPOS GENMASK(15, 0) /* Current Y position */
#define ISR_LIF BIT(0) /* Line Interrupt Flag */
-#define ISR_FUIF BIT(1) /* Fifo Underrun Interrupt Flag */
+#define ISR_FUWIF BIT(1) /* Fifo Underrun Warning Interrupt Flag */
#define ISR_TERRIF BIT(2) /* Transfer ERRor Interrupt Flag */
#define ISR_RRIF BIT(3) /* Register Reload Interrupt Flag */
+#define ISR_FUEIF BIT(6) /* Fifo Underrun Error Interrupt Flag */
+#define ISR_CRCIF BIT(7) /* CRC Error Interrupt Flag */
#define EDCR_OCYEN BIT(25) /* Output Conversion to YCbCr 422: ENable */
#define EDCR_OCYSEL BIT(26) /* Output Conversion to YCbCr 422: SELection of the CCIR */
@@ -180,6 +188,7 @@
#define LXCR_LEN BIT(0) /* Layer ENable */
#define LXCR_COLKEN BIT(1) /* Color Keying Enable */
#define LXCR_CLUTEN BIT(4) /* Color Look-Up Table ENable */
+#define LXCR_HMEN BIT(8) /* Horizontal Mirroring ENable */
#define LXWHPCR_WHSTPOS GENMASK(11, 0) /* Window Horizontal StarT POSition */
#define LXWHPCR_WHSPPOS GENMASK(27, 16) /* Window Horizontal StoP POSition */
@@ -194,9 +203,10 @@
#define LXBFCR_BF2 GENMASK(2, 0) /* Blending Factor 2 */
#define LXBFCR_BF1 GENMASK(10, 8) /* Blending Factor 1 */
+#define LXBFCR_BOR GENMASK(18, 16) /* Blending ORder */
#define LXCFBLR_CFBLL GENMASK(12, 0) /* Color Frame Buffer Line Length */
-#define LXCFBLR_CFBP GENMASK(28, 16) /* Color Frame Buffer Pitch in bytes */
+#define LXCFBLR_CFBP GENMASK(31, 16) /* Color Frame Buffer Pitch in bytes */
#define LXCFBLNR_CFBLN GENMASK(10, 0) /* Color Frame Buffer Line Number */
@@ -229,6 +239,8 @@
#define NB_PF 8 /* Max nb of HW pixel format */
+#define FUT_DFT 128 /* Default value of fifo underrun threshold */
+
/*
* Skip the first value and the second in case CRC was enabled during
* the thread irq. This is to be sure CRC value is relevant for the
@@ -709,12 +721,13 @@ static irqreturn_t ltdc_irq_thread(int irq, void *arg)
ltdc_irq_crc_handle(ldev, crtc);
}
- /* Save FIFO Underrun & Transfer Error status */
mutex_lock(&ldev->err_lock);
- if (ldev->irq_status & ISR_FUIF)
- ldev->error_status |= ISR_FUIF;
if (ldev->irq_status & ISR_TERRIF)
- ldev->error_status |= ISR_TERRIF;
+ ldev->transfer_err++;
+ if (ldev->irq_status & ISR_FUEIF)
+ ldev->fifo_err++;
+ if (ldev->irq_status & ISR_FUWIF)
+ ldev->fifo_warn++;
mutex_unlock(&ldev->err_lock);
return IRQ_HANDLED;
@@ -773,7 +786,7 @@ static void ltdc_crtc_atomic_enable(struct drm_crtc *crtc,
regmap_write(ldev->regmap, LTDC_BCCR, BCCR_BCBLACK);
/* Enable IRQ */
- regmap_set_bits(ldev->regmap, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
+ regmap_set_bits(ldev->regmap, LTDC_IER, IER_FUWIE | IER_FUEIE | IER_RRIE | IER_TERRIE);
/* Commit shadow registers = update planes at next vblank */
if (!ldev->caps.plane_reg_shadow)
@@ -787,19 +800,32 @@ static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc,
{
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
struct drm_device *ddev = crtc->dev;
+ int layer_index = 0;
DRM_DEBUG_DRIVER("\n");
drm_crtc_vblank_off(crtc);
+ /* Disable all layers */
+ for (layer_index = 0; layer_index < ldev->caps.nb_layers; layer_index++)
+ regmap_write_bits(ldev->regmap, LTDC_L1CR + layer_index * LAY_OFS,
+ LXCR_CLUTEN | LXCR_LEN, 0);
+
/* disable IRQ */
- regmap_clear_bits(ldev->regmap, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
+ regmap_clear_bits(ldev->regmap, LTDC_IER, IER_FUWIE | IER_FUEIE | IER_RRIE | IER_TERRIE);
/* immediately commit disable of layers before switching off LTDC */
if (!ldev->caps.plane_reg_shadow)
regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_IMR);
pm_runtime_put_sync(ddev->dev);
+
+ /* clear interrupt error counters */
+ mutex_lock(&ldev->err_lock);
+ ldev->transfer_err = 0;
+ ldev->fifo_err = 0;
+ ldev->fifo_warn = 0;
+ mutex_unlock(&ldev->err_lock);
}
#define CLK_TOLERANCE_HZ 50
@@ -902,9 +928,9 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
drm_connector_list_iter_end(&iter);
}
- if (bridge && bridge->timings)
+ if (bridge && bridge->timings) {
bus_flags = bridge->timings->input_bus_flags;
- else if (connector) {
+ } else if (connector) {
bus_flags = connector->display_info.bus_flags;
if (connector->display_info.num_bus_formats)
bus_formats = connector->display_info.bus_formats[0];
@@ -1160,6 +1186,18 @@ static int ltdc_crtc_verify_crc_source(struct drm_crtc *crtc,
return 0;
}
+static void ltdc_crtc_atomic_print_state(struct drm_printer *p,
+ const struct drm_crtc_state *state)
+{
+ struct drm_crtc *crtc = state->crtc;
+ struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+
+ drm_printf(p, "\ttransfer_error=%d\n", ldev->transfer_err);
+ drm_printf(p, "\tfifo_underrun_error=%d\n", ldev->fifo_err);
+ drm_printf(p, "\tfifo_underrun_warning=%d\n", ldev->fifo_warn);
+ drm_printf(p, "\tfifo_underrun_threshold=%d\n", ldev->fifo_threshold);
+}
+
static const struct drm_crtc_funcs ltdc_crtc_funcs = {
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
@@ -1170,6 +1208,7 @@ static const struct drm_crtc_funcs ltdc_crtc_funcs = {
.enable_vblank = ltdc_crtc_enable_vblank,
.disable_vblank = ltdc_crtc_disable_vblank,
.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
+ .atomic_print_state = ltdc_crtc_atomic_print_state,
};
static const struct drm_crtc_funcs ltdc_crtc_with_crc_support_funcs = {
@@ -1184,6 +1223,7 @@ static const struct drm_crtc_funcs ltdc_crtc_with_crc_support_funcs = {
.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
.set_crc_source = ltdc_crtc_set_crc_source,
.verify_crc_source = ltdc_crtc_verify_crc_source,
+ .atomic_print_state = ltdc_crtc_atomic_print_state,
};
/*
@@ -1209,7 +1249,8 @@ static int ltdc_plane_atomic_check(struct drm_plane *plane,
/* Reject scaling */
if (src_w != new_plane_state->crtc_w || src_h != new_plane_state->crtc_h) {
- DRM_ERROR("Scaling is not supported");
+ DRM_DEBUG_DRIVER("Scaling is not supported");
+
return -EINVAL;
}
@@ -1229,7 +1270,8 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
u32 y0 = newstate->crtc_y;
u32 y1 = newstate->crtc_y + newstate->crtc_h - 1;
u32 src_x, src_y, src_w, src_h;
- u32 val, pitch_in_bytes, line_length, line_number, paddr, ahbp, avbp, bpcr;
+ u32 val, pitch_in_bytes, line_length, line_number, ahbp, avbp, bpcr;
+ u32 paddr, paddr1, paddr2;
enum ltdc_pix_fmt pf;
if (!newstate->crtc || !fb) {
@@ -1281,13 +1323,6 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
}
regmap_write_bits(ldev->regmap, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
- /* Configures the color frame buffer pitch in bytes & line length */
- pitch_in_bytes = fb->pitches[0];
- line_length = fb->format->cpp[0] *
- (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
- val = ((pitch_in_bytes << 16) | line_length);
- regmap_write_bits(ldev->regmap, LTDC_L1CFBLR + lofs, LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
-
/* Specifies the constant alpha value */
val = newstate->alpha >> 8;
regmap_write_bits(ldev->regmap, LTDC_L1CACR + lofs, LXCACR_CONSTA, val);
@@ -1302,78 +1337,124 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
plane->type != DRM_PLANE_TYPE_PRIMARY)
val = BF1_PAXCA | BF2_1PAXCA;
- regmap_write_bits(ldev->regmap, LTDC_L1BFCR + lofs, LXBFCR_BF2 | LXBFCR_BF1, val);
-
- /* Configures the frame buffer line number */
- line_number = y1 - y0 + 1;
- regmap_write_bits(ldev->regmap, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, line_number);
+ if (ldev->caps.dynamic_zorder) {
+ val |= (newstate->normalized_zpos << 16);
+ regmap_write_bits(ldev->regmap, LTDC_L1BFCR + lofs,
+ LXBFCR_BF2 | LXBFCR_BF1 | LXBFCR_BOR, val);
+ } else {
+ regmap_write_bits(ldev->regmap, LTDC_L1BFCR + lofs,
+ LXBFCR_BF2 | LXBFCR_BF1, val);
+ }
/* Sets the FB address */
paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 0);
+ if (newstate->rotation & DRM_MODE_REFLECT_X)
+ paddr += (fb->format->cpp[0] * (x1 - x0 + 1)) - 1;
+
+ if (newstate->rotation & DRM_MODE_REFLECT_Y)
+ paddr += (fb->pitches[0] * (y1 - y0));
+
DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr);
regmap_write(ldev->regmap, LTDC_L1CFBAR + lofs, paddr);
+ /* Configures the color frame buffer pitch in bytes & line length */
+ line_length = fb->format->cpp[0] *
+ (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
+
+ if (newstate->rotation & DRM_MODE_REFLECT_Y)
+ /* Compute negative value (signed on 16 bits) for the picth */
+ pitch_in_bytes = 0x10000 - fb->pitches[0];
+ else
+ pitch_in_bytes = fb->pitches[0];
+
+ val = (pitch_in_bytes << 16) | line_length;
+ regmap_write_bits(ldev->regmap, LTDC_L1CFBLR + lofs, LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
+
+ /* Configures the frame buffer line number */
+ line_number = y1 - y0 + 1;
+ regmap_write_bits(ldev->regmap, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, line_number);
+
if (ldev->caps.ycbcr_input) {
if (fb->format->is_yuv) {
switch (fb->format->format) {
case DRM_FORMAT_NV12:
case DRM_FORMAT_NV21:
- /* Configure the auxiliary frame buffer address 0 & 1 */
- paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
- regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
- regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr + 1);
+ /* Configure the auxiliary frame buffer address 0 */
+ paddr1 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
- /* Configure the buffer length */
- val = ((pitch_in_bytes << 16) | line_length);
- regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
+ if (newstate->rotation & DRM_MODE_REFLECT_X)
+ paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
- /* Configure the frame buffer line number */
- val = (line_number >> 1);
- regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
+ if (newstate->rotation & DRM_MODE_REFLECT_Y)
+ paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
+
+ regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
break;
case DRM_FORMAT_YUV420:
- /* Configure the auxiliary frame buffer address 0 */
- paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
- regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
-
- /* Configure the auxiliary frame buffer address 1 */
- paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
- regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr);
+ /* Configure the auxiliary frame buffer address 0 & 1 */
+ paddr1 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
+ paddr2 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
- line_length = ((fb->format->cpp[0] * (x1 - x0 + 1)) >> 1) +
- (ldev->caps.bus_width >> 3) - 1;
+ if (newstate->rotation & DRM_MODE_REFLECT_X) {
+ paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
+ paddr2 += ((fb->format->cpp[2] * (x1 - x0 + 1)) >> 1) - 1;
+ }
- /* Configure the buffer length */
- val = (((pitch_in_bytes >> 1) << 16) | line_length);
- regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
+ if (newstate->rotation & DRM_MODE_REFLECT_Y) {
+ paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
+ paddr2 += (fb->pitches[2] * (y1 - y0 - 1)) >> 1;
+ }
- /* Configure the frame buffer line number */
- val = (line_number >> 1);
- regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
+ regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
+ regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr2);
break;
case DRM_FORMAT_YVU420:
- /* Configure the auxiliary frame buffer address 0 */
- paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
- regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
-
- /* Configure the auxiliary frame buffer address 1 */
- paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
- regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr);
+ /* Configure the auxiliary frame buffer address 0 & 1 */
+ paddr1 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
+ paddr2 = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
- line_length = ((fb->format->cpp[0] * (x1 - x0 + 1)) >> 1) +
- (ldev->caps.bus_width >> 3) - 1;
+ if (newstate->rotation & DRM_MODE_REFLECT_X) {
+ paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
+ paddr2 += ((fb->format->cpp[2] * (x1 - x0 + 1)) >> 1) - 1;
+ }
- /* Configure the buffer length */
- val = (((pitch_in_bytes >> 1) << 16) | line_length);
- regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
+ if (newstate->rotation & DRM_MODE_REFLECT_Y) {
+ paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
+ paddr2 += (fb->pitches[2] * (y1 - y0 - 1)) >> 1;
+ }
- /* Configure the frame buffer line number */
- val = (line_number >> 1);
- regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
+ regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
+ regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr2);
break;
}
+ /*
+ * Set the length and the number of lines of the auxiliary
+ * buffers if the framebuffer contains more than one plane.
+ */
+ if (fb->format->num_planes > 1) {
+ if (newstate->rotation & DRM_MODE_REFLECT_Y)
+ /*
+ * Compute negative value (signed on 16 bits)
+ * for the picth
+ */
+ pitch_in_bytes = 0x10000 - fb->pitches[1];
+ else
+ pitch_in_bytes = fb->pitches[1];
+
+ line_length = ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) +
+ (ldev->caps.bus_width >> 3) - 1;
+
+ /* Configure the auxiliary buffer length */
+ val = (pitch_in_bytes << 16) | line_length;
+ regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
+
+ /* Configure the auxiliary frame buffer line number */
+ val = line_number >> 1;
+ regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
+ }
+
/* Configure YCbC conversion coefficient */
ltdc_set_ycbcr_coeffs(plane);
@@ -1388,7 +1469,12 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
/* Enable layer and CLUT if needed */
val = fb->format->format == DRM_FORMAT_C8 ? LXCR_CLUTEN : 0;
val |= LXCR_LEN;
- regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN, val);
+
+ /* Enable horizontal mirroring if requested */
+ if (newstate->rotation & DRM_MODE_REFLECT_X)
+ val |= LXCR_HMEN;
+
+ regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN | LXCR_HMEN, val);
/* Commit shadow registers = update plane at next vblank */
if (ldev->caps.plane_reg_shadow)
@@ -1398,13 +1484,21 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
ldev->plane_fpsi[plane->index].counter++;
mutex_lock(&ldev->err_lock);
- if (ldev->error_status & ISR_FUIF) {
- DRM_WARN("ltdc fifo underrun: please verify display mode\n");
- ldev->error_status &= ~ISR_FUIF;
+ if (ldev->transfer_err) {
+ DRM_WARN("ltdc transfer error: %d\n", ldev->transfer_err);
+ ldev->transfer_err = 0;
}
- if (ldev->error_status & ISR_TERRIF) {
- DRM_WARN("ltdc transfer error\n");
- ldev->error_status &= ~ISR_TERRIF;
+
+ if (ldev->caps.fifo_threshold) {
+ if (ldev->fifo_err) {
+ DRM_WARN("ltdc fifo underrun: please verify display mode\n");
+ ldev->fifo_err = 0;
+ }
+ } else {
+ if (ldev->fifo_warn >= ldev->fifo_threshold) {
+ DRM_WARN("ltdc fifo underrun: please verify display mode\n");
+ ldev->fifo_warn = 0;
+ }
}
mutex_unlock(&ldev->err_lock);
}
@@ -1417,8 +1511,8 @@ static void ltdc_plane_atomic_disable(struct drm_plane *plane,
struct ltdc_device *ldev = plane_to_ltdc(plane);
u32 lofs = plane->index * LAY_OFS;
- /* disable layer */
- regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN, 0);
+ /* Disable layer */
+ regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN | LXCR_HMEN, 0);
/* Commit shadow registers = update plane at next vblank */
if (ldev->caps.plane_reg_shadow)
@@ -1562,6 +1656,7 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
{
struct ltdc_device *ldev = ddev->dev_private;
struct drm_plane *primary, *overlay;
+ int supported_rotations = DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y;
unsigned int i;
int ret;
@@ -1571,7 +1666,14 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
return -EINVAL;
}
- drm_plane_create_zpos_immutable_property(primary, 0);
+ if (ldev->caps.dynamic_zorder)
+ drm_plane_create_zpos_property(primary, 0, 0, ldev->caps.nb_layers - 1);
+ else
+ drm_plane_create_zpos_immutable_property(primary, 0);
+
+ if (ldev->caps.plane_rotation)
+ drm_plane_create_rotation_property(primary, DRM_MODE_ROTATE_0,
+ supported_rotations);
/* Init CRTC according to its hardware features */
if (ldev->caps.crc)
@@ -1600,7 +1702,14 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
DRM_ERROR("Can not create overlay plane %d\n", i);
goto cleanup;
}
- drm_plane_create_zpos_immutable_property(overlay, i);
+ if (ldev->caps.dynamic_zorder)
+ drm_plane_create_zpos_property(overlay, i, 0, ldev->caps.nb_layers - 1);
+ else
+ drm_plane_create_zpos_immutable_property(overlay, i);
+
+ if (ldev->caps.plane_rotation)
+ drm_plane_create_rotation_property(overlay, DRM_MODE_ROTATE_0,
+ supported_rotations);
}
return 0;
@@ -1631,6 +1740,10 @@ static void ltdc_encoder_enable(struct drm_encoder *encoder)
DRM_DEBUG_DRIVER("\n");
+ /* set fifo underrun threshold register */
+ if (ldev->caps.fifo_threshold)
+ regmap_write(ldev->regmap, LTDC_FUT, ldev->fifo_threshold);
+
/* Enable LTDC */
regmap_set_bits(ldev->regmap, LTDC_GCR, GCR_LTDCEN);
}
@@ -1730,6 +1843,9 @@ static int ltdc_get_caps(struct drm_device *ddev)
ldev->caps.ycbcr_output = false;
ldev->caps.plane_reg_shadow = false;
ldev->caps.crc = false;
+ ldev->caps.dynamic_zorder = false;
+ ldev->caps.plane_rotation = false;
+ ldev->caps.fifo_threshold = false;
break;
case HWVER_20101:
ldev->caps.layer_ofs = LAY_OFS_0;
@@ -1745,6 +1861,9 @@ static int ltdc_get_caps(struct drm_device *ddev)
ldev->caps.ycbcr_output = false;
ldev->caps.plane_reg_shadow = false;
ldev->caps.crc = false;
+ ldev->caps.dynamic_zorder = false;
+ ldev->caps.plane_rotation = false;
+ ldev->caps.fifo_threshold = false;
break;
case HWVER_40100:
ldev->caps.layer_ofs = LAY_OFS_1;
@@ -1760,6 +1879,9 @@ static int ltdc_get_caps(struct drm_device *ddev)
ldev->caps.ycbcr_output = true;
ldev->caps.plane_reg_shadow = true;
ldev->caps.crc = true;
+ ldev->caps.dynamic_zorder = true;
+ ldev->caps.plane_rotation = true;
+ ldev->caps.fifo_threshold = true;
break;
default:
return -ENODEV;
@@ -1884,9 +2006,6 @@ int ltdc_load(struct drm_device *ddev)
goto err;
}
- /* Disable interrupts */
- regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE | IER_RRIE | IER_FUIE | IER_TERRIE);
-
ret = ltdc_get_caps(ddev);
if (ret) {
DRM_ERROR("hardware identifier (0x%08x) not supported!\n",
@@ -1894,8 +2013,22 @@ int ltdc_load(struct drm_device *ddev)
goto err;
}
+ /* Disable interrupts */
+ if (ldev->caps.fifo_threshold)
+ regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE | IER_RRIE | IER_FUWIE |
+ IER_TERRIE);
+ else
+ regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE | IER_RRIE | IER_FUWIE |
+ IER_TERRIE | IER_FUEIE);
+
DRM_DEBUG_DRIVER("ltdc hw version 0x%08x\n", ldev->caps.hw_version);
+ /* initialize default value for fifo underrun threshold & clear interrupt error counters */
+ ldev->transfer_err = 0;
+ ldev->fifo_err = 0;
+ ldev->fifo_warn = 0;
+ ldev->fifo_threshold = FUT_DFT;
+
for (i = 0; i < ldev->caps.nb_irq; i++) {
irq = platform_get_irq(pdev, i);
if (irq < 0) {
@@ -1910,7 +2043,6 @@ int ltdc_load(struct drm_device *ddev)
DRM_ERROR("Failed to register LTDC interrupt\n");
goto err;
}
-
}
crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL);