878 lines · 23.0 KB · C
RawDownload
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)// Copyright(c) 2015-22 Intel Corporation. /* * Soundwire Intel Manager Driver */ #include <linux/acpi.h>#include <linux/debugfs.h>#include <linux/delay.h>#include <linux/module.h>#include <linux/interrupt.h>#include <linux/io.h>#include <linux/auxiliary_bus.h>#include <sound/pcm_params.h>#include <linux/pm_runtime.h>#include <sound/soc.h>#include <linux/soundwire/sdw_registers.h>#include <linux/soundwire/sdw.h>#include <linux/soundwire/sdw_intel.h>#include "cadence_master.h"#include "bus.h"#include "intel.h"#include "intel_auxdevice.h" #define INTEL_MASTER_SUSPEND_DELAY_MS	3000 /* * 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. */ #define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME		BIT(0)#define SDW_INTEL_MASTER_DISABLE_CLOCK_STOP		BIT(1)#define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE	BIT(2)#define SDW_INTEL_MASTER_DISABLE_MULTI_LINK		BIT(3) static int md_flags;module_param_named(sdw_md_flags, md_flags, int, 0444);MODULE_PARM_DESC(sdw_md_flags, "SoundWire Intel Master device flags (0x0 all off)"); static int mclk_divider;module_param_named(sdw_mclk_divider, mclk_divider, int, 0444);MODULE_PARM_DESC(sdw_mclk_divider, "SoundWire Intel mclk divider"); struct wake_capable_part {	const u16 mfg_id;	const u16 part_id;}; static struct wake_capable_part wake_capable_list[] = {	{0x01fa, 0x2A30},	{0x01fa, 0x2A3B},	{0x01fa, 0x4243},	{0x01fa, 0x4244},	{0x01fa, 0x4245},	{0x01fa, 0x4249},	{0x01fa, 0x4747},	{0x025d, 0x5682},	{0x025d, 0x700},	{0x025d, 0x711},	{0x025d, 0x1712},	{0x025d, 0x1713},	{0x025d, 0x1716},	{0x025d, 0x1717},	{0x025d, 0x712},	{0x025d, 0x713},	{0x025d, 0x714},	{0x025d, 0x715},	{0x025d, 0x716},	{0x025d, 0x717},	{0x025d, 0x721},	{0x025d, 0x722},	{0x04b3, 0x9356},}; static bool 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)			return true;	return false;} static int generic_bpt_send_async(struct sdw_bus *bus, struct sdw_slave *slave,				  struct sdw_bpt_msg *msg){	struct sdw_cdns *cdns = bus_to_cdns(bus);	struct sdw_intel *sdw = cdns_to_intel(cdns); 	if (sdw->link_res->hw_ops->bpt_send_async)		return sdw->link_res->hw_ops->bpt_send_async(sdw, slave, msg);	return -EOPNOTSUPP;} static int generic_bpt_wait(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg){	struct sdw_cdns *cdns = bus_to_cdns(bus);	struct sdw_intel *sdw = cdns_to_intel(cdns); 	if (sdw->link_res->hw_ops->bpt_wait)		return sdw->link_res->hw_ops->bpt_wait(sdw, slave, msg);	return -EOPNOTSUPP;} static int generic_pre_bank_switch(struct sdw_bus *bus){	struct sdw_cdns *cdns = bus_to_cdns(bus);	struct sdw_intel *sdw = cdns_to_intel(cdns); 	return sdw->link_res->hw_ops->pre_bank_switch(sdw);} static int generic_post_bank_switch(struct sdw_bus *bus){	struct sdw_cdns *cdns = bus_to_cdns(bus);	struct sdw_intel *sdw = cdns_to_intel(cdns); 	return sdw->link_res->hw_ops->post_bank_switch(sdw);} static void generic_new_peripheral_assigned(struct sdw_bus *bus,					    struct sdw_slave *slave,					    int dev_num){	struct sdw_cdns *cdns = bus_to_cdns(bus);	struct sdw_intel *sdw = cdns_to_intel(cdns);	int dev_num_min;	int dev_num_max;	bool wake_capable = slave->prop.wake_capable || is_wake_capable(slave); 	if (wake_capable) {		dev_num_min = SDW_INTEL_DEV_NUM_IDA_MIN;		dev_num_max = SDW_MAX_DEVICES;	} else {		dev_num_min = 1;		dev_num_max = SDW_INTEL_DEV_NUM_IDA_MIN - 1;	} 	/* paranoia check, this should never happen */	if (dev_num < dev_num_min || dev_num > dev_num_max)  {		dev_err(bus->dev, "%s: invalid dev_num %d, wake supported %d\n",			__func__, dev_num, slave->prop.wake_capable);		return;	} 	if (sdw->link_res->hw_ops->program_sdi && wake_capable)		sdw->link_res->hw_ops->program_sdi(sdw, dev_num);} static int sdw_master_read_intel_prop(struct sdw_bus *bus){	struct sdw_master_prop *prop = &bus->prop;	struct sdw_intel_prop *intel_prop;	struct fwnode_handle *link;	char name[32];	u32 quirk_mask; 	/* Find master handle */	snprintf(name, sizeof(name),		 "mipi-sdw-link-%d-subproperties", bus->link_id); 	link = device_get_named_child_node(bus->dev, name);	if (!link) {		dev_err(bus->dev, "Master node %s not found\n", name);		return -EIO;	} 	fwnode_property_read_u32(link,				 "intel-sdw-ip-clock",				 &prop->mclk_freq); 	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; 	fwnode_property_read_u32(link,				 "intel-quirk-mask",				 &quirk_mask); 	if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE)		prop->hw_disabled = true; 	prop->quirks = SDW_MASTER_QUIRKS_CLEAR_INITIAL_CLASH |		SDW_MASTER_QUIRKS_CLEAR_INITIAL_PARITY; 	intel_prop = devm_kzalloc(bus->dev, sizeof(*intel_prop), GFP_KERNEL);	if (!intel_prop) {		fwnode_handle_put(link);		return -ENOMEM;	} 	/* initialize with hardware defaults, in case the properties are not found */	intel_prop->clde = 0x0;	intel_prop->doaise2 = 0x0;	intel_prop->dodse2 = 0x0;	intel_prop->clds = 0x0;	intel_prop->clss = 0x0;	intel_prop->doaise = 0x1;	intel_prop->doais = 0x3;	intel_prop->dodse  = 0x0;	intel_prop->dods  = 0x1; 	fwnode_property_read_u16(link,				 "intel-sdw-clde",				 &intel_prop->clde);	fwnode_property_read_u16(link,				 "intel-sdw-doaise2",				 &intel_prop->doaise2);	fwnode_property_read_u16(link,				 "intel-sdw-dodse2",				 &intel_prop->dodse2);	fwnode_property_read_u16(link,				 "intel-sdw-clds",				 &intel_prop->clds);	fwnode_property_read_u16(link,				 "intel-sdw-clss",				 &intel_prop->clss);	fwnode_property_read_u16(link,				 "intel-sdw-doaise",				 &intel_prop->doaise);	fwnode_property_read_u16(link,				 "intel-sdw-doais",				 &intel_prop->doais);	fwnode_property_read_u16(link,				 "intel-sdw-dodse",				 &intel_prop->dodse);	fwnode_property_read_u16(link,				 "intel-sdw-dods",				 &intel_prop->dods);	bus->vendor_specific_prop = intel_prop; 	dev_dbg(bus->dev, "doaise %#x doais %#x dodse %#x dods %#x\n",		intel_prop->doaise,		intel_prop->doais,		intel_prop->dodse,		intel_prop->dods); 	fwnode_handle_put(link); 	return 0;} static int intel_prop_read(struct sdw_bus *bus){	/* Initialize with default handler to read all DisCo properties */	sdw_master_read_prop(bus); 	/* read Intel-specific properties */	sdw_master_read_intel_prop(bus); 	return 0;} static DEFINE_IDA(intel_peripheral_ida); static int intel_get_device_num_ida(struct sdw_bus *bus, struct sdw_slave *slave){	int bit; 	if (slave->prop.wake_capable || is_wake_capable(slave))		return ida_alloc_range(&intel_peripheral_ida,				       SDW_INTEL_DEV_NUM_IDA_MIN, SDW_MAX_DEVICES,				       GFP_KERNEL); 	bit = find_first_zero_bit(slave->bus->assigned, SDW_MAX_DEVICES);	if (bit == SDW_MAX_DEVICES)		return -ENODEV; 	return bit;} static void intel_put_device_num_ida(struct sdw_bus *bus, struct sdw_slave *slave){	if (slave->prop.wake_capable || is_wake_capable(slave))		ida_free(&intel_peripheral_ida, slave->dev_num);} static struct sdw_master_ops sdw_intel_ops = {	.read_prop = intel_prop_read,	.override_adr = sdw_dmi_override_adr,	.xfer_msg = cdns_xfer_msg,	.xfer_msg_defer = cdns_xfer_msg_defer,	.set_bus_conf = cdns_bus_conf,	.pre_bank_switch = generic_pre_bank_switch,	.post_bank_switch = generic_post_bank_switch,	.read_ping_status = cdns_read_ping_status,	.get_device_num =  intel_get_device_num_ida,	.put_device_num = intel_put_device_num_ida,	.new_peripheral_assigned = generic_new_peripheral_assigned, 	.bpt_send_async = generic_bpt_send_async,	.bpt_wait = generic_bpt_wait,}; /* * probe and init (aux_dev_id argument is required by function prototype but not used) */static int intel_link_probe(struct auxiliary_device *auxdev,			    const struct auxiliary_device_id *aux_dev_id) {	struct device *dev = &auxdev->dev;	struct sdw_intel_link_dev *ldev = auxiliary_dev_to_sdw_intel_link_dev(auxdev);	struct sdw_intel *sdw;	struct sdw_cdns *cdns;	struct sdw_bus *bus;	int ret; 	sdw = devm_kzalloc(dev, sizeof(*sdw), GFP_KERNEL);	if (!sdw)		return -ENOMEM; 	cdns = &sdw->cdns;	bus = &cdns->bus; 	sdw->instance = auxdev->id;	sdw->link_res = &ldev->link_res;	cdns->dev = dev;	cdns->registers = sdw->link_res->registers;	cdns->ip_offset = sdw->link_res->ip_offset;	cdns->instance = sdw->instance;	cdns->msg_count = 0; 	/* single controller for all SoundWire links */	bus->controller_id = 0; 	bus->link_id = auxdev->id;	bus->clk_stop_timeout = 1; 	/*	 * 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; 	return 0;} int intel_link_startup(struct auxiliary_device *auxdev){	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");	} 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;	} 	sdw_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); 		pm_runtime_resume(bus->dev);	} 	/* 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);	} 	sdw->startup_done = true;	return 0; err_pm_runtime:	if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME))		pm_runtime_disable(dev);err_power_up:	sdw_intel_link_power_down(sdw);err_init:	return ret;} static void intel_link_remove(struct auxiliary_device *auxdev){	struct sdw_cdns *cdns = auxiliary_get_drvdata(auxdev);	struct sdw_intel *sdw = cdns_to_intel(cdns);	struct sdw_bus *bus = &cdns->bus; 

Lines 1–500 of 878.