Skip to content

Commit

Permalink
Merge branch 'linux-4.19' of git://github.com/skeggsb/linux into drm-…
Browse files Browse the repository at this point in the history
…fixes

A bunch of fixes for MST/runpm problems and races, as well as fixes
for issues that prevent more recent laptops from booting.

Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Ben Skeggs <bskeggs@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/CABDvA==GF63dy8a9j611=-0x8G6FRu7uC-ZQypsLO_hqV4OAcA@mail.gmail.com
  • Loading branch information
Dave Airlie committed Sep 11, 2018
2 parents 11da3a7 + 53b0cc4 commit 2887e5c
Show file tree
Hide file tree
Showing 16 changed files with 297 additions and 114 deletions.
67 changes: 49 additions & 18 deletions drivers/gpu/drm/nouveau/dispnv50/disp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1123,17 +1123,21 @@ nv50_mstm_enable(struct nv50_mstm *mstm, u8 dpcd, int state)
int ret;

if (dpcd >= 0x12) {
ret = drm_dp_dpcd_readb(mstm->mgr.aux, DP_MSTM_CTRL, &dpcd);
/* Even if we're enabling MST, start with disabling the
* branching unit to clear any sink-side MST topology state
* that wasn't set by us
*/
ret = drm_dp_dpcd_writeb(mstm->mgr.aux, DP_MSTM_CTRL, 0);
if (ret < 0)
return ret;

dpcd &= ~DP_MST_EN;
if (state)
dpcd |= DP_MST_EN;

ret = drm_dp_dpcd_writeb(mstm->mgr.aux, DP_MSTM_CTRL, dpcd);
if (ret < 0)
return ret;
if (state) {
/* Now, start initializing */
ret = drm_dp_dpcd_writeb(mstm->mgr.aux, DP_MSTM_CTRL,
DP_MST_EN);
if (ret < 0)
return ret;
}
}

return nvif_mthd(disp, 0, &args, sizeof(args));
Expand All @@ -1142,31 +1146,58 @@ nv50_mstm_enable(struct nv50_mstm *mstm, u8 dpcd, int state)
int
nv50_mstm_detect(struct nv50_mstm *mstm, u8 dpcd[8], int allow)
{
int ret, state = 0;
struct drm_dp_aux *aux;
int ret;
bool old_state, new_state;
u8 mstm_ctrl;

if (!mstm)
return 0;

if (dpcd[0] >= 0x12) {
ret = drm_dp_dpcd_readb(mstm->mgr.aux, DP_MSTM_CAP, &dpcd[1]);
mutex_lock(&mstm->mgr.lock);

old_state = mstm->mgr.mst_state;
new_state = old_state;
aux = mstm->mgr.aux;

if (old_state) {
/* Just check that the MST hub is still as we expect it */
ret = drm_dp_dpcd_readb(aux, DP_MSTM_CTRL, &mstm_ctrl);
if (ret < 0 || !(mstm_ctrl & DP_MST_EN)) {
DRM_DEBUG_KMS("Hub gone, disabling MST topology\n");
new_state = false;
}
} else if (dpcd[0] >= 0x12) {
ret = drm_dp_dpcd_readb(aux, DP_MSTM_CAP, &dpcd[1]);
if (ret < 0)
return ret;
goto probe_error;

if (!(dpcd[1] & DP_MST_CAP))
dpcd[0] = 0x11;
else
state = allow;
new_state = allow;
}

if (new_state == old_state) {
mutex_unlock(&mstm->mgr.lock);
return new_state;
}

ret = nv50_mstm_enable(mstm, dpcd[0], state);
ret = nv50_mstm_enable(mstm, dpcd[0], new_state);
if (ret)
return ret;
goto probe_error;

mutex_unlock(&mstm->mgr.lock);

ret = drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, state);
ret = drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, new_state);
if (ret)
return nv50_mstm_enable(mstm, dpcd[0], 0);

return mstm->mgr.mst_state;
return new_state;

probe_error:
mutex_unlock(&mstm->mgr.lock);
return ret;
}

static void
Expand Down Expand Up @@ -2074,7 +2105,7 @@ nv50_disp_atomic_state_alloc(struct drm_device *dev)
static const struct drm_mode_config_funcs
nv50_disp_func = {
.fb_create = nouveau_user_framebuffer_create,
.output_poll_changed = drm_fb_helper_output_poll_changed,
.output_poll_changed = nouveau_fbcon_output_poll_changed,
.atomic_check = nv50_disp_atomic_check,
.atomic_commit = nv50_disp_atomic_commit,
.atomic_state_alloc = nv50_disp_atomic_state_alloc,
Expand Down
110 changes: 60 additions & 50 deletions drivers/gpu/drm/nouveau/nouveau_connector.c
Original file line number Diff line number Diff line change
Expand Up @@ -409,59 +409,45 @@ static struct nouveau_encoder *
nouveau_connector_ddc_detect(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_drm *drm = nouveau_drm(dev);
struct nvkm_gpio *gpio = nvxx_gpio(&drm->client.device);
struct nouveau_encoder *nv_encoder = NULL;
struct nouveau_encoder *nv_encoder = NULL, *found = NULL;
struct drm_encoder *encoder;
int i, panel = -ENODEV;

/* eDP panels need powering on by us (if the VBIOS doesn't default it
* to on) before doing any AUX channel transactions. LVDS panel power
* is handled by the SOR itself, and not required for LVDS DDC.
*/
if (nv_connector->type == DCB_CONNECTOR_eDP) {
panel = nvkm_gpio_get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff);
if (panel == 0) {
nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1);
msleep(300);
}
}
int i, ret;
bool switcheroo_ddc = false;

