Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2887e5ce authored by Dave Airlie's avatar Dave Airlie
Browse files

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: default avatarDave 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
parents 11da3a7f 53b0cc46
Loading
Loading
Loading
Loading
+49 −18
Original line number Diff line number Diff line
@@ -1123,18 +1123,22 @@ 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 (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));
}
@@ -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;

	ret = drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, state);
	mutex_unlock(&mstm->mgr.lock);

	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
@@ -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,
+60 −50
Original line number Diff line number Diff line
@@ -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)
			else if (ret == NOUVEAU_DP_SST)
				found = nv_encoder;

			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)
		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))
				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 *
@@ -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;
	}
@@ -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;
}
@@ -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);
@@ -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;
}

+32 −12
Original line number Diff line number Diff line
@@ -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,
};


@@ -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);
@@ -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.
			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;
@@ -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) {
@@ -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);
@@ -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);
}
@@ -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;
+1 −1
Original line number Diff line number Diff line
@@ -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);
+9 −12
Original line number Diff line number Diff line
@@ -230,7 +230,7 @@ nouveau_cli_init(struct nouveau_drm *drm, const char *sname,
		mutex_unlock(&drm->master.lock);
	}
	if (ret) {
		NV_ERROR(drm, "Client allocation failed: %d\n", ret);
		NV_PRINTK(err, cli, "Client allocation failed: %d\n", ret);
		goto done;
	}

@@ -240,37 +240,37 @@ nouveau_cli_init(struct nouveau_drm *drm, const char *sname,
			       }, sizeof(struct nv_device_v0),
			       &cli->device);
	if (ret) {
		NV_ERROR(drm, "Device allocation failed: %d\n", ret);
		NV_PRINTK(err, cli, "Device allocation failed: %d\n", ret);
		goto done;
	}

	ret = nvif_mclass(&cli->device.object, mmus);
	if (ret < 0) {
		NV_ERROR(drm, "No supported MMU class\n");
		NV_PRINTK(err, cli, "No supported MMU class\n");
		goto done;
	}

	ret = nvif_mmu_init(&cli->device.object, mmus[ret].oclass, &cli->mmu);
	if (ret) {
		NV_ERROR(drm, "MMU allocation failed: %d\n", ret);
		NV_PRINTK(err, cli, "MMU allocation failed: %d\n", ret);
		goto done;
	}

	ret = nvif_mclass(&cli->mmu.object, vmms);
	if (ret < 0) {
		NV_ERROR(drm, "No supported VMM class\n");
		NV_PRINTK(err, cli, "No supported VMM class\n");
		goto done;
	}

	ret = nouveau_vmm_init(cli, vmms[ret].oclass, &cli->vmm);
	if (ret) {
		NV_ERROR(drm, "VMM allocation failed: %d\n", ret);
		NV_PRINTK(err, cli, "VMM allocation failed: %d\n", ret);
		goto done;
	}

	ret = nvif_mclass(&cli->mmu.object, mems);
	if (ret < 0) {
		NV_ERROR(drm, "No supported MEM class\n");
		NV_PRINTK(err, cli, "No supported MEM class\n");
		goto done;
	}

@@ -592,10 +592,8 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
		pm_runtime_allow(dev->dev);
		pm_runtime_mark_last_busy(dev->dev);
		pm_runtime_put(dev->dev);
	} else {
		/* enable polling for external displays */
		drm_kms_helper_poll_enable(dev);
	}

	return 0;

fail_dispinit:
@@ -629,7 +627,7 @@ nouveau_drm_unload(struct drm_device *dev)
	nouveau_debugfs_fini(drm);

	if (dev->mode_config.num_crtc)
		nouveau_display_fini(dev, false);
		nouveau_display_fini(dev, false, false);
	nouveau_display_destroy(dev);

	nouveau_bios_takedown(dev);
@@ -835,7 +833,6 @@ nouveau_pmops_runtime_suspend(struct device *dev)
		return -EBUSY;
	}

	drm_kms_helper_poll_disable(drm_dev);
	nouveau_switcheroo_optimus_dsm();
	ret = nouveau_do_suspend(drm_dev, true);
	pci_save_state(pdev);
Loading