Commit 7d008eec authored by Yannick Fertre's avatar Yannick Fertre Committed by Philippe Cornu
Browse files

drm/stm: ltdc: update hardware error management



The latest hardware version (0x40100) supports a hardware threshold
register (aka FUTR) to trigger a fifo underrun interrupt.
A software threshold has been implemented for other hardware versions.
The threshold is set to 128 by default.

Signed-off-by: default avatarYannick Fertre <yannick.fertre@foss.st.com>
Signed-off-by: default avatarPhilippe Cornu <philippe.cornu@foss.st.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20220603134654.594373-1-yannick.fertre@foss.st.com
parent c6193dc5
Loading
Loading
Loading
Loading
+72 −18
Original line number Diff line number Diff line
@@ -165,16 +165,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 */
@@ -234,6 +238,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
@@ -714,12 +720,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;
@@ -778,7 +785,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)
@@ -804,13 +811,20 @@ static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc,
				  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
@@ -1171,6 +1185,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,
@@ -1181,6 +1207,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 = {
@@ -1195,6 +1222,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,
};

/*
@@ -1455,13 +1483,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) {
	if (ldev->transfer_err) {
		DRM_WARN("ltdc transfer error: %d\n", ldev->transfer_err);
		ldev->transfer_err = 0;
	}

	if (ldev->caps.fifo_threshold) {
		if (ldev->fifo_err) {
			DRM_WARN("ltdc fifo underrun: please verify display mode\n");
		ldev->error_status &= ~ISR_FUIF;
			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;
		}
	if (ldev->error_status & ISR_TERRIF) {
		DRM_WARN("ltdc transfer error\n");
		ldev->error_status &= ~ISR_TERRIF;
	}
	mutex_unlock(&ldev->err_lock);
}
@@ -1703,6 +1739,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);
}
@@ -1804,6 +1844,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
		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;
@@ -1821,6 +1862,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
		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;
@@ -1838,6 +1880,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
		ldev->caps.crc = true;
		ldev->caps.dynamic_zorder = true;
		ldev->caps.plane_rotation = true;
		ldev->caps.fifo_threshold = true;
		break;
	default:
		return -ENODEV;
@@ -1962,9 +2005,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",
@@ -1972,8 +2012,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) {
+5 −1
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ struct ltdc_caps {
	bool crc;		/* cyclic redundancy check supported */
	bool dynamic_zorder;	/* dynamic z-order */
	bool plane_rotation;	/* plane rotation */
	bool fifo_threshold;	/* fifo underrun threshold supported */
};

#define LTDC_MAX_LAYER	4
@@ -45,8 +46,11 @@ struct ltdc_device {
	struct clk *pixel_clk;	/* lcd pixel clock */
	struct mutex err_lock;	/* protecting error_status */
	struct ltdc_caps caps;
	u32 error_status;
	u32 irq_status;
	u32 fifo_err;		/* fifo underrun error counter */
	u32 fifo_warn;		/* fifo underrun warning counter */
	u32 fifo_threshold;	/* fifo underrun threshold */
	u32 transfer_err;	/* transfer error counter */
	struct fps_info plane_fpsi[LTDC_MAX_LAYER];
	struct drm_atomic_state *suspend_state;
	int crc_skip_count;