drm_connector_for_each_possible_encoder(connector, encoder, i) {
nv_encoder = nouveau_encoder(encoder);

if (nv_encoder->dcb->type == DCB_OUTPUT_DP) {
int ret = nouveau_dp_detect(nv_encoder);
switch (nv_encoder->dcb->type) {
case DCB_OUTPUT_DP:
ret = nouveau_dp_detect(nv_encoder);
if (ret == NOUVEAU_DP_MST)
return NULL;
if (ret == NOUVEAU_DP_SST)
break;
} else
if ((vga_switcheroo_handler_flags() &
VGA_SWITCHEROO_CAN_SWITCH_DDC) &&
nv_encoder->dcb->type == DCB_OUTPUT_LVDS &&
nv_encoder->i2c) {
int ret;
vga_switcheroo_lock_ddc(dev->pdev);
ret = nvkm_probe_i2c(nv_encoder->i2c, 0x50);
vga_switcheroo_unlock_ddc(dev->pdev);
if (ret)
else if (ret == NOUVEAU_DP_SST)
found = nv_encoder;

break;
case DCB_OUTPUT_LVDS:
switcheroo_ddc = !!(vga_switcheroo_handler_flags() &
VGA_SWITCHEROO_CAN_SWITCH_DDC);
/* fall-through */
default:
if (!nv_encoder->i2c)
break;
} else
if (nv_encoder->i2c) {

if (switcheroo_ddc)
vga_switcheroo_lock_ddc(dev->pdev);
if (nvkm_probe_i2c(nv_encoder->i2c, 0x50))
break;
found = nv_encoder;
if (switcheroo_ddc)
vga_switcheroo_unlock_ddc(dev->pdev);

break;
}
if (found)
break;
}

/* eDP panel not detected, restore panel power GPIO to previous
* state to avoid confusing the SOR for other output types.
*/
if (!nv_encoder && panel == 0)
nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, panel);

return nv_encoder;
return found;
}

static struct nouveau_encoder *
Expand Down Expand Up @@ -555,12 +541,16 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
nv_connector->edid = NULL;
}

/* Outputs are only polled while runtime active, so acquiring a
* runtime PM ref here is unnecessary (and would deadlock upon
* runtime suspend because it waits for polling to finish).
/* Outputs are only polled while runtime active, so resuming the
* device here is unnecessary (and would deadlock upon runtime suspend
* because it waits for polling to finish). We do however, want to
* prevent the autosuspend timer from elapsing during this operation
* if possible.
*/
if (!drm_kms_helper_is_poll_worker()) {
ret = pm_runtime_get_sync(connector->dev->dev);
if (drm_kms_helper_is_poll_worker()) {
pm_runtime_get_noresume(dev->dev);
} else {
ret = pm_runtime_get_sync(dev->dev);
if (ret < 0 && ret != -EACCES)
return conn_status;
}
Expand Down Expand Up @@ -638,10 +628,8 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)

out:

if (!drm_kms_helper_is_poll_worker()) {
pm_runtime_mark_last_busy(connector->dev->dev);
pm_runtime_put_autosuspend(connector->dev->dev);
}
pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);

return conn_status;
}
Expand Down Expand Up @@ -1105,6 +1093,26 @@ nouveau_connector_hotplug(struct nvif_notify *notify)
const struct nvif_notify_conn_rep_v0 *rep = notify->data;
const char *name = connector->name;
struct nouveau_encoder *nv_encoder;
int ret;

ret = pm_runtime_get(drm->dev->dev);
if (ret == 0) {
/* We can't block here if there's a pending PM request
* running, as we'll deadlock nouveau_display_fini() when it
* calls nvif_put() on our nvif_notify struct. So, simply
* defer the hotplug event until the device finishes resuming
*/
NV_DEBUG(drm, "Deferring HPD on %s until runtime resume\n",
name);
schedule_work(&drm->hpd_work);

pm_runtime_put_noidle(drm->dev->dev);
return NVIF_NOTIFY_KEEP;
} else if (ret != 1 && ret != -EACCES) {
NV_WARN(drm, "HPD on %s dropped due to RPM failure: %d\n",
name, ret);
return NVIF_NOTIFY_DROP;
}

