Skip to content

Commit 8df1ddb

Browse files
committed
drm/dp: Don't attempt AUX transfers when eDP panels are not powered
If an eDP panel is not powered on then any attempts to talk to it over the DP AUX channel will timeout. Unfortunately these attempts may be quite slow. Userspace can initiate these attempts either via a /dev/drm_dp_auxN device or via the created i2c device. Making the DP AUX drivers timeout faster is a difficult proposition. In theory we could just poll the panel's HPD line in the AUX transfer function and immediately return an error there. However, this is easier said than done. For one thing, there's no hard requirement to hook the HPD line up for eDP panels and it's OK to just delay a fixed amount. For another thing, the HPD line may not be fast to probe. On parade-ps8640 we need to wait for the bridge chip's firmware to boot before we can get the HPD line and this is a slow process. The fact that the transfers are taking so long to timeout is causing real problems. The open source fwupd daemon sometimes scans DP busses looking for devices whose firmware need updating. If it happens to scan while a panel is turned off this scan can take a long time. The fwupd daemon could try to be smarter and only scan when eDP panels are turned on, but we can also improve the behavior in the kernel. Let's let eDP panels drivers specify that a panel is turned off and then modify the common AUX transfer code not to attempt a transfer in this case. Tested-by: Steev Klimaszewski <steev@kali.org> Reviewed-by: Hsin-Yi Wang <hsinyi@chromium.org> Tested-by: Eizan Miyamoto <eizan@chromium.org> Acked-by: Neil Armstrong <neil.armstrong@linaro.org> Signed-off-by: Douglas Anderson <dianders@chromium.org> Link: https://patchwork.freedesktop.org/patch/msgid/20240202141109.1.I24277520ac754ea538c9b14578edc94e1df11b48@changeid
1 parent 594332e commit 8df1ddb

File tree

4 files changed

+46
-0
lines changed

4 files changed

+46
-0
lines changed

drivers/gpu/drm/display/drm_dp_helper.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,15 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
532532

533533
mutex_lock(&aux->hw_mutex);
534534

535+
/*
536+
* If the device attached to the aux bus is powered down then there's
537+
* no reason to attempt a transfer. Error out immediately.
538+
*/
539+
if (aux->powered_down) {
540+
ret = -EBUSY;
541+
goto unlock;
542+
}
543+
535544
/*
536545
* The specification doesn't give any recommendation on how often to
537546
* retry native transactions. We used to retry 7 times like for
@@ -599,6 +608,29 @@ int drm_dp_dpcd_probe(struct drm_dp_aux *aux, unsigned int offset)
599608
}
600609
EXPORT_SYMBOL(drm_dp_dpcd_probe);
601610

611+
/**
612+
* drm_dp_dpcd_set_powered() - Set whether the DP device is powered
613+
* @aux: DisplayPort AUX channel; for convenience it's OK to pass NULL here
614+
* and the function will be a no-op.
615+
* @powered: true if powered; false if not
616+
*
617+
* If the endpoint device on the DP AUX bus is known to be powered down
618+
* then this function can be called to make future transfers fail immediately
619+
* instead of needing to time out.
620+
*
621+
* If this function is never called then a device defaults to being powered.
622+
*/
623+
void drm_dp_dpcd_set_powered(struct drm_dp_aux *aux, bool powered)
624+
{
625+
if (!aux)
626+
return;
627+
628+
mutex_lock(&aux->hw_mutex);
629+
aux->powered_down = !powered;
630+
mutex_unlock(&aux->hw_mutex);
631+
}
632+
EXPORT_SYMBOL(drm_dp_dpcd_set_powered);
633+
602634
/**
603635
* drm_dp_dpcd_read() - read a series of bytes from the DPCD
604636
* @aux: DisplayPort AUX channel (SST or MST)
@@ -1858,6 +1890,9 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
18581890
struct drm_dp_aux_msg msg;
18591891
int err = 0;
18601892

1893+
if (aux->powered_down)
1894+
return -EBUSY;
1895+
18611896
dp_aux_i2c_transfer_size = clamp(dp_aux_i2c_transfer_size, 1, DP_AUX_MAX_PAYLOAD_BYTES);
18621897

18631898
memset(&msg, 0, sizeof(msg));

drivers/gpu/drm/panel/panel-edp.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ static int panel_edp_suspend(struct device *dev)
413413
{
414414
struct panel_edp *p = dev_get_drvdata(dev);
415415

416+
drm_dp_dpcd_set_powered(p->aux, false);
416417
gpiod_set_value_cansleep(p->enable_gpio, 0);
417418
regulator_disable(p->supply);
418419
p->unprepared_time = ktime_get_boottime();
@@ -469,6 +470,7 @@ static int panel_edp_prepare_once(struct panel_edp *p)
469470
}
470471

471472
gpiod_set_value_cansleep(p->enable_gpio, 1);
473+
drm_dp_dpcd_set_powered(p->aux, true);
472474

473475
p->powered_on_time = ktime_get_boottime();
474476

@@ -507,6 +509,7 @@ static int panel_edp_prepare_once(struct panel_edp *p)
507509
return 0;
508510

509511
error:
512+
drm_dp_dpcd_set_powered(p->aux, false);
510513
gpiod_set_value_cansleep(p->enable_gpio, 0);
511514
regulator_disable(p->supply);
512515
p->unprepared_time = ktime_get_boottime();

drivers/gpu/drm/panel/panel-samsung-atna33xc20.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ static int atana33xc20_suspend(struct device *dev)
7272
if (p->el3_was_on)
7373
atana33xc20_wait(p->el_on3_off_time, 150);
7474

75+
drm_dp_dpcd_set_powered(p->aux, false);
7576
ret = regulator_disable(p->supply);
7677
if (ret)
7778
return ret;
@@ -93,6 +94,7 @@ static int atana33xc20_resume(struct device *dev)
9394
ret = regulator_enable(p->supply);
9495
if (ret)
9596
return ret;
97+
drm_dp_dpcd_set_powered(p->aux, true);
9698
p->powered_on_time = ktime_get_boottime();
9799

98100
if (p->no_hpd) {

include/drm/display/drm_dp_helper.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,9 +464,15 @@ struct drm_dp_aux {
464464
* @is_remote: Is this AUX CH actually using sideband messaging.
465465
*/
466466
bool is_remote;
467+
468+
/**
469+
* @powered_down: If true then the remote endpoint is powered down.
470+
*/
471+
bool powered_down;
467472
};
468473

469474
int drm_dp_dpcd_probe(struct drm_dp_aux *aux, unsigned int offset);
475+
void drm_dp_dpcd_set_powered(struct drm_dp_aux *aux, bool powered);
470476
ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,
471477
void *buffer, size_t size);
472478
ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,

0 commit comments

Comments
 (0)