static void tb_tunnel_dp()

in tb.c [868:972]


static void tb_tunnel_dp(struct tb *tb)
{
	int available_up, available_down, ret;
	struct tb_cm *tcm = tb_priv(tb);
	struct tb_port *port, *in, *out;
	struct tb_tunnel *tunnel;

	if (!tb_acpi_may_tunnel_dp()) {
		tb_dbg(tb, "DP tunneling disabled, not creating tunnel\n");
		return;
	}

	/*
	 * Find pair of inactive DP IN and DP OUT adapters and then
	 * establish a DP tunnel between them.
	 */
	tb_dbg(tb, "looking for DP IN <-> DP OUT pairs:\n");

	in = NULL;
	out = NULL;
	list_for_each_entry(port, &tcm->dp_resources, list) {
		if (!tb_port_is_dpin(port))
			continue;

		if (tb_port_is_enabled(port)) {
			tb_port_dbg(port, "in use\n");
			continue;
		}

		tb_port_dbg(port, "DP IN available\n");

		out = tb_find_dp_out(tb, port);
		if (out) {
			in = port;
			break;
		}
	}

	if (!in) {
		tb_dbg(tb, "no suitable DP IN adapter available, not tunneling\n");
		return;
	}
	if (!out) {
		tb_dbg(tb, "no suitable DP OUT adapter available, not tunneling\n");
		return;
	}

	/*
	 * DP stream needs the domain to be active so runtime resume
	 * both ends of the tunnel.
	 *
	 * This should bring the routers in the middle active as well
	 * and keeps the domain from runtime suspending while the DP
	 * tunnel is active.
	 */
	pm_runtime_get_sync(&in->sw->dev);
	pm_runtime_get_sync(&out->sw->dev);

	if (tb_switch_alloc_dp_resource(in->sw, in)) {
		tb_port_dbg(in, "no resource available for DP IN, not tunneling\n");
		goto err_rpm_put;
	}

	/* Make all unused USB3 bandwidth available for the new DP tunnel */
	ret = tb_release_unused_usb3_bandwidth(tb, in, out);
	if (ret) {
		tb_warn(tb, "failed to release unused bandwidth\n");
		goto err_dealloc_dp;
	}

	ret = tb_available_bandwidth(tb, in, out, &available_up,
				     &available_down);
	if (ret)
		goto err_reclaim;

	tb_dbg(tb, "available bandwidth for new DP tunnel %u/%u Mb/s\n",
	       available_up, available_down);

	tunnel = tb_tunnel_alloc_dp(tb, in, out, available_up, available_down);
	if (!tunnel) {
		tb_port_dbg(out, "could not allocate DP tunnel\n");
		goto err_reclaim;
	}

	if (tb_tunnel_activate(tunnel)) {
		tb_port_info(out, "DP tunnel activation failed, aborting\n");
		goto err_free;
	}

	list_add_tail(&tunnel->list, &tcm->tunnel_list);
	tb_reclaim_usb3_bandwidth(tb, in, out);
	return;

err_free:
	tb_tunnel_free(tunnel);
err_reclaim:
	tb_reclaim_usb3_bandwidth(tb, in, out);
err_dealloc_dp:
	tb_switch_dealloc_dp_resource(in->sw, in);
err_rpm_put:
	pm_runtime_mark_last_busy(&out->sw->dev);
	pm_runtime_put_autosuspend(&out->sw->dev);
	pm_runtime_mark_last_busy(&in->sw->dev);
	pm_runtime_put_autosuspend(&in->sw->dev);
}