if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) {
NV_DEBUG(drm, "service %s\n", name);
Expand All @@ -1122,6 +1130,8 @@ nouveau_connector_hotplug(struct nvif_notify *notify)
drm_helper_hpd_irq_event(connector->dev);
}

pm_runtime_mark_last_busy(drm->dev->dev);
pm_runtime_put_autosuspend(drm->dev->dev);
return NVIF_NOTIFY_KEEP;
}

Expand Down
44 changes: 32 additions & 12 deletions drivers/gpu/drm/nouveau/nouveau_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ nouveau_user_framebuffer_create(struct drm_device *dev,

static const struct drm_mode_config_funcs nouveau_mode_config_funcs = {
.fb_create = nouveau_user_framebuffer_create,
.output_poll_changed = drm_fb_helper_output_poll_changed,
.output_poll_changed = nouveau_fbcon_output_poll_changed,
};


Expand Down Expand Up @@ -355,8 +355,6 @@ nouveau_display_hpd_work(struct work_struct *work)
pm_runtime_get_sync(drm->dev->dev);

drm_helper_hpd_irq_event(drm->dev);
/* enable polling for external displays */
drm_kms_helper_poll_enable(drm->dev);

pm_runtime_mark_last_busy(drm->dev->dev);
pm_runtime_put_sync(drm->dev->dev);
Expand All @@ -379,15 +377,29 @@ nouveau_display_acpi_ntfy(struct notifier_block *nb, unsigned long val,
{
struct nouveau_drm *drm = container_of(nb, typeof(*drm), acpi_nb);
struct acpi_bus_event *info = data;
int ret;

if (!strcmp(info->device_class, ACPI_VIDEO_CLASS)) {
if (info->type == ACPI_VIDEO_NOTIFY_PROBE) {
/*
* This may be the only indication we receive of a
* connector hotplug on a runtime suspended GPU,
* schedule hpd_work to check.
*/
schedule_work(&drm->hpd_work);
ret = pm_runtime_get(drm->dev->dev);
if (ret == 1 || ret == -EACCES) {
/* If the GPU is already awake, or in a state
* where we can't wake it up, it can handle
* it's own hotplug events.
*/
pm_runtime_put_autosuspend(drm->dev->dev);
} else if (ret == 0) {
/* This may be the only indication we receive
* of a connector hotplug on a runtime
* suspended GPU, schedule hpd_work to check.
*/
NV_DEBUG(drm, "ACPI requested connector reprobe\n");
schedule_work(&drm->hpd_work);
pm_runtime_put_noidle(drm->dev->dev);
} else {
NV_WARN(drm, "Dropped ACPI reprobe event due to RPM error: %d\n",
ret);
}

/* acpi-video should not generate keypresses for this */
return NOTIFY_BAD;
Expand All @@ -411,6 +423,11 @@ nouveau_display_init(struct drm_device *dev)
if (ret)
return ret;

/* enable connector detection and polling for connectors without HPD
* support
*/
drm_kms_helper_poll_enable(dev);

/* enable hotplug interrupts */
drm_connector_list_iter_begin(dev, &conn_iter);
nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) {
Expand All @@ -425,7 +442,7 @@ nouveau_display_init(struct drm_device *dev)
}

void
nouveau_display_fini(struct drm_device *dev, bool suspend)
nouveau_display_fini(struct drm_device *dev, bool suspend, bool runtime)
{
struct nouveau_display *disp = nouveau_display(dev);
struct nouveau_drm *drm = nouveau_drm(dev);
Expand All @@ -450,6 +467,9 @@ nouveau_display_fini(struct drm_device *dev, bool suspend)
}
drm_connector_list_iter_end(&conn_iter);

if (!runtime)
cancel_work_sync(&drm->hpd_work);

drm_kms_helper_poll_disable(dev);
disp->fini(dev);
}
Expand Down Expand Up @@ -618,11 +638,11 @@ nouveau_display_suspend(struct drm_device *dev, bool runtime)
}
}

nouveau_display_fini(dev, true);
nouveau_display_fini(dev, true, runtime);
return 0;
}

nouveau_display_fini(dev, true);
nouveau_display_fini(dev, true, runtime);

list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_framebuffer *nouveau_fb;
Expand Down
2 changes: 1 addition & 1 deletion drivers/gpu/drm/nouveau/nouveau_display.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ nouveau_display(struct drm_device *dev)
int nouveau_display_create(struct drm_device *dev);
void nouveau_display_destroy(struct drm_device *dev);
int nouveau_display_init(struct drm_device *dev);
void nouveau_display_fini(struct drm_device *dev, bool suspend);
void nouveau_display_fini(struct drm_device *dev, bool suspend, bool runtime);
int nouveau_display_suspend(struct drm_device *dev, bool runtime);
void nouveau_display_resume(struct drm_device *dev, bool runtime);
int nouveau_display_vblank_enable(struct drm_device *, unsigned int);
Expand Down
Loading

0 comments on commit 2887e5c

Please sign in to comment.