]> git.baikalelectronics.ru Git - kernel.git/commitdiff
drm/stm: ltdc: update hardware error management
authorYannick Fertre <yannick.fertre@foss.st.com>
Fri, 3 Jun 2022 13:46:54 +0000 (15:46 +0200)
committerPhilippe Cornu <philippe.cornu@foss.st.com>
Mon, 27 Jun 2022 14:01:50 +0000 (16:01 +0200)
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: Yannick Fertre <yannick.fertre@foss.st.com>
Signed-off-by: Philippe Cornu <philippe.cornu@foss.st.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20220603134654.594373-1-yannick.fertre@foss.st.com
drivers/gpu/drm/stm/ltdc.c
drivers/gpu/drm/stm/ltdc.h

index ba598225e16e85687a8a8d1839a4ed4a6a516111..cc6547de682f465e9d77e6d6461d47810d1c5c90 100644 (file)
 #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
@@ -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) {
-               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);
 }
@@ -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) {
index 15139980d8ea7d665446399136850c334e4f5ed2..9d488043ffdbc652deeede71c9d57d45fb89d3c6 100644 (file)
@@ -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;