]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
drm/bridge: it6505: fix hibernate to resume no display issue
authorKuro Chung <kuro.chung@ite.com.tw>
Wed, 22 May 2024 06:55:28 +0000 (14:55 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 3 Aug 2024 06:59:51 +0000 (08:59 +0200)
[ Upstream commit 484436ec5c2bffe8f346a09ae1cbc4cbf5e50005 ]

When the system power resumes, the TTL input of IT6505 may experience
some noise before the video signal stabilizes, necessitating a video
reset. This patch is implemented to prevent a loop of video error
interrupts, which can occur when a video reset in the video FIFO error
interrupt triggers another such interrupt. The patch processes the SCDT
and FIFO error interrupts simultaneously and ignores any video FIFO
error interrupts caused by a video reset.

Fixes: b5c84a9edcd4 ("drm/bridge: add it6505 driver")
Signed-off-by: Kuro Chung <kuro.chung@ite.com.tw>
Signed-off-by: Hermes Wu <hermes.wu@ite.com.tw>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Robert Foss <rfoss@kernel.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20240522065528.1053439-1-kuro.chung@ite.com.tw
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/gpu/drm/bridge/ite-it6505.c

index 3f68c82888c2c51c84b3fe29ec91052703c1fab6..cf59347d3d605612636bfe705d57e4fdfb0d9dc4 100644 (file)
@@ -1307,9 +1307,15 @@ static void it6505_video_reset(struct it6505 *it6505)
        it6505_link_reset_step_train(it6505);
        it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_VID_MUTE, EN_VID_MUTE);
        it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_VID_CTRL_PKT, 0x00);
-       it6505_set_bits(it6505, REG_RESET_CTRL, VIDEO_RESET, VIDEO_RESET);
+
+       it6505_set_bits(it6505, REG_VID_BUS_CTRL1, TX_FIFO_RESET, TX_FIFO_RESET);
+       it6505_set_bits(it6505, REG_VID_BUS_CTRL1, TX_FIFO_RESET, 0x00);
+
        it6505_set_bits(it6505, REG_501_FIFO_CTRL, RST_501_FIFO, RST_501_FIFO);
        it6505_set_bits(it6505, REG_501_FIFO_CTRL, RST_501_FIFO, 0x00);
+
+       it6505_set_bits(it6505, REG_RESET_CTRL, VIDEO_RESET, VIDEO_RESET);
+       usleep_range(1000, 2000);
        it6505_set_bits(it6505, REG_RESET_CTRL, VIDEO_RESET, 0x00);
 }
 
@@ -2245,12 +2251,11 @@ static void it6505_link_training_work(struct work_struct *work)
        if (ret) {
                it6505->auto_train_retry = AUTO_TRAIN_RETRY;
                it6505_link_train_ok(it6505);
-               return;
        } else {
                it6505->auto_train_retry--;
+               it6505_dump(it6505);
        }
 
-       it6505_dump(it6505);
 }
 
 static void it6505_plugged_status_to_codec(struct it6505 *it6505)
@@ -2471,31 +2476,53 @@ static void it6505_irq_link_train_fail(struct it6505 *it6505)
        schedule_work(&it6505->link_works);
 }
 
-static void it6505_irq_video_fifo_error(struct it6505 *it6505)
+static bool it6505_test_bit(unsigned int bit, const unsigned int *addr)
 {
-       struct device *dev = it6505->dev;
-
-       DRM_DEV_DEBUG_DRIVER(dev, "video fifo overflow interrupt");
-       it6505->auto_train_retry = AUTO_TRAIN_RETRY;
-       flush_work(&it6505->link_works);
-       it6505_stop_hdcp(it6505);
-       it6505_video_reset(it6505);
+       return 1 & (addr[bit / BITS_PER_BYTE] >> (bit % BITS_PER_BYTE));
 }
 
