#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 */
#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
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;
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)
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
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,
.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 = {
.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,
};
/*
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);
}
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);
}
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;
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;
ldev->caps.crc = true;
ldev->caps.dynamic_zorder = true;
ldev->caps.plane_rotation = true;
+ ldev->caps.fifo_threshold = true;
break;
default:
return -ENODEV;
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",
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) {