Loading drivers/soundwire/intel.c +14 −6 Original line number Diff line number Diff line Loading @@ -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, ¶ms_data); return -EIO; } Loading Loading @@ -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; Loading drivers/soundwire/intel.h +8 −5 Original line number Diff line number Diff line Loading @@ -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 */ drivers/soundwire/intel_init.c +8 −24 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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; Loading Loading @@ -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)); Loading @@ -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)) { Loading Loading @@ -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 Loading @@ -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); } Loading include/linux/soundwire/sdw.h +19 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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) Loading include/linux/soundwire/sdw_intel.h +147 −9 Original line number Diff line number Diff line Loading @@ -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 Loading
drivers/soundwire/intel.c +14 −6 Original line number Diff line number Diff line Loading @@ -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, ¶ms_data); return -EIO; } Loading Loading @@ -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; Loading
drivers/soundwire/intel.h +8 −5 Original line number Diff line number Diff line Loading @@ -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 */
drivers/soundwire/intel_init.c +8 −24 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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; Loading Loading @@ -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)); Loading @@ -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)) { Loading Loading @@ -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 Loading @@ -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); } Loading
include/linux/soundwire/sdw.h +19 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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) Loading
include/linux/soundwire/sdw_intel.h +147 −9 Original line number Diff line number Diff line Loading @@ -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