|
@@ -409,59 +409,45 @@ static struct nouveau_encoder *
|
|
nouveau_connector_ddc_detect(struct drm_connector *connector)
|
|
nouveau_connector_ddc_detect(struct drm_connector *connector)
|
|
{
|
|
{
|
|
struct drm_device *dev = connector->dev;
|
|
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;
|
|
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) {
|
|
drm_connector_for_each_possible_encoder(connector, encoder, i) {
|
|
nv_encoder = nouveau_encoder(encoder);
|
|
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)
|
|
if (ret == NOUVEAU_DP_MST)
|
|
return NULL;
|
|
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;
|
|
break;
|
|
- } else
|
|
|
|
- if (nv_encoder->i2c) {
|
|
|
|
|
|
+
|
|
|
|
+ if (switcheroo_ddc)
|
|
|
|
+ vga_switcheroo_lock_ddc(dev->pdev);
|
|
if (nvkm_probe_i2c(nv_encoder->i2c, 0x50))
|
|
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 *
|
|
static struct nouveau_encoder *
|
|
@@ -555,12 +541,16 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
|
|
nv_connector->edid = NULL;
|
|
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)
|
|
if (ret < 0 && ret != -EACCES)
|
|
return conn_status;
|
|
return conn_status;
|
|
}
|
|
}
|
|
@@ -638,10 +628,8 @@ detect_analog:
|
|
|
|
|
|
out:
|
|
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;
|
|
return conn_status;
|
|
}
|
|
}
|
|
@@ -1105,6 +1093,26 @@ nouveau_connector_hotplug(struct nvif_notify *notify)
|
|
const struct nvif_notify_conn_rep_v0 *rep = notify->data;
|
|
const struct nvif_notify_conn_rep_v0 *rep = notify->data;
|
|
const char *name = connector->name;
|
|
const char *name = connector->name;
|
|
struct nouveau_encoder *nv_encoder;
|
|
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) {
|
|
if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) {
|
|
NV_DEBUG(drm, "service %s\n", name);
|
|
NV_DEBUG(drm, "service %s\n", name);
|
|
@@ -1122,6 +1130,8 @@ nouveau_connector_hotplug(struct nvif_notify *notify)
|
|
drm_helper_hpd_irq_event(connector->dev);
|
|
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;
|
|
return NVIF_NOTIFY_KEEP;
|
|
}
|
|
}
|
|
|
|
|