diff options
author | Dave Airlie <airlied@redhat.com> | 2018-09-11 02:54:40 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2018-09-11 02:54:46 -0400 |
commit | 2887e5ce15ddaa2f9a19e66f7462bbf0fe6867e0 (patch) | |
tree | c0476d05aa3bfeb0db4cfc36e0ebd5ea54394dba | |
parent | 11da3a7f84f19c26da6f86af878298694ede0804 (diff) | |
parent | 53b0cc46f27cfc2cadca609b503a7d92b5185a47 (diff) |
Merge branch 'linux-4.19' of git://github.com/skeggsb/linux into drm-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
-rw-r--r-- | drivers/gpu/drm/nouveau/dispnv50/disp.c | 67 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_connector.c | 110 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_display.c | 44 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_display.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drm.c | 21 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_fbcon.c | 57 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_fbcon.h | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_vga.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c | 54 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c | 18 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm200.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c | 2 |
16 files changed, 297 insertions, 114 deletions
diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 8412119bd940..5691dfa1db6f 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c | |||
@@ -1123,17 +1123,21 @@ nv50_mstm_enable(struct nv50_mstm *mstm, u8 dpcd, int state) | |||
1123 | int ret; | 1123 | int ret; |
1124 | 1124 | ||
1125 | if (dpcd >= 0x12) { | 1125 | if (dpcd >= 0x12) { |
1126 | ret = drm_dp_dpcd_readb(mstm->mgr.aux, DP_MSTM_CTRL, &dpcd); | 1126 | /* Even if we're enabling MST, start with disabling the |
1127 | * branching unit to clear any sink-side MST topology state | ||
1128 | * that wasn't set by us | ||
1129 | */ | ||
1130 | ret = drm_dp_dpcd_writeb(mstm->mgr.aux, DP_MSTM_CTRL, 0); | ||
1127 | if (ret < 0) | 1131 | if (ret < 0) |
1128 | return ret; | 1132 | return ret; |
1129 | 1133 | ||
1130 | dpcd &= ~DP_MST_EN; | 1134 | if (state) { |
1131 | if (state) | 1135 | /* Now, start initializing */ |
1132 | dpcd |= DP_MST_EN; | 1136 | ret = drm_dp_dpcd_writeb(mstm->mgr.aux, DP_MSTM_CTRL, |
1133 | 1137 | DP_MST_EN); | |
1134 | ret = drm_dp_dpcd_writeb(mstm->mgr.aux, DP_MSTM_CTRL, dpcd); | 1138 | if (ret < 0) |
1135 | if (ret < 0) | 1139 | return ret; |
1136 | return ret; | 1140 | } |
1137 | } | 1141 | } |
1138 | 1142 | ||
1139 | return nvif_mthd(disp, 0, &args, sizeof(args)); | 1143 | return nvif_mthd(disp, 0, &args, sizeof(args)); |
@@ -1142,31 +1146,58 @@ nv50_mstm_enable(struct nv50_mstm *mstm, u8 dpcd, int state) | |||
1142 | int | 1146 | int |
1143 | nv50_mstm_detect(struct nv50_mstm *mstm, u8 dpcd[8], int allow) | 1147 | nv50_mstm_detect(struct nv50_mstm *mstm, u8 dpcd[8], int allow) |
1144 | { | 1148 | { |
1145 | int ret, state = 0; | 1149 | struct drm_dp_aux *aux; |
1150 | int ret; | ||
1151 | bool old_state, new_state; | ||
1152 | u8 mstm_ctrl; | ||
1146 | 1153 | ||
1147 | if (!mstm) | 1154 | if (!mstm) |
1148 | return 0; | 1155 | return 0; |
1149 | 1156 | ||
1150 | if (dpcd[0] >= 0x12) { | 1157 | mutex_lock(&mstm->mgr.lock); |
1151 | ret = drm_dp_dpcd_readb(mstm->mgr.aux, DP_MSTM_CAP, &dpcd[1]); | 1158 | |
1159 | old_state = mstm->mgr.mst_state; | ||
1160 | new_state = old_state; | ||
1161 | aux = mstm->mgr.aux; | ||
1162 | |||
1163 | if (old_state) { | ||
1164 | /* Just check that the MST hub is still as we expect it */ | ||
1165 | ret = drm_dp_dpcd_readb(aux, DP_MSTM_CTRL, &mstm_ctrl); | ||
1166 | if (ret < 0 || !(mstm_ctrl & DP_MST_EN)) { | ||
1167 | DRM_DEBUG_KMS("Hub gone, disabling MST topology\n"); | ||
1168 | new_state = false; | ||
1169 | } | ||
1170 | } else if (dpcd[0] >= 0x12) { | ||
1171 | ret = drm_dp_dpcd_readb(aux, DP_MSTM_CAP, &dpcd[1]); | ||
1152 | if (ret < 0) | 1172 | if (ret < 0) |
1153 | return ret; | 1173 | goto probe_error; |
1154 | 1174 | ||
1155 | if (!(dpcd[1] & DP_MST_CAP)) | 1175 | if (!(dpcd[1] & DP_MST_CAP)) |
1156 | dpcd[0] = 0x11; | 1176 | dpcd[0] = 0x11; |
1157 | else | 1177 | else |
1158 | state = allow; | 1178 | new_state = allow; |
1179 | } | ||
1180 | |||
1181 | if (new_state == old_state) { | ||
1182 | mutex_unlock(&mstm->mgr.lock); | ||
1183 | return new_state; | ||
1159 | } | 1184 | } |
1160 | 1185 | ||
1161 | ret = nv50_mstm_enable(mstm, dpcd[0], state); | 1186 | ret = nv50_mstm_enable(mstm, dpcd[0], new_state); |
1162 | if (ret) | 1187 | if (ret) |
1163 | return ret; | 1188 | goto probe_error; |
1189 | |||
1190 | mutex_unlock(&mstm->mgr.lock); | ||
1164 | 1191 | ||
1165 | ret = drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, state); | 1192 | ret = drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, new_state); |
1166 | if (ret) | 1193 | if (ret) |
1167 | return nv50_mstm_enable(mstm, dpcd[0], 0); | 1194 | return nv50_mstm_enable(mstm, dpcd[0], 0); |
1168 | 1195 | ||
1169 | return mstm->mgr.mst_state; | 1196 | return new_state; |
1197 | |||
1198 | probe_error: | ||
1199 | mutex_unlock(&mstm->mgr.lock); | ||
1200 | return ret; | ||
1170 | } | 1201 | } |
1171 | 1202 | ||
1172 | static void | 1203 | static void |
@@ -2074,7 +2105,7 @@ nv50_disp_atomic_state_alloc(struct drm_device *dev) | |||
2074 | static const struct drm_mode_config_funcs | 2105 | static const struct drm_mode_config_funcs |
2075 | nv50_disp_func = { | 2106 | nv50_disp_func = { |
2076 | .fb_create = nouveau_user_framebuffer_create, | 2107 | .fb_create = nouveau_user_framebuffer_create, |
2077 | .output_poll_changed = drm_fb_helper_output_poll_changed, | 2108 | .output_poll_changed = nouveau_fbcon_output_poll_changed, |
2078 | .atomic_check = nv50_disp_atomic_check, | 2109 | .atomic_check = nv50_disp_atomic_check, |
2079 | .atomic_commit = nv50_disp_atomic_commit, | 2110 | .atomic_commit = nv50_disp_atomic_commit, |
2080 | .atomic_state_alloc = nv50_disp_atomic_state_alloc, | 2111 | .atomic_state_alloc = nv50_disp_atomic_state_alloc, |
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 51932c72334e..247f72cc4d10 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c | |||
@@ -409,59 +409,45 @@ static struct nouveau_encoder * | |||
409 | nouveau_connector_ddc_detect(struct drm_connector *connector) | 409 | nouveau_connector_ddc_detect(struct drm_connector *connector) |
410 | { | 410 | { |
411 | struct drm_device *dev = connector->dev; | 411 | struct drm_device *dev = connector->dev; |
412 | struct nouveau_connector *nv_connector = nouveau_connector(connector); | 412 | struct nouveau_encoder *nv_encoder = NULL, *found = NULL; |
413 | struct nouveau_drm *drm = nouveau_drm(dev); | ||
414 | struct nvkm_gpio *gpio = nvxx_gpio(&drm->client.device); | ||
415 | struct nouveau_encoder *nv_encoder = NULL; | ||
416 | struct drm_encoder *encoder; | 413 | struct drm_encoder *encoder; |
417 | int i, panel = -ENODEV; | 414 | int i, ret; |
418 | 415 | bool switcheroo_ddc = false; | |
419 | /* eDP panels need powering on by us (if the VBIOS doesn't default it | ||
420 | * to on) before doing any AUX channel transactions. LVDS panel power | ||
421 | * is handled by the SOR itself, and not required for LVDS DDC. | ||
422 | */ | ||
423 | if (nv_connector->type == DCB_CONNECTOR_eDP) { | ||
424 | panel = nvkm_gpio_get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff); | ||
425 | if (panel == 0) { | ||
426 | nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1); | ||
427 | msleep(300); | ||
428 | } | ||
429 | } | ||
430 | 416 | ||
431 | drm_connector_for_each_possible_encoder(connector, encoder, i) { | 417 | drm_connector_for_each_possible_encoder(connector, encoder, i) { |
432 | nv_encoder = nouveau_encoder(encoder); | 418 | nv_encoder = nouveau_encoder(encoder); |
433 | 419 | ||
434 | if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { | 420 | switch (nv_encoder->dcb->type) { |
435 | int ret = nouveau_dp_detect(nv_encoder); | 421 | case DCB_OUTPUT_DP: |
422 | ret = nouveau_dp_detect(nv_encoder); | ||
436 | if (ret == NOUVEAU_DP_MST) | 423 | if (ret == NOUVEAU_DP_MST) |
437 | return NULL; | 424 | return NULL; |
438 | if (ret == NOUVEAU_DP_SST) | 425 | else if (ret == NOUVEAU_DP_SST) |
439 | break; | 426 | found = nv_encoder; |
440 | } else | 427 | |
441 | if ((vga_switcheroo_handler_flags() & | 428 | break; |
442 | VGA_SWITCHEROO_CAN_SWITCH_DDC) && | 429 | case DCB_OUTPUT_LVDS: |
443 | nv_encoder->dcb->type == DCB_OUTPUT_LVDS && | 430 | switcheroo_ddc = !!(vga_switcheroo_handler_flags() & |
444 | nv_encoder->i2c) { | 431 | VGA_SWITCHEROO_CAN_SWITCH_DDC); |
445 | int ret; | 432 | /* fall-through */ |
446 | vga_switcheroo_lock_ddc(dev->pdev); | 433 | default: |
447 | ret = nvkm_probe_i2c(nv_encoder->i2c, 0x50); | 434 | if (!nv_encoder->i2c) |
448 | vga_switcheroo_unlock_ddc(dev->pdev); | ||
449 | if (ret) | ||
450 | break; | 435 | break; |
451 | } else | 436 | |
452 | if (nv_encoder->i2c) { | 437 | if (switcheroo_ddc) |
438 | vga_switcheroo_lock_ddc(dev->pdev); | ||
453 | if (nvkm_probe_i2c(nv_encoder->i2c, 0x50)) | 439 | if (nvkm_probe_i2c(nv_encoder->i2c, 0x50)) |
454 | break; | 440 | found = nv_encoder; |
441 | if (switcheroo_ddc) | ||
442 | vga_switcheroo_unlock_ddc(dev->pdev); | ||
443 | |||
444 | break; | ||
455 | } | 445 | } |
446 | if (found) | ||
447 | break; | ||
456 | } | 448 | } |
457 | 449 | ||
458 | /* eDP panel not detected, restore panel power GPIO to previous | 450 | return found; |
459 | * state to avoid confusing the SOR for other output types. | ||
460 | */ | ||
461 | if (!nv_encoder && panel == 0) | ||
462 | nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, panel); | ||
463 | |||
464 | return nv_encoder; | ||
465 | } | 451 | } |
466 | 452 | ||
467 | static struct nouveau_encoder * | 453 | static struct nouveau_encoder * |
@@ -555,12 +541,16 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) | |||
555 | nv_connector->edid = NULL; | 541 | nv_connector->edid = NULL; |
556 | } | 542 | } |
557 | 543 | ||
558 | /* Outputs are only polled while runtime active, so acquiring a | 544 | /* Outputs are only polled while runtime active, so resuming the |
559 | * runtime PM ref here is unnecessary (and would deadlock upon | 545 | * device here is unnecessary (and would deadlock upon runtime suspend |
560 | * runtime suspend because it waits for polling to finish). | 546 | * because it waits for polling to finish). We do however, want to |
547 | * prevent the autosuspend timer from elapsing during this operation | ||
548 | * if possible. | ||
561 | */ | 549 | */ |
562 | if (!drm_kms_helper_is_poll_worker()) { | 550 | if (drm_kms_helper_is_poll_worker()) { |
563 | ret = pm_runtime_get_sync(connector->dev->dev); | 551 | pm_runtime_get_noresume(dev->dev); |
552 | } else { | ||
553 | ret = pm_runtime_get_sync(dev->dev); | ||
564 | if (ret < 0 && ret != -EACCES) | 554 | if (ret < 0 && ret != -EACCES) |
565 | return conn_status; | 555 | return conn_status; |
566 | } | 556 | } |
@@ -638,10 +628,8 @@ detect_analog: | |||
638 | 628 | ||
639 | out: | 629 | out: |
640 | 630 | ||
641 | if (!drm_kms_helper_is_poll_worker()) { | 631 | pm_runtime_mark_last_busy(dev->dev); |
642 | pm_runtime_mark_last_busy(connector->dev->dev); | 632 | pm_runtime_put_autosuspend(dev->dev); |
643 | pm_runtime_put_autosuspend(connector->dev->dev); | ||
644 | } | ||
645 | 633 | ||
646 | return conn_status; | 634 | return conn_status; |
647 | } | 635 | } |
@@ -1105,6 +1093,26 @@ nouveau_connector_hotplug(struct nvif_notify *notify) | |||
1105 | const struct nvif_notify_conn_rep_v0 *rep = notify->data; | 1093 | const struct nvif_notify_conn_rep_v0 *rep = notify->data; |
1106 | const char *name = connector->name; | 1094 | const char *name = connector->name; |
1107 | struct nouveau_encoder *nv_encoder; | 1095 | struct nouveau_encoder *nv_encoder; |
1096 | int ret; | ||
1097 | |||
1098 | ret = pm_runtime_get(drm->dev->dev); | ||
1099 | if (ret == 0) { | ||
1100 | /* We can't block here if there's a pending PM request | ||
1101 | * running, as we'll deadlock nouveau_display_fini() when it | ||
1102 | * calls nvif_put() on our nvif_notify struct. So, simply | ||
1103 | * defer the hotplug event until the device finishes resuming | ||
1104 | */ | ||
1105 | NV_DEBUG(drm, "Deferring HPD on %s until runtime resume\n", | ||
1106 | name); | ||
1107 | schedule_work(&drm->hpd_work); | ||
1108 | |||
1109 | pm_runtime_put_noidle(drm->dev->dev); | ||
1110 | return NVIF_NOTIFY_KEEP; | ||
1111 | } else if (ret != 1 && ret != -EACCES) { | ||
1112 | NV_WARN(drm, "HPD on %s dropped due to RPM failure: %d\n", | ||
1113 | name, ret); | ||
1114 | return NVIF_NOTIFY_DROP; | ||
1115 | } | ||
1108 | 1116 | ||
1109 | if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) { | 1117 | if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) { |
1110 | NV_DEBUG(drm, "service %s\n", name); | 1118 | NV_DEBUG(drm, "service %s\n", name); |
@@ -1122,6 +1130,8 @@ nouveau_connector_hotplug(struct nvif_notify *notify) | |||
1122 | drm_helper_hpd_irq_event(connector->dev); | 1130 | drm_helper_hpd_irq_event(connector->dev); |
1123 | } | 1131 | } |
1124 | 1132 | ||
1133 | pm_runtime_mark_last_busy(drm->dev->dev); | ||
1134 | pm_runtime_put_autosuspend(drm->dev->dev); | ||
1125 | return NVIF_NOTIFY_KEEP; | 1135 | return NVIF_NOTIFY_KEEP; |
1126 | } | 1136 | } |
1127 | 1137 | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 139368b31916..540c0cbbfcee 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c | |||
@@ -293,7 +293,7 @@ nouveau_user_framebuffer_create(struct drm_device *dev, | |||
293 | 293 | ||
294 | static const struct drm_mode_config_funcs nouveau_mode_config_funcs = { | 294 | static const struct drm_mode_config_funcs nouveau_mode_config_funcs = { |
295 | .fb_create = nouveau_user_framebuffer_create, | 295 | .fb_create = nouveau_user_framebuffer_create, |
296 | .output_poll_changed = drm_fb_helper_output_poll_changed, | 296 | .output_poll_changed = nouveau_fbcon_output_poll_changed, |
297 | }; | 297 | }; |
298 | 298 | ||
299 | 299 | ||
@@ -355,8 +355,6 @@ nouveau_display_hpd_work(struct work_struct *work) | |||
355 | pm_runtime_get_sync(drm->dev->dev); | 355 | pm_runtime_get_sync(drm->dev->dev); |
356 | 356 | ||
357 | drm_helper_hpd_irq_event(drm->dev); | 357 | drm_helper_hpd_irq_event(drm->dev); |
358 | /* enable polling for external displays */ | ||
359 | drm_kms_helper_poll_enable(drm->dev); | ||
360 | 358 | ||
361 | pm_runtime_mark_last_busy(drm->dev->dev); | 359 | pm_runtime_mark_last_busy(drm->dev->dev); |
362 | pm_runtime_put_sync(drm->dev->dev); | 360 | pm_runtime_put_sync(drm->dev->dev); |
@@ -379,15 +377,29 @@ nouveau_display_acpi_ntfy(struct notifier_block *nb, unsigned long val, | |||
379 | { | 377 | { |
380 | struct nouveau_drm *drm = container_of(nb, typeof(*drm), acpi_nb); | 378 | struct nouveau_drm *drm = container_of(nb, typeof(*drm), acpi_nb); |
381 | struct acpi_bus_event *info = data; | 379 | struct acpi_bus_event *info = data; |
380 | int ret; | ||
382 | 381 | ||
383 | if (!strcmp(info->device_class, ACPI_VIDEO_CLASS)) { | 382 | if (!strcmp(info->device_class, ACPI_VIDEO_CLASS)) { |
384 | if (info->type == ACPI_VIDEO_NOTIFY_PROBE) { | 383 | if (info->type == ACPI_VIDEO_NOTIFY_PROBE) { |
385 | /* | 384 | ret = pm_runtime_get(drm->dev->dev); |
386 | * This may be the only indication we receive of a | 385 | if (ret == 1 || ret == -EACCES) { |
387 | * connector hotplug on a runtime suspended GPU, | 386 | /* If the GPU is already awake, or in a state |
388 | * schedule hpd_work to check. | 387 | * where we can't wake it up, it can handle |
389 | */ | 388 | * it's own hotplug events. |
390 | schedule_work(&drm->hpd_work); | 389 | */ |
390 | pm_runtime_put_autosuspend(drm->dev->dev); | ||
391 | } else if (ret == 0) { | ||
392 | /* This may be the only indication we receive | ||
393 | * of a connector hotplug on a runtime | ||
394 | * suspended GPU, schedule hpd_work to check. | ||
395 | */ | ||
396 | NV_DEBUG(drm, "ACPI requested connector reprobe\n"); | ||
397 | schedule_work(&drm->hpd_work); | ||
398 | pm_runtime_put_noidle(drm->dev->dev); | ||
399 | } else { | ||
400 | NV_WARN(drm, "Dropped ACPI reprobe event due to RPM error: %d\n", | ||
401 | ret); | ||
402 | } | ||
391 | 403 | ||
392 | /* acpi-video should not generate keypresses for this */ | 404 | /* acpi-video should not generate keypresses for this */ |
393 | return NOTIFY_BAD; | 405 | return NOTIFY_BAD; |
@@ -411,6 +423,11 @@ nouveau_display_init(struct drm_device *dev) | |||
411 | if (ret) | 423 | if (ret) |
412 | return ret; | 424 | return ret; |
413 | 425 | ||
426 | /* enable connector detection and polling for connectors without HPD | ||
427 | * support | ||
428 | */ | ||
429 | drm_kms_helper_poll_enable(dev); | ||
430 | |||
414 | /* enable hotplug interrupts */ | 431 | /* enable hotplug interrupts */ |
415 | drm_connector_list_iter_begin(dev, &conn_iter); | 432 | drm_connector_list_iter_begin(dev, &conn_iter); |
416 | nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) { | 433 | nouveau_for_each_non_mst_connector_iter(connector, &conn_iter) { |
@@ -425,7 +442,7 @@ nouveau_display_init(struct drm_device *dev) | |||
425 | } | 442 | } |
426 | 443 | ||
427 | void | 444 | void |
428 | nouveau_display_fini(struct drm_device *dev, bool suspend) | 445 | nouveau_display_fini(struct drm_device *dev, bool suspend, bool runtime) |
429 | { | 446 | { |
430 | struct nouveau_display *disp = nouveau_display(dev); | 447 | struct nouveau_display *disp = nouveau_display(dev); |
431 | struct nouveau_drm *drm = nouveau_drm(dev); | 448 | struct nouveau_drm *drm = nouveau_drm(dev); |
@@ -450,6 +467,9 @@ nouveau_display_fini(struct drm_device *dev, bool suspend) | |||
450 | } | 467 | } |
451 | drm_connector_list_iter_end(&conn_iter); | 468 | drm_connector_list_iter_end(&conn_iter); |
452 | 469 | ||
470 | if (!runtime) | ||
471 | cancel_work_sync(&drm->hpd_work); | ||
472 | |||
453 | drm_kms_helper_poll_disable(dev); | 473 | drm_kms_helper_poll_disable(dev); |
454 | disp->fini(dev); | 474 | disp->fini(dev); |
455 | } | 475 | } |
@@ -618,11 +638,11 @@ nouveau_display_suspend(struct drm_device *dev, bool runtime) | |||
618 | } | 638 | } |
619 | } | 639 | } |
620 | 640 | ||
621 | nouveau_display_fini(dev, true); | 641 | nouveau_display_fini(dev, true, runtime); |
622 | return 0; | 642 | return 0; |
623 | } | 643 | } |
624 | 644 | ||
625 | nouveau_display_fini(dev, true); | 645 | nouveau_display_fini(dev, true, runtime); |
626 | 646 | ||
627 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | 647 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
628 | struct nouveau_framebuffer *nouveau_fb; | 648 | struct nouveau_framebuffer *nouveau_fb; |
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h index 54aa7c3fa42d..ff92b54ce448 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.h +++ b/drivers/gpu/drm/nouveau/nouveau_display.h | |||
@@ -62,7 +62,7 @@ nouveau_display(struct drm_device *dev) | |||
62 | int nouveau_display_create(struct drm_device *dev); | 62 | int nouveau_display_create(struct drm_device *dev); |
63 | void nouveau_display_destroy(struct drm_device *dev); | 63 | void nouveau_display_destroy(struct drm_device *dev); |
64 | int nouveau_display_init(struct drm_device *dev); | 64 | int nouveau_display_init(struct drm_device *dev); |
65 | void nouveau_display_fini(struct drm_device *dev, bool suspend); | 65 | void nouveau_display_fini(struct drm_device *dev, bool suspend, bool runtime); |
66 | int nouveau_display_suspend(struct drm_device *dev, bool runtime); | 66 | int nouveau_display_suspend(struct drm_device *dev, bool runtime); |
67 | void nouveau_display_resume(struct drm_device *dev, bool runtime); | 67 | void nouveau_display_resume(struct drm_device *dev, bool runtime); |
68 | int nouveau_display_vblank_enable(struct drm_device *, unsigned int); | 68 | int nouveau_display_vblank_enable(struct drm_device *, unsigned int); |
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index c7ec86d6c3c9..74d2283f2c28 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c | |||
@@ -230,7 +230,7 @@ nouveau_cli_init(struct nouveau_drm *drm, const char *sname, | |||
230 | mutex_unlock(&drm->master.lock); | 230 | mutex_unlock(&drm->master.lock); |
231 | } | 231 | } |
232 | if (ret) { | 232 | if (ret) { |
233 | NV_ERROR(drm, "Client allocation failed: %d\n", ret); | 233 | NV_PRINTK(err, cli, "Client allocation failed: %d\n", ret); |
234 | goto done; | 234 | goto done; |
235 | } | 235 | } |
236 | 236 | ||
@@ -240,37 +240,37 @@ nouveau_cli_init(struct nouveau_drm *drm, const char *sname, | |||
240 | }, sizeof(struct nv_device_v0), | 240 | }, sizeof(struct nv_device_v0), |
241 | &cli->device); | 241 | &cli->device); |
242 | if (ret) { | 242 | if (ret) { |
243 | NV_ERROR(drm, "Device allocation failed: %d\n", ret); | 243 | NV_PRINTK(err, cli, "Device allocation failed: %d\n", ret); |
244 | goto done; | 244 | goto done; |
245 | } | 245 | } |
246 | 246 | ||
247 | ret = nvif_mclass(&cli->device.object, mmus); | 247 | ret = nvif_mclass(&cli->device.object, mmus); |
248 | if (ret < 0) { | 248 | if (ret < 0) { |
249 | NV_ERROR(drm, "No supported MMU class\n"); | 249 | NV_PRINTK(err, cli, "No supported MMU class\n"); |
250 | goto done; | 250 | goto done; |
251 | } | 251 | } |
252 | 252 | ||
253 | ret = nvif_mmu_init(&cli->device.object, mmus[ret].oclass, &cli->mmu); | 253 | ret = nvif_mmu_init(&cli->device.object, mmus[ret].oclass, &cli->mmu); |
254 | if (ret) { | 254 | if (ret) { |
255 | NV_ERROR(drm, "MMU allocation failed: %d\n", ret); | 255 | NV_PRINTK(err, cli, "MMU allocation failed: %d\n", ret); |
256 | goto done; | 256 | goto done; |
257 | } | 257 | } |
258 | 258 | ||
259 | ret = nvif_mclass(&cli->mmu.object, vmms); | 259 | ret = nvif_mclass(&cli->mmu.object, vmms); |
260 | if (ret < 0) { | 260 | if (ret < 0) { |
261 | NV_ERROR(drm, "No supported VMM class\n"); | 261 | NV_PRINTK(err, cli, "No supported VMM class\n"); |
262 | goto done; | 262 | goto done; |
263 | } | 263 | } |
264 | 264 | ||
265 | ret = nouveau_vmm_init(cli, vmms[ret].oclass, &cli->vmm); | 265 | ret = nouveau_vmm_init(cli, vmms[ret].oclass, &cli->vmm); |
266 | if (ret) { | 266 | if (ret) { |
267 | NV_ERROR(drm, "VMM allocation failed: %d\n", ret); | 267 | NV_PRINTK(err, cli, "VMM allocation failed: %d\n", ret); |
268 | goto done; | 268 | goto done; |
269 | } | 269 | } |
270 | 270 | ||
271 | ret = nvif_mclass(&cli->mmu.object, mems); | 271 | ret = nvif_mclass(&cli->mmu.object, mems); |
272 | if (ret < 0) { | 272 | if (ret < 0) { |
273 | NV_ERROR(drm, "No supported MEM class\n"); | 273 | NV_PRINTK(err, cli, "No supported MEM class\n"); |
274 | goto done; | 274 | goto done; |
275 | } | 275 | } |
276 | 276 | ||
@@ -592,10 +592,8 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags) | |||
592 | pm_runtime_allow(dev->dev); | 592 | pm_runtime_allow(dev->dev); |
593 | pm_runtime_mark_last_busy(dev->dev); | 593 | pm_runtime_mark_last_busy(dev->dev); |
594 | pm_runtime_put(dev->dev); | 594 | pm_runtime_put(dev->dev); |
595 | } else { | ||
596 | /* enable polling for external displays */ | ||
597 | drm_kms_helper_poll_enable(dev); | ||
598 | } | 595 | } |
596 | |||
599 | return 0; | 597 | return 0; |
600 | 598 | ||
601 | fail_dispinit: | 599 | fail_dispinit: |
@@ -629,7 +627,7 @@ nouveau_drm_unload(struct drm_device *dev) | |||
629 | nouveau_debugfs_fini(drm); | 627 | nouveau_debugfs_fini(drm); |
630 | 628 | ||
631 | if (dev->mode_config.num_crtc) | 629 | if (dev->mode_config.num_crtc) |
632 | nouveau_display_fini(dev, false); | 630 | nouveau_display_fini(dev, false, false); |
633 | nouveau_display_destroy(dev); | 631 | nouveau_display_destroy(dev); |
634 | 632 | ||
635 | nouveau_bios_takedown(dev); | 633 | nouveau_bios_takedown(dev); |
@@ -835,7 +833,6 @@ nouveau_pmops_runtime_suspend(struct device *dev) | |||
835 | return -EBUSY; | 833 | return -EBUSY; |
836 | } | 834 | } |
837 | 835 | ||
838 | drm_kms_helper_poll_disable(drm_dev); | ||
839 | nouveau_switcheroo_optimus_dsm(); | 836 | nouveau_switcheroo_optimus_dsm(); |
840 | ret = nouveau_do_suspend(drm_dev, true); | 837 | ret = nouveau_do_suspend(drm_dev, true); |
841 | pci_save_state(pdev); | 838 | pci_save_state(pdev); |
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 844498c4267c..0f64c0a1d4b3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c | |||
@@ -466,6 +466,7 @@ nouveau_fbcon_set_suspend_work(struct work_struct *work) | |||
466 | console_unlock(); | 466 | console_unlock(); |
467 | 467 | ||
468 | if (state == FBINFO_STATE_RUNNING) { | 468 | if (state == FBINFO_STATE_RUNNING) { |
469 | nouveau_fbcon_hotplug_resume(drm->fbcon); | ||
469 | pm_runtime_mark_last_busy(drm->dev->dev); | 470 | pm_runtime_mark_last_busy(drm->dev->dev); |
470 | pm_runtime_put_sync(drm->dev->dev); | 471 | pm_runtime_put_sync(drm->dev->dev); |
471 | } | 472 | } |
@@ -487,6 +488,61 @@ nouveau_fbcon_set_suspend(struct drm_device *dev, int state) | |||
487 | schedule_work(&drm->fbcon_work); | 488 | schedule_work(&drm->fbcon_work); |
488 | } | 489 | } |
489 | 490 | ||
491 | void | ||
492 | nouveau_fbcon_output_poll_changed(struct drm_device *dev) | ||
493 | { | ||
494 | struct nouveau_drm *drm = nouveau_drm(dev); | ||
495 | struct nouveau_fbdev *fbcon = drm->fbcon; | ||
496 | int ret; | ||
497 | |||
498 | if (!fbcon) | ||
499 | return; | ||
500 | |||
501 | mutex_lock(&fbcon->hotplug_lock); | ||
502 | |||
503 | ret = pm_runtime_get(dev->dev); | ||
504 | if (ret == 1 || ret == -EACCES) { | ||
505 | drm_fb_helper_hotplug_event(&fbcon->helper); | ||
506 | |||
507 | pm_runtime_mark_last_busy(dev->dev); | ||
508 | pm_runtime_put_autosuspend(dev->dev); | ||
509 | } else if (ret == 0) { | ||
510 | /* If the GPU was already in the process of suspending before | ||
511 | * this event happened, then we can't block here as we'll | ||
512 | * deadlock the runtime pmops since they wait for us to | ||
513 | * finish. So, just defer this event for when we runtime | ||
514 | * resume again. It will be handled by fbcon_work. | ||
515 | */ | ||
516 | NV_DEBUG(drm, "fbcon HPD event deferred until runtime resume\n"); | ||
517 | fbcon->hotplug_waiting = true; | ||
518 | pm_runtime_put_noidle(drm->dev->dev); | ||
519 | } else { | ||
520 | DRM_WARN("fbcon HPD event lost due to RPM failure: %d\n", | ||
521 | ret); | ||
522 | } | ||
523 | |||
524 | mutex_unlock(&fbcon->hotplug_lock); | ||
525 | } | ||
526 | |||
527 | void | ||
528 | nouveau_fbcon_hotplug_resume(struct nouveau_fbdev *fbcon) | ||
529 | { | ||
530 | struct nouveau_drm *drm; | ||
531 | |||
532 | if (!fbcon) | ||
533 | return; | ||
534 | drm = nouveau_drm(fbcon->helper.dev); | ||
535 | |||
536 | mutex_lock(&fbcon->hotplug_lock); | ||
537 | if (fbcon->hotplug_waiting) { | ||
538 | fbcon->hotplug_waiting = false; | ||
539 | |||
540 | NV_DEBUG(drm, "Handling deferred fbcon HPD events\n"); | ||
541 | drm_fb_helper_hotplug_event(&fbcon->helper); | ||
542 | } | ||
543 | mutex_unlock(&fbcon->hotplug_lock); | ||
544 | } | ||
545 | |||
490 | int | 546 | int |
491 | nouveau_fbcon_init(struct drm_device *dev) | 547 | nouveau_fbcon_init(struct drm_device *dev) |
492 | { | 548 | { |
@@ -505,6 +561,7 @@ nouveau_fbcon_init(struct drm_device *dev) | |||
505 | 561 | ||
506 | drm->fbcon = fbcon; | 562 | drm->fbcon = fbcon; |
507 | INIT_WORK(&drm->fbcon_work, nouveau_fbcon_set_suspend_work); | 563 | INIT_WORK(&drm->fbcon_work, nouveau_fbcon_set_suspend_work); |
564 | mutex_init(&fbcon->hotplug_lock); | ||
508 | 565 | ||
509 | drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs); | 566 | drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs); |
510 | 567 | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h index a6f192ea3fa6..db9d52047ef8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.h +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h | |||
@@ -41,6 +41,9 @@ struct nouveau_fbdev { | |||
41 | struct nvif_object gdi; | 41 | struct nvif_object gdi; |
42 | struct nvif_object blit; | 42 | struct nvif_object blit; |
43 | struct nvif_object twod; | 43 | struct nvif_object twod; |
44 | |||
45 | struct mutex hotplug_lock; | ||
46 | bool hotplug_waiting; | ||
44 | }; | 47 | }; |
45 | 48 | ||
46 | void nouveau_fbcon_restore(void); | 49 | void nouveau_fbcon_restore(void); |
@@ -68,6 +71,8 @@ void nouveau_fbcon_set_suspend(struct drm_device *dev, int state); | |||
68 | void nouveau_fbcon_accel_save_disable(struct drm_device *dev); | 71 | void nouveau_fbcon_accel_save_disable(struct drm_device *dev); |
69 | void nouveau_fbcon_accel_restore(struct drm_device *dev); | 72 | void nouveau_fbcon_accel_restore(struct drm_device *dev); |
70 | 73 | ||
74 | void nouveau_fbcon_output_poll_changed(struct drm_device *dev); | ||
75 | void nouveau_fbcon_hotplug_resume(struct nouveau_fbdev *fbcon); | ||
71 | extern int nouveau_nofbaccel; | 76 | extern int nouveau_nofbaccel; |
72 | 77 | ||
73 | #endif /* __NV50_FBCON_H__ */ | 78 | #endif /* __NV50_FBCON_H__ */ |
diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c index 3da5a4305aa4..8f1ce4833230 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vga.c +++ b/drivers/gpu/drm/nouveau/nouveau_vga.c | |||
@@ -46,12 +46,10 @@ nouveau_switcheroo_set_state(struct pci_dev *pdev, | |||
46 | pr_err("VGA switcheroo: switched nouveau on\n"); | 46 | pr_err("VGA switcheroo: switched nouveau on\n"); |
47 | dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; | 47 | dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; |
48 | nouveau_pmops_resume(&pdev->dev); | 48 | nouveau_pmops_resume(&pdev->dev); |
49 | drm_kms_helper_poll_enable(dev); | ||
50 | dev->switch_power_state = DRM_SWITCH_POWER_ON; | 49 | dev->switch_power_state = DRM_SWITCH_POWER_ON; |
51 | } else { | 50 | } else { |
52 | pr_err("VGA switcheroo: switched nouveau off\n"); | 51 | pr_err("VGA switcheroo: switched nouveau off\n"); |
53 | dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; | 52 | dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; |
54 | drm_kms_helper_poll_disable(dev); | ||
55 | nouveau_switcheroo_optimus_dsm(); | 53 | nouveau_switcheroo_optimus_dsm(); |
56 | nouveau_pmops_suspend(&pdev->dev); | 54 | nouveau_pmops_suspend(&pdev->dev); |
57 | dev->switch_power_state = DRM_SWITCH_POWER_OFF; | 55 | dev->switch_power_state = DRM_SWITCH_POWER_OFF; |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c index 32fa94a9773f..cbd33e87b799 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c | |||
@@ -275,6 +275,7 @@ nvkm_disp_oneinit(struct nvkm_engine *engine) | |||
275 | struct nvkm_outp *outp, *outt, *pair; | 275 | struct nvkm_outp *outp, *outt, *pair; |
276 | struct nvkm_conn *conn; | 276 | struct nvkm_conn *conn; |
277 | struct nvkm_head *head; | 277 | struct nvkm_head *head; |
278 | struct nvkm_ior *ior; | ||
278 | struct nvbios_connE connE; | 279 | struct nvbios_connE connE; |
279 | struct dcb_output dcbE; | 280 | struct dcb_output dcbE; |
280 | u8 hpd = 0, ver, hdr; | 281 | u8 hpd = 0, ver, hdr; |
@@ -399,6 +400,19 @@ nvkm_disp_oneinit(struct nvkm_engine *engine) | |||
399 | return ret; | 400 | return ret; |
400 | } | 401 | } |
401 | 402 | ||
403 | /* Enforce identity-mapped SOR assignment for panels, which have | ||
404 | * certain bits (ie. backlight controls) wired to a specific SOR. | ||
405 | */ | ||
406 | list_for_each_entry(outp, &disp->outp, head) { | ||
407 | if (outp->conn->info.type == DCB_CONNECTOR_LVDS || | ||
408 | outp->conn->info.type == DCB_CONNECTOR_eDP) { | ||
409 | ior = nvkm_ior_find(disp, SOR, ffs(outp->info.or) - 1); | ||
410 | if (!WARN_ON(!ior)) | ||
411 | ior->identity = true; | ||
412 | outp->identity = true; | ||
413 | } | ||
414 | } | ||
415 | |||
402 | i = 0; | 416 | i = 0; |
403 | list_for_each_entry(head, &disp->head, head) | 417 | list_for_each_entry(head, &disp->head, head) |
404 | i = max(i, head->id + 1); | 418 | i = max(i, head->id + 1); |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c index 7c5bed29ffef..5f301e632599 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c | |||
@@ -28,6 +28,7 @@ | |||
28 | 28 | ||
29 | #include <subdev/bios.h> | 29 | #include <subdev/bios.h> |
30 | #include <subdev/bios/init.h> | 30 | #include <subdev/bios/init.h> |
31 | #include <subdev/gpio.h> | ||
31 | #include <subdev/i2c.h> | 32 | #include <subdev/i2c.h> |
32 | 33 | ||
33 | #include <nvif/event.h> | 34 | #include <nvif/event.h> |
@@ -412,14 +413,10 @@ nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps) | |||
412 | } | 413 | } |
413 | 414 | ||
414 | static void | 415 | static void |
415 | nvkm_dp_release(struct nvkm_outp *outp, struct nvkm_ior *ior) | 416 | nvkm_dp_disable(struct nvkm_outp *outp, struct nvkm_ior *ior) |
416 | { | 417 | { |
417 | struct nvkm_dp *dp = nvkm_dp(outp); | 418 | struct nvkm_dp *dp = nvkm_dp(outp); |
418 | 419 | ||
419 | /* Prevent link from being retrained if sink sends an IRQ. */ | ||
420 | atomic_set(&dp->lt.done, 0); | ||
421 | ior->dp.nr = 0; | ||
422 | |||
423 | /* Execute DisableLT script from DP Info Table. */ | 420 | /* Execute DisableLT script from DP Info Table. */ |
424 | nvbios_init(&ior->disp->engine.subdev, dp->info.script[4], | 421 | nvbios_init(&ior->disp->engine.subdev, dp->info.script[4], |
425 | init.outp = &dp->outp.info; | 422 | init.outp = &dp->outp.info; |
@@ -428,6 +425,16 @@ nvkm_dp_release(struct nvkm_outp *outp, struct nvkm_ior *ior) | |||
428 | ); | 425 | ); |
429 | } | 426 | } |
430 | 427 | ||
428 | static void | ||
429 | nvkm_dp_release(struct nvkm_outp *outp) | ||
430 | { | ||
431 | struct nvkm_dp *dp = nvkm_dp(outp); | ||
432 | |||
433 | /* Prevent link from being retrained if sink sends an IRQ. */ | ||
434 | atomic_set(&dp->lt.done, 0); | ||
435 | dp->outp.ior->dp.nr = 0; | ||
436 | } | ||
437 | |||
431 | static int | 438 | static int |
432 | nvkm_dp_acquire(struct nvkm_outp *outp) | 439 | nvkm_dp_acquire(struct nvkm_outp *outp) |
433 | { | 440 | { |
@@ -491,7 +498,7 @@ done: | |||
491 | return ret; | 498 | return ret; |
492 | } | 499 | } |
493 | 500 | ||
494 | static void | 501 | static bool |
495 | nvkm_dp_enable(struct nvkm_dp *dp, bool enable) | 502 | nvkm_dp_enable(struct nvkm_dp *dp, bool enable) |
496 | { | 503 | { |
497 | struct nvkm_i2c_aux *aux = dp->aux; | 504 | struct nvkm_i2c_aux *aux = dp->aux; |
@@ -505,7 +512,7 @@ nvkm_dp_enable(struct nvkm_dp *dp, bool enable) | |||
505 | 512 | ||
506 | if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, dp->dpcd, | 513 | if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, dp->dpcd, |
507 | sizeof(dp->dpcd))) | 514 | sizeof(dp->dpcd))) |
508 | return; | 515 | return true; |
509 | } | 516 | } |
510 | 517 | ||
511 | if (dp->present) { | 518 | if (dp->present) { |
@@ -515,6 +522,7 @@ nvkm_dp_enable(struct nvkm_dp *dp, bool enable) | |||
515 | } | 522 | } |
516 | 523 | ||
517 | atomic_set(&dp->lt.done, 0); | 524 | atomic_set(&dp->lt.done, 0); |
525 | return false; | ||
518 | } | 526 | } |
519 | 527 | ||
520 | static int | 528 | static int |
@@ -555,9 +563,38 @@ nvkm_dp_fini(struct nvkm_outp *outp) | |||
555 | static void | 563 | static void |
556 | nvkm_dp_init(struct nvkm_outp *outp) | 564 | nvkm_dp_init(struct nvkm_outp *outp) |
557 | { | 565 | { |
566 | struct nvkm_gpio *gpio = outp->disp->engine.subdev.device->gpio; | ||
558 | struct nvkm_dp *dp = nvkm_dp(outp); | 567 | struct nvkm_dp *dp = nvkm_dp(outp); |
568 | |||
559 | nvkm_notify_put(&dp->outp.conn->hpd); | 569 | nvkm_notify_put(&dp->outp.conn->hpd); |
560 | nvkm_dp_enable(dp, true); | 570 | |
571 | /* eDP panels need powering on by us (if the VBIOS doesn't default it | ||
572 | * to on) before doing any AUX channel transactions. LVDS panel power | ||
573 | * is handled by the SOR itself, and not required for LVDS DDC. | ||
574 | */ | ||
575 | if (dp->outp.conn->info.type == DCB_CONNECTOR_eDP) { | ||
576 | int power = nvkm_gpio_get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff); | ||
577 | if (power == 0) | ||
578 | nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1); | ||
579 | |||
580 | /* We delay here unconditionally, even if already powered, | ||
581 | * because some laptop panels having a significant resume | ||
582 | * delay before the panel begins responding. | ||
583 | * | ||
584 | * This is likely a bit of a hack, but no better idea for | ||
585 | * handling this at the moment. | ||
586 | */ | ||
587 | msleep(300); | ||
588 | |||
589 | /* If the eDP panel can't be detected, we need to restore | ||
590 | * the panel power GPIO to avoid breaking another output. | ||
591 | */ | ||
592 | if (!nvkm_dp_enable(dp, true) && power == 0) | ||
593 | nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 0); | ||
594 | } else { | ||
595 | nvkm_dp_enable(dp, true); | ||
596 | } | ||
597 | |||
561 | nvkm_notify_get(&dp->hpd); | 598 | nvkm_notify_get(&dp->hpd); |
562 | } | 599 | } |
563 | 600 | ||
@@ -576,6 +613,7 @@ nvkm_dp_func = { | |||
576 | .fini = nvkm_dp_fini, | 613 | .fini = nvkm_dp_fini, |
577 | .acquire = nvkm_dp_acquire, | 614 | .acquire = nvkm_dp_acquire, |
578 | .release = nvkm_dp_release, | 615 | .release = nvkm_dp_release, |
616 | .disable = nvkm_dp_disable, | ||
579 | }; | 617 | }; |
580 | 618 | ||
581 | static int | 619 | static int |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h index e0b4e0c5704e..19911211a12a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h | |||
@@ -16,6 +16,7 @@ struct nvkm_ior { | |||
16 | char name[8]; | 16 | char name[8]; |
17 | 17 | ||
18 | struct list_head head; | 18 | struct list_head head; |
19 | bool identity; | ||
19 | 20 | ||
20 | struct nvkm_ior_state { | 21 | struct nvkm_ior_state { |
21 | struct nvkm_outp *outp; | 22 | struct nvkm_outp *outp; |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c index f89c7b977aa5..def005dd5fda 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c | |||
@@ -501,11 +501,11 @@ nv50_disp_super_2_0(struct nv50_disp *disp, struct nvkm_head *head) | |||
501 | nv50_disp_super_ied_off(head, ior, 2); | 501 | nv50_disp_super_ied_off(head, ior, 2); |
502 | 502 | ||
503 | /* If we're shutting down the OR's only active head, execute | 503 | /* If we're shutting down the OR's only active head, execute |
504 | * the output path's release function. | 504 | * the output path's disable function. |
505 | */ | 505 | */ |
506 | if (ior->arm.head == (1 << head->id)) { | 506 | if (ior->arm.head == (1 << head->id)) { |
507 | if ((outp = ior->arm.outp) && outp->func->release) | 507 | if ((outp = ior->arm.outp) && outp->func->disable) |
508 | outp->func->release(outp, ior); | 508 | outp->func->disable(outp, ior); |
509 | } | 509 | } |
510 | } | 510 | } |
511 | 511 | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c index be9e7f8c3b23..c62030c96fba 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c | |||
@@ -93,6 +93,8 @@ nvkm_outp_release(struct nvkm_outp *outp, u8 user) | |||
93 | if (ior) { | 93 | if (ior) { |
94 | outp->acquired &= ~user; | 94 | outp->acquired &= ~user; |
95 | if (!outp->acquired) { | 95 | if (!outp->acquired) { |
96 | if (outp->func->release && outp->ior) | ||
97 | outp->func->release(outp); | ||
96 | outp->ior->asy.outp = NULL; | 98 | outp->ior->asy.outp = NULL; |
97 | outp->ior = NULL; | 99 | outp->ior = NULL; |
98 | } | 100 | } |
@@ -127,17 +129,26 @@ nvkm_outp_acquire(struct nvkm_outp *outp, u8 user) | |||
127 | if (proto == UNKNOWN) | 129 | if (proto == UNKNOWN) |
128 | return -ENOSYS; | 130 | return -ENOSYS; |
129 | 131 | ||
132 | /* Deal with panels requiring identity-mapped SOR assignment. */ | ||
133 | if (outp->identity) { | ||
134 | ior = nvkm_ior_find(outp->disp, SOR, ffs(outp->info.or) - 1); | ||
135 | if (WARN_ON(!ior)) | ||
136 | return -ENOSPC; | ||
137 | return nvkm_outp_acquire_ior(outp, user, ior); | ||
138 | } | ||
139 | |||
130 | /* First preference is to reuse the OR that is currently armed | 140 | /* First preference is to reuse the OR that is currently armed |
131 | * on HW, if any, in order to prevent unnecessary switching. | 141 | * on HW, if any, in order to prevent unnecessary switching. |
132 | */ | 142 | */ |
133 | list_for_each_entry(ior, &outp->disp->ior, head) { | 143 | list_for_each_entry(ior, &outp->disp->ior, head) { |
134 | if (!ior->asy.outp && ior->arm.outp == outp) | 144 | if (!ior->identity && !ior->asy.outp && ior->arm.outp == outp) |
135 | return nvkm_outp_acquire_ior(outp, user, ior); | 145 | return nvkm_outp_acquire_ior(outp, user, ior); |
136 | } | 146 | } |
137 | 147 | ||
138 | /* Failing that, a completely unused OR is the next best thing. */ | 148 | /* Failing that, a completely unused OR is the next best thing. */ |
139 | list_for_each_entry(ior, &outp->disp->ior, head) { | 149 | list_for_each_entry(ior, &outp->disp->ior, head) { |
140 | if (!ior->asy.outp && ior->type == type && !ior->arm.outp && | 150 | if (!ior->identity && |
151 | !ior->asy.outp && ior->type == type && !ior->arm.outp && | ||
141 | (ior->func->route.set || ior->id == __ffs(outp->info.or))) | 152 | (ior->func->route.set || ior->id == __ffs(outp->info.or))) |
142 | return nvkm_outp_acquire_ior(outp, user, ior); | 153 | return nvkm_outp_acquire_ior(outp, user, ior); |
143 | } | 154 | } |
@@ -146,7 +157,7 @@ nvkm_outp_acquire(struct nvkm_outp *outp, u8 user) | |||
146 | * but will be released during the next modeset. | 157 | * but will be released during the next modeset. |
147 | */ | 158 | */ |
148 | list_for_each_entry(ior, &outp->disp->ior, head) { | 159 | list_for_each_entry(ior, &outp->disp->ior, head) { |
149 | if (!ior->asy.outp && ior->type == type && | 160 | if (!ior->identity && !ior->asy.outp && ior->type == type && |
150 | (ior->func->route.set || ior->id == __ffs(outp->info.or))) | 161 | (ior->func->route.set || ior->id == __ffs(outp->info.or))) |
151 | return nvkm_outp_acquire_ior(outp, user, ior); | 162 | return nvkm_outp_acquire_ior(outp, user, ior); |
152 | } | 163 | } |
@@ -245,7 +256,6 @@ nvkm_outp_ctor(const struct nvkm_outp_func *func, struct nvkm_disp *disp, | |||
245 | outp->index = index; | 256 | outp->index = index; |
246 | outp->info = *dcbE; | 257 | outp->info = *dcbE; |
247 | outp->i2c = nvkm_i2c_bus_find(i2c, dcbE->i2c_index); | 258 | outp->i2c = nvkm_i2c_bus_find(i2c, dcbE->i2c_index); |
248 | outp->or = ffs(outp->info.or) - 1; | ||
249 | 259 | ||
250 | OUTP_DBG(outp, "type %02x loc %d or %d link %d con %x " | 260 | OUTP_DBG(outp, "type %02x loc %d or %d link %d con %x " |
251 | "edid %x bus %d head %x", | 261 | "edid %x bus %d head %x", |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h index ea84d7d5741a..6c8aa5cfed9d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h | |||
@@ -13,10 +13,10 @@ struct nvkm_outp { | |||
13 | struct dcb_output info; | 13 | struct dcb_output info; |
14 | 14 | ||
15 | struct nvkm_i2c_bus *i2c; | 15 | struct nvkm_i2c_bus *i2c; |
16 | int or; | ||
17 | 16 | ||
18 | struct list_head head; | 17 | struct list_head head; |
19 | struct nvkm_conn *conn; | 18 | struct nvkm_conn *conn; |
19 | bool identity; | ||
20 | 20 | ||
21 | /* Assembly state. */ | 21 | /* Assembly state. */ |
22 | #define NVKM_OUTP_PRIV 1 | 22 | #define NVKM_OUTP_PRIV 1 |
@@ -41,7 +41,8 @@ struct nvkm_outp_func { | |||
41 | void (*init)(struct nvkm_outp *); | 41 | void (*init)(struct nvkm_outp *); |
42 | void (*fini)(struct nvkm_outp *); | 42 | void (*fini)(struct nvkm_outp *); |
43 | int (*acquire)(struct nvkm_outp *); | 43 | int (*acquire)(struct nvkm_outp *); |
44 | void (*release)(struct nvkm_outp *, struct nvkm_ior *); | 44 | void (*release)(struct nvkm_outp *); |
45 | void (*disable)(struct nvkm_outp *, struct nvkm_ior *); | ||
45 | }; | 46 | }; |
46 | 47 | ||
47 | #define OUTP_MSG(o,l,f,a...) do { \ | 48 | #define OUTP_MSG(o,l,f,a...) do { \ |
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm200.c index b80618e35491..d65959ef0564 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm200.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm200.c | |||
@@ -158,7 +158,8 @@ gm200_devinit_post(struct nvkm_devinit *base, bool post) | |||
158 | } | 158 | } |
159 | 159 | ||
160 | /* load and execute some other ucode image (bios therm?) */ | 160 | /* load and execute some other ucode image (bios therm?) */ |
161 | return pmu_load(init, 0x01, post, NULL, NULL); | 161 | pmu_load(init, 0x01, post, NULL, NULL); |
162 | return 0; | ||
162 | } | 163 | } |
163 | 164 | ||
164 | static const struct nvkm_devinit_func | 165 | static const struct nvkm_devinit_func |
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c index de269eb482dd..7459def78d50 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c | |||
@@ -1423,7 +1423,7 @@ nvkm_vmm_get(struct nvkm_vmm *vmm, u8 page, u64 size, struct nvkm_vma **pvma) | |||
1423 | void | 1423 | void |
1424 | nvkm_vmm_part(struct nvkm_vmm *vmm, struct nvkm_memory *inst) | 1424 | nvkm_vmm_part(struct nvkm_vmm *vmm, struct nvkm_memory *inst) |
1425 | { | 1425 | { |
1426 | if (vmm->func->part && inst) { | 1426 | if (inst && vmm->func->part) { |
1427 | mutex_lock(&vmm->mutex); | 1427 | mutex_lock(&vmm->mutex); |
1428 | vmm->func->part(vmm, inst); | 1428 | vmm->func->part(vmm, inst); |
1429 | mutex_unlock(&vmm->mutex); | 1429 | mutex_unlock(&vmm->mutex); |