Message ID | 20240419073809.706471-2-kuro.chung@ite.com.tw (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | drm/bridge: it6505: fix hibernate to resume no display issue | expand |
On Fri, Apr 19, 2024 at 03:38:07PM +0800, kuro wrote: > From: Kuro <kuro.chung@ite.com.tw> > > ITE added a FIFO reset bit for input video. When system power resume, > the TTL input of it6505 may get some noise before video signal stable > and the hardware function reset is required. > But the input FIFO reset will also trigger error interrupts of output module rising. > Thus, it6505 have to wait a period can clear those expected error interrupts > caused by manual hardware reset in one interrupt handler calling to avoid interrupt looping. > > Signed-off-by: Kuro Chung <kuro.chung@ite.corp-partner.google.com> Generic, please drop 'UPSTREAM' part of the subject. Do not use such tags for sending patches upstream. > > --- > drivers/gpu/drm/bridge/ite-it6505.c | 181 +++++++++++++++++++--------- > 1 file changed, 124 insertions(+), 57 deletions(-) > > diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c > index b53da9bb65a16..4fd693b4b68ca 100644 > --- a/drivers/gpu/drm/bridge/ite-it6505.c > +++ b/drivers/gpu/drm/bridge/ite-it6505.c > @@ -802,8 +802,8 @@ static void it6505_int_mask_enable(struct it6505 *it6505) > BIT(INT_RECEIVE_HPD_IRQ) | BIT(INT_SCDT_CHANGE) | > BIT(INT_HDCP_FAIL) | BIT(INT_HDCP_DONE)); > > - it6505_write(it6505, INT_MASK_02, BIT(INT_AUX_CMD_FAIL) | > - BIT(INT_HDCP_KSV_CHECK) | BIT(INT_AUDIO_FIFO_ERROR)); > + it6505_write(it6505, INT_MASK_02, BIT(INT_HDCP_KSV_CHECK) | > + BIT(INT_AUDIO_FIFO_ERROR)); > > it6505_write(it6505, INT_MASK_03, BIT(INT_LINK_TRAIN_FAIL) | > BIT(INT_VID_FIFO_ERROR) | BIT(INT_IO_LATCH_FIFO_OVERFLOW)); > @@ -1317,10 +1317,17 @@ 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, 0x02); > + 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); > + Extra empty line. > } > > static void it6505_update_video_parameter(struct it6505 *it6505, > @@ -1861,16 +1868,29 @@ static void it6505_reset_hdcp(struct it6505 *it6505) > /* Disable CP_Desired */ > it6505_set_bits(it6505, REG_HDCP_CTRL1, HDCP_CP_ENABLE, 0x00); > it6505_set_bits(it6505, REG_RESET_CTRL, HDCP_RESET, HDCP_RESET); > + it6505_set_bits(it6505, REG_RESET_CTRL, HDCP_RESET, 0x00); > } > > static void it6505_start_hdcp(struct it6505 *it6505) > { > struct device *dev = &it6505->client->dev; > > - DRM_DEV_DEBUG_DRIVER(dev, "start"); > - it6505_reset_hdcp(it6505); > - queue_delayed_work(system_wq, &it6505->hdcp_work, > - msecs_to_jiffies(2400)); > + /* > + * If video not stable, no need turn on HDCP > + * After video stable > + * SCDT IRQ ->Link Training-> HDCP > + */ > + if (it6505_get_video_status(it6505)) { Won't this be easier: if (!it6505_get_video_status(it6505)) { DRM_DEV_DEBUG_DRIVER(dev, "Video not ready for HDCP"); return; } > + DRM_DEV_DEBUG_DRIVER(dev, "start"); > + it6505_reset_hdcp(it6505); > + > + queue_delayed_work(system_wq, &it6505->hdcp_work, > + msecs_to_jiffies(2400)); > + > + return; > + } > + > + DRM_DEV_DEBUG_DRIVER(dev, "Video not ready for HDCP"); > } > > static void it6505_stop_hdcp(struct it6505 *it6505) > @@ -2249,12 +2269,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); This seems like an unrelated change. Please split this to a separate commit. > } > > - it6505_dump(it6505); > } > > static void it6505_plugged_status_to_codec(struct it6505 *it6505) > @@ -2309,14 +2328,24 @@ static int it6505_process_hpd_irq(struct it6505 *it6505) > DRM_DEV_DEBUG_DRIVER(dev, "dp_irq_vector = 0x%02x", dp_irq_vector); > > if (dp_irq_vector & DP_CP_IRQ) { > - it6505_set_bits(it6505, REG_HDCP_TRIGGER, HDCP_TRIGGER_CPIRQ, > - HDCP_TRIGGER_CPIRQ); > + > + DRM_DEV_DEBUG_DRIVER(dev, "DP_CP_IRQ :hdcp_status = 0x%02x", it6505->hdcp_status); > + > + /* > + * Some TYPE-C devces trigger CP_IRQ when system resume > + * And IT6505 HDCP is in idle state > + * No Need trigger 6505 HDCP control. > + */ > + if (it6505->hdcp_status == HDCP_AUTH_GOING) > + it6505_set_bits(it6505, REG_HDCP_TRIGGER, HDCP_TRIGGER_CPIRQ, > + HDCP_TRIGGER_CPIRQ); > > bstatus = it6505_dpcd_read(it6505, DP_AUX_HDCP_BSTATUS); > if (bstatus < 0) > return bstatus; > > DRM_DEV_DEBUG_DRIVER(dev, "Bstatus = 0x%02x", bstatus); > + Separate commit too. > } > > ret = drm_dp_dpcd_read_link_status(&it6505->aux, link_status); > @@ -2328,9 +2357,24 @@ static int it6505_process_hpd_irq(struct it6505 *it6505) > DRM_DEV_DEBUG_DRIVER(dev, "link status = 0x%*ph", > (int)ARRAY_SIZE(link_status), link_status); > > - if (!drm_dp_channel_eq_ok(link_status, it6505->lane_count)) { > + /* > + * when recive HPD_IRQ signal from DP SINK > + * No need to process link status when DP link is not in ready state > + */ > + if ((it6505->link_state == LINK_OK) && > + (!drm_dp_channel_eq_ok(link_status, it6505->lane_count))) { Please indent to the opening bracket. > + > + if (it6505->hdcp_desired) > + it6505_stop_hdcp(it6505); > + > it6505->auto_train_retry = AUTO_TRAIN_RETRY; > - it6505_video_reset(it6505); > + > + /* > + * Link symble lost , need restart trainig > + * if no video, wait for video SCDT IRQ > + */ > + if (it6505_get_video_status(it6505)) > + schedule_work(&it6505->link_works); > } > > return 0; > @@ -2408,21 +2452,6 @@ static void it6505_irq_hpd_irq(struct it6505 *it6505) > DRM_DEV_DEBUG_DRIVER(dev, "process hpd_irq fail!"); > } > > -static void it6505_irq_scdt(struct it6505 *it6505) > -{ > - struct device *dev = &it6505->client->dev; > - bool data; > - > - data = it6505_get_video_status(it6505); > - DRM_DEV_DEBUG_DRIVER(dev, "video stable change interrupt, %s", > - data ? "stable" : "unstable"); > - it6505_calc_video_info(it6505); > - it6505_link_reset_step_train(it6505); > - > - if (data) > - schedule_work(&it6505->link_works); > -} > - It looks now that you have a separate set of changes here, realted to IRQ processing. Please split HDCP fixes and the IRQ-related changes. > static void it6505_irq_hdcp_done(struct it6505 *it6505) > { > struct device *dev = &it6505->client->dev; > @@ -2442,13 +2471,6 @@ static void it6505_irq_hdcp_fail(struct it6505 *it6505) > it6505_start_hdcp(it6505); > } > > -static void it6505_irq_aux_cmd_fail(struct it6505 *it6505) > -{ > - struct device *dev = &it6505->client->dev; > - > - DRM_DEV_DEBUG_DRIVER(dev, "AUX PC Request Fail Interrupt"); > -} > - > static void it6505_irq_hdcp_ksv_check(struct it6505 *it6505) > { > struct device *dev = &it6505->client->dev; > @@ -2475,31 +2497,69 @@ 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) Any reason for not using the existing test_bit() ? > { > - struct device *dev = &it6505->client->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_scdt(struct it6505 *it6505) Please refrain from moving the functions in the same commit as changing them. It is hard to check what happened with it6505_irq_scdt(). > { > struct device *dev = &it6505->client->dev; > + bool data; > > - 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); > + data = it6505_get_video_status(it6505); > + DRM_DEV_DEBUG_DRIVER(dev, "video stable change interrupt, %s", data ? "stable" : "unstable"); > + > + it6505_calc_video_info(it6505); > + it6505_link_reset_step_train(it6505); > + > + if (data) > + schedule_work(&it6505->link_works); > } > > -static bool it6505_test_bit(unsigned int bit, const unsigned int *addr) > + > +static void it6505_irq_video_handler(struct it6505 *it6505, const int *int_status) > { > - return 1 & (addr[bit / BITS_PER_BYTE] >> (bit % BITS_PER_BYTE)); > + struct device *dev = &it6505->client->dev; > + int reg_0d, reg_int03; > + > + /* > + * When video SCDT change with video not stable, > + * Or video FIFO error, need video reset > + */ > + > + 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) > @@ -2512,15 +2572,11 @@ 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,10 +2602,13 @@ static irqreturn_t it6505_int_threaded_handler(int unused, void *data) > irq_vec[0].handler(it6505); > > if (it6505->hpd_state) { > + > for (i = 1; i < ARRAY_SIZE(irq_vec); i++) { > 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); > @@ -3072,9 +3131,17 @@ static void it6505_bridge_atomic_disable(struct drm_bridge *bridge, > DRM_DEV_DEBUG_DRIVER(dev, "start"); > > if (it6505->powered) { > - it6505_drm_dp_link_set_power(&it6505->aux, &it6505->link, > - DP_SET_POWER_D3); > + > it6505_video_disable(it6505); > + > + /* > + * After Set link video mute, > + * wait 20ms before send D3 to DP sink > + */ > + usleep_range(20000, 25000); Separate commit. > + > + it6505_drm_dp_link_set_power(&it6505->aux, &it6505->link, > + DP_SET_POWER_D3); > } > } > > -- > 2.25.1 >
diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index b53da9bb65a16..4fd693b4b68ca 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -802,8 +802,8 @@ static void it6505_int_mask_enable(struct it6505 *it6505) BIT(INT_RECEIVE_HPD_IRQ) | BIT(INT_SCDT_CHANGE) | BIT(INT_HDCP_FAIL) | BIT(INT_HDCP_DONE)); - it6505_write(it6505, INT_MASK_02, BIT(INT_AUX_CMD_FAIL) | - BIT(INT_HDCP_KSV_CHECK) | BIT(INT_AUDIO_FIFO_ERROR)); + it6505_write(it6505, INT_MASK_02, BIT(INT_HDCP_KSV_CHECK) | + BIT(INT_AUDIO_FIFO_ERROR)); it6505_write(it6505, INT_MASK_03, BIT(INT_LINK_TRAIN_FAIL) | BIT(INT_VID_FIFO_ERROR) | BIT(INT_IO_LATCH_FIFO_OVERFLOW)); @@ -1317,10 +1317,17 @@ 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, 0x02); + 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); + } static void it6505_update_video_parameter(struct it6505 *it6505, @@ -1861,16 +1868,29 @@ static void it6505_reset_hdcp(struct it6505 *it6505) /* Disable CP_Desired */ it6505_set_bits(it6505, REG_HDCP_CTRL1, HDCP_CP_ENABLE, 0x00); it6505_set_bits(it6505, REG_RESET_CTRL, HDCP_RESET, HDCP_RESET); + it6505_set_bits(it6505, REG_RESET_CTRL, HDCP_RESET, 0x00); } static void it6505_start_hdcp(struct it6505 *it6505) { struct device *dev = &it6505->client->dev; - DRM_DEV_DEBUG_DRIVER(dev, "start"); - it6505_reset_hdcp(it6505); - queue_delayed_work(system_wq, &it6505->hdcp_work, - msecs_to_jiffies(2400)); + /* + * If video not stable, no need turn on HDCP + * After video stable + * SCDT IRQ ->Link Training-> HDCP + */ + if (it6505_get_video_status(it6505)) { + DRM_DEV_DEBUG_DRIVER(dev, "start"); + it6505_reset_hdcp(it6505); + + queue_delayed_work(system_wq, &it6505->hdcp_work, + msecs_to_jiffies(2400)); + + return; + } + + DRM_DEV_DEBUG_DRIVER(dev, "Video not ready for HDCP"); } static void it6505_stop_hdcp(struct it6505 *it6505) @@ -2249,12 +2269,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) @@ -2309,14 +2328,24 @@ static int it6505_process_hpd_irq(struct it6505 *it6505) DRM_DEV_DEBUG_DRIVER(dev, "dp_irq_vector = 0x%02x", dp_irq_vector); if (dp_irq_vector & DP_CP_IRQ) { - it6505_set_bits(it6505, REG_HDCP_TRIGGER, HDCP_TRIGGER_CPIRQ, - HDCP_TRIGGER_CPIRQ); + + DRM_DEV_DEBUG_DRIVER(dev, "DP_CP_IRQ :hdcp_status = 0x%02x", it6505->hdcp_status); + + /* + * Some TYPE-C devces trigger CP_IRQ when system resume + * And IT6505 HDCP is in idle state + * No Need trigger 6505 HDCP control. + */ + if (it6505->hdcp_status == HDCP_AUTH_GOING) + it6505_set_bits(it6505, REG_HDCP_TRIGGER, HDCP_TRIGGER_CPIRQ, + HDCP_TRIGGER_CPIRQ); bstatus = it6505_dpcd_read(it6505, DP_AUX_HDCP_BSTATUS); if (bstatus < 0) return bstatus; DRM_DEV_DEBUG_DRIVER(dev, "Bstatus = 0x%02x", bstatus); + } ret = drm_dp_dpcd_read_link_status(&it6505->aux, link_status); @@ -2328,9 +2357,24 @@ static int it6505_process_hpd_irq(struct it6505 *it6505) DRM_DEV_DEBUG_DRIVER(dev, "link status = 0x%*ph", (int)ARRAY_SIZE(link_status), link_status); - if (!drm_dp_channel_eq_ok(link_status, it6505->lane_count)) { + /* + * when recive HPD_IRQ signal from DP SINK + * No need to process link status when DP link is not in ready state + */ + if ((it6505->link_state == LINK_OK) && + (!drm_dp_channel_eq_ok(link_status, it6505->lane_count))) { + + if (it6505->hdcp_desired) + it6505_stop_hdcp(it6505); + it6505->auto_train_retry = AUTO_TRAIN_RETRY; - it6505_video_reset(it6505); + + /* + * Link symble lost , need restart trainig + * if no video, wait for video SCDT IRQ + */ + if (it6505_get_video_status(it6505)) + schedule_work(&it6505->link_works); } return 0; @@ -2408,21 +2452,6 @@ static void it6505_irq_hpd_irq(struct it6505 *it6505) DRM_DEV_DEBUG_DRIVER(dev, "process hpd_irq fail!"); } -static void it6505_irq_scdt(struct it6505 *it6505) -{ - struct device *dev = &it6505->client->dev; - bool data; - - data = it6505_get_video_status(it6505); - DRM_DEV_DEBUG_DRIVER(dev, "video stable change interrupt, %s", - data ? "stable" : "unstable"); - it6505_calc_video_info(it6505); - it6505_link_reset_step_train(it6505); - - if (data) - schedule_work(&it6505->link_works); -} - static void it6505_irq_hdcp_done(struct it6505 *it6505) { struct device *dev = &it6505->client->dev; @@ -2442,13 +2471,6 @@ static void it6505_irq_hdcp_fail(struct it6505 *it6505) it6505_start_hdcp(it6505); } -static void it6505_irq_aux_cmd_fail(struct it6505 *it6505) -{ - struct device *dev = &it6505->client->dev; - - DRM_DEV_DEBUG_DRIVER(dev, "AUX PC Request Fail Interrupt"); -} - static void it6505_irq_hdcp_ksv_check(struct it6505 *it6505) { struct device *dev = &it6505->client->dev; @@ -2475,31 +2497,69 @@ 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->client->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_scdt(struct it6505 *it6505) { struct device *dev = &it6505->client->dev; + bool data; - 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); + data = it6505_get_video_status(it6505); + DRM_DEV_DEBUG_DRIVER(dev, "video stable change interrupt, %s", data ? "stable" : "unstable"); + + it6505_calc_video_info(it6505); + it6505_link_reset_step_train(it6505); + + if (data) + schedule_work(&it6505->link_works); } -static bool it6505_test_bit(unsigned int bit, const unsigned int *addr) + +static void it6505_irq_video_handler(struct it6505 *it6505, const int *int_status) { - return 1 & (addr[bit / BITS_PER_BYTE] >> (bit % BITS_PER_BYTE)); + struct device *dev = &it6505->client->dev; + int reg_0d, reg_int03; + + /* + * When video SCDT change with video not stable, + * Or video FIFO error, need video reset + */ + + 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) @@ -2512,15 +2572,11 @@ 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,10 +2602,13 @@ static irqreturn_t it6505_int_threaded_handler(int unused, void *data) irq_vec[0].handler(it6505); if (it6505->hpd_state) { + for (i = 1; i < ARRAY_SIZE(irq_vec); i++) { 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); @@ -3072,9 +3131,17 @@ static void it6505_bridge_atomic_disable(struct drm_bridge *bridge, DRM_DEV_DEBUG_DRIVER(dev, "start"); if (it6505->powered) { - it6505_drm_dp_link_set_power(&it6505->aux, &it6505->link, - DP_SET_POWER_D3); + it6505_video_disable(it6505); + + /* + * After Set link video mute, + * wait 20ms before send D3 to DP sink + */ + usleep_range(20000, 25000); + + it6505_drm_dp_link_set_power(&it6505->aux, &it6505->link, + DP_SET_POWER_D3); } }