/* * debug/config flags for the Intel SoundWire Master. * * Since we may have multiple masters active, we can have up to 8 * flags reused in each byte, with master0 using the ls-byte, etc.
*/
staticbool is_wake_capable(struct sdw_slave *slave)
{ int i;
for (i = 0; i < ARRAY_SIZE(wake_capable_list); i++) if (slave->id.part_id == wake_capable_list[i].part_id &&
slave->id.mfg_id == wake_capable_list[i].mfg_id) returntrue; returnfalse;
}
if (mclk_divider) /* use kernel parameter for BIOS or board work-arounds */
prop->mclk_freq /= mclk_divider; else /* the values reported by BIOS are the 2x clock, not the bus clock */
prop->mclk_freq /= 2;
/* * probe and init (aux_dev_id argument is required by function prototype but not used)
*/ staticint intel_link_probe(struct auxiliary_device *auxdev, conststruct auxiliary_device_id *aux_dev_id)
/* * paranoia check: make sure ACPI-reported number of links is aligned with * hardware capabilities.
*/
ret = sdw_intel_get_link_count(sdw); if (ret < 0) {
dev_err(dev, "%s: sdw_intel_get_link_count failed: %d\n", __func__, ret); return ret;
} if (ret <= sdw->instance) {
dev_err(dev, "%s: invalid link id %d, link count %d\n", __func__, auxdev->id, ret); return -EINVAL;
}
sdw_cdns_probe(cdns);
/* Set ops */
bus->ops = &sdw_intel_ops;
/* set driver data, accessed by snd_soc_dai_get_drvdata() */
auxiliary_set_drvdata(auxdev, cdns);
/* use generic bandwidth allocation algorithm */
sdw->cdns.bus.compute_params = sdw_compute_params;
ret = sdw_bus_master_add(bus, dev, dev->fwnode); if (ret) {
dev_err(dev, "sdw_bus_master_add fail: %d\n", ret); return ret;
}
if (bus->prop.hw_disabled)
dev_info(dev, "SoundWire master %d is disabled, will be ignored\n",
bus->link_id); /* * Ignore BIOS err_threshold, it's a really bad idea when dealing * with multiple hardware synchronized links
*/
bus->prop.err_threshold = 0;
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");
} 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->hw_sync_min_links = 1;
}
bus->multi_link = multi_link;
/* Initialize shim, controller */
ret = sdw_intel_link_power_up(sdw); if (ret) goto err_init;
/* Register DAIs */
ret = sdw_intel_register_dai(sdw); if (ret) {
dev_err(dev, "DAI registration failed: %d\n", ret); goto err_power_up;
}
/* start bus */
ret = sdw_intel_start_bus(sdw); if (ret) {
dev_err(dev, "bus start failed: %d\n", ret); goto err_pm_runtime;
}
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_mark_last_busy(bus->dev);
pm_runtime_mark_last_busy(dev);
pm_runtime_idle(dev);
}
/* * Since pm_runtime is already disabled, we don't decrease * the refcount when the clock_stop_quirk is * SDW_INTEL_CLK_STOP_NOT_ALLOWED
*/ if (!bus->prop.hw_disabled) {
sdw_intel_debugfs_exit(sdw);
cancel_delayed_work_sync(&cdns->attach_dwork);
sdw_cdns_enable_interrupt(cdns, false);
}
sdw_bus_master_delete(bus);
}
/* * resume the Master, which will generate a bus reset and result in * Slaves re-attaching and be re-enumerated. The SoundWire physical * device which generated the wake will trigger an interrupt, which * will in turn cause the corresponding Linux Slave device to be * resumed and the Slave codec driver to check the status.
*/
pm_request_resume(dev);
return 0;
}
/* * PM calls
*/
int intel_resume_child_device(struct device *dev, void *data)
{ int ret; struct sdw_slave *slave = dev_to_sdw_dev(dev);
if (!slave->probed) {
dev_dbg(dev, "skipping device, no probed driver\n"); return 0;
} if (!slave->dev_num_sticky) {
dev_dbg(dev, "skipping device, never detected on bus\n"); return 0;
}
ret = pm_runtime_resume(dev); if (ret < 0) {
dev_err(dev, "%s: pm_runtime_resume failed: %d\n", __func__, ret); return ret;
}
if (pm_runtime_suspended(dev) &&
pm_runtime_suspended(dev->parent) &&
((clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) ||
!clock_stop_quirks)) { /* * if we've enabled clock stop, and the parent is suspended, the SHIM registers * are not accessible and the shim wake cannot be disabled. * The only solution is to resume the entire bus to full power
*/
/* * If any operation in this block fails, we keep going since we don't want * to prevent system suspend from happening and errors should be recoverable * on resume.
*/
/* * first resume the device for this link. This will also by construction * resume the PCI parent device.
*/
ret = pm_runtime_resume(dev); if (ret < 0) {
dev_err(dev, "%s: pm_runtime_resume failed: %d\n", __func__, ret); return 0;
}
/* * Continue resuming the entire bus (parent + child devices) to exit * the clock stop mode. If there are no devices connected on this link * this is a no-op. * The resume to full power could have been implemented with a .prepare * step in SoundWire codec drivers. This would however require a lot * of code to handle an Intel-specific corner case. It is simpler in * practice to add a loop at the link level.
*/
ret = device_for_each_child(bus->dev, NULL, intel_resume_child_device);
if ((clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) ||
!clock_stop_quirks) {
if (pm_runtime_status_suspended(dev->parent)) { /* * paranoia check: this should not happen with the .prepare * resume to full power
*/
dev_err(dev, "%s: invalid config: parent is suspended\n", __func__);
} else {
sdw_intel_shim_wake(sdw, false);
}
}
return 0;
}
ret = sdw_intel_stop_bus(sdw, false); if (ret < 0) {
dev_err(dev, "%s: cannot stop bus: %d\n", __func__, ret); return ret;
}
if (bus->prop.hw_disabled || !sdw->startup_done) {
dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n",
bus->link_id); return 0;
}
ret = sdw_intel_link_power_up(sdw); if (ret) {
dev_err(dev, "%s failed: %d\n", __func__, ret); return ret;
}
/* * make sure all Slaves are tagged as UNATTACHED and provide * reason for reinitialization
*/
sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
ret = sdw_intel_start_bus(sdw); if (ret < 0) {
dev_err(dev, "cannot start bus during resume\n");
sdw_intel_link_power_down(sdw); return ret;
}
/* * Runtime PM has been disabled in intel_suspend(), so set the status * to active because the device has just been resumed and re-enable * runtime PM.
*/
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
/* * after system resume, the pm_runtime suspend() may kick in * during the enumeration, before any children device force the * master device to remain active. Using pm_runtime_get() * routines is not really possible, since it'd prevent the * master from suspending. * A reasonable compromise is to update the pm_runtime * counters and delay the pm_runtime suspend by several * seconds, by when all enumeration should be complete.
*/
pm_runtime_mark_last_busy(bus->dev);
pm_runtime_mark_last_busy(dev);
if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) {
ret = sdw_intel_link_power_up(sdw); if (ret) {
dev_err(dev, "%s: power_up failed after teardown: %d\n", __func__, ret); return ret;
}
/* * make sure all Slaves are tagged as UNATTACHED and provide * reason for reinitialization
*/
sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
ret = sdw_intel_start_bus(sdw); if (ret < 0) {
dev_err(dev, "%s: cannot start bus after teardown: %d\n", __func__, ret);
sdw_intel_link_power_down(sdw); return ret;
}
} elseif (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) {
ret = sdw_intel_link_power_up(sdw); if (ret) {
dev_err(dev, "%s: power_up failed after bus reset: %d\n", __func__, ret); return ret;
}
ret = sdw_intel_start_bus_after_reset(sdw); if (ret < 0) {
dev_err(dev, "%s: cannot start bus after reset: %d\n", __func__, ret);
sdw_intel_link_power_down(sdw); return ret;
}
} elseif (!clock_stop_quirks) {
sdw_intel_check_clock_stop(sdw);
ret = sdw_intel_link_power_up(sdw); if (ret) {
dev_err(dev, "%s: power_up failed: %d\n", __func__, ret); return ret;
}
ret = sdw_intel_start_bus_after_clock_stop(sdw); if (ret < 0) {
dev_err(dev, "%s: cannot start bus after clock stop: %d\n", __func__, ret);
sdw_intel_link_power_down(sdw); return ret;
}
} else {
dev_err(dev, "%s: clock_stop_quirks %x unsupported\n",
__func__, clock_stop_quirks);
ret = -EINVAL;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.