int intel_link_startup()

in intel.c [1315:1456]


int intel_link_startup(struct auxiliary_device *auxdev)
{
	struct sdw_cdns_stream_config config;
	struct device *dev = &auxdev->dev;
	struct sdw_cdns *cdns = auxiliary_get_drvdata(auxdev);
	struct sdw_intel *sdw = cdns_to_intel(cdns);
	struct sdw_bus *bus = &cdns->bus;
	int link_flags;
	bool multi_link;
	u32 clock_stop_quirks;
	int ret;

	if (bus->prop.hw_disabled) {
		dev_info(dev,
			 "SoundWire master %d is disabled, ignoring\n",
			 sdw->instance);
		return 0;
	}

	link_flags = md_flags >> (bus->link_id * 8);
	multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK);
	if (!multi_link) {
		dev_dbg(dev, "Multi-link is disabled\n");
		bus->multi_link = false;
	} else {
		/*
		 * hardware-based synchronization is required regardless
		 * of the number of segments used by a stream: SSP-based
		 * synchronization is gated by gsync when the multi-master
		 * mode is set.
		 */
		bus->multi_link = true;
		bus->hw_sync_min_links = 1;
	}

	/* Initialize shim, controller */
	ret = intel_init(sdw);
	if (ret)
		goto err_init;

	/* Read the PDI config and initialize cadence PDI */
	intel_pdi_init(sdw, &config);
	ret = sdw_cdns_pdi_init(cdns, config);
	if (ret)
		goto err_init;

	intel_pdi_ch_update(sdw);

	ret = sdw_cdns_enable_interrupt(cdns, true);
	if (ret < 0) {
		dev_err(dev, "cannot enable interrupts\n");
		goto err_init;
	}

	/*
	 * follow recommended programming flows to avoid timeouts when
	 * gsync is enabled
	 */
	if (multi_link)
		intel_shim_sync_arm(sdw);

	ret = sdw_cdns_init(cdns);
	if (ret < 0) {
		dev_err(dev, "unable to initialize Cadence IP\n");
		goto err_interrupt;
	}

	ret = sdw_cdns_exit_reset(cdns);
	if (ret < 0) {
		dev_err(dev, "unable to exit bus reset sequence\n");
		goto err_interrupt;
	}

	if (multi_link) {
		ret = intel_shim_sync_go(sdw);
		if (ret < 0) {
			dev_err(dev, "sync go failed: %d\n", ret);
			goto err_interrupt;
		}
	}
	sdw_cdns_check_self_clearing_bits(cdns, __func__,
					  true, INTEL_MASTER_RESET_ITERATIONS);

	/* Register DAIs */
	ret = intel_register_dai(sdw);
	if (ret) {
		dev_err(dev, "DAI registration failed: %d\n", ret);
		snd_soc_unregister_component(dev);
		goto err_interrupt;
	}

	intel_debugfs_init(sdw);

	/* Enable runtime PM */
	if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) {
		pm_runtime_set_autosuspend_delay(dev,
						 INTEL_MASTER_SUSPEND_DELAY_MS);
		pm_runtime_use_autosuspend(dev);
		pm_runtime_mark_last_busy(dev);

		pm_runtime_set_active(dev);
		pm_runtime_enable(dev);
	}

	clock_stop_quirks = sdw->link_res->clock_stop_quirks;
	if (clock_stop_quirks & SDW_INTEL_CLK_STOP_NOT_ALLOWED) {
		/*
		 * To keep the clock running we need to prevent
		 * pm_runtime suspend from happening by increasing the
		 * reference count.
		 * This quirk is specified by the parent PCI device in
		 * case of specific latency requirements. It will have
		 * no effect if pm_runtime is disabled by the user via
		 * a module parameter for testing purposes.
		 */
		pm_runtime_get_noresume(dev);
	}

	/*
	 * The runtime PM status of Slave devices is "Unsupported"
	 * until they report as ATTACHED. If they don't, e.g. because
	 * there are no Slave devices populated or if the power-on is
	 * delayed or dependent on a power switch, the Master will
	 * remain active and prevent its parent from suspending.
	 *
	 * Conditionally force the pm_runtime core to re-evaluate the
	 * Master status in the absence of any Slave activity. A quirk
	 * is provided to e.g. deal with Slaves that may be powered on
	 * with a delay. A more complete solution would require the
	 * definition of Master properties.
	 */
	if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE))
		pm_runtime_idle(dev);

	sdw->startup_done = true;
	return 0;

err_interrupt:
	sdw_cdns_enable_interrupt(cdns, false);
err_init:
	return ret;
}