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

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
...@@ -165,16 +165,20 @@ ...@@ -165,16 +165,20 @@
#define BCCR_BCWHITE GENMASK(23, 0) /* Background Color WHITE */ #define BCCR_BCWHITE GENMASK(23, 0) /* Background Color WHITE */
#define IER_LIE BIT(0) /* Line Interrupt Enable */ #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_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 CPSR_CYPOS GENMASK(15, 0) /* Current Y position */
#define ISR_LIF BIT(0) /* Line Interrupt Flag */ #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_TERRIF BIT(2) /* Transfer ERRor Interrupt Flag */
#define ISR_RRIF BIT(3) /* Register Reload 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_OCYEN BIT(25) /* Output Conversion to YCbCr 422: ENable */
#define EDCR_OCYSEL BIT(26) /* Output Conversion to YCbCr 422: SELection of the CCIR */ #define EDCR_OCYSEL BIT(26) /* Output Conversion to YCbCr 422: SELection of the CCIR */
...@@ -234,6 +238,8 @@ ...@@ -234,6 +238,8 @@
#define NB_PF 8 /* Max nb of HW pixel format */ #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 * 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 * 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) ...@@ -714,12 +720,13 @@ static irqreturn_t ltdc_irq_thread(int irq, void *arg)
ltdc_irq_crc_handle(ldev, crtc); ltdc_irq_crc_handle(ldev, crtc);
} }
/* Save FIFO Underrun & Transfer Error status */
mutex_lock(&ldev->err_lock); mutex_lock(&ldev->err_lock);
if (ldev->irq_status & ISR_FUIF)
ldev->error_status |= ISR_FUIF;
if (ldev->irq_status & ISR_TERRIF) 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); mutex_unlock(&ldev->err_lock);
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -778,7 +785,7 @@ static void ltdc_crtc_atomic_enable(struct drm_crtc *crtc, ...@@ -778,7 +785,7 @@ static void ltdc_crtc_atomic_enable(struct drm_crtc *crtc,
regmap_write(ldev->regmap, LTDC_BCCR, BCCR_BCBLACK); regmap_write(ldev->regmap, LTDC_BCCR, BCCR_BCBLACK);
/* Enable IRQ */ /* 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 */ /* Commit shadow registers = update planes at next vblank */
if (!ldev->caps.plane_reg_shadow) if (!ldev->caps.plane_reg_shadow)
...@@ -804,13 +811,20 @@ static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc, ...@@ -804,13 +811,20 @@ static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc,
LXCR_CLUTEN | LXCR_LEN, 0); LXCR_CLUTEN | LXCR_LEN, 0);
/* disable IRQ */ /* 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 */ /* immediately commit disable of layers before switching off LTDC */
if (!ldev->caps.plane_reg_shadow) if (!ldev->caps.plane_reg_shadow)
regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_IMR); regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_IMR);
pm_runtime_put_sync(ddev->dev); 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 #define CLK_TOLERANCE_HZ 50
...@@ -1171,6 +1185,18 @@ static int ltdc_crtc_verify_crc_source(struct drm_crtc *crtc, ...@@ -1171,6 +1185,18 @@ static int ltdc_crtc_verify_crc_source(struct drm_crtc *crtc,
return 0; 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 = { static const struct drm_crtc_funcs ltdc_crtc_funcs = {
.destroy = drm_crtc_cleanup, .destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config, .set_config = drm_atomic_helper_set_config,
...@@ -1181,6 +1207,7 @@ static const struct drm_crtc_funcs ltdc_crtc_funcs = { ...@@ -1181,6 +1207,7 @@ static const struct drm_crtc_funcs ltdc_crtc_funcs = {
.enable_vblank = ltdc_crtc_enable_vblank, .enable_vblank = ltdc_crtc_enable_vblank,
.disable_vblank = ltdc_crtc_disable_vblank, .disable_vblank = ltdc_crtc_disable_vblank,
.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, .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 = { 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 = { ...@@ -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, .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
.set_crc_source = ltdc_crtc_set_crc_source, .set_crc_source = ltdc_crtc_set_crc_source,
.verify_crc_source = ltdc_crtc_verify_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, ...@@ -1455,13 +1483,21 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
ldev->plane_fpsi[plane->index].counter++; ldev->plane_fpsi[plane->index].counter++;
mutex_lock(&ldev->err_lock); mutex_lock(&ldev->err_lock);
if (ldev->error_status & ISR_FUIF) { if (ldev->transfer_err) {
DRM_WARN("ltdc fifo underrun: please verify display mode\n"); DRM_WARN("ltdc transfer error: %d\n", ldev->transfer_err);
ldev->error_status &= ~ISR_FUIF; ldev->transfer_err = 0;
} }
if (ldev->error_status & ISR_TERRIF) {
DRM_WARN("ltdc transfer error\n"); if (ldev->caps.fifo_threshold) {
ldev->error_status &= ~ISR_TERRIF; 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); mutex_unlock(&ldev->err_lock);
} }
...@@ -1703,6 +1739,10 @@ static void ltdc_encoder_enable(struct drm_encoder *encoder) ...@@ -1703,6 +1739,10 @@ static void ltdc_encoder_enable(struct drm_encoder *encoder)
DRM_DEBUG_DRIVER("\n"); 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 */ /* Enable LTDC */
regmap_set_bits(ldev->regmap, LTDC_GCR, GCR_LTDCEN); regmap_set_bits(ldev->regmap, LTDC_GCR, GCR_LTDCEN);
} }
...@@ -1804,6 +1844,7 @@ static int ltdc_get_caps(struct drm_device *ddev) ...@@ -1804,6 +1844,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
ldev->caps.crc = false; ldev->caps.crc = false;
ldev->caps.dynamic_zorder = false; ldev->caps.dynamic_zorder = false;
ldev->caps.plane_rotation = false; ldev->caps.plane_rotation = false;
ldev->caps.fifo_threshold = false;
break; break;
case HWVER_20101: case HWVER_20101:
ldev->caps.layer_ofs = LAY_OFS_0; ldev->caps.layer_ofs = LAY_OFS_0;
...@@ -1821,6 +1862,7 @@ static int ltdc_get_caps(struct drm_device *ddev) ...@@ -1821,6 +1862,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
ldev->caps.crc = false; ldev->caps.crc = false;
ldev->caps.dynamic_zorder = false; ldev->caps.dynamic_zorder = false;
ldev->caps.plane_rotation = false; ldev->caps.plane_rotation = false;
ldev->caps.fifo_threshold = false;
break; break;
case HWVER_40100: case HWVER_40100:
ldev->caps.layer_ofs = LAY_OFS_1; ldev->caps.layer_ofs = LAY_OFS_1;
...@@ -1838,6 +1880,7 @@ static int ltdc_get_caps(struct drm_device *ddev) ...@@ -1838,6 +1880,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
ldev->caps.crc = true; ldev->caps.crc = true;
ldev->caps.dynamic_zorder = true; ldev->caps.dynamic_zorder = true;
ldev->caps.plane_rotation = true; ldev->caps.plane_rotation = true;
ldev->caps.fifo_threshold = true;
break; break;
default: default:
return -ENODEV; return -ENODEV;
...@@ -1962,9 +2005,6 @@ int ltdc_load(struct drm_device *ddev) ...@@ -1962,9 +2005,6 @@ int ltdc_load(struct drm_device *ddev)
goto err; goto err;
} }
/* Disable interrupts */
regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE | IER_RRIE | IER_FUIE | IER_TERRIE);
ret = ltdc_get_caps(ddev); ret = ltdc_get_caps(ddev);
if (ret) { if (ret) {
DRM_ERROR("hardware identifier (0x%08x) not supported!\n", DRM_ERROR("hardware identifier (0x%08x) not supported!\n",
...@@ -1972,8 +2012,22 @@ int ltdc_load(struct drm_device *ddev) ...@@ -1972,8 +2012,22 @@ int ltdc_load(struct drm_device *ddev)
goto err; 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); 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++) { for (i = 0; i < ldev->caps.nb_irq; i++) {
irq = platform_get_irq(pdev, i); irq = platform_get_irq(pdev, i);
if (irq < 0) { if (irq < 0) {
......
...@@ -30,6 +30,7 @@ struct ltdc_caps { ...@@ -30,6 +30,7 @@ struct ltdc_caps {
bool crc; /* cyclic redundancy check supported */ bool crc; /* cyclic redundancy check supported */
bool dynamic_zorder; /* dynamic z-order */ bool dynamic_zorder; /* dynamic z-order */
bool plane_rotation; /* plane rotation */ bool plane_rotation; /* plane rotation */
bool fifo_threshold; /* fifo underrun threshold supported */
}; };
#define LTDC_MAX_LAYER 4 #define LTDC_MAX_LAYER 4
...@@ -45,8 +46,11 @@ struct ltdc_device { ...@@ -45,8 +46,11 @@ struct ltdc_device {
struct clk *pixel_clk; /* lcd pixel clock */ struct clk *pixel_clk; /* lcd pixel clock */
struct mutex err_lock; /* protecting error_status */ struct mutex err_lock; /* protecting error_status */
struct ltdc_caps caps; struct ltdc_caps caps;
u32 error_status;
u32 irq_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 fps_info plane_fpsi[LTDC_MAX_LAYER];
struct drm_atomic_state *suspend_state; struct drm_atomic_state *suspend_state;
int crc_skip_count; int crc_skip_count;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment