aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2020-01-09 21:21:51 +0000
committerMark Brown <broonie@kernel.org>2020-01-09 21:21:51 +0000
commitc23ff4b3ba6547e857427524a5ba577d56249b0d (patch)
treed6a54e54c33e3834c8b28746111d3524668db0fe
parentASoC: codecs: add wsa881x amplifier support (diff)
parentsoundwire: intel: add clock stop quirks (diff)
downloadlinux-dev-c23ff4b3ba6547e857427524a5ba577d56249b0d.tar.xz
linux-dev-c23ff4b3ba6547e857427524a5ba577d56249b0d.zip
Merge tag 'sdw_interfaces_5.6' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire into asoc-5.6
SoundWire tag for ASoC This contains the recently merged soundwire interface changes for ASoC subsystem.
-rw-r--r--drivers/soundwire/intel.c20
-rw-r--r--drivers/soundwire/intel.h13
-rw-r--r--drivers/soundwire/intel_init.c32
-rw-r--r--include/linux/soundwire/sdw.h19
-rw-r--r--include/linux/soundwire/sdw_intel.h156
5 files changed, 196 insertions, 44 deletions
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c
index 99dc61021211..0371d3d5501a 100644
--- a/drivers/soundwire/intel.c
+++ b/drivers/soundwire/intel.c
@@ -529,17 +529,24 @@ intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf);
}
-static int intel_config_stream(struct sdw_intel *sdw,
+static int intel_params_stream(struct sdw_intel *sdw,
struct snd_pcm_substream *substream,
struct snd_soc_dai *dai,
- struct snd_pcm_hw_params *hw_params, int link_id)
+ struct snd_pcm_hw_params *hw_params,
+ int link_id, int alh_stream_id)
{
struct sdw_intel_link_res *res = sdw->res;
+ struct sdw_intel_stream_params_data params_data;
- if (res->ops && res->ops->config_stream && res->arg)
- return res->ops->config_stream(res->arg,
- substream, dai, hw_params, link_id);
+ params_data.substream = substream;
+ params_data.dai = dai;
+ params_data.hw_params = hw_params;
+ params_data.link_id = link_id;
+ params_data.alh_stream_id = alh_stream_id;
+ if (res->ops && res->ops->params_stream && res->dev)
+ return res->ops->params_stream(res->dev,
+ &params_data);
return -EIO;
}
@@ -654,7 +661,8 @@ static int intel_hw_params(struct snd_pcm_substream *substream,
/* Inform DSP about PDI stream number */
- ret = intel_config_stream(sdw, substream, dai, params,
+ ret = intel_params_stream(sdw, substream, dai, params,
+ sdw->instance,
pdi->intel_alh_id);
if (ret)
goto error;
diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h
index d923b6262330..38b7c125fb10 100644
--- a/drivers/soundwire/intel.h
+++ b/drivers/soundwire/intel.h
@@ -5,23 +5,26 @@
#define __SDW_INTEL_LOCAL_H
/**
- * struct sdw_intel_link_res - Soundwire link resources
+ * struct sdw_intel_link_res - Soundwire Intel link resource structure,
+ * typically populated by the controller driver.
+ * @pdev: platform_device
+ * @mmio_base: mmio base of SoundWire registers
* @registers: Link IO registers base
* @shim: Audio shim pointer
* @alh: ALH (Audio Link Hub) pointer
* @irq: Interrupt line
* @ops: Shim callback ops
- * @arg: Shim callback ops argument
- *
- * This is set as pdata for each link instance.
+ * @dev: device implementing hw_params and free callbacks
*/
struct sdw_intel_link_res {
+ struct platform_device *pdev;
+ void __iomem *mmio_base; /* not strictly needed, useful for debug */
void __iomem *registers;
void __iomem *shim;
void __iomem *alh;
int irq;
const struct sdw_intel_ops *ops;
- void *arg;
+ struct device *dev;
};
#endif /* __SDW_INTEL_LOCAL_H */
diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c
index 2a2b4d8df462..4b769409f6f8 100644
--- a/drivers/soundwire/intel_init.c
+++ b/drivers/soundwire/intel_init.c
@@ -27,19 +27,9 @@ static int link_mask;
module_param_named(sdw_link_mask, link_mask, int, 0444);
MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)");
-struct sdw_link_data {
- struct sdw_intel_link_res res;
- struct platform_device *pdev;
-};
-
-struct sdw_intel_ctx {
- int count;
- struct sdw_link_data *links;
-};
-
static int sdw_intel_cleanup_pdev(struct sdw_intel_ctx *ctx)
{
- struct sdw_link_data *link = ctx->links;
+ struct sdw_intel_link_res *link = ctx->links;
int i;
if (!link)
@@ -62,7 +52,7 @@ static struct sdw_intel_ctx
{
struct platform_device_info pdevinfo;
struct platform_device *pdev;
- struct sdw_link_data *link;
+ struct sdw_intel_link_res *link;
struct sdw_intel_ctx *ctx;
struct acpi_device *adev;
int ret, i;
@@ -123,14 +113,13 @@ static struct sdw_intel_ctx
continue;
}
- link->res.irq = res->irq;
- link->res.registers = res->mmio_base + SDW_LINK_BASE
+ link->registers = res->mmio_base + SDW_LINK_BASE
+ (SDW_LINK_SIZE * i);
- link->res.shim = res->mmio_base + SDW_SHIM_BASE;
- link->res.alh = res->mmio_base + SDW_ALH_BASE;
+ link->shim = res->mmio_base + SDW_SHIM_BASE;
+ link->alh = res->mmio_base + SDW_ALH_BASE;
- link->res.ops = res->ops;
- link->res.arg = res->arg;
+ link->ops = res->ops;
+ link->dev = res->dev;
memset(&pdevinfo, 0, sizeof(pdevinfo));
@@ -138,8 +127,6 @@ static struct sdw_intel_ctx
pdevinfo.name = "int-sdw";
pdevinfo.id = i;
pdevinfo.fwnode = acpi_fwnode_handle(adev);
- pdevinfo.data = &link->res;
- pdevinfo.size_data = sizeof(link->res);
pdev = platform_device_register_full(&pdevinfo);
if (IS_ERR(pdev)) {
@@ -216,7 +203,6 @@ void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res)
return sdw_intel_add_controller(res);
}
-EXPORT_SYMBOL(sdw_intel_init);
/**
* sdw_intel_exit() - SoundWire Intel exit
@@ -224,10 +210,8 @@ EXPORT_SYMBOL(sdw_intel_init);
*
* Delete the controller instances created and cleanup
*/
-void sdw_intel_exit(void *arg)
+void sdw_intel_exit(struct sdw_intel_ctx *ctx)
{
- struct sdw_intel_ctx *ctx = arg;
-
sdw_intel_cleanup_pdev(ctx);
kfree(ctx);
}
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h
index 28745b9ba279..b7c9eca4332a 100644
--- a/include/linux/soundwire/sdw.h
+++ b/include/linux/soundwire/sdw.h
@@ -547,6 +547,20 @@ struct sdw_slave_ops {
* @node: node for bus list
* @port_ready: Port ready completion flag for each Slave port
* @dev_num: Device Number assigned by Bus
+ * @probed: boolean tracking driver state
+ * @probe_complete: completion utility to control potential races
+ * on startup between driver probe/initialization and SoundWire
+ * Slave state changes/implementation-defined interrupts
+ * @enumeration_complete: completion utility to control potential races
+ * on startup between device enumeration and read/write access to the
+ * Slave device
+ * @initialization_complete: completion utility to control potential races
+ * on startup between device enumeration and settings being restored
+ * @unattach_request: mask field to keep track why the Slave re-attached and
+ * was re-initialized. This is useful to deal with potential race conditions
+ * between the Master suspending and the codec resuming, and make sure that
+ * when the Master triggered a reset the Slave is properly enumerated and
+ * initialized
*/
struct sdw_slave {
struct sdw_slave_id id;
@@ -561,6 +575,11 @@ struct sdw_slave {
struct list_head node;
struct completion *port_ready;
u16 dev_num;
+ bool probed;
+ struct completion probe_complete;
+ struct completion enumeration_complete;
+ struct completion initialization_complete;
+ u32 unattach_request;
};
#define dev_to_sdw_dev(_dev) container_of(_dev, struct sdw_slave, dev)
diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h
index c9427cb6020b..93b83bdf8035 100644
--- a/include/linux/soundwire/sdw_intel.h
+++ b/include/linux/soundwire/sdw_intel.h
@@ -4,36 +4,174 @@
#ifndef __SDW_INTEL_H
#define __SDW_INTEL_H
+#include <linux/irqreturn.h>
+
+/**
+ * struct sdw_intel_stream_params_data: configuration passed during
+ * the @params_stream callback, e.g. for interaction with DSP
+ * firmware.
+ */
+struct sdw_intel_stream_params_data {
+ struct snd_pcm_substream *substream;
+ struct snd_soc_dai *dai;
+ struct snd_pcm_hw_params *hw_params;
+ int link_id;
+ int alh_stream_id;
+};
+
+/**
+ * struct sdw_intel_stream_free_data: configuration passed during
+ * the @free_stream callback, e.g. for interaction with DSP
+ * firmware.
+ */
+struct sdw_intel_stream_free_data {
+ struct snd_pcm_substream *substream;
+ struct snd_soc_dai *dai;
+ int link_id;
+};
+
/**
* struct sdw_intel_ops: Intel audio driver callback ops
*
- * @config_stream: configure the stream with the hw_params
- * the first argument containing the context is mandatory
*/
struct sdw_intel_ops {
- int (*config_stream)(void *arg, void *substream,
- void *dai, void *hw_params, int stream_num);
+ int (*params_stream)(struct device *dev,
+ struct sdw_intel_stream_params_data *params_data);
+ int (*free_stream)(struct device *dev,
+ struct sdw_intel_stream_free_data *free_data);
+};
+
+/**
+ * struct sdw_intel_acpi_info - Soundwire Intel information found in ACPI tables
+ * @handle: ACPI controller handle
+ * @count: link count found with "sdw-master-count" property
+ * @link_mask: bit-wise mask listing links enabled by BIOS menu
+ *
+ * this structure could be expanded to e.g. provide all the _ADR
+ * information in case the link_mask is not sufficient to identify
+ * platform capabilities.
+ */
+struct sdw_intel_acpi_info {
+ acpi_handle handle;
+ int count;
+ u32 link_mask;
+};
+
+struct sdw_intel_link_res;
+
+/* Intel clock-stop/pm_runtime quirk definitions */
+
+/*
+ * Force the clock to remain on during pm_runtime suspend. This might
+ * be needed if Slave devices do not have an alternate clock source or
+ * if the latency requirements are very strict.
+ */
+#define SDW_INTEL_CLK_STOP_NOT_ALLOWED BIT(0)
+
+/*
+ * Stop the bus during pm_runtime suspend. If set, a complete bus
+ * reset and re-enumeration will be performed when the bus
+ * restarts. This mode shall not be used if Slave devices can generate
+ * in-band wakes.
+ */
+#define SDW_INTEL_CLK_STOP_TEARDOWN BIT(1)
+
+/*
+ * Stop the bus during pm_suspend if Slaves are not wake capable
+ * (e.g. speaker amplifiers). The clock-stop mode is typically
+ * slightly higher power than when the IP is completely powered-off.
+ */
+#define SDW_INTEL_CLK_STOP_WAKE_CAPABLE_ONLY BIT(2)
+
+/*
+ * Require a bus reset (and complete re-enumeration) when exiting
+ * clock stop modes. This may be needed if the controller power was
+ * turned off and all context lost. This quirk shall not be used if a
+ * Slave device needs to remain enumerated and keep its context,
+ * e.g. to provide the reasons for the wake, report acoustic events or
+ * pass a history buffer.
+ */
+#define SDW_INTEL_CLK_STOP_BUS_RESET BIT(3)
+
+/**
+ * struct sdw_intel_ctx - context allocated by the controller
+ * driver probe
+ * @count: link count
+ * @mmio_base: mmio base of SoundWire registers, only used to check
+ * hardware capabilities after all power dependencies are settled.
+ * @link_mask: bit-wise mask listing SoundWire links reported by the
+ * Controller
+ * @handle: ACPI parent handle
+ * @links: information for each link (controller-specific and kept
+ * opaque here)
+ * @link_list: list to handle interrupts across all links
+ * @shim_lock: mutex to handle concurrent rmw access to shared SHIM registers.
+ */
+struct sdw_intel_ctx {
+ int count;
+ void __iomem *mmio_base;
+ u32 link_mask;
+ acpi_handle handle;
+ struct sdw_intel_link_res *links;
+ struct list_head link_list;
+ struct mutex shim_lock; /* lock for access to shared SHIM registers */
};
/**
- * struct sdw_intel_res - Soundwire Intel resource structure
+ * struct sdw_intel_res - Soundwire Intel global resource structure,
+ * typically populated by the DSP driver
+ *
+ * @count: link count
* @mmio_base: mmio base of SoundWire registers
* @irq: interrupt number
* @handle: ACPI parent handle
* @parent: parent device
* @ops: callback ops
- * @arg: callback arg
+ * @dev: device implementing hwparams and free callbacks
+ * @link_mask: bit-wise mask listing links selected by the DSP driver
+ * This mask may be a subset of the one reported by the controller since
+ * machine-specific quirks are handled in the DSP driver.
+ * @clock_stop_quirks: mask array of possible behaviors requested by the
+ * DSP driver. The quirks are common for all links for now.
*/
struct sdw_intel_res {
+ int count;
void __iomem *mmio_base;
int irq;
acpi_handle handle;
struct device *parent;
const struct sdw_intel_ops *ops;
- void *arg;
+ struct device *dev;
+ u32 link_mask;
+ u32 clock_stop_quirks;
};
-void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res);
-void sdw_intel_exit(void *arg);
+/*
+ * On Intel platforms, the SoundWire IP has dependencies on power
+ * rails shared with the DSP, and the initialization steps are split
+ * in three. First an ACPI scan to check what the firmware describes
+ * in DSDT tables, then an allocation step (with no hardware
+ * configuration but with all the relevant devices created) and last
+ * the actual hardware configuration. The final stage is a global
+ * interrupt enable which is controlled by the DSP driver. Splitting
+ * these phases helps simplify the boot flow and make early decisions
+ * on e.g. which machine driver to select (I2S mode, HDaudio or
+ * SoundWire).
+ */
+int sdw_intel_acpi_scan(acpi_handle *parent_handle,
+ struct sdw_intel_acpi_info *info);
+
+void sdw_intel_process_wakeen_event(struct sdw_intel_ctx *ctx);
+
+struct sdw_intel_ctx *
+sdw_intel_probe(struct sdw_intel_res *res);
+
+int sdw_intel_startup(struct sdw_intel_ctx *ctx);
+
+void sdw_intel_exit(struct sdw_intel_ctx *ctx);
+
+void sdw_intel_enable_irq(void __iomem *mmio_base, bool enable);
+
+irqreturn_t sdw_intel_thread(int irq, void *dev_id);
#endif