-static void it6505_irq_io_latch_fifo_overflow(struct it6505 *it6505)
+static void it6505_irq_video_handler(struct it6505 *it6505, const int *int_status)
 {
        struct device *dev = it6505->dev;
+       int reg_0d, reg_int03;
 
-       DRM_DEV_DEBUG_DRIVER(dev, "IO latch fifo overflow interrupt");
-       it6505->auto_train_retry = AUTO_TRAIN_RETRY;
-       flush_work(&it6505->link_works);
-       it6505_stop_hdcp(it6505);
-       it6505_video_reset(it6505);
-}
+       /*
+        * When video SCDT change with video not stable,
+        * Or video FIFO error, need video reset
+        */
 
-static bool it6505_test_bit(unsigned int bit, const unsigned int *addr)
-{
-       return 1 & (addr[bit / BITS_PER_BYTE] >> (bit % BITS_PER_BYTE));
+       if ((!it6505_get_video_status(it6505) &&
+            (it6505_test_bit(INT_SCDT_CHANGE, (unsigned int *)int_status))) ||
+           (it6505_test_bit(BIT_INT_IO_FIFO_OVERFLOW,
+                            (unsigned int *)int_status)) ||
+           (it6505_test_bit(BIT_INT_VID_FIFO_ERROR,
+                            (unsigned int *)int_status))) {
+               it6505->auto_train_retry = AUTO_TRAIN_RETRY;
+               flush_work(&it6505->link_works);
+               it6505_stop_hdcp(it6505);
+               it6505_video_reset(it6505);
+
+               usleep_range(10000, 11000);
+
+               /*
+                * Clear FIFO error IRQ to prevent fifo error -> reset loop
+                * HW will trigger SCDT change IRQ again when video stable
+                */
+
+               reg_int03 = it6505_read(it6505, INT_STATUS_03);
+               reg_0d = it6505_read(it6505, REG_SYSTEM_STS);
+
+               reg_int03 &= (BIT(INT_VID_FIFO_ERROR) | BIT(INT_IO_LATCH_FIFO_OVERFLOW));
+               it6505_write(it6505, INT_STATUS_03, reg_int03);
+
+               DRM_DEV_DEBUG_DRIVER(dev, "reg08 = 0x%02x", reg_int03);
+               DRM_DEV_DEBUG_DRIVER(dev, "reg0D = 0x%02x", reg_0d);
+
+               return;
+       }
+
+       if (it6505_test_bit(INT_SCDT_CHANGE, (unsigned int *)int_status))
+               it6505_irq_scdt(it6505);
 }
 
 static irqreturn_t it6505_int_threaded_handler(int unused, void *data)
@@ -2508,15 +2535,12 @@ static irqreturn_t it6505_int_threaded_handler(int unused, void *data)
        } irq_vec[] = {
                { BIT_INT_HPD, it6505_irq_hpd },
                { BIT_INT_HPD_IRQ, it6505_irq_hpd_irq },
-               { BIT_INT_SCDT, it6505_irq_scdt },
                { BIT_INT_HDCP_FAIL, it6505_irq_hdcp_fail },
                { BIT_INT_HDCP_DONE, it6505_irq_hdcp_done },
                { BIT_INT_AUX_CMD_FAIL, it6505_irq_aux_cmd_fail },
                { BIT_INT_HDCP_KSV_CHECK, it6505_irq_hdcp_ksv_check },
                { BIT_INT_AUDIO_FIFO_ERROR, it6505_irq_audio_fifo_error },
                { BIT_INT_LINK_TRAIN_FAIL, it6505_irq_link_train_fail },
-               { BIT_INT_VID_FIFO_ERROR, it6505_irq_video_fifo_error },
-               { BIT_INT_IO_FIFO_OVERFLOW, it6505_irq_io_latch_fifo_overflow },
        };
        int int_status[3], i;
 
@@ -2546,6 +2570,7 @@ static irqreturn_t it6505_int_threaded_handler(int unused, void *data)
                        if (it6505_test_bit(irq_vec[i].bit, (unsigned int *)int_status))
                                irq_vec[i].handler(it6505);
                }
+               it6505_irq_video_handler(it6505, (unsigned int *)int_status);
        }
 
        pm_runtime_put_sync(dev);