From 41227b20c2ca43b84d4a6c934bc9cbd566df202d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 9 Oct 2019 11:34:58 -0500 Subject: [PATCH 001/110] soundwire: sdw_slave: add new fields to track probe status Changes to the sdw_slave structure needed to solve race conditions on driver probe. The functionality is added in the next patch. Signed-off-by: Pierre-Louis Bossart --- include/linux/soundwire/sdw.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 688b40e65c8988..a381a596212b15 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -545,6 +545,10 @@ 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/imp-def interrupts */ struct sdw_slave { struct sdw_slave_id id; @@ -559,6 +563,8 @@ struct sdw_slave { struct list_head node; struct completion *port_ready; u16 dev_num; + bool probed; + struct completion probe_complete; }; #define dev_to_sdw_dev(_dev) container_of(_dev, struct sdw_slave, dev) From 0b61091a0f0081512bbe0fb0df2b04c44a703d4c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 26 Sep 2019 18:42:47 -0500 Subject: [PATCH 002/110] soundwire: add enumeration_complete structure We need an async mechanism to prevent access to Slaves that are not fully-enumerated. init_completion() will be invoked when the Slave becomes UNATTACHED, and complete() will be invoked when the state become ATTACHED. Any read/write before this status change will be delayed with a wait_for_completion(). Signed-off-by: Pierre-Louis Bossart --- include/linux/soundwire/sdw.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index a381a596212b15..2c6688eb4c0951 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -549,6 +549,9 @@ struct sdw_slave_ops { * @probe_complete: completion utility to control potential races * on startup between driver probe/initialization and SoundWire * Slave state changes/imp-def interrupts + * @enumeration_complete: completion utility to control potential races + * on startup between device enumeration and read/write access to the + * Slave device */ struct sdw_slave { struct sdw_slave_id id; @@ -565,6 +568,7 @@ struct sdw_slave { u16 dev_num; bool probed; struct completion probe_complete; + struct completion enumeration_complete; }; #define dev_to_sdw_dev(_dev) container_of(_dev, struct sdw_slave, dev) From 3da420b819a7d0d7a76a28bd998dac2761077daf Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 4 Nov 2019 12:44:46 -0600 Subject: [PATCH 003/110] soundwire: add initialization_complete definition This helper is useful to make sure that the resume stages wait for the device to be fully initialized, not just enumerated. Depending on how initialization is handled, a Slave device driver may wait for enumeration_complete, or for initialization_complete, both are valid synchronization points. Signed-off-by: Pierre-Louis Bossart --- include/linux/soundwire/sdw.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 2c6688eb4c0951..6d667224ae8f8a 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -552,6 +552,8 @@ struct sdw_slave_ops { * @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 */ struct sdw_slave { struct sdw_slave_id id; @@ -569,6 +571,7 @@ struct sdw_slave { bool probed; struct completion probe_complete; struct completion enumeration_complete; + struct completion initialization_complete; }; #define dev_to_sdw_dev(_dev) container_of(_dev, struct sdw_slave, dev) From 9ff2159d18da82f88e3e296fef34dae377896499 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 23 Oct 2019 12:10:43 -0500 Subject: [PATCH 004/110] soundwire: intel: update interfaces between ASoC and SoundWire Expose updated interfaces in sdw_intel.h, mainly to allow SoundWire and ASoC parts to be merged separately once the header files are shared between trees. To avoid compilation issues, the conflicts in intel_init.c are blindly removed. This would in theory prevent the code from working, but since there are no users of the Intel Soundwire driver this has no impact. Functionality will be restored when the removal of platform devices is complete. Support for SoundWire + SOF builds will only be provided once all the required pieces are upstream. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.h | 9 ++-- drivers/soundwire/intel_init.c | 30 +++--------- include/linux/soundwire/sdw_intel.h | 71 +++++++++++++++++++++++++++-- 3 files changed, 79 insertions(+), 31 deletions(-) diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index d923b626233026..e4cc1d3804ff32 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -5,17 +5,20 @@ #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. */ 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; diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 2a2b4d8df46222..f8ae199094d39f 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,12 @@ 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; memset(&pdevinfo, 0, sizeof(pdevinfo)); @@ -138,8 +126,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)) { @@ -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_intel.h b/include/linux/soundwire/sdw_intel.h index c9427cb6020b3e..2cb65329973106 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -16,24 +16,85 @@ struct sdw_intel_ops { }; /** - * struct sdw_intel_res - Soundwire Intel resource structure + * 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_res - Soundwire Intel global resource structure, + * typically populated by the DSP driver + * + * @count: link count (may be filtered by DSP driver) * @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 */ 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; }; -void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res); -void sdw_intel_exit(void *arg); +struct sdw_intel_link_res; + +/** + * 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. + * @arg: Shim callback ops argument + */ +struct sdw_intel_ctx { + int count; + void __iomem *mmio_base; + u32 link_mask; + acpi_handle handle; + struct sdw_intel_link_res *links; +}; + +/* + * 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); + +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); #endif From e8a55aa7e8105efeac98a7241369beb99a1b3378 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Mon, 19 Aug 2019 13:32:09 -0500 Subject: [PATCH 005/110] soundwire: intel: update stream callbacks for hwparams/free stream operations Rename 'config_stream' as 'params_stream' to be closer to ALSA/ASoC concepts. Add a 'free_stream' callback in case any resources allocated in the 'params_stream' stage need to be released. Define structures for callbacks, mainly to allow for extensions as needed. Add the link_id and alh_stream_id parameters which are needed to align with firmware IPC needs. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 20 ++++++++++++------ drivers/soundwire/intel.h | 4 ++-- drivers/soundwire/intel_init.c | 1 + include/linux/soundwire/sdw_intel.h | 32 +++++++++++++++++++++++++---- 4 files changed, 45 insertions(+), 12 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 585a83fad75de1..90e9197ac8059f 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -523,17 +523,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; } @@ -648,7 +655,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 e4cc1d3804ff32..38b7c125fb1052 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -14,7 +14,7 @@ * @alh: ALH (Audio Link Hub) pointer * @irq: Interrupt line * @ops: Shim callback ops - * @arg: Shim callback ops argument + * @dev: device implementing hw_params and free callbacks */ struct sdw_intel_link_res { struct platform_device *pdev; @@ -24,7 +24,7 @@ struct sdw_intel_link_res { 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 f8ae199094d39f..6bc167c83b475e 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -119,6 +119,7 @@ static struct sdw_intel_ctx link->alh = res->mmio_base + SDW_ALH_BASE; link->ops = res->ops; + link->dev = res->dev; memset(&pdevinfo, 0, sizeof(pdevinfo)); diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h index 2cb65329973106..36e2b522a7494a 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -4,15 +4,39 @@ #ifndef __SDW_INTEL_H #define __SDW_INTEL_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); }; /** From 1ace40f3fedf7474a1efef699957b6322f0b9b1d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 17 Sep 2019 13:44:26 -0500 Subject: [PATCH 006/110] soundwire: intel: fix PDI/stream mapping for Bulk The previous formula is incorrect for PDI0/1, the mapping is not linear but has a discontinuity between PDI1 and PDI2. This change has no effect on PCM PDIs (same mapping). Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 90e9197ac8059f..0371d3d5501a97 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -480,7 +480,10 @@ intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) unsigned int link_id = sdw->instance; int pdi_conf = 0; - pdi->intel_alh_id = (link_id * 16) + pdi->num + 5; + /* the Bulk and PCM streams are not contiguous */ + pdi->intel_alh_id = (link_id * 16) + pdi->num + 3; + if (pdi->num >= 2) + pdi->intel_alh_id += 2; /* * Program stream parameters to stream SHIM register @@ -509,7 +512,10 @@ intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) unsigned int link_id = sdw->instance; unsigned int conf; - pdi->intel_alh_id = (link_id * 16) + pdi->num + 5; + /* the Bulk and PCM streams are not contiguous */ + pdi->intel_alh_id = (link_id * 16) + pdi->num + 3; + if (pdi->num >= 2) + pdi->intel_alh_id += 2; /* Program Stream config ALH register */ conf = intel_readl(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id)); From 9c71273b66d7b86c016cdb991265199a00b7eb74 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 21 Oct 2019 14:46:34 -0500 Subject: [PATCH 007/110] soundwire: remove bitfield for unique_id, use u8 There is no good reason why the unique_id needs to be stored as 4 bits. The code will work without changes with a u8 since all values are already filtered while parsing the ACPI tables and Slave devID registers. Use u8 representation. This will allow us to encode a "IGNORE_UNIQUE_ID" value to account for firmware/BIOS creativity. Signed-off-by: Pierre-Louis Bossart --- include/linux/soundwire/sdw.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 6d667224ae8f8a..a58939d3b611ac 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -403,6 +403,8 @@ int sdw_slave_read_prop(struct sdw_slave *slave); * SDW Slave Structures and APIs */ +#define SDW_IGNORED_UNIQUE_ID 0xFF + /** * struct sdw_slave_id - Slave ID * @mfg_id: MIPI Manufacturer ID @@ -418,7 +420,7 @@ struct sdw_slave_id { __u16 mfg_id; __u16 part_id; __u8 class_id; - __u8 unique_id:4; + __u8 unique_id; __u8 sdw_version:4; }; From fb8e530f42a1ffb4000c91cc99a49583f57f6da1 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 21 Oct 2019 12:30:48 -0500 Subject: [PATCH 008/110] soundwire: slave: add helper to extract slave ID Simplify the loop with a helper. The only functionality change is that we continue the loop even with an ACPI error. Follow-up patches will build on this change. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/slave.c | 50 ++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index 6473fa602f82cc..5dbc76772d21f3 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -64,6 +64,36 @@ static int sdw_slave_add(struct sdw_bus *bus, } #if IS_ENABLED(CONFIG_ACPI) + +static bool find_slave(struct sdw_bus *bus, + struct acpi_device *adev, + struct sdw_slave_id *id) +{ + unsigned long long addr; + unsigned int link_id; + acpi_status status; + + status = acpi_evaluate_integer(adev->handle, + METHOD_NAME__ADR, NULL, &addr); + + if (ACPI_FAILURE(status)) { + dev_err(bus->dev, "_ADR resolution failed: %x\n", + status); + return false; + } + + /* Extract link id from ADR, Bit 51 to 48 (included) */ + link_id = (addr >> 48) & GENMASK(3, 0); + + /* Check for link_id match */ + if (link_id != bus->link_id) + return false; + + sdw_extract_slave_id(bus, addr, id); + + return true; +} + /* * sdw_acpi_find_slaves() - Find Slave devices in Master ACPI node * @bus: SDW bus instance @@ -81,29 +111,11 @@ int sdw_acpi_find_slaves(struct sdw_bus *bus) } list_for_each_entry(adev, &parent->children, node) { - unsigned long long addr; struct sdw_slave_id id; - unsigned int link_id; - acpi_status status; - - status = acpi_evaluate_integer(adev->handle, - METHOD_NAME__ADR, NULL, &addr); - - if (ACPI_FAILURE(status)) { - dev_err(bus->dev, "_ADR resolution failed: %x\n", - status); - return status; - } - /* Extract link id from ADR, Bit 51 to 48 (included) */ - link_id = (addr >> 48) & GENMASK(3, 0); - - /* Check for link_id match */ - if (link_id != bus->link_id) + if (!find_slave(bus, adev, &id)) continue; - sdw_extract_slave_id(bus, addr, &id); - /* * don't error check for sdw_slave_add as we want to continue * adding Slaves From 5e68982ad54b255e17b7ccfeaf2ea53b40356292 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 21 Oct 2019 13:26:12 -0500 Subject: [PATCH 009/110] soundwire: ignore uniqueID when irrelevant The uniqueID is useful when there are two or more devices of the same type (identical manufacturer ID, part ID) on the same link. When there is a single device of a given type on a link, its uniqueID is irrelevant. It's not uncommon on actual platforms to see variations of the uniqueID, or differences between devID registers and ACPI _ADR fields. This patch suggests a filter on startup to identify 'single' devices and tag them accordingly. The uniqueID is then not used for the probe, and the device name omits the uniqueID as well. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 7 +++--- drivers/soundwire/slave.c | 52 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index fc53dbe57f8545..be5d437058ed19 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -422,10 +422,11 @@ static struct sdw_slave *sdw_get_slave(struct sdw_bus *bus, int i) static int sdw_compare_devid(struct sdw_slave *slave, struct sdw_slave_id id) { - if (slave->id.unique_id != id.unique_id || - slave->id.mfg_id != id.mfg_id || + if (slave->id.mfg_id != id.mfg_id || slave->id.part_id != id.part_id || - slave->id.class_id != id.class_id) + slave->id.class_id != id.class_id || + (slave->id.unique_id != SDW_IGNORED_UNIQUE_ID && + slave->id.unique_id != id.unique_id)) return -ENODEV; return 0; diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index 5dbc76772d21f3..19919975bb6da4 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -29,10 +29,17 @@ static int sdw_slave_add(struct sdw_bus *bus, slave->dev.parent = bus->dev; slave->dev.fwnode = fwnode; - /* name shall be sdw:link:mfg:part:class:unique */ - dev_set_name(&slave->dev, "sdw:%x:%x:%x:%x:%x", - bus->link_id, id->mfg_id, id->part_id, - id->class_id, id->unique_id); + if (id->unique_id == SDW_IGNORED_UNIQUE_ID) { + /* name shall be sdw:link:mfg:part:class */ + dev_set_name(&slave->dev, "sdw:%x:%x:%x:%x", + bus->link_id, id->mfg_id, id->part_id, + id->class_id); + } else { + /* name shall be sdw:link:mfg:part:class:unique */ + dev_set_name(&slave->dev, "sdw:%x:%x:%x:%x:%x", + bus->link_id, id->mfg_id, id->part_id, + id->class_id, id->unique_id); + } slave->dev.release = sdw_slave_release; slave->dev.bus = &sdw_bus_type; @@ -103,6 +110,7 @@ static bool find_slave(struct sdw_bus *bus, int sdw_acpi_find_slaves(struct sdw_bus *bus) { struct acpi_device *adev, *parent; + struct acpi_device *adev2, *parent2; parent = ACPI_COMPANION(bus->dev); if (!parent) { @@ -112,10 +120,46 @@ int sdw_acpi_find_slaves(struct sdw_bus *bus) list_for_each_entry(adev, &parent->children, node) { struct sdw_slave_id id; + struct sdw_slave_id id2; + bool ignore_unique_id = true; if (!find_slave(bus, adev, &id)) continue; + /* brute-force O(N^2) search for duplicates */ + parent2 = parent; + list_for_each_entry(adev2, &parent2->children, node) { + + if (adev == adev2) + continue; + + if (!find_slave(bus, adev2, &id2)) + continue; + + if (id.sdw_version != id2.sdw_version || + id.mfg_id != id2.mfg_id || + id.part_id != id2.part_id || + id.class_id != id2.class_id) + continue; + + if (id.unique_id != id2.unique_id) { + dev_dbg(bus->dev, + "Valid unique IDs %x %x for Slave mfg %x part %d\n", + id.unique_id, id2.unique_id, + id.mfg_id, id.part_id); + ignore_unique_id = false; + } else { + dev_err(bus->dev, + "Invalid unique IDs %x %x for Slave mfg %x part %d\n", + id.unique_id, id2.unique_id, + id.mfg_id, id.part_id); + return -ENODEV; + } + } + + if (ignore_unique_id) + id.unique_id = SDW_IGNORED_UNIQUE_ID; + /* * don't error check for sdw_slave_add as we want to continue * adding Slaves From f58212cc069163d5a0948e6128bffbceff2659a0 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 10 Sep 2019 18:46:08 -0500 Subject: [PATCH 010/110] soundwire: renames to prepare support for master drivers/devices Add clearer references to sdw_slave_driver for internal macros No change for sdw_driver and module_sdw_driver to avoid compatibility issues with existing codec devices No functionality change. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus_type.c | 21 +++++++++++---------- include/linux/soundwire/sdw_type.h | 18 ++++++++++-------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 4a465f55039f4b..370b9475266228 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -34,7 +34,7 @@ sdw_get_device_id(struct sdw_slave *slave, struct sdw_driver *drv) static int sdw_bus_match(struct device *dev, struct device_driver *ddrv) { struct sdw_slave *slave = dev_to_sdw_dev(dev); - struct sdw_driver *drv = drv_to_sdw_driver(ddrv); + struct sdw_driver *drv = drv_to_sdw_slave_driver(ddrv); return !!sdw_get_device_id(slave, drv); } @@ -70,7 +70,7 @@ EXPORT_SYMBOL_GPL(sdw_bus_type); static int sdw_drv_probe(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); - struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); + struct sdw_driver *drv = drv_to_sdw_slave_driver(dev->driver); const struct sdw_device_id *id; int ret; @@ -116,7 +116,7 @@ static int sdw_drv_probe(struct device *dev) static int sdw_drv_remove(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); - struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); + struct sdw_driver *drv = drv_to_sdw_slave_driver(dev->driver); int ret = 0; if (drv->remove) @@ -130,20 +130,21 @@ static int sdw_drv_remove(struct device *dev) static void sdw_drv_shutdown(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); - struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); + struct sdw_driver *drv = drv_to_sdw_slave_driver(dev->driver); if (drv->shutdown) drv->shutdown(slave); } /** - * __sdw_register_driver() - register a SoundWire Slave driver + * __sdw_register_slave_driver() - register a SoundWire Slave driver * @drv: driver to register * @owner: owning module/driver * * Return: zero on success, else a negative error code. */ -int __sdw_register_driver(struct sdw_driver *drv, struct module *owner) +int __sdw_register_slave_driver(struct sdw_driver *drv, + struct module *owner) { drv->driver.bus = &sdw_bus_type; @@ -164,17 +165,17 @@ int __sdw_register_driver(struct sdw_driver *drv, struct module *owner) return driver_register(&drv->driver); } -EXPORT_SYMBOL_GPL(__sdw_register_driver); +EXPORT_SYMBOL_GPL(__sdw_register_slave_driver); /** - * sdw_unregister_driver() - unregisters the SoundWire Slave driver + * sdw_unregister_slave_driver() - unregisters the SoundWire Slave driver * @drv: driver to unregister */ -void sdw_unregister_driver(struct sdw_driver *drv) +void sdw_unregister_slave_driver(struct sdw_driver *drv) { driver_unregister(&drv->driver); } -EXPORT_SYMBOL_GPL(sdw_unregister_driver); +EXPORT_SYMBOL_GPL(sdw_unregister_slave_driver); static int __init sdw_bus_init(void) { diff --git a/include/linux/soundwire/sdw_type.h b/include/linux/soundwire/sdw_type.h index aaa7f4267c149a..abaa2127815254 100644 --- a/include/linux/soundwire/sdw_type.h +++ b/include/linux/soundwire/sdw_type.h @@ -6,13 +6,15 @@ extern struct bus_type sdw_bus_type; -#define drv_to_sdw_driver(_drv) container_of(_drv, struct sdw_driver, driver) +#define drv_to_sdw_slave_driver(_drv) \ + container_of(_drv, struct sdw_driver, driver) -#define sdw_register_driver(drv) \ - __sdw_register_driver(drv, THIS_MODULE) +#define sdw_register_slave_driver(drv) \ + __sdw_register_slave_driver(drv, THIS_MODULE) -int __sdw_register_driver(struct sdw_driver *drv, struct module *owner); -void sdw_unregister_driver(struct sdw_driver *drv); +int __sdw_register_slave_driver(struct sdw_driver *drv, + struct module *owner); +void sdw_unregister_slave_driver(struct sdw_driver *drv); int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size); @@ -24,7 +26,7 @@ int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size); * module init/exit. This eliminates a lot of boilerplate. Each module may only * use this macro once, and calling it replaces module_init() and module_exit() */ -#define module_sdw_driver(__sdw_driver) \ - module_driver(__sdw_driver, sdw_register_driver, \ - sdw_unregister_driver) +#define module_sdw_driver(__sdw_slave_driver) \ + module_driver(__sdw_slave_driver, sdw_register_slave_driver, \ + sdw_unregister_slave_driver) #endif /* __SOUNDWIRE_TYPES_H */ From ea80c01a910d3d0d302ce0caca34c0e8c49d2b95 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 10 Sep 2019 19:58:17 -0500 Subject: [PATCH 011/110] soundwire: rename dev_to_sdw_dev macro Since we want to introduce master devices, rename macro so that we have consistency between slave and master device access, following the Grey Bus example. Signed-off-by: Pierre-Louis Bossart --- drivers/base/regmap/regmap-sdw.c | 4 ++-- drivers/soundwire/bus.c | 2 +- drivers/soundwire/bus_type.c | 11 ++++++----- drivers/soundwire/slave.c | 2 +- include/linux/soundwire/sdw.h | 3 ++- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/base/regmap/regmap-sdw.c b/drivers/base/regmap/regmap-sdw.c index 50a66382d87d09..d1fc0c22180a17 100644 --- a/drivers/base/regmap/regmap-sdw.c +++ b/drivers/base/regmap/regmap-sdw.c @@ -10,7 +10,7 @@ static int regmap_sdw_write(void *context, unsigned int reg, unsigned int val) { struct device *dev = context; - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = to_sdw_slave_device(dev); return sdw_write(slave, reg, val); } @@ -18,7 +18,7 @@ static int regmap_sdw_write(void *context, unsigned int reg, unsigned int val) static int regmap_sdw_read(void *context, unsigned int reg, unsigned int *val) { struct device *dev = context; - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = to_sdw_slave_device(dev); int read; read = sdw_read(slave, reg); diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index be5d437058ed19..4b22ee996a6566 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -110,7 +110,7 @@ EXPORT_SYMBOL(sdw_add_bus_master); static int sdw_delete_slave(struct device *dev, void *data) { - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = to_sdw_slave_device(dev); struct sdw_bus *bus = slave->bus; sdw_slave_debugfs_exit(slave); diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 370b9475266228..c0585bcc8a41b8 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -33,7 +33,7 @@ sdw_get_device_id(struct sdw_slave *slave, struct sdw_driver *drv) static int sdw_bus_match(struct device *dev, struct device_driver *ddrv) { - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = to_sdw_slave_device(dev); struct sdw_driver *drv = drv_to_sdw_slave_driver(ddrv); return !!sdw_get_device_id(slave, drv); @@ -49,7 +49,7 @@ int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size) static int sdw_uevent(struct device *dev, struct kobj_uevent_env *env) { - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = to_sdw_slave_device(dev); char modalias[32]; sdw_slave_modalias(slave, modalias, sizeof(modalias)); @@ -69,7 +69,7 @@ EXPORT_SYMBOL_GPL(sdw_bus_type); static int sdw_drv_probe(struct device *dev) { - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = to_sdw_slave_device(dev); struct sdw_driver *drv = drv_to_sdw_slave_driver(dev->driver); const struct sdw_device_id *id; int ret; @@ -115,8 +115,9 @@ static int sdw_drv_probe(struct device *dev) static int sdw_drv_remove(struct device *dev) { - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = to_sdw_slave_device(dev); struct sdw_driver *drv = drv_to_sdw_slave_driver(dev->driver); + int ret = 0; if (drv->remove) @@ -129,7 +130,7 @@ static int sdw_drv_remove(struct device *dev) static void sdw_drv_shutdown(struct device *dev) { - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = to_sdw_slave_device(dev); struct sdw_driver *drv = drv_to_sdw_slave_driver(dev->driver); if (drv->shutdown) diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index 19919975bb6da4..48a513680db665 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -9,7 +9,7 @@ static void sdw_slave_release(struct device *dev) { - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = to_sdw_slave_device(dev); kfree(slave); } diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index a58939d3b611ac..3b618d7e664143 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -576,7 +576,8 @@ struct sdw_slave { struct completion initialization_complete; }; -#define dev_to_sdw_dev(_dev) container_of(_dev, struct sdw_slave, dev) +#define to_sdw_slave_device(d) \ + container_of(d, struct sdw_slave, dev) struct sdw_driver { const char *name; From c46560f094b0ebcf446688141cae401d86853e51 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 10 Sep 2019 20:27:41 -0500 Subject: [PATCH 012/110] soundwire: rename drv_to_sdw_slave_driver macro Align with previous renames and shorten macro No functionality change Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus_type.c | 9 ++++----- include/linux/soundwire/sdw_type.h | 3 ++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index c0585bcc8a41b8..2b2830b622faef 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -34,7 +34,7 @@ sdw_get_device_id(struct sdw_slave *slave, struct sdw_driver *drv) static int sdw_bus_match(struct device *dev, struct device_driver *ddrv) { struct sdw_slave *slave = to_sdw_slave_device(dev); - struct sdw_driver *drv = drv_to_sdw_slave_driver(ddrv); + struct sdw_driver *drv = to_sdw_slave_driver(ddrv); return !!sdw_get_device_id(slave, drv); } @@ -70,7 +70,7 @@ EXPORT_SYMBOL_GPL(sdw_bus_type); static int sdw_drv_probe(struct device *dev) { struct sdw_slave *slave = to_sdw_slave_device(dev); - struct sdw_driver *drv = drv_to_sdw_slave_driver(dev->driver); + struct sdw_driver *drv = to_sdw_slave_driver(dev->driver); const struct sdw_device_id *id; int ret; @@ -116,8 +116,7 @@ static int sdw_drv_probe(struct device *dev) static int sdw_drv_remove(struct device *dev) { struct sdw_slave *slave = to_sdw_slave_device(dev); - struct sdw_driver *drv = drv_to_sdw_slave_driver(dev->driver); - + struct sdw_driver *drv = to_sdw_slave_driver(dev->driver); int ret = 0; if (drv->remove) @@ -131,7 +130,7 @@ static int sdw_drv_remove(struct device *dev) static void sdw_drv_shutdown(struct device *dev) { struct sdw_slave *slave = to_sdw_slave_device(dev); - struct sdw_driver *drv = drv_to_sdw_slave_driver(dev->driver); + struct sdw_driver *drv = to_sdw_slave_driver(dev->driver); if (drv->shutdown) drv->shutdown(slave); diff --git a/include/linux/soundwire/sdw_type.h b/include/linux/soundwire/sdw_type.h index abaa2127815254..7d4bc6a979bf6b 100644 --- a/include/linux/soundwire/sdw_type.h +++ b/include/linux/soundwire/sdw_type.h @@ -6,7 +6,7 @@ extern struct bus_type sdw_bus_type; -#define drv_to_sdw_slave_driver(_drv) \ +#define to_sdw_slave_driver(_drv) \ container_of(_drv, struct sdw_driver, driver) #define sdw_register_slave_driver(drv) \ @@ -29,4 +29,5 @@ int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size); #define module_sdw_driver(__sdw_slave_driver) \ module_driver(__sdw_slave_driver, sdw_register_slave_driver, \ sdw_unregister_slave_driver) + #endif /* __SOUNDWIRE_TYPES_H */ From 9e6843f604e171096bc1eb3b633d14a29aadd6a2 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 10 Sep 2019 21:53:10 -0500 Subject: [PATCH 013/110] soundwire: bus_type: rename sdw_drv_ to sdw_slave_drv Before we add master driver support, make sure there is no ambiguity and no occurrences of sdw_drv_ functions. No functionality change. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus_type.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 2b2830b622faef..9a0fd3ee1014a7 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -67,7 +67,7 @@ struct bus_type sdw_bus_type = { }; EXPORT_SYMBOL_GPL(sdw_bus_type); -static int sdw_drv_probe(struct device *dev) +static int sdw_slave_drv_probe(struct device *dev) { struct sdw_slave *slave = to_sdw_slave_device(dev); struct sdw_driver *drv = to_sdw_slave_driver(dev->driver); @@ -113,7 +113,7 @@ static int sdw_drv_probe(struct device *dev) return 0; } -static int sdw_drv_remove(struct device *dev) +static int sdw_slave_drv_remove(struct device *dev) { struct sdw_slave *slave = to_sdw_slave_device(dev); struct sdw_driver *drv = to_sdw_slave_driver(dev->driver); @@ -127,7 +127,7 @@ static int sdw_drv_remove(struct device *dev) return ret; } -static void sdw_drv_shutdown(struct device *dev) +static void sdw_slave_drv_shutdown(struct device *dev) { struct sdw_slave *slave = to_sdw_slave_device(dev); struct sdw_driver *drv = to_sdw_slave_driver(dev->driver); @@ -155,13 +155,13 @@ int __sdw_register_slave_driver(struct sdw_driver *drv, } drv->driver.owner = owner; - drv->driver.probe = sdw_drv_probe; + drv->driver.probe = sdw_slave_drv_probe; if (drv->remove) - drv->driver.remove = sdw_drv_remove; + drv->driver.remove = sdw_slave_drv_remove; if (drv->shutdown) - drv->driver.shutdown = sdw_drv_shutdown; + drv->driver.shutdown = sdw_slave_drv_shutdown; return driver_register(&drv->driver); } From a53c311db84dcc0be433a03438354e23d8f195ea Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 11 Sep 2019 14:26:53 -0500 Subject: [PATCH 014/110] soundwire: intel: rename res field as link_res There are too many fields called 'res' so add prefix to make it easier to track what the structures are. Pure rename, no functionality change Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 0371d3d5501a97..64f97bb1a13573 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -103,7 +103,7 @@ enum intel_pdi_type { struct sdw_intel { struct sdw_cdns cdns; int instance; - struct sdw_intel_link_res *res; + struct sdw_intel_link_res *link_res; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; #endif @@ -193,8 +193,8 @@ static ssize_t intel_sprintf(void __iomem *mem, bool l, static int intel_reg_show(struct seq_file *s_file, void *data) { struct sdw_intel *sdw = s_file->private; - void __iomem *s = sdw->res->shim; - void __iomem *a = sdw->res->alh; + void __iomem *s = sdw->link_res->shim; + void __iomem *a = sdw->link_res->alh; char *buf; ssize_t ret; int i, j; @@ -289,7 +289,7 @@ static void intel_debugfs_exit(struct sdw_intel *sdw) {} static int intel_link_power_up(struct sdw_intel *sdw) { unsigned int link_id = sdw->instance; - void __iomem *shim = sdw->res->shim; + void __iomem *shim = sdw->link_res->shim; int spa_mask, cpa_mask; int link_control, ret; @@ -309,7 +309,7 @@ static int intel_link_power_up(struct sdw_intel *sdw) static int intel_shim_init(struct sdw_intel *sdw) { - void __iomem *shim = sdw->res->shim; + void __iomem *shim = sdw->link_res->shim; unsigned int link_id = sdw->instance; int sync_reg, ret; u16 ioctl = 0, act = 0; @@ -370,7 +370,7 @@ static int intel_shim_init(struct sdw_intel *sdw) static void intel_pdi_init(struct sdw_intel *sdw, struct sdw_cdns_stream_config *config) { - void __iomem *shim = sdw->res->shim; + void __iomem *shim = sdw->link_res->shim; unsigned int link_id = sdw->instance; int pcm_cap, pdm_cap; @@ -404,7 +404,7 @@ static void intel_pdi_init(struct sdw_intel *sdw, static int intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm) { - void __iomem *shim = sdw->res->shim; + void __iomem *shim = sdw->link_res->shim; unsigned int link_id = sdw->instance; int count; @@ -476,7 +476,7 @@ static int intel_pdi_ch_update(struct sdw_intel *sdw) static void intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) { - void __iomem *shim = sdw->res->shim; + void __iomem *shim = sdw->link_res->shim; unsigned int link_id = sdw->instance; int pdi_conf = 0; @@ -508,7 +508,7 @@ intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) static void intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) { - void __iomem *alh = sdw->res->alh; + void __iomem *alh = sdw->link_res->alh; unsigned int link_id = sdw->instance; unsigned int conf; @@ -535,7 +535,7 @@ static int intel_params_stream(struct sdw_intel *sdw, 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_link_res *res = sdw->link_res; struct sdw_intel_stream_params_data params_data; params_data.substream = substream; @@ -558,7 +558,7 @@ static int intel_pre_bank_switch(struct sdw_bus *bus) { struct sdw_cdns *cdns = bus_to_cdns(bus); struct sdw_intel *sdw = cdns_to_intel(cdns); - void __iomem *shim = sdw->res->shim; + void __iomem *shim = sdw->link_res->shim; int sync_reg; /* Write to register only for multi-link */ @@ -577,7 +577,7 @@ static int intel_post_bank_switch(struct sdw_bus *bus) { struct sdw_cdns *cdns = bus_to_cdns(bus); struct sdw_intel *sdw = cdns_to_intel(cdns); - void __iomem *shim = sdw->res->shim; + void __iomem *shim = sdw->link_res->shim; int sync_reg, ret; /* Write to register only for multi-link */ @@ -934,9 +934,9 @@ static int intel_probe(struct platform_device *pdev) return -ENOMEM; sdw->instance = pdev->id; - sdw->res = dev_get_platdata(&pdev->dev); + sdw->link_res = dev_get_platdata(&pdev->dev); sdw->cdns.dev = &pdev->dev; - sdw->cdns.registers = sdw->res->registers; + sdw->cdns.registers = sdw->link_res->registers; sdw->cdns.instance = sdw->instance; sdw->cdns.msg_count = 0; sdw->cdns.bus.dev = &pdev->dev; @@ -976,11 +976,12 @@ static int intel_probe(struct platform_device *pdev) intel_pdi_ch_update(sdw); /* Acquire IRQ */ - ret = request_threaded_irq(sdw->res->irq, sdw_cdns_irq, sdw_cdns_thread, + ret = request_threaded_irq(sdw->link_res->irq, + sdw_cdns_irq, sdw_cdns_thread, IRQF_SHARED, KBUILD_MODNAME, &sdw->cdns); if (ret < 0) { dev_err(sdw->cdns.dev, "unable to grab IRQ %d, disabling device\n", - sdw->res->irq); + sdw->link_res->irq); goto err_init; } @@ -1010,7 +1011,7 @@ static int intel_probe(struct platform_device *pdev) err_interrupt: sdw_cdns_enable_interrupt(&sdw->cdns, false); - free_irq(sdw->res->irq, sdw); + free_irq(sdw->link_res->irq, sdw); err_init: sdw_delete_bus_master(&sdw->cdns.bus); return ret; @@ -1025,7 +1026,7 @@ static int intel_remove(struct platform_device *pdev) if (!sdw->cdns.bus.prop.hw_disabled) { intel_debugfs_exit(sdw); sdw_cdns_enable_interrupt(&sdw->cdns, false); - free_irq(sdw->res->irq, sdw); + free_irq(sdw->link_res->irq, sdw); snd_soc_unregister_component(sdw->cdns.dev); } sdw_delete_bus_master(&sdw->cdns.bus); From 55b771fbf4b809b00eeb8d4a92eb8b38d26582a3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 10 Sep 2019 19:11:58 -0500 Subject: [PATCH 015/110] soundwire: add support for sdw_slave_type Currently the bus does not have any explicit support for master devices. First add explicit support for sdw_slave_type and error checks if this type is not set. In follow-up patches we can add support for the sdw_md_type (md==Master Device), following the Grey Bus example. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus_type.c | 16 ++++++++++++---- drivers/soundwire/slave.c | 7 ++++++- include/linux/soundwire/sdw_type.h | 6 ++++++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 9a0fd3ee1014a7..bbdedce5eb261a 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -49,13 +49,21 @@ int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size) static int sdw_uevent(struct device *dev, struct kobj_uevent_env *env) { - struct sdw_slave *slave = to_sdw_slave_device(dev); + struct sdw_slave *slave; char modalias[32]; - sdw_slave_modalias(slave, modalias, sizeof(modalias)); + if (is_sdw_slave(dev)) { + slave = to_sdw_slave_device(dev); + + sdw_slave_modalias(slave, modalias, sizeof(modalias)); - if (add_uevent_var(env, "MODALIAS=%s", modalias)) - return -ENOMEM; + if (add_uevent_var(env, "MODALIAS=%s", modalias)) + return -ENOMEM; + } else { + /* only Slave device type supported */ + dev_warn(dev, "uevent for unknown Soundwire type\n"); + return -EINVAL; + } return 0; } diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index 48a513680db665..c87267f12a3b97 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -14,6 +14,11 @@ static void sdw_slave_release(struct device *dev) kfree(slave); } +struct device_type sdw_slave_type = { + .name = "sdw_slave", + .release = sdw_slave_release, +}; + static int sdw_slave_add(struct sdw_bus *bus, struct sdw_slave_id *id, struct fwnode_handle *fwnode) { @@ -41,9 +46,9 @@ static int sdw_slave_add(struct sdw_bus *bus, id->class_id, id->unique_id); } - slave->dev.release = sdw_slave_release; slave->dev.bus = &sdw_bus_type; slave->dev.of_node = of_node_get(to_of_node(fwnode)); + slave->dev.type = &sdw_slave_type; slave->bus = bus; slave->status = SDW_SLAVE_UNATTACHED; slave->dev_num = 0; diff --git a/include/linux/soundwire/sdw_type.h b/include/linux/soundwire/sdw_type.h index 7d4bc6a979bf6b..c681b3426478e7 100644 --- a/include/linux/soundwire/sdw_type.h +++ b/include/linux/soundwire/sdw_type.h @@ -5,6 +5,12 @@ #define __SOUNDWIRE_TYPES_H extern struct bus_type sdw_bus_type; +extern struct device_type sdw_slave_type; + +static inline int is_sdw_slave(const struct device *dev) +{ + return dev->type == &sdw_slave_type; +} #define to_sdw_slave_driver(_drv) \ container_of(_drv, struct sdw_driver, driver) From 466cf6cbb577acf1b46a66c43143449397d23371 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 10 Sep 2019 19:23:22 -0500 Subject: [PATCH 016/110] soundwire: add initial definitions for sdw_master_device Since we want an explicit support for the SoundWire Master device, add the definitions, following the Grey Bus example. Unlike for the Slave device, we do not set a variable when dealing with the master uevent. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/Makefile | 2 +- drivers/soundwire/bus_type.c | 4 +- drivers/soundwire/master.c | 62 ++++++++++++++++++++++++++++++ include/linux/soundwire/sdw.h | 35 +++++++++++++++++ include/linux/soundwire/sdw_type.h | 9 +++++ 5 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 drivers/soundwire/master.c diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index 563894e5ecaf50..89b29819dd3ad8 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -4,7 +4,7 @@ # #Bus Objs -soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o stream.o +soundwire-bus-objs := bus_type.o bus.o master.o slave.o mipi_disco.o stream.o obj-$(CONFIG_SOUNDWIRE) += soundwire-bus.o ifdef CONFIG_DEBUG_FS diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index bbdedce5eb261a..fd234f63bf2f4e 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -59,8 +59,8 @@ static int sdw_uevent(struct device *dev, struct kobj_uevent_env *env) if (add_uevent_var(env, "MODALIAS=%s", modalias)) return -ENOMEM; - } else { - /* only Slave device type supported */ + } else if (!is_sdw_md(dev)) { + /* only Slave and Master device type supported */ dev_warn(dev, "uevent for unknown Soundwire type\n"); return -EINVAL; } diff --git a/drivers/soundwire/master.c b/drivers/soundwire/master.c new file mode 100644 index 00000000000000..6210098c892bad --- /dev/null +++ b/drivers/soundwire/master.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2019 Intel Corporation. + +#include +#include +#include +#include +#include "bus.h" + +static void sdw_md_release(struct device *dev) +{ + struct sdw_master_device *md = to_sdw_master_device(dev); + + kfree(md); +} + +struct device_type sdw_md_type = { + .name = "soundwire_master", + .release = sdw_md_release, +}; + +struct sdw_master_device *sdw_md_add(struct sdw_md_driver *driver, + struct device *parent, + struct fwnode_handle *fwnode, + int link_id) +{ + struct sdw_master_device *md; + int ret; + + if (!driver->probe) { + dev_err(parent, "mandatory probe callback missing\n"); + return ERR_PTR(-EINVAL); + } + + md = kzalloc(sizeof(*md), GFP_KERNEL); + if (!md) + return ERR_PTR(-ENOMEM); + + md->link_id = link_id; + + md->driver = driver; + + md->dev.parent = parent; + md->dev.fwnode = fwnode; + md->dev.bus = &sdw_bus_type; + md->dev.type = &sdw_md_type; + md->dev.dma_mask = md->dev.parent->dma_mask; + dev_set_name(&md->dev, "sdw-master-%d", md->link_id); + + ret = device_register(&md->dev); + if (ret) { + dev_err(parent, "Failed to add master: ret %d\n", ret); + /* + * On err, don't free but drop ref as this will be freed + * when release method is invoked. + */ + put_device(&md->dev); + } + + return md; +} +EXPORT_SYMBOL(sdw_md_add); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 3b618d7e664143..9efb71e9d2f6ba 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -579,6 +579,16 @@ struct sdw_slave { #define to_sdw_slave_device(d) \ container_of(d, struct sdw_slave, dev) +struct sdw_master_device { + struct device dev; + int link_id; + struct sdw_md_driver *driver; + void *pdata; /* core does not touch */ +}; + +#define to_sdw_master_device(d) \ + container_of(d, struct sdw_master_device, dev) + struct sdw_driver { const char *name; @@ -593,6 +603,26 @@ struct sdw_driver { struct device_driver driver; }; +struct sdw_md_driver { + /* initializations and allocations */ + int (*probe)(struct sdw_master_device *md, void *link_ctx); + /* hardware enablement, all clock/power dependencies are available */ + int (*startup)(struct sdw_master_device *md); + /* hardware disabled */ + int (*shutdown)(struct sdw_master_device *md); + /* free all resources */ + int (*remove)(struct sdw_master_device *md); + /* + * enable/disable driver control while in clock-stop mode, + * typically in always-on/D0ix modes. When the driver yields + * control, another entity in the system (typically firmware + * running on an always-on microprocessor) is responsible to + * tracking Slave-initiated wakes + */ + int (*autonomous_clock_stop_enable)(struct sdw_master_device *md, + bool state); +}; + #define SDW_SLAVE_ENTRY(_mfg_id, _part_id, _drv_data) \ { .mfg_id = (_mfg_id), .part_id = (_part_id), \ .driver_data = (unsigned long)(_drv_data) } @@ -782,6 +812,11 @@ struct sdw_bus { int sdw_add_bus_master(struct sdw_bus *bus); void sdw_delete_bus_master(struct sdw_bus *bus); +struct sdw_master_device *sdw_md_add(struct sdw_md_driver *driver, + struct device *parent, + struct fwnode_handle *fwnode, + int link_id); + /** * sdw_port_config: Master or Slave Port configuration * diff --git a/include/linux/soundwire/sdw_type.h b/include/linux/soundwire/sdw_type.h index c681b3426478e7..463d6d018d568d 100644 --- a/include/linux/soundwire/sdw_type.h +++ b/include/linux/soundwire/sdw_type.h @@ -6,15 +6,24 @@ extern struct bus_type sdw_bus_type; extern struct device_type sdw_slave_type; +extern struct device_type sdw_md_type; static inline int is_sdw_slave(const struct device *dev) { return dev->type == &sdw_slave_type; } +static inline int is_sdw_md(const struct device *dev) +{ + return dev->type == &sdw_md_type; +} + #define to_sdw_slave_driver(_drv) \ container_of(_drv, struct sdw_driver, driver) +#define to_sdw_md_driver(_drv) \ + container_of(_drv, struct sdw_md_driver, driver) + #define sdw_register_slave_driver(drv) \ __sdw_register_slave_driver(drv, THIS_MODULE) From 169ea8963787726d31572d5b95a7ee8df9a5a53f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 23 Oct 2019 12:12:51 -0500 Subject: [PATCH 017/110] soundwire: intel: remove platform devices and provide new interface Use sdw_master_device and driver instead of platform devices To quote GregKH: "Don't mess with a platform device unless you really have no other possible choice. And even then, don't do it and try to do something else. Platform devices are really abused, don't perpetuate it " In addition, rather than a plain-vanilla init/exit, this patch provides 3 steps in the initialization (ACPI scan, probe, startup) which make it easier to verify support and allocate required resources as early as possible, and conversely help make the startup lighter-weight with only hardware register setup. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 92 ++++++----- drivers/soundwire/intel.h | 8 +- drivers/soundwire/intel_init.c | 276 ++++++++++++++++++++++++--------- 3 files changed, 268 insertions(+), 108 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 64f97bb1a13573..ba3bc410d816fc 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -92,8 +92,6 @@ #define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0) #define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16) -#define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1) - enum intel_pdi_type { INTEL_PDI_IN = 0, INTEL_PDI_OUT = 1, @@ -923,24 +921,23 @@ static int intel_init(struct sdw_intel *sdw) /* * probe and init */ -static int intel_probe(struct platform_device *pdev) +static int intel_master_probe(struct sdw_master_device *md, void *link_ctx) { - struct sdw_cdns_stream_config config; struct sdw_intel *sdw; int ret; - sdw = devm_kzalloc(&pdev->dev, sizeof(*sdw), GFP_KERNEL); + sdw = devm_kzalloc(&md->dev, sizeof(*sdw), GFP_KERNEL); if (!sdw) return -ENOMEM; - sdw->instance = pdev->id; - sdw->link_res = dev_get_platdata(&pdev->dev); - sdw->cdns.dev = &pdev->dev; + sdw->instance = md->link_id; + sdw->link_res = link_ctx; + sdw->cdns.dev = &md->dev; sdw->cdns.registers = sdw->link_res->registers; - sdw->cdns.instance = sdw->instance; + sdw->cdns.instance = md->link_id; sdw->cdns.msg_count = 0; - sdw->cdns.bus.dev = &pdev->dev; - sdw->cdns.bus.link_id = pdev->id; + sdw->cdns.bus.dev = &md->dev; + sdw->cdns.bus.link_id = md->link_id; sdw_cdns_probe(&sdw->cdns); @@ -948,16 +945,50 @@ static int intel_probe(struct platform_device *pdev) sdw_intel_ops.read_prop = intel_prop_read; sdw->cdns.bus.ops = &sdw_intel_ops; - platform_set_drvdata(pdev, sdw); + md->pdata = sdw; + + /* set driver data, accessed by snd_soc_dai_set_drvdata() */ + dev_set_drvdata(&md->dev, &sdw->cdns); ret = sdw_add_bus_master(&sdw->cdns.bus); if (ret) { - dev_err(&pdev->dev, "sdw_add_bus_master fail: %d\n", ret); + dev_err(&md->dev, "sdw_add_bus_master fail: %d\n", ret); return ret; } if (sdw->cdns.bus.prop.hw_disabled) { - dev_info(&pdev->dev, "SoundWire master %d is disabled, ignoring\n", + dev_info(&md->dev, "SoundWire master %d is disabled, ignoring\n", + sdw->cdns.bus.link_id); + return 0; + } + + /* Acquire IRQ */ + ret = request_threaded_irq(sdw->link_res->irq, + sdw_cdns_irq, sdw_cdns_thread, + IRQF_SHARED, KBUILD_MODNAME, &sdw->cdns); + if (ret < 0) { + dev_err(sdw->cdns.dev, "unable to grab IRQ %d, disabling device\n", + sdw->link_res->irq); + goto err_init; + } + + return 0; + +err_init: + sdw_delete_bus_master(&sdw->cdns.bus); + return ret; +} + +static int intel_master_startup(struct sdw_master_device *md) +{ + struct sdw_cdns_stream_config config; + struct sdw_intel *sdw; + int ret; + + sdw = md->pdata; + + if (sdw->cdns.bus.prop.hw_disabled) { + dev_info(&md->dev, "SoundWire master %d is disabled, ignoring\n", sdw->cdns.bus.link_id); return 0; } @@ -975,16 +1006,6 @@ static int intel_probe(struct platform_device *pdev) intel_pdi_ch_update(sdw); - /* Acquire IRQ */ - ret = request_threaded_irq(sdw->link_res->irq, - sdw_cdns_irq, sdw_cdns_thread, - IRQF_SHARED, KBUILD_MODNAME, &sdw->cdns); - if (ret < 0) { - dev_err(sdw->cdns.dev, "unable to grab IRQ %d, disabling device\n", - sdw->link_res->irq); - goto err_init; - } - ret = sdw_cdns_enable_interrupt(&sdw->cdns, true); if (ret < 0) { dev_err(sdw->cdns.dev, "cannot enable interrupts\n"); @@ -1011,17 +1032,17 @@ static int intel_probe(struct platform_device *pdev) err_interrupt: sdw_cdns_enable_interrupt(&sdw->cdns, false); - free_irq(sdw->link_res->irq, sdw); err_init: + free_irq(sdw->link_res->irq, sdw); sdw_delete_bus_master(&sdw->cdns.bus); return ret; } -static int intel_remove(struct platform_device *pdev) +static int intel_master_remove(struct sdw_master_device *md) { struct sdw_intel *sdw; - sdw = platform_get_drvdata(pdev); + sdw = md->pdata; if (!sdw->cdns.bus.prop.hw_disabled) { intel_debugfs_exit(sdw); @@ -1031,19 +1052,18 @@ static int intel_remove(struct platform_device *pdev) } sdw_delete_bus_master(&sdw->cdns.bus); + md->dev.driver = NULL; + device_unregister(&md->dev); + return 0; } -static struct platform_driver sdw_intel_drv = { - .probe = intel_probe, - .remove = intel_remove, - .driver = { - .name = "int-sdw", - - }, +struct sdw_md_driver intel_sdw_driver = { + .probe = intel_master_probe, + .startup = intel_master_startup, + .remove = intel_master_remove, }; - -module_platform_driver(sdw_intel_drv); +EXPORT_SYMBOL(intel_sdw_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_ALIAS("platform:int-sdw"); diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index 38b7c125fb1052..cfab2f00214d25 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -7,7 +7,7 @@ /** * struct sdw_intel_link_res - Soundwire Intel link resource structure, * typically populated by the controller driver. - * @pdev: platform_device + * @md: master device * @mmio_base: mmio base of SoundWire registers * @registers: Link IO registers base * @shim: Audio shim pointer @@ -17,7 +17,7 @@ * @dev: device implementing hw_params and free callbacks */ struct sdw_intel_link_res { - struct platform_device *pdev; + struct sdw_master_device *md; void __iomem *mmio_base; /* not strictly needed, useful for debug */ void __iomem *registers; void __iomem *shim; @@ -27,4 +27,8 @@ struct sdw_intel_link_res { struct device *dev; }; +#define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1) + +extern struct sdw_md_driver intel_sdw_driver; + #endif /* __SDW_INTEL_LOCAL_H */ diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 6bc167c83b475e..42f7ae034beabf 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include "intel.h" @@ -23,22 +23,47 @@ #define SDW_LINK_BASE 0x30000 #define SDW_LINK_SIZE 0x10000 -static int link_mask; -module_param_named(sdw_link_mask, link_mask, int, 0444); +static int ctrl_link_mask; +module_param_named(sdw_link_mask, ctrl_link_mask, int, 0444); MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)"); -static int sdw_intel_cleanup_pdev(struct sdw_intel_ctx *ctx) +static bool is_link_enabled(struct fwnode_handle *fw_node, int i) +{ + struct fwnode_handle *link; + char name[32]; + u32 quirk_mask = 0; + + /* Find master handle */ + snprintf(name, sizeof(name), + "mipi-sdw-link-%d-subproperties", i); + + link = fwnode_get_named_child_node(fw_node, name); + if (!link) + return false; + + fwnode_property_read_u32(link, + "intel-quirk-mask", + &quirk_mask); + + if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE) + return false; + + return true; +} + +static int sdw_intel_cleanup(struct sdw_intel_ctx *ctx) { struct sdw_intel_link_res *link = ctx->links; + struct sdw_master_device *md; int i; if (!link) return 0; - for (i = 0; i < ctx->count; i++) { - if (link->pdev) - platform_device_unregister(link->pdev); - link++; + for (i = 0; i < ctx->count; i++, link++) { + md = link->md; + if (md) + md->driver->remove(md); } kfree(ctx->links); @@ -47,112 +72,194 @@ static int sdw_intel_cleanup_pdev(struct sdw_intel_ctx *ctx) return 0; } -static struct sdw_intel_ctx -*sdw_intel_add_controller(struct sdw_intel_res *res) +static int +sdw_intel_scan_controller(struct sdw_intel_acpi_info *info) { - struct platform_device_info pdevinfo; - struct platform_device *pdev; - struct sdw_intel_link_res *link; - struct sdw_intel_ctx *ctx; struct acpi_device *adev; int ret, i; u8 count; - u32 caps; - if (acpi_bus_get_device(res->handle, &adev)) - return NULL; + if (acpi_bus_get_device(info->handle, &adev)) + return -EINVAL; /* Found controller, find links supported */ count = 0; ret = fwnode_property_read_u8_array(acpi_fwnode_handle(adev), "mipi-sdw-master-count", &count, 1); - /* Don't fail on error, continue and use hw value */ + /* + * In theory we could check the number of links supported in + * hardware, but in that step we cannot assume SoundWire IP is + * powered. + * + * In addition, if the BIOS doesn't even provide this + * 'master-count' property then all the inits based on link + * masks will fail as well. + * + * We will check the hardware capabilities in the startup() step + */ + if (ret) { dev_err(&adev->dev, "Failed to read mipi-sdw-master-count: %d\n", ret); - count = SDW_MAX_LINKS; + return -EINVAL; } - /* Check SNDWLCAP.LCOUNT */ - caps = ioread32(res->mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP); - caps &= GENMASK(2, 0); - - /* Check HW supported vs property value and use min of two */ - count = min_t(u8, caps, count); - /* Check count is within bounds */ if (count > SDW_MAX_LINKS) { dev_err(&adev->dev, "Link count %d exceeds max %d\n", count, SDW_MAX_LINKS); - return NULL; + return -EINVAL; } else if (!count) { dev_warn(&adev->dev, "No SoundWire links detected\n"); - return NULL; + return -EINVAL; } + dev_dbg(&adev->dev, "ACPI reports %d SDW Link devices\n", count); + + info->count = count; + for (i = 0; i < count; i++) { + if (ctrl_link_mask && !(ctrl_link_mask & BIT(i))) { + dev_dbg(&adev->dev, + "Link %d masked, will not be enabled\n", i); + continue; + } + + if (!is_link_enabled(acpi_fwnode_handle(adev), i)) { + dev_dbg(&adev->dev, + "Link %d not selected in firmware\n", i); + continue; + } + + info->link_mask |= BIT(i); + } + + return 0; +} + +static struct sdw_intel_ctx +*sdw_intel_probe_controller(struct sdw_intel_res *res) +{ + struct sdw_intel_link_res *link; + struct sdw_intel_ctx *ctx; + struct acpi_device *adev; + struct sdw_master_device *md; + u32 link_mask; + int count; + int i; + + if (!res) + return NULL; + + if (acpi_bus_get_device(res->handle, &adev)) + return NULL; + + if (!res->count) + return NULL; + + count = res->count; dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count); ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return NULL; - ctx->count = count; - ctx->links = kcalloc(ctx->count, sizeof(*ctx->links), GFP_KERNEL); + ctx->links = kcalloc(count, sizeof(*ctx->links), GFP_KERNEL); if (!ctx->links) goto link_err; + ctx->count = count; + ctx->mmio_base = res->mmio_base; + ctx->link_mask = res->link_mask; + ctx->handle = res->handle; + link = ctx->links; + link_mask = ctx->link_mask; /* Create SDW Master devices */ - for (i = 0; i < count; i++) { - if (link_mask && !(link_mask & BIT(i))) { - dev_dbg(&adev->dev, - "Link %d masked, will not be enabled\n", i); - link++; + for (i = 0; i < count; i++, link++) { + if (link_mask && !(link_mask & BIT(i))) continue; - } + md = sdw_md_add(&intel_sdw_driver, + res->parent, + acpi_fwnode_handle(adev), + i); + + if (IS_ERR(md)) { + dev_err(&adev->dev, "Could not create link %d\n", i); + goto err; + } + link->md = md; + link->mmio_base = res->mmio_base; link->registers = res->mmio_base + SDW_LINK_BASE - + (SDW_LINK_SIZE * i); + + (SDW_LINK_SIZE * i); link->shim = res->mmio_base + SDW_SHIM_BASE; link->alh = res->mmio_base + SDW_ALH_BASE; - + link->irq = res->irq; link->ops = res->ops; link->dev = res->dev; - memset(&pdevinfo, 0, sizeof(pdevinfo)); - - pdevinfo.parent = res->parent; - pdevinfo.name = "int-sdw"; - pdevinfo.id = i; - pdevinfo.fwnode = acpi_fwnode_handle(adev); - - pdev = platform_device_register_full(&pdevinfo); - if (IS_ERR(pdev)) { - dev_err(&adev->dev, - "platform device creation failed: %ld\n", - PTR_ERR(pdev)); - goto pdev_err; - } - - link->pdev = pdev; - link++; + /* let the SoundWire master driver to its probe */ + md->driver->probe(md, link); } return ctx; -pdev_err: - sdw_intel_cleanup_pdev(ctx); +err: + sdw_intel_cleanup(ctx); link_err: kfree(ctx); return NULL; } +static int +sdw_intel_startup_controller(struct sdw_intel_ctx *ctx) +{ + struct acpi_device *adev; + struct sdw_intel_link_res *link; + struct sdw_master_device *md; + u32 caps; + u32 link_mask; + int i; + + if (acpi_bus_get_device(ctx->handle, &adev)) + return -EINVAL; + + /* Check SNDWLCAP.LCOUNT */ + caps = ioread32(ctx->mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP); + caps &= GENMASK(2, 0); + + /* Check HW supported vs property value */ + if (caps < ctx->count) { + dev_err(&adev->dev, + "BIOS master count is larger than hardware capabilities\n"); + return -EINVAL; + } + + if (!ctx->links) + return -EINVAL; + + link = ctx->links; + link_mask = ctx->link_mask; + + /* Create SDW Master devices */ + for (i = 0; i < ctx->count; i++, link++) { + if (link_mask && !(link_mask & BIT(i))) + continue; + + md = link->md; + + md->driver->startup(md); + } + + return 0; +} + static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, void *cdata, void **return_value) { - struct sdw_intel_res *res = cdata; + struct sdw_intel_acpi_info *info = cdata; struct acpi_device *adev; acpi_status status; u64 adr; @@ -166,7 +273,7 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, return AE_NOT_FOUND; } - res->handle = handle; + info->handle = handle; /* * On some Intel platforms, multiple children of the HDAS @@ -183,37 +290,66 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, } /** - * sdw_intel_init() - SoundWire Intel init routine + * sdw_intel_acpi_scan() - SoundWire Intel init routine * @parent_handle: ACPI parent handle - * @res: resource data + * @info: description of what firmware/DSDT tables expose * - * This scans the namespace and creates SoundWire link controller devices - * based on the info queried. + * This scans the namespace and queries firmware to figure out which + * links to enable. A follow-up use of sdw_intel_probe() and + * sdw_intel_startup() is required for creation of devices and bus + * startup */ -void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res) +int sdw_intel_acpi_scan(acpi_handle *parent_handle, + struct sdw_intel_acpi_info *info) { acpi_status status; status = acpi_walk_namespace(ACPI_TYPE_DEVICE, parent_handle, 1, sdw_intel_acpi_cb, - NULL, res, NULL); + NULL, info, NULL); if (ACPI_FAILURE(status)) - return NULL; + return -ENODEV; - return sdw_intel_add_controller(res); + return sdw_intel_scan_controller(info); } -EXPORT_SYMBOL(sdw_intel_init); +EXPORT_SYMBOL(sdw_intel_acpi_scan); +/** + * sdw_intel_probe() - SoundWire Intel probe routine + * @parent_handle: ACPI parent handle + * @res: resource data + * + * This creates SoundWire Master and Slave devices below the controller. + * All the information necessary is stored in the context, and the res + * argument pointer can be freed after this step. + */ +struct sdw_intel_ctx +*sdw_intel_probe(struct sdw_intel_res *res) +{ + return sdw_intel_probe_controller(res); +} +EXPORT_SYMBOL(sdw_intel_probe); + +/** + * sdw_intel_startup() - SoundWire Intel startup + * @ctx: SoundWire context allocated in the probe + * + */ +int sdw_intel_startup(struct sdw_intel_ctx *ctx) +{ + return sdw_intel_startup_controller(ctx); +} +EXPORT_SYMBOL(sdw_intel_startup); /** * sdw_intel_exit() - SoundWire Intel exit - * @arg: callback context + * @ctx: SoundWire context allocated in the probe * * Delete the controller instances created and cleanup */ void sdw_intel_exit(struct sdw_intel_ctx *ctx) { - sdw_intel_cleanup_pdev(ctx); + sdw_intel_cleanup(ctx); kfree(ctx); } EXPORT_SYMBOL(sdw_intel_exit); From a255d99efe366e1ffa45899bc2534c86da8d185e Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 12 Sep 2019 17:35:48 +0800 Subject: [PATCH 018/110] soundwire: add device driver to sdw_md_driver Setting an device driver is necessary for ASoC to register DAI components. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 4 ++++ drivers/soundwire/master.c | 2 ++ include/linux/soundwire/sdw.h | 1 + 3 files changed, 7 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index ba3bc410d816fc..492684ba08c324 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1059,6 +1059,10 @@ static int intel_master_remove(struct sdw_master_device *md) } struct sdw_md_driver intel_sdw_driver = { + .driver = { + .name = "intel-sdw", + .owner = THIS_MODULE, + }, .probe = intel_master_probe, .startup = intel_master_startup, .remove = intel_master_remove, diff --git a/drivers/soundwire/master.c b/drivers/soundwire/master.c index 6210098c892bad..d23111d43fd6f3 100644 --- a/drivers/soundwire/master.c +++ b/drivers/soundwire/master.c @@ -57,6 +57,8 @@ struct sdw_master_device *sdw_md_add(struct sdw_md_driver *driver, put_device(&md->dev); } + md->dev.driver = &driver->driver; + return md; } EXPORT_SYMBOL(sdw_md_add); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 9efb71e9d2f6ba..17ec786d3a1531 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -621,6 +621,7 @@ struct sdw_md_driver { */ int (*autonomous_clock_stop_enable)(struct sdw_master_device *md, bool state); + struct device_driver driver; }; #define SDW_SLAVE_ENTRY(_mfg_id, _part_id, _drv_data) \ From 3102cf09c6d02cacab16fcde3a27c292bccef06e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 11 Sep 2019 10:27:59 -0500 Subject: [PATCH 019/110] ASoC: SOF: Intel: add SoundWire configuration interface Now that the SoundWire core supports the multi-step initialization, call the relevant APIs. The actual hardware enablement can be done in two places, ideally we'd want to startup the SoundWire IP as soon as possible (while still taking power rail dependencies into account) However when suspend/resume is implemented, the DSP device will be resumed first, and only when the DSP firmware is downloaded/booted would the SoundWire child devices be resumed, so there are only marginal benefits in starting the IP earlier for the first probe. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-loader.c | 13 ++++ sound/soc/sof/intel/hda.c | 121 ++++++++++++++++++++++++++++++- sound/soc/sof/intel/hda.h | 44 +++++++++++ 3 files changed, 177 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index b1783360fe106b..2f5cd2c1ea3c4d 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -397,6 +397,19 @@ int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev) /* post fw run operations */ int hda_dsp_post_fw_run(struct snd_sof_dev *sdev) { + int ret; + + if (sdev->first_boot) { + ret = hda_sdw_startup(sdev); + if (ret < 0) { + dev_err(sdev->dev, + "error: could not startup SoundWire links\n"); + return ret; + } + } + + hda_sdw_int_enable(sdev, true); + /* re-enable clock gating and power gating */ return hda_dsp_ctrl_clock_power_gating(sdev, true); } diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 91bd88fddac7aa..43c3cb583590c6 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -18,7 +18,9 @@ #include #include +#include #include +#include #include #include #include @@ -34,6 +36,98 @@ #define EXCEPT_MAX_HDR_SIZE 0x400 +#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) + +void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable) +{ + sdw_intel_enable_irq(sdev->bar[HDA_DSP_BAR], enable); +} + +static int hda_sdw_acpi_scan(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + acpi_handle handle; + int ret; + + handle = ACPI_HANDLE(sdev->dev); + + /* save ACPI info for the probe step */ + hdev = sdev->pdata->hw_pdata; + + ret = sdw_intel_acpi_scan(handle, &hdev->info); + if (ret < 0) { + dev_err(sdev->dev, "%s failed\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int hda_sdw_probe(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + struct sdw_intel_res res; + acpi_handle handle; + void *sdw; + + handle = ACPI_HANDLE(sdev->dev); + + hdev = sdev->pdata->hw_pdata; + + memset(&res, 0, sizeof(res)); + + res.mmio_base = sdev->bar[HDA_DSP_BAR]; + res.irq = sdev->ipc_irq; + res.handle = hdev->info.handle; + res.parent = sdev->dev; + + /* + * ops and arg fields are not populated for now, + * they will be needed when the DAI callbacks are + * provided + */ + + /* we could filter links here if needed, e.g for quirks */ + res.count = hdev->info.count; + res.link_mask = hdev->info.link_mask; + + sdw = sdw_intel_probe(&res); + if (!sdw) { + dev_err(sdev->dev, "error: SoundWire probe failed\n"); + return -EINVAL; + } + + /* save context */ + hdev->sdw = sdw; + + return 0; +} + +int hda_sdw_startup(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + + hdev = sdev->pdata->hw_pdata; + + return sdw_intel_startup(hdev->sdw); +} + +static int hda_sdw_exit(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + + hdev = sdev->pdata->hw_pdata; + + hda_sdw_int_enable(sdev, false); + + if (hdev->sdw) + sdw_intel_exit(hdev->sdw); + hdev->sdw = NULL; + + return 0; +} +#endif + /* * Debug */ @@ -342,11 +436,11 @@ static const char *fixup_tplg_name(struct snd_sof_dev *sdev, static int hda_init_caps(struct snd_sof_dev *sdev) { struct hdac_bus *bus = sof_to_bus(sdev); + struct snd_sof_pdata *pdata = sdev->pdata; #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) struct hdac_ext_link *hlink; struct snd_soc_acpi_mach_params *mach_params; struct snd_soc_acpi_mach *hda_mach; - struct snd_sof_pdata *pdata = sdev->pdata; struct snd_soc_acpi_mach *mach; const char *tplg_filename; const char *idisp_str; @@ -355,6 +449,8 @@ static int hda_init_caps(struct snd_sof_dev *sdev) int codec_num = 0; int i; #endif + struct sof_intel_hda_dev *hdev = pdata->hw_pdata; + u32 link_mask; int ret = 0; device_disable_async_suspend(bus->dev); @@ -383,6 +479,27 @@ static int hda_init_caps(struct snd_sof_dev *sdev) return ret; } + /* scan SoundWire capabilities exposed by DSDT */ + ret = hda_sdw_acpi_scan(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: SoundWire ACPI scan error\n"); + return ret; + } + + link_mask = hdev->info.link_mask; + if (!link_mask) { + /* + * probe/allocated SoundWire resources. + * The hardware configuration takes place in hda_sdw_startup + * after power rails are enabled. + */ + ret = hda_sdw_probe(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: SoundWire probe error\n"); + return ret; + } + } + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) if (bus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(bus); @@ -695,6 +812,8 @@ int hda_dsp_remove(struct snd_sof_dev *sdev) snd_hdac_ext_bus_device_remove(bus); #endif + hda_sdw_exit(sdev); + if (!IS_ERR_OR_NULL(hda->dmic_dev)) platform_device_unregister(hda->dmic_dev); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 18d7e72bf9b722..c76d11dd48f2f6 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -11,6 +11,8 @@ #ifndef __SOF_INTEL_HDA_H #define __SOF_INTEL_HDA_H +#include +#include #include #include #include "shim.h" @@ -413,6 +415,12 @@ struct sof_intel_hda_dev { /* DMIC device */ struct platform_device *dmic_dev; + + /* ACPI information stored between scan and probe steps */ + struct sdw_intel_acpi_info info; + + /* sdw context allocated by SoundWire driver */ + struct sdw_intel_ctx *sdw; }; static inline struct hdac_bus *sof_to_bus(struct snd_sof_dev *s) @@ -605,6 +613,42 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag); int hda_dsp_trace_release(struct snd_sof_dev *sdev); int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd); +/* + * SoundWire support + */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) + +int hda_sdw_startup(struct snd_sof_dev *sdev); +void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable); + +#else + +static inline int hda_sdw_acpi_scan(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline int hda_sdw_probe(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline int hda_sdw_startup(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline int hda_sdw_exit(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable) +{ +} + +#endif + /* common dai driver */ extern struct snd_soc_dai_driver skl_dai[]; From a7f233b473e6760074c99605dbf5061170bbad1b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 16 Aug 2019 17:53:00 -0500 Subject: [PATCH 020/110] ASoC: SOF: IPC: dai-intel: move ALH declarations in header file ALH was inserted in the wrong place during integration, add after DMIC to mirror the file used by SOF firmware. No functional change, just text move in the same file to better track changes, if any. Signed-off-by: Pierre-Louis Bossart --- include/sound/sof/dai-intel.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/sound/sof/dai-intel.h b/include/sound/sof/dai-intel.h index 5f1ef5565be69f..04e48227f542b5 100644 --- a/include/sound/sof/dai-intel.h +++ b/include/sound/sof/dai-intel.h @@ -87,6 +87,15 @@ struct sof_ipc_dai_hda_params { uint32_t link_dma_ch; } __packed; +/* ALH Configuration Request - SOF_IPC_DAI_ALH_CONFIG */ +struct sof_ipc_dai_alh_params { + struct sof_ipc_hdr hdr; + uint32_t stream_id; + + /* reserved for future use */ + uint32_t reserved[15]; +} __packed; + /* DMIC Configuration Request - SOF_IPC_DAI_DMIC_CONFIG */ /* This struct is defined per 2ch PDM controller available in the platform. @@ -179,13 +188,4 @@ struct sof_ipc_dai_dmic_params { struct sof_ipc_dai_dmic_pdm_ctrl pdm[0]; } __packed; -/* ALH Configuration Request - SOF_IPC_DAI_ALH_CONFIG */ -struct sof_ipc_dai_alh_params { - struct sof_ipc_hdr hdr; - uint32_t stream_id; - - /* reserved for future use */ - uint32_t reserved[15]; -} __packed; - #endif From 696348de6fb27172a856aeb5ca4d04e73cbacf72 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jun 2019 02:45:55 -0500 Subject: [PATCH 021/110] ASoC: SOF: Intel: hda: add SoundWire stream config/free callbacks These callbacks are invoked when a matching hw_params/hw_free() DAI operation takes place, and will result in IPC operations with the SOF firmware. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda.c | 70 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 43c3cb583590c6..2b4f8a491cb47d 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -38,6 +38,74 @@ #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) +static int sdw_params_stream(struct device *dev, + struct sdw_intel_stream_params_data *params_data) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct snd_soc_dai *d = params_data->dai; + struct sof_ipc_dai_config config; + struct sof_ipc_reply reply; + int link_id = params_data->link_id; + int alh_stream_id = params_data->alh_stream_id; + int ret; + u32 size = sizeof(config); + + memset(&config, 0, size); + config.hdr.size = size; + config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; + config.type = SOF_DAI_INTEL_ALH; + config.dai_index = (link_id << 8) | (d->id); + config.alh.stream_id = alh_stream_id; + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + config.hdr.cmd, &config, size, &reply, + sizeof(reply)); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to set DAI hw_params for link %d dai->id %d ALH %d\n", + link_id, d->id, alh_stream_id); + } + + return ret; +} + +static int sdw_free_stream(struct device *dev, + struct sdw_intel_stream_free_data *free_data) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct snd_soc_dai *d = free_data->dai; + struct sof_ipc_dai_config config; + struct sof_ipc_reply reply; + int link_id = free_data->link_id; + int ret; + u32 size = sizeof(config); + + memset(&config, 0, size); + config.hdr.size = size; + config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; + config.type = SOF_DAI_INTEL_ALH; + config.dai_index = (link_id << 8) | d->id; + config.alh.stream_id = 0xFFFF; /* invalid value on purpose */ + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + config.hdr.cmd, &config, size, &reply, + sizeof(reply)); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to free stream for link %d dai->id %d\n", + link_id, d->id); + } + + return ret; +} + +static const struct sdw_intel_ops sdw_callback = { + .params_stream = sdw_params_stream, + .free_stream = sdw_free_stream, +}; + void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable) { sdw_intel_enable_irq(sdev->bar[HDA_DSP_BAR], enable); @@ -80,6 +148,8 @@ static int hda_sdw_probe(struct snd_sof_dev *sdev) res.irq = sdev->ipc_irq; res.handle = hdev->info.handle; res.parent = sdev->dev; + res.ops = &sdw_callback; + res.dev = sdev->dev; /* * ops and arg fields are not populated for now, From 3e4b73f941077dd72f55877cf11e0dbdcedb560f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 12 Sep 2019 11:13:05 -0500 Subject: [PATCH 022/110] ASoC: SOF: Intel: hda: initial SoundWire machine driver autodetect For now we have a limited number of machine driver configurations, and we can detect them based on the link configuration returned after checking hardware and firmware (BIOS) configurations. It's likely that in the future we will need to check for _ADR match as well, which can easily be done by extending the acpi_info structure. There is a chance that in extreme cases where the BIOS contains too much information we would need to detect which Slave devices actually report as 'attached'. This would be more accurate than static table-based solutions, but it also introduces timing dependencies since we don't know when those devices might become attached, so will only be only be looked at if we see limitations with static methods and the usual quirks based e.g. on DMI information. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda.c | 61 ++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 2b4f8a491cb47d..8e6275097b028c 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -179,6 +179,9 @@ int hda_sdw_startup(struct snd_sof_dev *sdev) hdev = sdev->pdata->hw_pdata; + if (!hdev->sdw) + return 0; + return sdw_intel_startup(hdev->sdw); } @@ -507,11 +510,11 @@ static int hda_init_caps(struct snd_sof_dev *sdev) { struct hdac_bus *bus = sof_to_bus(sdev); struct snd_sof_pdata *pdata = sdev->pdata; + struct snd_soc_acpi_mach *mach; #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) struct hdac_ext_link *hlink; - struct snd_soc_acpi_mach_params *mach_params; struct snd_soc_acpi_mach *hda_mach; - struct snd_soc_acpi_mach *mach; + struct snd_soc_acpi_mach_params *mach_params; const char *tplg_filename; const char *idisp_str; const char *dmic_str; @@ -552,24 +555,56 @@ static int hda_init_caps(struct snd_sof_dev *sdev) /* scan SoundWire capabilities exposed by DSDT */ ret = hda_sdw_acpi_scan(sdev); if (ret < 0) { - dev_err(sdev->dev, "error: SoundWire ACPI scan error\n"); - return ret; + dev_dbg(sdev->dev, "skipping SoundWire, ACPI scan error\n"); + goto skip_soundwire; } link_mask = hdev->info.link_mask; if (!link_mask) { - /* - * probe/allocated SoundWire resources. - * The hardware configuration takes place in hda_sdw_startup - * after power rails are enabled. - */ - ret = hda_sdw_probe(sdev); - if (ret < 0) { - dev_err(sdev->dev, "error: SoundWire probe error\n"); - return ret; + dev_dbg(sdev->dev, "skipping SoundWire, no links enabled\n"); + goto skip_soundwire; + } + + /* + * probe/allocate SoundWire resources. + * The hardware configuration takes place in hda_sdw_startup + * after power rails are enabled. + * It's entirely possible to have a mix of I2S/DMIC/SoundWire + * devices, so we allocate the resources in all cases. + */ + ret = hda_sdw_probe(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: SoundWire probe error\n"); + return ret; + } + + /* + * Select SoundWire machine driver if needed using the + * alternate tables. This case deals with SoundWire-only + * machines, for mixed cases with I2C/I2S the detection relies + * on the HID list. + */ + if (!pdata->machine) { + mach = pdata->desc->alt_machines; + while (mach && mach->link_mask && mach->link_mask != link_mask) + mach++; + if (mach && mach->link_mask) { + dev_dbg(bus->dev, + "SoundWire machine driver %s topology %s\n", + mach->drv_name, + mach->sof_tplg_filename); + pdata->machine = mach; + mach->mach_params.platform = dev_name(sdev->dev); + pdata->fw_filename = mach->sof_fw_filename; + pdata->tplg_filename = mach->sof_tplg_filename; + } else { + dev_info(sdev->dev, + "No SoundWire machine driver found\n"); } } +skip_soundwire: + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) if (bus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(bus); From 417004c8197d394a2dabbb338ff7f4ab259075b5 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 24 Sep 2019 10:36:44 -0500 Subject: [PATCH 023/110] ASoC: SOF: Intel: hda: disable SoundWire interrupts on suspend Doing this avoid conflicts and errors reported on the bus. The interrupts are only re-enabled on resume after the firmware is downloaded, so the behavior is not fully symmetric Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-dsp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 4a4d318f97ffa5..58388bccf27f30 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -390,6 +390,8 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) #endif int ret; + hda_sdw_int_enable(sdev, false); + /* disable IPC interrupts */ hda_dsp_ipc_int_disable(sdev); From 25319841ab88b1c67e1bedf8aa63828234772be5 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 4 Sep 2019 09:42:38 -0500 Subject: [PATCH 024/110] ASoC: SOF: Intel: add build support for SoundWire Select SoundWire capabilities on newer Intel platforms, starting with CannonLake/CoffeeLake/CometLake. As done for HDaudio, the SoundWire link is an opt-in capability. We explicitly test for ACPI to avoid warnings on unmet dependencies on the SoundWire side. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/Kconfig | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index b27fd3fdf335d7..1e29c2cac549eb 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -167,6 +167,7 @@ config SND_SOC_SOF_CANNONLAKE_SUPPORT config SND_SOC_SOF_CANNONLAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -182,6 +183,7 @@ config SND_SOC_SOF_COFFEELAKE_SUPPORT config SND_SOC_SOF_COFFEELAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -197,6 +199,7 @@ config SND_SOC_SOF_ICELAKE_SUPPORT config SND_SOC_SOF_ICELAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -204,6 +207,7 @@ config SND_SOC_SOF_ICELAKE config SND_SOC_SOF_COMETLAKE_LP tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -219,6 +223,7 @@ config SND_SOC_SOF_COMETLAKE_LP_SUPPORT config SND_SOC_SOF_COMETLAKE_H tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -242,6 +247,7 @@ config SND_SOC_SOF_TIGERLAKE_SUPPORT config SND_SOC_SOF_TIGERLAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -257,6 +263,7 @@ config SND_SOC_SOF_ELKHARTLAKE_SUPPORT config SND_SOC_SOF_ELKHARTLAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -343,6 +350,29 @@ config SND_SOC_SOF_HDA This option is not user-selectable but automagically handled by 'select' statements at a higher level +config SND_SOC_SOF_INTEL_SOUNDWIRE_LINK + bool "SOF support for SoundWire" + depends on SOUNDWIRE && ACPI + help + This adds support for SoundWire with Sound Open Firmware + for Intel(R) platforms. + Say Y if you want to enable SoundWire links with SOF. + If unsure select "N". + +config SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE + tristate + select SND_SOC_SOF_INTEL_SOUNDWIRE if SND_SOC_SOF_INTEL_SOUNDWIRE_LINK + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level + +config SND_SOC_SOF_INTEL_SOUNDWIRE + tristate + select SOUNDWIRE_INTEL + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level + endif ## SND_SOC_SOF_INTEL_PCI endif ## SND_SOC_SOF_INTEL_TOPLEVEL From acdb78144649dbf3523ff06da82181e6e8d9b038 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 21 Oct 2019 11:27:21 -0500 Subject: [PATCH 025/110] ASoC: SOF: Intel: hda-ctrl: add reset cycle before parsing capabilities Without this cycle, HDaudio capability parsing fails on some devices. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-ctrl.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index df1909e1d95060..72d7fa3a0be4f5 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -57,15 +57,32 @@ int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev) struct hdac_bus *bus = sof_to_bus(sdev); u32 cap, offset, feature; int count = 0; + int ret; + + /* + * On some devices, one reset cycle is necessary before reading + * capabilities + */ + ret = hda_dsp_ctrl_link_reset(sdev, true); + if (ret < 0) + return ret; + ret = hda_dsp_ctrl_link_reset(sdev, false); + if (ret < 0) + return ret; offset = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_LLCH); do { - cap = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, offset); - dev_dbg(sdev->dev, "checking for capabilities at offset 0x%x\n", offset & SOF_HDA_CAP_NEXT_MASK); + cap = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, offset); + + if (cap == -1) { + dev_dbg(bus->dev, "Invalid capability reg read\n"); + break; + } + feature = (cap & SOF_HDA_CAP_ID_MASK) >> SOF_HDA_CAP_ID_OFF; switch (feature) { @@ -98,8 +115,8 @@ int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev) bus->mlcap = bus->remap_addr + offset; break; default: - dev_vdbg(sdev->dev, "found capability %d at 0x%x\n", - feature, offset); + dev_dbg(sdev->dev, "found capability %d at 0x%x\n", + feature, offset); break; } From f5e99e24a00bfdef55f6c1460b0887fcff3bd1cb Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 18 Apr 2018 10:35:14 +0530 Subject: [PATCH 026/110] ASoC: codecs: rt700: add Soundwire support Initial support for ALC/RT700 in SoundWire mode This codec is present on Intel CNL/CML and ICL RVP boards and the code was tested by both Realtek and Intel. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Signed-off-by: Shuming Fan --- sound/soc/codecs/Kconfig | 9 + sound/soc/codecs/Makefile | 3 + sound/soc/codecs/rt700-sdw.c | 530 +++++++++++++++ sound/soc/codecs/rt700-sdw.h | 335 ++++++++++ sound/soc/codecs/rt700.c | 1217 ++++++++++++++++++++++++++++++++++ sound/soc/codecs/rt700.h | 176 +++++ 6 files changed, 2270 insertions(+) create mode 100644 sound/soc/codecs/rt700-sdw.c create mode 100644 sound/soc/codecs/rt700-sdw.h create mode 100644 sound/soc/codecs/rt700.c create mode 100644 sound/soc/codecs/rt700.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 925e5f852348a8..b94548b5e8a802 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1056,6 +1056,15 @@ config SND_SOC_RT5677_SPI config SND_SOC_RT5682 tristate +config SND_SOC_RT700 + tristate + +config SND_SOC_RT700_SDW + tristate "Realtek RT700 Codec - SDW" + depends on SOUNDWIRE + select SND_SOC_RT700 + select REGMAP_SOUNDWIRE + #Freescale sgtl5000 codec config SND_SOC_SGTL5000 tristate "Freescale SGTL5000 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index ddfd07071925c2..d825351bb41893 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -278,6 +278,8 @@ snd-soc-wm9712-objs := wm9712.o snd-soc-wm9713-objs := wm9713.o snd-soc-wm-hubs-objs := wm_hubs.o snd-soc-zx-aud96p22-objs := zx_aud96p22.o +snd-soc-rt700-objs := rt700.o rt700-sdw.o + # Amp snd-soc-max9877-objs := max9877.o snd-soc-max98504-objs := max98504.o @@ -567,6 +569,7 @@ obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o obj-$(CONFIG_SND_SOC_ZX_AUD96P22) += snd-soc-zx-aud96p22.o +obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o # Amp obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c new file mode 100644 index 00000000000000..2ec16a5b9015aa --- /dev/null +++ b/sound/soc/codecs/rt700-sdw.c @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt700-sdw.c -- rt700 ALSA SoC audio driver +// +// Copyright(c) 2019 Realtek Semiconductor Corp. +// +// ALC700 ASoC Codec Driver based Intel Dummy SdW codec driver +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rt700.h" +#include "rt700-sdw.h" + +static bool rt700_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00e0: + case 0x00f0: + case 0x2000 ... 0x200e: + case 0x2012 ... 0x2016: + case 0x201a ... 0x2027: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2200 ... 0x2204: + case 0x2206 ... 0x2212: + case 0x2220 ... 0x2223: + case 0x2230 ... 0x2231: + case 0x3000 ... 0x3fff: + case 0x7000 ... 0x7fff: + case 0x8300 ... 0x83ff: + case 0x9c00 ... 0x9cff: + case 0xb900 ... 0xb9ff: + case 0x7520001a: + case 0x75200045: + case 0x75200046: + case 0x75200048: + case 0x7520004a: + case 0x7520006b: + case 0x75200080: + case 0x75200081: + return true; + default: + return false; + } +} + +static bool rt700_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2009: + case 0x2016: + case 0x201b: + case 0x201c: + case 0x201d: + case 0x201f: + case 0x2021: + case 0x2023: + case 0x2230: + case 0x200b ... 0x200e: /* i2c read */ + case 0x2012 ... 0x2015: /* HD-A read */ + case 0x202d ... 0x202f: /* BRA */ + case 0x2201 ... 0x2212: /* i2c debug */ + case 0x2220 ... 0x2223: /* decoded HD-A */ + case 0x9c00 ... 0x9cff: + case 0xb900 ... 0xb9ff: + case 0xff01: + case 0x7520001a: + case 0x75200046: + case 0x75200080: + case 0x75200081: + return true; + default: + return false; + } +} + +static int rt700_sdw_read(void *context, unsigned int reg, unsigned int *val) +{ + struct device *dev = context; + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2; + unsigned int is_hda_reg = 1, is_index_reg = 0; + int ret; + + if (reg > 0xffff) + is_index_reg = 1; + + mask = reg & 0xf000; + + if (is_index_reg) { /* index registers */ + val2 = reg & 0xffff; + reg = reg >> 16; + nid = reg & 0xff; + ret = regmap_write(rt700->sdw_regmap, reg, ((val2 >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg2, (val2 & 0xff)); + if (ret < 0) + return ret; + + reg3 = RT700_PRIV_DATA_R_H | nid; + ret = regmap_write(rt700->sdw_regmap, reg3, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg4 = reg3 + 0x1000; + reg4 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg4, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0x3000) { + reg += 0x8000; + ret = regmap_write(rt700->sdw_regmap, reg, *val); + if (ret < 0) + return ret; + } else if (mask == 0x7000) { + reg += 0x2000; + reg |= 0x800; + ret = regmap_write(rt700->sdw_regmap, reg, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg2, (*val & 0xff)); + if (ret < 0) + return ret; + } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ + reg2 = reg - 0x1000; + reg2 &= ~0x80; + ret = regmap_write(rt700->sdw_regmap, reg2, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + ret = regmap_write(rt700->sdw_regmap, reg, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0x9000) { + ret = regmap_write(rt700->sdw_regmap, reg, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg2, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0xb000) { + ret = regmap_write(rt700->sdw_regmap, reg, *val); + if (ret < 0) + return ret; + } else { + ret = regmap_read(rt700->sdw_regmap, reg, val); + if (ret < 0) + return ret; + is_hda_reg = 0; + } + + if (is_hda_reg || is_index_reg) { + sdw_data_3 = 0; + sdw_data_2 = 0; + sdw_data_1 = 0; + sdw_data_0 = 0; + ret = regmap_read(rt700->sdw_regmap, RT700_READ_HDA_3, &sdw_data_3); + if (ret < 0) + return ret; + ret = regmap_read(rt700->sdw_regmap, RT700_READ_HDA_2, &sdw_data_2); + if (ret < 0) + return ret; + ret = regmap_read(rt700->sdw_regmap, RT700_READ_HDA_1, &sdw_data_1); + if (ret < 0) + return ret; + ret = regmap_read(rt700->sdw_regmap, RT700_READ_HDA_0, &sdw_data_0); + if (ret < 0) + return ret; + *val = ((sdw_data_3 & 0xff) << 24) | ((sdw_data_2 & 0xff) << 16) | + ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); + } + + if (is_hda_reg == 0) + dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val); + else if (is_index_reg) + dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n", __func__, + reg, reg2, reg3, reg4, *val); + else + dev_dbg(dev, "[%s] %04x %04x => %08x\n", __func__, reg, reg2, *val); + + return 0; +} + +static int rt700_sdw_write(void *context, unsigned int reg, unsigned int val) +{ + struct device *dev = context; + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned int reg2 = 0, reg3, reg4, nid, mask, val2; + unsigned int is_index_reg = 0; + int ret; + + if (reg > 0xffff) + is_index_reg = 1; + + mask = reg & 0xf000; + + if (is_index_reg) { /* index registers */ + val2 = reg & 0xffff; + reg = reg >> 16; + nid = reg & 0xff; + ret = regmap_write(rt700->sdw_regmap, reg, ((val2 >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg2, (val2 & 0xff)); + if (ret < 0) + return ret; + + reg3 = RT700_PRIV_DATA_W_H | nid; + ret = regmap_write(rt700->sdw_regmap, reg3, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg4 = reg3 + 0x1000; + reg4 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg4, (val & 0xff)); + if (ret < 0) + return ret; + is_index_reg = 1; + } else if (reg < 0x4fff) { + ret = regmap_write(rt700->sdw_regmap, reg, val); + if (ret < 0) + return ret; + } else if (reg == 0xff01) { + ret = regmap_write(rt700->sdw_regmap, reg, val); + if (ret < 0) + return ret; + } else if (mask == 0x7000) { + ret = regmap_write(rt700->sdw_regmap, reg, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg2, (val & 0xff)); + if (ret < 0) + return ret; + } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ + reg2 = reg - 0x1000; + reg2 &= ~0x80; + ret = regmap_write(rt700->sdw_regmap, reg2, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + ret = regmap_write(rt700->sdw_regmap, reg, (val & 0xff)); + if (ret < 0) + return ret; + } + + if (reg2 == 0) + dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val); + else if (is_index_reg) + dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n", __func__, + reg, reg2, reg3, reg4, val2, val); + else + dev_dbg(dev, "[%s] %04x %04x <= %04x\n", __func__, reg, reg2, val); + + return 0; +} + +static const struct regmap_config rt700_regmap = { + .reg_bits = 32, + .val_bits = 32, + .readable_reg = rt700_readable_register, /* Readable registers */ + .volatile_reg = rt700_volatile_register, /* volatile register */ + .max_register = 0x75580000, /* Maximum number of register */ + .reg_defaults = rt700_reg_defaults, /* Defaults */ + .num_reg_defaults = ARRAY_SIZE(rt700_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, + .reg_read = rt700_sdw_read, + .reg_write = rt700_sdw_write, +}; + +static const struct regmap_config rt700_sdw_regmap = { + .name = "sdw", + .reg_bits = 32, /* Total register space for SDW */ + .val_bits = 8, /* Total number of bits in register */ + .readable_reg = rt700_readable_register, /* Readable registers */ + .max_register = 0xff01, /* Maximum number of register */ + .cache_type = REGCACHE_NONE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt700_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); + + /* Update the status */ + rt700->status = status; + + if (status == SDW_SLAVE_UNATTACHED) + rt700->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt700->hw_init || rt700->status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt700_io_init(&slave->dev, slave); +} + +static int rt700_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval, i, num_of_ports = 1; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->paging_support = false; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = 0x14; /* BITMAP: 00010100 */ + prop->sink_ports = 0xA; /* BITMAP: 00001010 */ + + nval = hweight32(prop->source_ports); + num_of_ports += nval; + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + num_of_ports += nval; + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* Allocate port_ready based on num_of_ports */ + slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports, + sizeof(*slave->port_ready), + GFP_KERNEL); + if (!slave->port_ready) + return -ENOMEM; + + /* Initialize completion */ + for (i = 0; i < num_of_ports; i++) + init_completion(&slave->port_ready[i]); + + /* set the timeout values */ + prop->clk_stop_timeout = 20; + + return 0; +} + +static int rt700_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); + int ret; + + memcpy(&rt700->params, params, sizeof(*params)); + + ret = rt700_clock_config(&slave->dev); + if (ret < 0) + dev_err(&slave->dev, "Invalid clk config"); + + return ret; +} + +static int rt700_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); + + dev_dbg(&slave->dev, + "%s control_port_stat=%x", __func__, status->control_port); + + if (status->control_port & 0x4) { + mod_delayed_work(system_power_efficient_wq, + &rt700->jack_detect_work, msecs_to_jiffies(250)); + } + + return 0; +} + +/* + * slave_ops: callbacks for get_clock_stop_mode, clock_stop and + * port_prep are not defined for now + */ +static struct sdw_slave_ops rt700_slave_ops = { + .read_prop = rt700_read_prop, + .interrupt_callback = rt700_interrupt_callback, + .update_status = rt700_update_status, + .bus_config = rt700_bus_config, +}; + +static int rt700_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *sdw_regmap, *regmap; + + /* Assign ops */ + slave->ops = &rt700_slave_ops; + + /* Regmap Initialization */ + sdw_regmap = devm_regmap_init_sdw(slave, &rt700_sdw_regmap); + if (!sdw_regmap) + return -EINVAL; + + regmap = devm_regmap_init(&slave->dev, NULL, &slave->dev, &rt700_regmap); + if (!regmap) + return -EINVAL; + + rt700_init(&slave->dev, sdw_regmap, regmap, slave); + + return 0; +} + +static int rt700_sdw_remove(struct sdw_slave *slave) +{ + struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); + + if (rt700 && rt700->hw_init) { + cancel_delayed_work(&rt700->jack_detect_work); + cancel_delayed_work(&rt700->jack_btn_check_work); + } + + return 0; +} + +static const struct sdw_device_id rt700_id[] = { + SDW_SLAVE_ENTRY(0x025d, 0x700, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt700_id); + +static int rt700_dev_suspend(struct device *dev) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + + if (!rt700->hw_init) + return 0; + + regcache_cache_only(rt700->regmap, true); + + return 0; +} + +#define RT700_PROBE_TIMEOUT 2000 + +static int rt700_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = to_sdw_slave_device(dev); + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt700->hw_init) + return 0; + + time = wait_for_completion_timeout(&slave->enumeration_complete, + msecs_to_jiffies(RT700_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Enumeration not complete, timed out\n"); + return -ETIMEDOUT; + } + + regcache_cache_only(rt700->regmap, false); + regcache_sync_region(rt700->regmap, 0x3000, 0x8fff); + regcache_sync_region(rt700->regmap, 0x75200010, 0x7520006b); + + return 0; +} + +static const struct dev_pm_ops rt700_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt700_dev_suspend, rt700_dev_resume) + SET_RUNTIME_PM_OPS(rt700_dev_suspend, rt700_dev_resume, NULL) +}; + +static struct sdw_driver rt700_sdw_driver = { + .driver = { + .name = "rt700", + .owner = THIS_MODULE, + .pm = &rt700_pm, + }, + .probe = rt700_sdw_probe, + .remove = rt700_sdw_remove, + .ops = &rt700_slave_ops, + .id_table = rt700_id, +}; +module_sdw_driver(rt700_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT700 driver SDW"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt700-sdw.h b/sound/soc/codecs/rt700-sdw.h new file mode 100644 index 00000000000000..2f5301b0bd8dff --- /dev/null +++ b/sound/soc/codecs/rt700-sdw.h @@ -0,0 +1,335 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt700-sdw.h -- RT700 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT700_SDW_H__ +#define __RT700_SDW_H__ + +static const struct reg_default rt700_reg_defaults[] = { + { 0x0000, 0x0000 }, + { 0x0001, 0x0000 }, + { 0x0002, 0x0000 }, + { 0x0003, 0x0000 }, + { 0x0004, 0x0000 }, + { 0x0005, 0x0001 }, + { 0x0020, 0x0000 }, + { 0x0022, 0x0000 }, + { 0x0023, 0x0000 }, + { 0x0024, 0x0000 }, + { 0x0025, 0x0000 }, + { 0x0026, 0x0000 }, + { 0x0030, 0x0000 }, + { 0x0032, 0x0000 }, + { 0x0033, 0x0000 }, + { 0x0034, 0x0000 }, + { 0x0035, 0x0000 }, + { 0x0036, 0x0000 }, + { 0x0040, 0x0000 }, + { 0x0041, 0x0000 }, + { 0x0042, 0x0000 }, + { 0x0043, 0x0000 }, + { 0x0044, 0x0020 }, + { 0x0045, 0x0001 }, + { 0x0046, 0x0000 }, + { 0x0050, 0x0000 }, + { 0x0051, 0x0000 }, + { 0x0052, 0x0000 }, + { 0x0053, 0x0000 }, + { 0x0054, 0x0000 }, + { 0x0055, 0x0000 }, + { 0x0060, 0x0000 }, + { 0x0070, 0x0000 }, + { 0x00e0, 0x0000 }, + { 0x00f0, 0x0000 }, + { 0x0100, 0x0000 }, + { 0x0101, 0x0000 }, + { 0x0102, 0x0000 }, + { 0x0103, 0x0000 }, + { 0x0104, 0x0000 }, + { 0x0105, 0x0000 }, + { 0x0120, 0x0000 }, + { 0x0121, 0x0000 }, + { 0x0122, 0x0000 }, + { 0x0123, 0x0000 }, + { 0x0124, 0x0000 }, + { 0x0125, 0x0000 }, + { 0x0126, 0x0000 }, + { 0x0127, 0x0000 }, + { 0x0130, 0x0000 }, + { 0x0131, 0x0000 }, + { 0x0132, 0x0000 }, + { 0x0133, 0x0000 }, + { 0x0134, 0x0000 }, + { 0x0135, 0x0000 }, + { 0x0136, 0x0000 }, + { 0x0137, 0x0000 }, + { 0x0200, 0x0000 }, + { 0x0201, 0x0000 }, + { 0x0202, 0x0000 }, + { 0x0203, 0x0000 }, + { 0x0204, 0x0000 }, + { 0x0205, 0x0000 }, + { 0x0220, 0x0000 }, + { 0x0221, 0x0000 }, + { 0x0222, 0x0000 }, + { 0x0223, 0x0000 }, + { 0x0224, 0x0000 }, + { 0x0225, 0x0000 }, + { 0x0226, 0x0000 }, + { 0x0227, 0x0000 }, + { 0x0230, 0x0000 }, + { 0x0231, 0x0000 }, + { 0x0232, 0x0000 }, + { 0x0233, 0x0000 }, + { 0x0234, 0x0000 }, + { 0x0235, 0x0000 }, + { 0x0236, 0x0000 }, + { 0x0237, 0x0000 }, + { 0x0300, 0x0000 }, + { 0x0301, 0x0000 }, + { 0x0302, 0x0000 }, + { 0x0303, 0x0000 }, + { 0x0304, 0x0000 }, + { 0x0305, 0x0000 }, + { 0x0320, 0x0000 }, + { 0x0321, 0x0000 }, + { 0x0322, 0x0000 }, + { 0x0323, 0x0000 }, + { 0x0324, 0x0000 }, + { 0x0325, 0x0000 }, + { 0x0326, 0x0000 }, + { 0x0327, 0x0000 }, + { 0x0330, 0x0000 }, + { 0x0331, 0x0000 }, + { 0x0332, 0x0000 }, + { 0x0333, 0x0000 }, + { 0x0334, 0x0000 }, + { 0x0335, 0x0000 }, + { 0x0336, 0x0000 }, + { 0x0337, 0x0000 }, + { 0x0400, 0x0000 }, + { 0x0401, 0x0000 }, + { 0x0402, 0x0000 }, + { 0x0403, 0x0000 }, + { 0x0404, 0x0000 }, + { 0x0405, 0x0000 }, + { 0x0420, 0x0000 }, + { 0x0421, 0x0000 }, + { 0x0422, 0x0000 }, + { 0x0423, 0x0000 }, + { 0x0424, 0x0000 }, + { 0x0425, 0x0000 }, + { 0x0426, 0x0000 }, + { 0x0427, 0x0000 }, + { 0x0430, 0x0000 }, + { 0x0431, 0x0000 }, + { 0x0432, 0x0000 }, + { 0x0433, 0x0000 }, + { 0x0434, 0x0000 }, + { 0x0435, 0x0000 }, + { 0x0436, 0x0000 }, + { 0x0437, 0x0000 }, + { 0x0500, 0x0000 }, + { 0x0501, 0x0000 }, + { 0x0502, 0x0000 }, + { 0x0503, 0x0000 }, + { 0x0504, 0x0000 }, + { 0x0505, 0x0000 }, + { 0x0520, 0x0000 }, + { 0x0521, 0x0000 }, + { 0x0522, 0x0000 }, + { 0x0523, 0x0000 }, + { 0x0524, 0x0000 }, + { 0x0525, 0x0000 }, + { 0x0526, 0x0000 }, + { 0x0527, 0x0000 }, + { 0x0530, 0x0000 }, + { 0x0531, 0x0000 }, + { 0x0532, 0x0000 }, + { 0x0533, 0x0000 }, + { 0x0534, 0x0000 }, + { 0x0535, 0x0000 }, + { 0x0536, 0x0000 }, + { 0x0537, 0x0000 }, + { 0x0600, 0x0000 }, + { 0x0601, 0x0000 }, + { 0x0602, 0x0000 }, + { 0x0603, 0x0000 }, + { 0x0604, 0x0000 }, + { 0x0605, 0x0000 }, + { 0x0620, 0x0000 }, + { 0x0621, 0x0000 }, + { 0x0622, 0x0000 }, + { 0x0623, 0x0000 }, + { 0x0624, 0x0000 }, + { 0x0625, 0x0000 }, + { 0x0626, 0x0000 }, + { 0x0627, 0x0000 }, + { 0x0630, 0x0000 }, + { 0x0631, 0x0000 }, + { 0x0632, 0x0000 }, + { 0x0633, 0x0000 }, + { 0x0634, 0x0000 }, + { 0x0635, 0x0000 }, + { 0x0636, 0x0000 }, + { 0x0637, 0x0000 }, + { 0x0700, 0x0000 }, + { 0x0701, 0x0000 }, + { 0x0702, 0x0000 }, + { 0x0703, 0x0000 }, + { 0x0704, 0x0000 }, + { 0x0705, 0x0000 }, + { 0x0720, 0x0000 }, + { 0x0721, 0x0000 }, + { 0x0722, 0x0000 }, + { 0x0723, 0x0000 }, + { 0x0724, 0x0000 }, + { 0x0725, 0x0000 }, + { 0x0726, 0x0000 }, + { 0x0727, 0x0000 }, + { 0x0730, 0x0000 }, + { 0x0731, 0x0000 }, + { 0x0732, 0x0000 }, + { 0x0733, 0x0000 }, + { 0x0734, 0x0000 }, + { 0x0735, 0x0000 }, + { 0x0736, 0x0000 }, + { 0x0737, 0x0000 }, + { 0x0800, 0x0000 }, + { 0x0801, 0x0000 }, + { 0x0802, 0x0000 }, + { 0x0803, 0x0000 }, + { 0x0804, 0x0000 }, + { 0x0805, 0x0000 }, + { 0x0820, 0x0000 }, + { 0x0821, 0x0000 }, + { 0x0822, 0x0000 }, + { 0x0823, 0x0000 }, + { 0x0824, 0x0000 }, + { 0x0825, 0x0000 }, + { 0x0826, 0x0000 }, + { 0x0827, 0x0000 }, + { 0x0830, 0x0000 }, + { 0x0831, 0x0000 }, + { 0x0832, 0x0000 }, + { 0x0833, 0x0000 }, + { 0x0834, 0x0000 }, + { 0x0835, 0x0000 }, + { 0x0836, 0x0000 }, + { 0x0837, 0x0000 }, + { 0x0f00, 0x0000 }, + { 0x0f01, 0x0000 }, + { 0x0f02, 0x0000 }, + { 0x0f03, 0x0000 }, + { 0x0f04, 0x0000 }, + { 0x0f05, 0x0000 }, + { 0x0f20, 0x0000 }, + { 0x0f21, 0x0000 }, + { 0x0f22, 0x0000 }, + { 0x0f23, 0x0000 }, + { 0x0f24, 0x0000 }, + { 0x0f25, 0x0000 }, + { 0x0f26, 0x0000 }, + { 0x0f27, 0x0000 }, + { 0x0f30, 0x0000 }, + { 0x0f31, 0x0000 }, + { 0x0f32, 0x0000 }, + { 0x0f33, 0x0000 }, + { 0x0f34, 0x0000 }, + { 0x0f35, 0x0000 }, + { 0x0f36, 0x0000 }, + { 0x0f37, 0x0000 }, + { 0x2000, 0x0000 }, + { 0x2001, 0x0000 }, + { 0x2002, 0x0000 }, + { 0x2003, 0x0000 }, + { 0x2004, 0x0000 }, + { 0x2005, 0x0000 }, + { 0x2006, 0x0000 }, + { 0x2007, 0x0000 }, + { 0x2008, 0x0000 }, + { 0x2009, 0x0003 }, + { 0x200a, 0x0003 }, + { 0x200b, 0x0000 }, + { 0x200c, 0x0000 }, + { 0x200d, 0x0000 }, + { 0x200e, 0x0000 }, + { 0x2012, 0x0000 }, + { 0x2013, 0x0000 }, + { 0x2014, 0x0000 }, + { 0x2015, 0x0000 }, + { 0x2016, 0x0000 }, + { 0x201a, 0x0000 }, + { 0x201b, 0x0000 }, + { 0x201c, 0x0000 }, + { 0x201d, 0x0000 }, + { 0x201e, 0x0000 }, + { 0x201f, 0x0000 }, + { 0x2020, 0x0000 }, + { 0x2021, 0x0000 }, + { 0x2022, 0x0000 }, + { 0x2023, 0x0000 }, + { 0x2024, 0x0000 }, + { 0x2025, 0x0002 }, + { 0x2026, 0x0000 }, + { 0x2027, 0x0000 }, + { 0x2029, 0x0000 }, + { 0x202a, 0x0000 }, + { 0x202d, 0x0000 }, + { 0x202e, 0x0000 }, + { 0x202f, 0x0000 }, + { 0x2030, 0x0000 }, + { 0x2031, 0x0000 }, + { 0x2032, 0x0000 }, + { 0x2033, 0x0000 }, + { 0x2034, 0x0000 }, + { 0x2200, 0x0000 }, + { 0x2201, 0x0000 }, + { 0x2202, 0x0000 }, + { 0x2203, 0x0000 }, + { 0x2204, 0x0000 }, + { 0x2206, 0x0000 }, + { 0x2207, 0x0000 }, + { 0x2208, 0x0000 }, + { 0x2209, 0x0000 }, + { 0x220a, 0x0000 }, + { 0x220b, 0x0000 }, + { 0x220c, 0x0000 }, + { 0x220d, 0x0000 }, + { 0x220e, 0x0000 }, + { 0x220f, 0x0000 }, + { 0x2211, 0x0000 }, + { 0x2212, 0x0000 }, + { 0x2220, 0x0000 }, + { 0x2221, 0x0000 }, + { 0x2222, 0x0000 }, + { 0x2223, 0x0000 }, + { 0x2230, 0x0000 }, + { 0x2231, 0x0000 }, + { 0x3121, 0x0001 }, + { 0x3122, 0x0000 }, + { 0x3123, 0x0000 }, + { 0x7303, 0x0057 }, + { 0x7303, 0x0057 }, + { 0x8383, 0x0057 }, + { 0x7308, 0x0097 }, + { 0x8388, 0x0097 }, + { 0x7309, 0x0097 }, + { 0x8389, 0x0097 }, + { 0x7312, 0x0000 }, + { 0x8392, 0x0000 }, + { 0x7313, 0x0000 }, + { 0x8393, 0x0000 }, + { 0x7319, 0x0000 }, + { 0x8399, 0x0000 }, + { 0x7520001a, 0x8003 }, + { 0x75200045, 0x5289 }, + { 0x75200048, 0xd049 }, + { 0x7520004a, 0xa83b }, + { 0x7520006b, 0x5064 }, +}; + +#endif /* __RT700_H__ */ diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c new file mode 100644 index 00000000000000..2bfa9b180e431d --- /dev/null +++ b/sound/soc/codecs/rt700.c @@ -0,0 +1,1217 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt700.c -- rt700 ALSA SoC audio driver +// +// Copyright(c) 2019 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt700.h" + +static int rt700_index_write(struct regmap *regmap, + unsigned int reg, unsigned int value) +{ + int ret; + unsigned int addr = (RT700_PRIV_INDEX_W_H << 16) | reg; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + pr_err("Failed to set private value: %08x <= %04x %d\n", ret, addr, value); + + return ret; +} + +static int rt700_index_read(struct regmap *regmap, + unsigned int reg, unsigned int *value) +{ + int ret; + unsigned int addr = (RT700_PRIV_INDEX_W_H << 16) | reg; + + *value = 0; + ret = regmap_read(regmap, addr, value); + if (ret < 0) + pr_err("Failed to get private value: %08x => %04x %d\n", ret, addr, *value); + + return ret; +} + +static unsigned int rt700_button_detect(struct rt700_priv *rt700) +{ + unsigned int btn_type = 0, val80, val81; + int ret; + + ret = rt700_index_read(rt700->regmap, RT700_IRQ_FLAG_TABLE1, &val80); + if (ret < 0) + goto read_error; + ret = rt700_index_read(rt700->regmap, RT700_IRQ_FLAG_TABLE2, &val81); + if (ret < 0) + goto read_error; + + val80 &= 0x0381; + val81 &= 0xff00; + + switch (val80) { + case 0x0200: + case 0x0100: + case 0x0080: + btn_type |= SND_JACK_BTN_0; + break; + case 0x0001: + btn_type |= SND_JACK_BTN_3; + break; + } + switch (val81) { + case 0x8000: + case 0x4000: + case 0x2000: + btn_type |= SND_JACK_BTN_1; + break; + case 0x1000: + case 0x0800: + case 0x0400: + btn_type |= SND_JACK_BTN_2; + break; + case 0x0200: + case 0x0100: + btn_type |= SND_JACK_BTN_3; + break; + } +read_error: + return btn_type; +} + +static int rt700_headset_detect(struct rt700_priv *rt700) +{ + unsigned int buf, loop = 0; + int ret; + + ret = rt700_index_read(rt700->regmap, + RT700_COMBO_JACK_AUTO_CTL2, &buf); + if (ret < 0) + goto io_error; + + while (loop < 500 && + (buf & RT700_COMBOJACK_AUTO_DET_STATUS) == 0) { + loop++; + + usleep_range(9000, 10000); + ret = rt700_index_read(rt700->regmap, + RT700_COMBO_JACK_AUTO_CTL2, &buf); + if (ret < 0) + goto io_error; + } + + if (loop >= 500) + goto to_error; + + if (buf & RT700_COMBOJACK_AUTO_DET_TRS) + rt700->jack_type = SND_JACK_HEADPHONE; + else if ((buf & RT700_COMBOJACK_AUTO_DET_CTIA) || + (buf & RT700_COMBOJACK_AUTO_DET_OMTP)) + rt700->jack_type = SND_JACK_HEADSET; + + return 0; + +to_error: + ret = -ETIMEDOUT; + pr_err_ratelimited("Time-out error in %s\n", __func__); + return ret; +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static void rt700_jack_detect_handler(struct work_struct *work) +{ + struct rt700_priv *rt700 = + container_of(work, struct rt700_priv, jack_detect_work.work); + int btn_type = 0, ret; + unsigned int jack_status = 0, reg; + + if (!rt700->hs_jack) + return; + + if (!rt700->component->card->instantiated) + return; + + reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT; + ret = regmap_read(rt700->regmap, reg, &jack_status); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (jack_status & (1 << 31)) { + /* jack in */ + if (rt700->jack_type == 0) { + ret = rt700_headset_detect(rt700); + if (ret < 0) + return; + if (rt700->jack_type == SND_JACK_HEADSET) + btn_type = rt700_button_detect(rt700); + } else if (rt700->jack_type == SND_JACK_HEADSET) { + /* jack is already in, report button event */ + btn_type = rt700_button_detect(rt700); + } + } else { + /* jack out */ + rt700->jack_type = 0; + } + + dev_dbg(&rt700->slave->dev, + "in %s, jack_type=0x%x\n", __func__, rt700->jack_type); + dev_dbg(&rt700->slave->dev, + "in %s, btn_type=0x%x\n", __func__, btn_type); + + snd_soc_jack_report(rt700->hs_jack, rt700->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt700->hs_jack, rt700->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt700->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt700_btn_check_handler(struct work_struct *work) +{ + struct rt700_priv *rt700 = container_of(work, struct rt700_priv, + jack_btn_check_work.work); + int btn_type = 0, ret; + unsigned int jack_status = 0, reg; + + reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT; + ret = regmap_read(rt700->regmap, reg, &jack_status); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (jack_status & (1 << 31)) { + if (rt700->jack_type == SND_JACK_HEADSET) { + /* jack is already in, report button event */ + btn_type = rt700_button_detect(rt700); + } + } else { + rt700->jack_type = 0; + } + + /* cbj comparator */ + ret = rt700_index_read(rt700->regmap, RT700_COMBO_JACK_AUTO_CTL2, ®); + if (ret < 0) + goto io_error; + + if ((reg & 0xf0) == 0xf0) + btn_type = 0; + + dev_dbg(&rt700->slave->dev, + "%s, btn_type=0x%x\n", __func__, btn_type); + snd_soc_jack_report(rt700->hs_jack, rt700->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt700->hs_jack, rt700->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt700->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt700_jack_init(struct rt700_priv *rt700) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(rt700->component); + + /* power on */ + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + if (rt700->hs_jack) { + /* Enable Jack Detection */ + regmap_write(rt700->regmap, + RT700_SET_MIC2_UNSOLICITED_ENABLE, 0x82); + regmap_write(rt700->regmap, + RT700_SET_HP_UNSOLICITED_ENABLE, 0x81); + regmap_write(rt700->regmap, + RT700_SET_INLINE_UNSOLICITED_ENABLE, 0x83); + rt700_index_write(rt700->regmap, 0x10, 0x2420); + rt700_index_write(rt700->regmap, 0x19, 0x2e11); + + dev_dbg(&rt700->slave->dev, "in %s enable\n", __func__); + + mod_delayed_work(system_power_efficient_wq, + &rt700->jack_detect_work, msecs_to_jiffies(250)); + } else { + regmap_write(rt700->regmap, + RT700_SET_MIC2_UNSOLICITED_ENABLE, 0x00); + regmap_write(rt700->regmap, + RT700_SET_HP_UNSOLICITED_ENABLE, 0x00); + regmap_write(rt700->regmap, + RT700_SET_INLINE_UNSOLICITED_ENABLE, 0x00); + + dev_dbg(&rt700->slave->dev, "in %s disable\n", __func__); + } + + /* power off */ + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); +} + +static int rt700_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + rt700->hs_jack = hs_jack; + + if (!rt700->hw_init) { + dev_dbg(&rt700->slave->dev, + "%s hw_init not ready yet\n", __func__); + return 0; + } + + rt700_jack_init(rt700); + + return 0; +} + +static void rt700_get_gain(struct rt700_priv *rt700, unsigned int addr_h, + unsigned int addr_l, unsigned int val_h, + unsigned int *r_val, unsigned int *l_val) +{ + /* R Channel */ + *r_val = (val_h << 8); + regmap_read(rt700->regmap, addr_l, r_val); + + /* L Channel */ + val_h |= 0x20; + *l_val = (val_h << 8); + regmap_read(rt700->regmap, addr_h, l_val); +} + +/* For Verb-Set Amplifier Gain (Verb ID = 3h) */ +static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + unsigned int addr_h, addr_l, val_h, val_ll, val_lr; + unsigned int read_ll, read_rl; + int i; + + /* Can't use update bit function, so read the original value first */ + addr_h = mc->reg; + addr_l = mc->rreg; + if (mc->shift == RT700_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt700_get_gain(rt700, addr_h, addr_l, val_h, &read_rl, &read_ll); + + /* L Channel */ + if (mc->invert) { + /* for mute */ + val_ll = (mc->max - ucontrol->value.integer.value[0]) << 7; + /* keep gain */ + read_ll = read_ll & 0x7f; + val_ll |= read_ll; + } else { + /* for gain */ + val_ll = ((ucontrol->value.integer.value[0]) & 0x7f); + if (val_ll > mc->max) + val_ll = mc->max; + /* keep mute status */ + read_ll = read_ll & 0x80; + val_ll |= read_ll; + } + + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + /* R Channel */ + if (mc->invert) { + /* for mute */ + val_lr = (mc->max - ucontrol->value.integer.value[1]) << 7; + /* keep gain */ + read_rl = read_rl & 0x7f; + val_lr |= read_rl; + } else { + /* for gain */ + val_lr = ((ucontrol->value.integer.value[1]) & 0x7f); + if (val_lr > mc->max) + val_lr = mc->max; + /* keep mute status */ + read_rl = read_rl & 0x80; + val_lr |= read_rl; + } + + for (i = 0; i < 3; i++) { /* retry 3 times at most */ + if (val_ll == val_lr) { + /* Set both L/R channels at the same time */ + val_h = (1 << mc->shift) | (3 << 4); + regmap_write(rt700->regmap, addr_h, (val_h << 8 | val_ll)); + regmap_write(rt700->regmap, addr_l, (val_h << 8 | val_ll)); + } else { + /* Lch*/ + val_h = (1 << mc->shift) | (1 << 5); + regmap_write(rt700->regmap, addr_h, (val_h << 8 | val_ll)); + + /* Rch */ + val_h = (1 << mc->shift) | (1 << 4); + regmap_write(rt700->regmap, addr_l, (val_h << 8 | val_lr)); + } + /* check result */ + if (mc->shift == RT700_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt700_get_gain(rt700, addr_h, addr_l, val_h, + &read_rl, &read_ll); + if (read_rl == val_lr && read_ll == val_ll) + break; + } + + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + return 0; +} + +static int rt700_set_amp_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int addr_h, addr_l, val_h; + unsigned int read_ll, read_rl; + + addr_h = mc->reg; + addr_l = mc->rreg; + if (mc->shift == RT700_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt700_get_gain(rt700, addr_h, addr_l, val_h, &read_rl, &read_ll); + + if (mc->invert) { + /* for mute status */ + read_ll = !((read_ll & 0x80) >> RT700_MUTE_SFT); + read_rl = !((read_rl & 0x80) >> RT700_MUTE_SFT); + } else { + /* for gain */ + read_ll = read_ll & 0x7f; + read_rl = read_rl & 0x7f; + } + ucontrol->value.integer.value[0] = read_ll; + ucontrol->value.integer.value[1] = read_rl; + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt700_snd_controls[] = { + SOC_DOUBLE_R_EXT_TLV("DAC Front Playback Volume", RT700_SET_GAIN_DAC1_H, + RT700_SET_GAIN_DAC1_L, RT700_DIR_OUT_SFT, 0x57, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + out_vol_tlv), + SOC_DOUBLE_R_EXT("ADC 08 Capture Switch", RT700_SET_GAIN_ADC2_H, + RT700_SET_GAIN_ADC2_L, RT700_DIR_IN_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT("ADC 09 Capture Switch", RT700_SET_GAIN_ADC1_H, + RT700_SET_GAIN_ADC1_L, RT700_DIR_IN_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume", RT700_SET_GAIN_ADC2_H, + RT700_SET_GAIN_ADC2_L, RT700_DIR_IN_SFT, 0x3f, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume", RT700_SET_GAIN_ADC1_H, + RT700_SET_GAIN_ADC1_L, RT700_DIR_IN_SFT, 0x3f, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("AMIC Volume", RT700_SET_GAIN_AMIC_H, + RT700_SET_GAIN_AMIC_L, RT700_DIR_IN_SFT, 3, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + mic_vol_tlv), +}; + +static int rt700_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + unsigned int reg, val = 0, nid; + int ret; + + if (strstr(ucontrol->id.name, "HPO Mux")) + nid = RT700_HP_OUT; + else if (strstr(ucontrol->id.name, "ADC 22 Mux")) + nid = RT700_MIXER_IN1; + else if (strstr(ucontrol->id.name, "ADC 23 Mux")) + nid = RT700_MIXER_IN2; + else + return -EINVAL; + + /* vid = 0xf01 */ + reg = RT700_VERB_SET_CONNECT_SEL | nid; + ret = regmap_read(rt700->regmap, reg, &val); + if (ret < 0) + return ret; + + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int rt700_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, reg, nid; + int ret; + + if (item[0] >= e->items) + return -EINVAL; + + if (strstr(ucontrol->id.name, "HPO Mux")) + nid = RT700_HP_OUT; + else if (strstr(ucontrol->id.name, "ADC 22 Mux")) + nid = RT700_MIXER_IN1; + else if (strstr(ucontrol->id.name, "ADC 23 Mux")) + nid = RT700_MIXER_IN2; + else + return -EINVAL; + + /* Verb ID = 0x701h */ + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + reg = RT700_VERB_SET_CONNECT_SEL | nid; + ret = regmap_read(rt700->regmap, reg, &val2); + if (ret < 0) + return ret; + + if (val == val2) + change = 0; + else + change = 1; + + if (change) { + reg = RT700_VERB_SET_CONNECT_SEL | nid; + regmap_write(rt700->regmap, reg, val); + } + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, NULL); + + return change; +} + +static const char * const adc_mux_text[] = { + "MIC2", + "LINE1", + "LINE2", + "DMIC", +}; + +static SOC_ENUM_SINGLE_DECL( + rt700_adc22_enum, SND_SOC_NOPM, 0, adc_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt700_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text); + +static const struct snd_kcontrol_new rt700_adc22_mux = + SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt700_adc22_enum, + rt700_mux_get, rt700_mux_put); + +static const struct snd_kcontrol_new rt700_adc23_mux = + SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt700_adc23_enum, + rt700_mux_get, rt700_mux_put); + +static const char * const out_mux_text[] = { + "Front", + "Surround", +}; + +static SOC_ENUM_SINGLE_DECL( + rt700_hp_enum, SND_SOC_NOPM, 0, out_mux_text); + +static const struct snd_kcontrol_new rt700_hp_mux = + SOC_DAPM_ENUM_EXT("HP Mux", rt700_hp_enum, + rt700_mux_get, rt700_mux_put); + +static int rt700_dac_front_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_DAC1, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_DAC1, 0x00); + break; + } + return 0; +} + +static int rt700_dac_surround_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_DAC2, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_DAC2, 0x00); + break; + } + return 0; +} + +static int rt700_adc_09_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_ADC1, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_ADC1, 0x00); + break; + } + return 0; +} + +static int rt700_adc_08_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_ADC2, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_ADC2, 0x00); + break; + } + return 0; +} + +static int rt700_hpo_mux_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + unsigned int val_h = (1 << RT700_DIR_OUT_SFT) | (0x3 << 4); + unsigned int val_l; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val_l = 0x00; + regmap_write(rt700->regmap, + RT700_SET_GAIN_HP_H, (val_h << 8 | val_l)); + break; + case SND_SOC_DAPM_PRE_PMD: + val_l = (1 << RT700_MUTE_SFT); + regmap_write(rt700->regmap, + RT700_SET_GAIN_HP_H, (val_h << 8 | val_l)); + break; + } + return 0; +} + +static int rt700_spk_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + unsigned int val_h = (1 << RT700_DIR_OUT_SFT) | (0x3 << 4); + unsigned int val_l; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val_l = 0x00; + regmap_write(rt700->regmap, + RT700_SET_GAIN_SPK_H, (val_h << 8 | val_l)); + break; + case SND_SOC_DAPM_PRE_PMD: + val_l = (1 << RT700_MUTE_SFT); + regmap_write(rt700->regmap, + RT700_SET_GAIN_SPK_H, (val_h << 8 | val_l)); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget rt700_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + SND_SOC_DAPM_DAC_E("DAC Front", NULL, SND_SOC_NOPM, 0, 0, + rt700_dac_front_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC_E("DAC Surround", NULL, SND_SOC_NOPM, 0, 0, + rt700_dac_surround_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX_E("HPO Mux", SND_SOC_NOPM, 0, 0, &rt700_hp_mux, + rt700_hpo_mux_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("SPK PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + rt700_spk_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC 09", NULL, SND_SOC_NOPM, 0, 0, + rt700_adc_09_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC 08", NULL, SND_SOC_NOPM, 0, 0, + rt700_adc_08_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, + &rt700_adc22_mux), + SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0, + &rt700_adc23_mux), + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt700_audio_map[] = { + {"DAC Front", NULL, "DP1RX"}, + {"DAC Surround", NULL, "DP3RX"}, + {"DP2TX", NULL, "ADC 09"}, + {"DP4TX", NULL, "ADC 08"}, + {"ADC 09", NULL, "ADC 22 Mux"}, + {"ADC 08", NULL, "ADC 23 Mux"}, + {"ADC 22 Mux", "DMIC", "DMIC1"}, + {"ADC 22 Mux", "LINE1", "LINE1"}, + {"ADC 22 Mux", "LINE2", "LINE2"}, + {"ADC 22 Mux", "MIC2", "MIC2"}, + {"ADC 23 Mux", "DMIC", "DMIC2"}, + {"ADC 23 Mux", "LINE1", "LINE1"}, + {"ADC 23 Mux", "LINE2", "LINE2"}, + {"ADC 23 Mux", "MIC2", "MIC2"}, + {"HPO Mux", "Front", "DAC Front"}, + {"HPO Mux", "Surround", "DAC Surround"}, + {"HP", NULL, "HPO Mux"}, + {"SPK PGA", NULL, "DAC Front"}, + {"SPK", NULL, "SPK PGA"}, +}; + +static int rt700_probe(struct snd_soc_component *component) +{ + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + rt700->component = component; + + return 0; +} + +static int rt700_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, + AC_PWRST_D0); + } + break; + + case SND_SOC_BIAS_STANDBY: + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, + AC_PWRST_D3); + break; + + default: + break; + } + dapm->bias_level = level; + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_rt700 = { + .probe = rt700_probe, + .set_bias_level = rt700_set_bias_level, + .controls = rt700_snd_controls, + .num_controls = ARRAY_SIZE(rt700_snd_controls), + .dapm_widgets = rt700_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt700_dapm_widgets), + .dapm_routes = rt700_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt700_audio_map), + .set_jack = rt700_set_jack_detect, +}; + +static int rt700_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + struct sdw_stream_data *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + + stream->sdw_stream = (struct sdw_stream_runtime *)sdw_stream; + + /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dai->playback_dma_data = stream; + else + dai->capture_dma_data = stream; + + return 0; +} + +static void rt700_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_stream_data *stream; + + stream = snd_soc_dai_get_dma_data(dai, substream); + snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(stream); +} + +static int rt700_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_data *stream; + int retval, port, num_channels; + unsigned int val = 0; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!stream) + return -EINVAL; + + if (!rt700->slave) + return -EINVAL; + + /* SoundWire specific configuration */ + /* This code assumes port 1 for playback and port 2 for capture */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + port = 1; + } else { + direction = SDW_DATA_DIR_TX; + port = 2; + } + + switch (dai->id) { + case RT700_AIF1: + break; + case RT700_AIF2: + port += 2; + break; + default: + dev_err(component->dev, "Invalid DAI id %d\n", dai->id); + return -EINVAL; + } + + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + num_channels = params_channels(params); + port_config.ch_mask = (1 << (num_channels)) - 1; + port_config.num = port; + + retval = sdw_stream_add_slave(rt700->slave, &stream_config, + &port_config, 1, stream->sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + if (params_channels(params) <= 16) { + /* bit 3:0 Number of Channel */ + val |= (params_channels(params) - 1); + } else { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + switch (params_width(params)) { + /* bit 6:4 Bits per Sample */ + case 8: + break; + case 16: + val |= (0x1 << 4); + break; + case 20: + val |= (0x2 << 4); + break; + case 24: + val |= (0x3 << 4); + break; + case 32: + val |= (0x4 << 4); + break; + default: + return -EINVAL; + } + + /* 48Khz */ + regmap_write(rt700->regmap, RT700_DAC_FORMAT_H, val); + regmap_write(rt700->regmap, RT700_ADC_FORMAT_H, val); + + return retval; +} + +static int rt700_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + struct sdw_stream_data *stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt700->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt700->slave, stream->sdw_stream); + return 0; +} + +#define RT700_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define RT700_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static struct snd_soc_dai_ops rt700_ops = { + .hw_params = rt700_pcm_hw_params, + .hw_free = rt700_pcm_hw_free, + .set_sdw_stream = rt700_set_sdw_stream, + .shutdown = rt700_shutdown, +}; + +static struct snd_soc_dai_driver rt700_dai[] = { + { + .name = "rt700-aif1", + .id = RT700_AIF1, + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .ops = &rt700_ops, + }, + { + .name = "rt700-aif2", + .id = RT700_AIF2, + .playback = { + .stream_name = "DP3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .ops = &rt700_ops, + }, +}; + +/* Bus clock frequency */ +#define RT700_CLK_FREQ_9600000HZ 9600000 +#define RT700_CLK_FREQ_12000000HZ 12000000 +#define RT700_CLK_FREQ_6000000HZ 6000000 +#define RT700_CLK_FREQ_4800000HZ 4800000 +#define RT700_CLK_FREQ_2400000HZ 2400000 +#define RT700_CLK_FREQ_12288000HZ 12288000 + +int rt700_clock_config(struct device *dev) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned int clk_freq, value; + + clk_freq = (rt700->params.curr_dr_freq >> 1); + + switch (clk_freq) { + case RT700_CLK_FREQ_12000000HZ: + value = 0x0; + break; + case RT700_CLK_FREQ_6000000HZ: + value = 0x1; + break; + case RT700_CLK_FREQ_9600000HZ: + value = 0x2; + break; + case RT700_CLK_FREQ_4800000HZ: + value = 0x3; + break; + case RT700_CLK_FREQ_2400000HZ: + value = 0x4; + break; + case RT700_CLK_FREQ_12288000HZ: + value = 0x5; + break; + default: + return -EINVAL; + } + + regmap_write(rt700->regmap, 0xe0, value); + regmap_write(rt700->regmap, 0xf0, value); + + dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq); + + return 0; +} + +int rt700_init(struct device *dev, struct regmap *sdw_regmap, + struct regmap *regmap, struct sdw_slave *slave) + +{ + struct rt700_priv *rt700; + int ret; + + rt700 = devm_kzalloc(dev, sizeof(*rt700), GFP_KERNEL); + if (!rt700) + return -ENOMEM; + + dev_set_drvdata(dev, rt700); + rt700->slave = slave; + rt700->sdw_regmap = sdw_regmap; + rt700->regmap = regmap; + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt700->hw_init = false; + rt700->first_init = false; + + ret = devm_snd_soc_register_component(dev, + &soc_codec_dev_rt700, + rt700_dai, + ARRAY_SIZE(rt700_dai)); + + dev_dbg(&slave->dev, "%s\n", __func__); + + return ret; +} + +int rt700_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + + if (rt700->hw_init) + return 0; + + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + if (!rt700->first_init) { + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + + rt700->first_init = true; + } + + pm_runtime_get_noresume(&slave->dev); + + /* reset */ + regmap_write(rt700->regmap, 0xff01, 0x0000); + regmap_write(rt700->regmap, 0x7520, 0x001a); + regmap_write(rt700->regmap, 0x7420, 0xc003); + + /* power on */ + regmap_write(rt700->regmap, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + /* Set Pin Widget */ + regmap_write(rt700->regmap, RT700_SET_PIN_HP, 0x40); + regmap_write(rt700->regmap, RT700_SET_PIN_SPK, 0x40); + regmap_write(rt700->regmap, RT700_SET_EAPD_SPK, RT700_EAPD_HIGH); + regmap_write(rt700->regmap, RT700_SET_PIN_DMIC1, 0x20); + regmap_write(rt700->regmap, RT700_SET_PIN_DMIC2, 0x20); + regmap_write(rt700->regmap, RT700_SET_PIN_MIC2, 0x20); + + /* Set Configuration Default */ + regmap_write(rt700->regmap, 0x4f12, 0x91); + regmap_write(rt700->regmap, 0x4e12, 0xd6); + regmap_write(rt700->regmap, 0x4d12, 0x11); + regmap_write(rt700->regmap, 0x4c12, 0x20); + regmap_write(rt700->regmap, 0x4f13, 0x91); + regmap_write(rt700->regmap, 0x4e13, 0xd6); + regmap_write(rt700->regmap, 0x4d13, 0x11); + regmap_write(rt700->regmap, 0x4c13, 0x21); + + regmap_write(rt700->regmap, 0x4f19, 0x02); + regmap_write(rt700->regmap, 0x4e19, 0xa1); + regmap_write(rt700->regmap, 0x4d19, 0x90); + regmap_write(rt700->regmap, 0x4c19, 0x80); + + /* Enable Line2 */ + regmap_write(rt700->regmap, 0x371b, 0x40); + regmap_write(rt700->regmap, 0x731b, 0xb0); + regmap_write(rt700->regmap, 0x839b, 0x00); + + /* Set index */ + rt700_index_write(rt700->regmap, 0x4a, 0x201b); + rt700_index_write(rt700->regmap, 0x45, 0x5089); + rt700_index_write(rt700->regmap, 0x6b, 0x5064); + rt700_index_write(rt700->regmap, 0x48, 0xd249); + + /* Finish Initial Settings, set power to D3 */ + regmap_write(rt700->regmap, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + + INIT_DELAYED_WORK(&rt700->jack_detect_work, + rt700_jack_detect_handler); + INIT_DELAYED_WORK(&rt700->jack_btn_check_work, + rt700_btn_check_handler); + + /* + * if set_jack callback occurred early than io_init, + * we set up the jack detection function now + */ + if (rt700->hs_jack) + rt700_jack_init(rt700); + + /* Mark Slave initialization complete */ + rt700->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + + return 0; +} + +MODULE_DESCRIPTION("ASoC RT700 driver SDW"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt700.h b/sound/soc/codecs/rt700.h new file mode 100644 index 00000000000000..11f046e6255e29 --- /dev/null +++ b/sound/soc/codecs/rt700.h @@ -0,0 +1,176 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt700.h -- RT700 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT700_H__ +#define __RT700_H__ + +#include + +extern const struct dev_pm_ops rt700_runtime_pm; + +struct rt700_priv { + struct snd_soc_component *component; + struct regmap *regmap; + struct regmap *sdw_regmap; + struct sdw_slave *slave; + enum sdw_slave_status status; + struct sdw_bus_params params; + bool hw_init; + bool first_init; + struct snd_soc_jack *hs_jack; + struct delayed_work jack_detect_work; + struct delayed_work jack_btn_check_work; + int jack_type; +}; + +struct sdw_stream_data { + struct sdw_stream_runtime *sdw_stream; +}; + +/* NID */ +#define RT700_AUDIO_FUNCTION_GROUP 0x01 +#define RT700_DAC_OUT1 0x02 +#define RT700_DAC_OUT2 0x03 +#define RT700_ADC_IN1 0x09 +#define RT700_ADC_IN2 0x08 +#define RT700_DMIC1 0x12 +#define RT700_DMIC2 0x13 +#define RT700_SPK_OUT 0x14 +#define RT700_MIC2 0x19 +#define RT700_LINE1 0x1a +#define RT700_LINE2 0x1b +#define RT700_BEEP 0x1d +#define RT700_SPDIF 0x1e +#define RT700_VENDOR_REGISTERS 0x20 +#define RT700_HP_OUT 0x21 +#define RT700_MIXER_IN1 0x22 +#define RT700_MIXER_IN2 0x23 +#define RT700_INLINE_CMD 0x55 + +/* Index (NID:20h) */ +#define RT700_DAC_DC_CALI_CTL1 0x00 +#define RT700_PARA_VERB_CTL 0x1a +#define RT700_COMBO_JACK_AUTO_CTL1 0x45 +#define RT700_COMBO_JACK_AUTO_CTL2 0x46 +#define RT700_INLINE_CMD_CTL 0x48 +#define RT700_DIGITAL_MISC_CTRL4 0x4a +#define RT700_VREFOUT_CTL 0x6b +#define RT700_FSM_CTL 0x6f +#define RT700_IRQ_FLAG_TABLE1 0x80 +#define RT700_IRQ_FLAG_TABLE2 0x81 +#define RT700_IRQ_FLAG_TABLE3 0x82 + +/* Verb */ +#define RT700_VERB_SET_CONNECT_SEL 0x3100 +#define RT700_VERB_SET_EAPD_BTLENABLE 0x3c00 +#define RT700_VERB_GET_CONNECT_SEL 0xb100 +#define RT700_VERB_SET_POWER_STATE 0x3500 +#define RT700_VERB_SET_CHANNEL_STREAMID 0x3600 +#define RT700_VERB_SET_PIN_WIDGET_CONTROL 0x3700 +#define RT700_VERB_SET_UNSOLICITED_ENABLE 0x3800 +#define RT700_SET_AMP_GAIN_MUTE_H 0x7300 +#define RT700_SET_AMP_GAIN_MUTE_L 0x8380 +#define RT700_VERB_GET_PIN_SENSE 0xb900 + +#define RT700_READ_HDA_3 0x2012 +#define RT700_READ_HDA_2 0x2013 +#define RT700_READ_HDA_1 0x2014 +#define RT700_READ_HDA_0 0x2015 +#define RT700_PRIV_INDEX_W_H 0x7520 +#define RT700_PRIV_INDEX_W_L 0x85a0 +#define RT700_PRIV_DATA_W_H 0x7420 +#define RT700_PRIV_DATA_W_L 0x84a0 +#define RT700_PRIV_INDEX_R_H 0x9d20 +#define RT700_PRIV_INDEX_R_L 0xada0 +#define RT700_PRIV_DATA_R_H 0x9c20 +#define RT700_PRIV_DATA_R_L 0xaca0 +#define RT700_DAC_FORMAT_H 0x7203 +#define RT700_DAC_FORMAT_L 0x8283 +#define RT700_ADC_FORMAT_H 0x7209 +#define RT700_ADC_FORMAT_L 0x8289 +#define RT700_SET_AUDIO_POWER_STATE\ + (RT700_VERB_SET_POWER_STATE | RT700_AUDIO_FUNCTION_GROUP) +#define RT700_SET_PIN_DMIC1\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_DMIC1) +#define RT700_SET_PIN_DMIC2\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_DMIC2) +#define RT700_SET_PIN_SPK\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_SPK_OUT) +#define RT700_SET_PIN_HP\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_HP_OUT) +#define RT700_SET_PIN_MIC2\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_MIC2) +#define RT700_SET_PIN_LINE1\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_LINE1) +#define RT700_SET_PIN_LINE2\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_LINE2) +#define RT700_SET_MIC2_UNSOLICITED_ENABLE\ + (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_MIC2) +#define RT700_SET_HP_UNSOLICITED_ENABLE\ + (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_HP_OUT) +#define RT700_SET_INLINE_UNSOLICITED_ENABLE\ + (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_INLINE_CMD) +#define RT700_SET_STREAMID_DAC1\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_DAC_OUT1) +#define RT700_SET_STREAMID_DAC2\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_DAC_OUT2) +#define RT700_SET_STREAMID_ADC1\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_ADC_IN1) +#define RT700_SET_STREAMID_ADC2\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_ADC_IN2) +#define RT700_SET_GAIN_DAC1_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_DAC_OUT1) +#define RT700_SET_GAIN_DAC1_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_DAC_OUT1) +#define RT700_SET_GAIN_ADC1_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_ADC_IN1) +#define RT700_SET_GAIN_ADC1_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_ADC_IN1) +#define RT700_SET_GAIN_ADC2_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_ADC_IN2) +#define RT700_SET_GAIN_ADC2_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_ADC_IN2) +#define RT700_SET_GAIN_AMIC_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_MIC2) +#define RT700_SET_GAIN_AMIC_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_MIC2) +#define RT700_SET_GAIN_HP_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_HP_OUT) +#define RT700_SET_GAIN_HP_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_HP_OUT) +#define RT700_SET_GAIN_SPK_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_SPK_OUT) +#define RT700_SET_GAIN_SPK_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_SPK_OUT) +#define RT700_SET_EAPD_SPK\ + (RT700_VERB_SET_EAPD_BTLENABLE | RT700_SPK_OUT) + +/* combo jack auto switch control 2 (0x46)(NID:20h) */ +#define RT700_COMBOJACK_AUTO_DET_STATUS (0x1 << 11) +#define RT700_COMBOJACK_AUTO_DET_TRS (0x1 << 10) +#define RT700_COMBOJACK_AUTO_DET_CTIA (0x1 << 9) +#define RT700_COMBOJACK_AUTO_DET_OMTP (0x1 << 8) + +#define RT700_EAPD_HIGH 0x2 +#define RT700_EAPD_LOW 0x0 +#define RT700_MUTE_SFT 7 +#define RT700_DIR_IN_SFT 6 +#define RT700_DIR_OUT_SFT 7 + +enum { + RT700_AIF1, + RT700_AIF2, + RT700_AIFS, +}; + +int rt700_io_init(struct device *dev, struct sdw_slave *slave); +int rt700_init(struct device *dev, struct regmap *sdw_regmap, + struct regmap *regmap, struct sdw_slave *slave); + +int rt700_jack_detect(struct rt700_priv *rt700, bool *hp, bool *mic); +int rt700_clock_config(struct device *dev); +#endif /* __RT700_H__ */ From 2d35c7ee9f211fe20b388fb90531d3a22890b6f0 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Fri, 2 Aug 2019 10:26:02 +0800 Subject: [PATCH 027/110] ASoC: codecs: rt1308: add SoundWire support The driver for this amplifier was tested by Realtek and Intel using the Realtek 3-in-1 add-on boards connected to CometLake and IceLake platforms Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Signed-off-by: Shuming Fan --- sound/soc/codecs/Kconfig | 6 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt1308-sdw.c | 702 ++++++++++++++++++++++++++++++++++ sound/soc/codecs/rt1308-sdw.h | 169 ++++++++ sound/soc/codecs/rt1308.c | 13 +- 5 files changed, 886 insertions(+), 6 deletions(-) create mode 100644 sound/soc/codecs/rt1308-sdw.c create mode 100644 sound/soc/codecs/rt1308-sdw.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index b94548b5e8a802..be71a3b4617ef6 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1065,6 +1065,12 @@ config SND_SOC_RT700_SDW select SND_SOC_RT700 select REGMAP_SOUNDWIRE +config SND_SOC_RT1308_SDW + tristate "Realtek RT1308 Codec - SDW" + depends on SOUNDWIRE + select SND_SOC_RT1308 + select REGMAP_SOUNDWIRE + #Freescale sgtl5000 codec config SND_SOC_SGTL5000 tristate "Freescale SGTL5000 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d825351bb41893..3c4fb8b39bcdc8 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -279,6 +279,7 @@ snd-soc-wm9713-objs := wm9713.o snd-soc-wm-hubs-objs := wm_hubs.o snd-soc-zx-aud96p22-objs := zx_aud96p22.o snd-soc-rt700-objs := rt700.o rt700-sdw.o +snd-soc-rt1308-sdw-objs := rt1308-sdw.o # Amp snd-soc-max9877-objs := max9877.o @@ -570,6 +571,7 @@ obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o obj-$(CONFIG_SND_SOC_ZX_AUD96P22) += snd-soc-zx-aud96p22.o obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o +obj-$(CONFIG_SND_SOC_RT1308_SDW) += snd-soc-rt1308-sdw.o # Amp obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c new file mode 100644 index 00000000000000..5730f59ae08022 --- /dev/null +++ b/sound/soc/codecs/rt1308-sdw.c @@ -0,0 +1,702 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt1308-sdw.c -- rt1308 ALSA SoC audio driver +// +// Copyright(c) 2019 Realtek Semiconductor Corp. +// +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt1308.h" +#include "rt1308-sdw.h" + +static bool rt1308_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00e0: + case 0x00f0: + case 0x2f01 ... 0x2f07: + case 0x3000 ... 0x3001: + case 0x3004 ... 0x3005: + case 0x3008: + case 0x300a: + case 0xc000 ... 0xcff3: + return true; + default: + return false; + } +} + +static bool rt1308_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f01 ... 0x2f07: + case 0x3000 ... 0x3001: + case 0x3004 ... 0x3005: + case 0x3008: + case 0x300a: + case 0xc000: + return true; + default: + return false; + } +} + +static const struct regmap_config rt1308_sdw_regmap = { + .reg_bits = 32, /* Total register space for SDW */ + .val_bits = 8, /* Total number of bits in register */ + .readable_reg = rt1308_readable_register, /* Readable registers */ + .volatile_reg = rt1308_volatile_register, /* volatile register */ + .max_register = 0xcfff, /* Maximum number of register */ + .reg_defaults = rt1308_reg_defaults, /* Defaults */ + .num_reg_defaults = ARRAY_SIZE(rt1308_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, +}; + +/* Bus clock frequency */ +#define RT1308_CLK_FREQ_9600000HZ 9600000 +#define RT1308_CLK_FREQ_12000000HZ 12000000 +#define RT1308_CLK_FREQ_6000000HZ 6000000 +#define RT1308_CLK_FREQ_4800000HZ 4800000 +#define RT1308_CLK_FREQ_2400000HZ 2400000 +#define RT1308_CLK_FREQ_12288000HZ 12288000 + +static int rt1308_clock_config(struct device *dev) +{ + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); + unsigned int clk_freq, value; + + clk_freq = (rt1308->params.curr_dr_freq >> 1); + + switch (clk_freq) { + case RT1308_CLK_FREQ_12000000HZ: + value = 0x0; + break; + case RT1308_CLK_FREQ_6000000HZ: + value = 0x1; + break; + case RT1308_CLK_FREQ_9600000HZ: + value = 0x2; + break; + case RT1308_CLK_FREQ_4800000HZ: + value = 0x3; + break; + case RT1308_CLK_FREQ_2400000HZ: + value = 0x4; + break; + case RT1308_CLK_FREQ_12288000HZ: + value = 0x5; + break; + default: + return -EINVAL; + } + + regmap_write(rt1308->regmap, 0xe0, value); + regmap_write(rt1308->regmap, 0xf0, value); + + dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq); + + return 0; +} + +static int rt1308_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval, i, num_of_ports = 1; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->paging_support = true; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = 0x00; /* BITMAP: 00010100 (not enable yet) */ + prop->sink_ports = 0x2; /* BITMAP: 00000010 */ + + /* for sink */ + nval = hweight32(prop->sink_ports); + num_of_ports += nval; + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* Allocate port_ready based on num_of_ports */ + slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports, + sizeof(*slave->port_ready), + GFP_KERNEL); + if (!slave->port_ready) + return -ENOMEM; + + /* Initialize completion */ + for (i = 0; i < num_of_ports; i++) + init_completion(&slave->port_ready[i]); + + /* set the timeout values */ + prop->clk_stop_timeout = 20; + + dev_dbg(&slave->dev, "%s\n", __func__); + + return 0; +} + +static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); + int ret = 0; + + if (rt1308->hw_init) + return 0; + + ret = rt1308_read_prop(slave); + if (ret < 0) + goto _io_init_err_; + + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + if (!rt1308->first_init) { + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + + rt1308->first_init = true; + } + + pm_runtime_get_noresume(&slave->dev); + + /* sw reset */ + regmap_write(rt1308->regmap, RT1308_SDW_RESET, 0); + + /* read efuse */ + regmap_write(rt1308->regmap, 0xc360, 0x01); + regmap_write(rt1308->regmap, 0xc360, 0x80); + regmap_write(rt1308->regmap, 0xc7f0, 0x04); + regmap_write(rt1308->regmap, 0xc7f1, 0xfe); + msleep(100); + regmap_write(rt1308->regmap, 0xc7f0, 0x44); + msleep(20); + regmap_write(rt1308->regmap, 0xc240, 0x10); + + /* initial settings */ + regmap_write(rt1308->regmap, 0xc103, 0xc0); + regmap_write(rt1308->regmap, 0xc030, 0x17); + regmap_write(rt1308->regmap, 0xc031, 0x81); + regmap_write(rt1308->regmap, 0xc032, 0x26); + regmap_write(rt1308->regmap, 0xc040, 0x80); + regmap_write(rt1308->regmap, 0xc041, 0x80); + regmap_write(rt1308->regmap, 0xc042, 0x06); + regmap_write(rt1308->regmap, 0xc052, 0x0a); + regmap_write(rt1308->regmap, 0xc080, 0x0a); + regmap_write(rt1308->regmap, 0xc060, 0x02); + regmap_write(rt1308->regmap, 0xc061, 0x75); + regmap_write(rt1308->regmap, 0xc062, 0x05); + regmap_write(rt1308->regmap, 0xc171, 0x07); + regmap_write(rt1308->regmap, 0xc173, 0x0d); + regmap_write(rt1308->regmap, 0xc311, 0x7f); + regmap_write(rt1308->regmap, 0xc900, 0x90); + regmap_write(rt1308->regmap, 0xc1a0, 0x84); + regmap_write(rt1308->regmap, 0xc1a1, 0x01); + regmap_write(rt1308->regmap, 0xc360, 0x78); + regmap_write(rt1308->regmap, 0xc361, 0x87); + regmap_write(rt1308->regmap, 0xc0a1, 0x71); + regmap_write(rt1308->regmap, 0xc210, 0x00); + regmap_write(rt1308->regmap, 0xc070, 0x00); + regmap_write(rt1308->regmap, 0xc100, 0xaf); + regmap_write(rt1308->regmap, 0xc101, 0xaf); + regmap_write(rt1308->regmap, 0xc310, 0x24); + + /* Mark Slave initialization complete */ + rt1308->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + +_io_init_err_: + return ret; +} + +static int rt1308_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev); + + /* Update the status */ + rt1308->status = status; + + if (status == SDW_SLAVE_UNATTACHED) + rt1308->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt1308->hw_init || rt1308->status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt1308_io_init(&slave->dev, slave); +} + +static int rt1308_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev); + int ret; + + memcpy(&rt1308->params, params, sizeof(*params)); + + ret = rt1308_clock_config(&slave->dev); + if (ret < 0) + dev_err(&slave->dev, "Invalid clk config"); + + return ret; +} + +static int rt1308_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + dev_dbg(&slave->dev, + "%s control_port_stat=%x", __func__, status->control_port); + + return 0; +} + +static int rt1308_classd_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msleep(30); + snd_soc_component_update_bits(component, + RT1308_SDW_OFFSET | (RT1308_POWER_STATUS << 4), + 0x3, 0x3); + msleep(40); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, + RT1308_SDW_OFFSET | (RT1308_POWER_STATUS << 4), + 0x3, 0); + usleep_range(150000, 200000); + break; + + default: + break; + } + + return 0; +} + +static const char * const rt1308_rx_data_ch_select[] = { + "LR", + "LL", + "RL", + "RR", +}; + +static SOC_ENUM_SINGLE_DECL(rt1308_rx_data_ch_enum, + RT1308_SDW_OFFSET | (RT1308_DATA_PATH << 4), 0, + rt1308_rx_data_ch_select); + +static const struct snd_kcontrol_new rt1308_snd_controls[] = { + + /* I2S Data Channel Selection */ + SOC_ENUM("RX Channel Select", rt1308_rx_data_ch_enum), +}; + +static const struct snd_kcontrol_new rt1308_sto_dac_l = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", + RT1308_SDW_OFFSET_BYTE3 | (RT1308_DAC_SET << 4), + RT1308_DVOL_MUTE_L_EN_SFT, 1, 1); + +static const struct snd_kcontrol_new rt1308_sto_dac_r = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", + RT1308_SDW_OFFSET_BYTE3 | (RT1308_DAC_SET << 4), + RT1308_DVOL_MUTE_R_EN_SFT, 1, 1); + +static const struct snd_soc_dapm_widget rt1308_dapm_widgets[] = { + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Supply Widgets */ + SND_SOC_DAPM_SUPPLY("MBIAS20U", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ALDO", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 6, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DBG", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DACL", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLK25M", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC_R", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC_L", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Power", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 3, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DLDO", + RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF", + RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIXER_R", + RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIXER_L", + RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MBIAS4U", + RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("PLL2_LDO", + RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2B", + RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2F", + RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2F2", + RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2B2", + RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 0, 0, NULL, 0), + + /* Digital Interface */ + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SWITCH("DAC L", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_l), + SND_SOC_DAPM_SWITCH("DAC R", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_r), + + /* Output Lines */ + SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0, + rt1308_classd_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), +}; + +static const struct snd_soc_dapm_route rt1308_dapm_routes[] = { + + { "DAC", NULL, "AIF1RX" }, + + { "DAC", NULL, "MBIAS20U" }, + { "DAC", NULL, "ALDO" }, + { "DAC", NULL, "DBG" }, + { "DAC", NULL, "DACL" }, + { "DAC", NULL, "CLK25M" }, + { "DAC", NULL, "ADC_R" }, + { "DAC", NULL, "ADC_L" }, + { "DAC", NULL, "DLDO" }, + { "DAC", NULL, "VREF" }, + { "DAC", NULL, "MIXER_R" }, + { "DAC", NULL, "MIXER_L" }, + { "DAC", NULL, "MBIAS4U" }, + { "DAC", NULL, "PLL2_LDO" }, + { "DAC", NULL, "PLL2B" }, + { "DAC", NULL, "PLL2F" }, + { "DAC", NULL, "PLL2F2" }, + { "DAC", NULL, "PLL2B2" }, + + { "DAC L", "Switch", "DAC" }, + { "DAC R", "Switch", "DAC" }, + { "DAC L", NULL, "DAC Power" }, + { "DAC R", NULL, "DAC Power" }, + + { "CLASS D", NULL, "DAC L" }, + { "CLASS D", NULL, "DAC R" }, + { "SPOL", NULL, "CLASS D" }, + { "SPOR", NULL, "CLASS D" }, +}; + +static int rt1308_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + struct sdw_stream_data *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + + stream->sdw_stream = (struct sdw_stream_runtime *)sdw_stream; + + /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dai->playback_dma_data = stream; + else + dai->capture_dma_data = stream; + + return 0; +} + +static void rt1308_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_stream_data *stream; + + stream = snd_soc_dai_get_dma_data(dai, substream); + snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(stream); +} + +static int rt1308_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1308_sdw_priv *rt1308 = + snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_data *stream; + int retval, port, num_channels; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!stream) + return -EINVAL; + + if (!rt1308->sdw_slave) + return -EINVAL; + + /* SoundWire specific configuration */ + /* port 1 for playback */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + port = 1; + } else { + return -EINVAL; + } + + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + num_channels = params_channels(params); + port_config.ch_mask = (1 << (num_channels)) - 1; + port_config.num = port; + + retval = sdw_stream_add_slave(rt1308->sdw_slave, &stream_config, + &port_config, 1, stream->sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + return retval; +} + +static int rt1308_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1308_sdw_priv *rt1308 = + snd_soc_component_get_drvdata(component); + struct sdw_stream_data *stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt1308->sdw_slave) + return -EINVAL; + + sdw_stream_remove_slave(rt1308->sdw_slave, stream->sdw_stream); + return 0; +} + +/* + * slave_ops: callbacks for get_clock_stop_mode, clock_stop and + * port_prep are not defined for now + */ +static struct sdw_slave_ops rt1308_slave_ops = { + .read_prop = rt1308_read_prop, + .interrupt_callback = rt1308_interrupt_callback, + .update_status = rt1308_update_status, + .bus_config = rt1308_bus_config, +}; + +static const struct snd_soc_component_driver soc_component_sdw_rt1308 = { + .controls = rt1308_snd_controls, + .num_controls = ARRAY_SIZE(rt1308_snd_controls), + .dapm_widgets = rt1308_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1308_dapm_widgets), + .dapm_routes = rt1308_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1308_dapm_routes), +}; + +static const struct snd_soc_dai_ops rt1308_aif_dai_ops = { + .hw_params = rt1308_sdw_hw_params, + .hw_free = rt1308_sdw_pcm_hw_free, + .set_sdw_stream = rt1308_set_sdw_stream, + .shutdown = rt1308_sdw_shutdown, +}; + +#define RT1308_STEREO_RATES SNDRV_PCM_RATE_48000 +#define RT1308_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver rt1308_sdw_dai[] = { + { + .name = "rt1308-aif", + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT1308_STEREO_RATES, + .formats = RT1308_FORMATS, + }, + .ops = &rt1308_aif_dai_ops, + }, +}; + +static int rt1308_sdw_init(struct device *dev, struct regmap *regmap, + struct sdw_slave *slave) +{ + struct rt1308_sdw_priv *rt1308; + int ret; + + rt1308 = devm_kzalloc(dev, sizeof(*rt1308), GFP_KERNEL); + if (!rt1308) + return -ENOMEM; + + dev_set_drvdata(dev, rt1308); + rt1308->sdw_slave = slave; + rt1308->regmap = regmap; + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt1308->hw_init = false; + rt1308->first_init = false; + + ret = devm_snd_soc_register_component(dev, + &soc_component_sdw_rt1308, + rt1308_sdw_dai, + ARRAY_SIZE(rt1308_sdw_dai)); + + dev_dbg(&slave->dev, "%s\n", __func__); + + return ret; +} + +static int rt1308_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap; + + /* Assign ops */ + slave->ops = &rt1308_slave_ops; + + /* Regmap Initialization */ + regmap = devm_regmap_init_sdw(slave, &rt1308_sdw_regmap); + if (!regmap) + return -EINVAL; + + rt1308_sdw_init(&slave->dev, regmap, slave); + + return 0; +} + +static const struct sdw_device_id rt1308_id[] = { + SDW_SLAVE_ENTRY(0x025d, 0x1308, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt1308_id); + +static int rt1308_dev_suspend(struct device *dev) +{ + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); + + if (!rt1308->hw_init) + return 0; + + regcache_cache_only(rt1308->regmap, true); + + return 0; +} + +#define RT1308_PROBE_TIMEOUT 2000 + +static int rt1308_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = to_sdw_slave_device(dev); + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt1308->hw_init) + return 0; + + time = wait_for_completion_timeout(&slave->enumeration_complete, + msecs_to_jiffies(RT1308_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Enumeration not complete, timed out\n"); + return -ETIMEDOUT; + } + + regcache_cache_only(rt1308->regmap, false); + regcache_sync_region(rt1308->regmap, 0xc000, 0xcfff); + + return 0; +} + +static const struct dev_pm_ops rt1308_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume) + SET_RUNTIME_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume, NULL) +}; + +static struct sdw_driver rt1308_sdw_driver = { + .driver = { + .name = "rt1308", + .owner = THIS_MODULE, + .pm = &rt1308_pm, + }, + .probe = rt1308_sdw_probe, + .ops = &rt1308_slave_ops, + .id_table = rt1308_id, +}; +module_sdw_driver(rt1308_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT1308 driver SDW"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt1308-sdw.h b/sound/soc/codecs/rt1308-sdw.h new file mode 100644 index 00000000000000..e83440be32f240 --- /dev/null +++ b/sound/soc/codecs/rt1308-sdw.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt1308-sdw.h -- RT1308 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT1308_SDW_H__ +#define __RT1308_SDW_H__ + +static const struct reg_default rt1308_reg_defaults[] = { + { 0x0000, 0x00 }, + { 0x0001, 0x00 }, + { 0x0002, 0x00 }, + { 0x0003, 0x00 }, + { 0x0004, 0x00 }, + { 0x0005, 0x01 }, + { 0x0020, 0x00 }, + { 0x0022, 0x00 }, + { 0x0023, 0x00 }, + { 0x0024, 0x00 }, + { 0x0025, 0x00 }, + { 0x0026, 0x00 }, + { 0x0030, 0x00 }, + { 0x0032, 0x00 }, + { 0x0033, 0x00 }, + { 0x0034, 0x00 }, + { 0x0035, 0x00 }, + { 0x0036, 0x00 }, + { 0x0040, 0x00 }, + { 0x0041, 0x00 }, + { 0x0042, 0x00 }, + { 0x0043, 0x00 }, + { 0x0044, 0x20 }, + { 0x0045, 0x01 }, + { 0x0046, 0x01 }, + { 0x0048, 0x00 }, + { 0x0049, 0x00 }, + { 0x0050, 0x20 }, + { 0x0051, 0x02 }, + { 0x0052, 0x5D }, + { 0x0053, 0x13 }, + { 0x0054, 0x08 }, + { 0x0055, 0x00 }, + { 0x0060, 0x00 }, + { 0x0070, 0x00 }, + { 0x00E0, 0x00 }, + { 0x00F0, 0x00 }, + { 0x0100, 0x00 }, + { 0x0101, 0x00 }, + { 0x0102, 0x20 }, + { 0x0103, 0x00 }, + { 0x0104, 0x00 }, + { 0x0105, 0x03 }, + { 0x0120, 0x00 }, + { 0x0122, 0x00 }, + { 0x0123, 0x00 }, + { 0x0124, 0x00 }, + { 0x0125, 0x00 }, + { 0x0126, 0x00 }, + { 0x0127, 0x00 }, + { 0x0130, 0x00 }, + { 0x0132, 0x00 }, + { 0x0133, 0x00 }, + { 0x0134, 0x00 }, + { 0x0135, 0x00 }, + { 0x0136, 0x00 }, + { 0x0137, 0x00 }, + { 0x0200, 0x00 }, + { 0x0201, 0x00 }, + { 0x0202, 0x00 }, + { 0x0203, 0x00 }, + { 0x0204, 0x00 }, + { 0x0205, 0x03 }, + { 0x0220, 0x00 }, + { 0x0222, 0x00 }, + { 0x0223, 0x00 }, + { 0x0224, 0x00 }, + { 0x0225, 0x00 }, + { 0x0226, 0x00 }, + { 0x0227, 0x00 }, + { 0x0230, 0x00 }, + { 0x0232, 0x00 }, + { 0x0233, 0x00 }, + { 0x0234, 0x00 }, + { 0x0235, 0x00 }, + { 0x0236, 0x00 }, + { 0x0237, 0x00 }, + { 0x0400, 0x00 }, + { 0x0401, 0x00 }, + { 0x0402, 0x00 }, + { 0x0403, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x03 }, + { 0x0420, 0x00 }, + { 0x0422, 0x00 }, + { 0x0423, 0x00 }, + { 0x0424, 0x00 }, + { 0x0425, 0x00 }, + { 0x0426, 0x00 }, + { 0x0427, 0x00 }, + { 0x0430, 0x00 }, + { 0x0432, 0x00 }, + { 0x0433, 0x00 }, + { 0x0434, 0x00 }, + { 0x0435, 0x00 }, + { 0x0436, 0x00 }, + { 0x0437, 0x00 }, + { 0x0f00, 0x00 }, + { 0x0f01, 0x00 }, + { 0x0f02, 0x00 }, + { 0x0f03, 0x00 }, + { 0x0f04, 0x00 }, + { 0x0f05, 0x00 }, + { 0x0f20, 0x00 }, + { 0x0f22, 0x00 }, + { 0x0f23, 0x00 }, + { 0x0f24, 0x00 }, + { 0x0f25, 0x00 }, + { 0x0f26, 0x00 }, + { 0x0f27, 0x00 }, + { 0x0f30, 0x00 }, + { 0x0f32, 0x00 }, + { 0x0f33, 0x00 }, + { 0x0f34, 0x00 }, + { 0x0f35, 0x00 }, + { 0x0f36, 0x00 }, + { 0x0f37, 0x00 }, + { 0x2f01, 0x01 }, + { 0x2f02, 0x09 }, + { 0x2f03, 0x00 }, + { 0x2f04, 0x0f }, + { 0x2f05, 0x0b }, + { 0x2f06, 0x01 }, + { 0x2f07, 0x8e }, + { 0x3000, 0x00 }, + { 0x3001, 0x00 }, + { 0x3004, 0x01 }, + { 0x3005, 0x23 }, + { 0x3008, 0x02 }, + { 0x300a, 0x00 }, + { 0xc003 | (RT1308_DAC_SET << 4), 0x00 }, + { 0xc001 | (RT1308_POWER << 4), 0x00 }, + { 0xc002 | (RT1308_POWER << 4), 0x00 }, +}; + +#define RT1308_SDW_OFFSET 0xc000 +#define RT1308_SDW_OFFSET_BYTE0 0xc000 +#define RT1308_SDW_OFFSET_BYTE1 0xc001 +#define RT1308_SDW_OFFSET_BYTE2 0xc002 +#define RT1308_SDW_OFFSET_BYTE3 0xc003 + +#define RT1308_SDW_RESET (RT1308_SDW_OFFSET | (RT1308_RESET << 4)) + +struct rt1308_sdw_priv { + struct snd_soc_component *component; + struct regmap *regmap; + struct sdw_slave *sdw_slave; + enum sdw_slave_status status; + struct sdw_bus_params params; + bool hw_init; + bool first_init; +}; + +struct sdw_stream_data { + struct sdw_stream_runtime *sdw_stream; +}; + +#endif /* __RT1308_SDW_H__ */ diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c index b75931a69a1cc0..462fd668753bd6 100644 --- a/sound/soc/codecs/rt1308.c +++ b/sound/soc/codecs/rt1308.c @@ -1,10 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 -// -// rt1308.c -- RT1308 ALSA SoC amplifier component driver -// -// Copyright 2019 Realtek Semiconductor Corp. -// Author: Derek Fang -// +/* + * rt1308.c -- RT1308 ALSA SoC amplifier component driver + * + * Copyright 2019 Realtek Semiconductor Corp. + * Author: Derek Fang + * + */ #include #include From 592284ee571b3b1e3ac3dd21d031da02319d07a3 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 15 Aug 2019 10:49:56 +0800 Subject: [PATCH 028/110] ASoC: codecs: rt711: add SoundWire support The driver for this codec was tested by Realtek and Intel using the Realtek 3-in-1 add-on board connected to CometLake and IceLake platforms. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Signed-off-by: Shuming Fan --- sound/soc/codecs/Kconfig | 9 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt711-sdw.c | 528 +++++++++++++++ sound/soc/codecs/rt711-sdw.h | 279 ++++++++ sound/soc/codecs/rt711.c | 1225 ++++++++++++++++++++++++++++++++++ sound/soc/codecs/rt711.h | 212 ++++++ 6 files changed, 2255 insertions(+) create mode 100644 sound/soc/codecs/rt711-sdw.c create mode 100644 sound/soc/codecs/rt711-sdw.h create mode 100644 sound/soc/codecs/rt711.c create mode 100644 sound/soc/codecs/rt711.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index be71a3b4617ef6..64eab6c1bcbecd 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1071,6 +1071,15 @@ config SND_SOC_RT1308_SDW select SND_SOC_RT1308 select REGMAP_SOUNDWIRE +config SND_SOC_RT711 + tristate + +config SND_SOC_RT711_SDW + tristate "Realtek RT711 Codec - SDW" + depends on SOUNDWIRE + select SND_SOC_RT711 + select REGMAP_SOUNDWIRE + #Freescale sgtl5000 codec config SND_SOC_SGTL5000 tristate "Freescale SGTL5000 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 3c4fb8b39bcdc8..ada517d0c81e53 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -280,6 +280,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o snd-soc-zx-aud96p22-objs := zx_aud96p22.o snd-soc-rt700-objs := rt700.o rt700-sdw.o snd-soc-rt1308-sdw-objs := rt1308-sdw.o +snd-soc-rt711-objs := rt711.o rt711-sdw.o # Amp snd-soc-max9877-objs := max9877.o @@ -572,6 +573,7 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o obj-$(CONFIG_SND_SOC_ZX_AUD96P22) += snd-soc-zx-aud96p22.o obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o obj-$(CONFIG_SND_SOC_RT1308_SDW) += snd-soc-rt1308-sdw.o +obj-$(CONFIG_SND_SOC_RT711) += snd-soc-rt711.o # Amp obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c new file mode 100644 index 00000000000000..b044d12a515dd2 --- /dev/null +++ b/sound/soc/codecs/rt711-sdw.c @@ -0,0 +1,528 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt711-sdw.c -- rt711 ALSA SoC audio driver +// +// Copyright(c) 2019 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rt711.h" +#include "rt711-sdw.h" + +static bool rt711_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00e0: + case 0x00f0: + case 0x2012 ... 0x2016: + case 0x201a ... 0x2027: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2201 ... 0x2204: + case 0x2206 ... 0x2212: + case 0x2220 ... 0x2223: + case 0x2230 ... 0x2239: + case 0x2f01 ... 0x2f0f: + case 0x3000 ... 0x3fff: + case 0x7000 ... 0x7fff: + case 0x8300 ... 0x83ff: + case 0x9c00 ... 0x9cff: + case 0xb900 ... 0xb9ff: + case 0x7520001a: + case 0x75200045: + case 0x75200046: + case 0x75200048: + case 0x7520004a: + case 0x7520006b: + case 0x7520006f: + case 0x75200080: + case 0x75200081: + case 0x75200091: + case 0x75580000: + return true; + default: + return false; + } +} + +static bool rt711_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2016: + case 0x201b: + case 0x201c: + case 0x201d: + case 0x201f: + case 0x2021: + case 0x2023: + case 0x2230: + case 0x2012 ... 0x2015: /* HD-A read */ + case 0x202d ... 0x202f: /* BRA */ + case 0x2201 ... 0x2212: /* i2c debug */ + case 0x2220 ... 0x2223: /* decoded HD-A */ + case 0x9c00 ... 0x9cff: + case 0xb900 ... 0xb9ff: + case 0xff01: + case 0x7520001a: + case 0x75200046: + case 0x75200080: + case 0x75200081: + case 0x75580000: + return true; + default: + return false; + } +} + +static int rt711_sdw_read(void *context, unsigned int reg, unsigned int *val) +{ + struct device *dev = context; + struct rt711_priv *rt711 = dev_get_drvdata(dev); + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2; + unsigned int is_hda_reg = 1, is_index_reg = 0; + int ret; + + if (reg > 0xffff) + is_index_reg = 1; + + mask = reg & 0xf000; + + if (is_index_reg) { /* index registers */ + val2 = reg & 0xffff; + reg = reg >> 16; + nid = reg & 0xff; + ret = regmap_write(rt711->sdw_regmap, reg, ((val2 >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg2, (val2 & 0xff)); + if (ret < 0) + return ret; + + reg3 = RT711_PRIV_DATA_R_H | nid; + ret = regmap_write(rt711->sdw_regmap, reg3, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg4 = reg3 + 0x1000; + reg4 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg4, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0x3000) { + reg += 0x8000; + ret = regmap_write(rt711->sdw_regmap, reg, *val); + if (ret < 0) + return ret; + } else if (mask == 0x7000) { + reg += 0x2000; + reg |= 0x800; + ret = regmap_write(rt711->sdw_regmap, reg, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg2, (*val & 0xff)); + if (ret < 0) + return ret; + } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ + reg2 = reg - 0x1000; + reg2 &= ~0x80; + ret = regmap_write(rt711->sdw_regmap, reg2, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + ret = regmap_write(rt711->sdw_regmap, reg, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0x9000) { + ret = regmap_write(rt711->sdw_regmap, reg, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg2, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0xb000) { + ret = regmap_write(rt711->sdw_regmap, reg, *val); + if (ret < 0) + return ret; + } else { + ret = regmap_read(rt711->sdw_regmap, reg, val); + if (ret < 0) + return ret; + is_hda_reg = 0; + } + + if (is_hda_reg || is_index_reg) { + sdw_data_3 = 0; + sdw_data_2 = 0; + sdw_data_1 = 0; + sdw_data_0 = 0; + ret = regmap_read(rt711->sdw_regmap, RT711_READ_HDA_3, &sdw_data_3); + if (ret < 0) + return ret; + ret = regmap_read(rt711->sdw_regmap, RT711_READ_HDA_2, &sdw_data_2); + if (ret < 0) + return ret; + ret = regmap_read(rt711->sdw_regmap, RT711_READ_HDA_1, &sdw_data_1); + if (ret < 0) + return ret; + ret = regmap_read(rt711->sdw_regmap, RT711_READ_HDA_0, &sdw_data_0); + if (ret < 0) + return ret; + *val = ((sdw_data_3 & 0xff) << 24) | ((sdw_data_2 & 0xff) << 16) | + ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); + } + + if (is_hda_reg == 0) + dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val); + else if (is_index_reg) + dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n", __func__, + reg, reg2, reg3, reg4, *val); + else + dev_dbg(dev, "[%s] %04x %04x => %08x\n", __func__, reg, reg2, *val); + + return 0; +} + +static int rt711_sdw_write(void *context, unsigned int reg, unsigned int val) +{ + struct device *dev = context; + struct rt711_priv *rt711 = dev_get_drvdata(dev); + unsigned int reg2 = 0, reg3, reg4, nid, mask, val2; + unsigned int is_index_reg = 0; + int ret; + + if (reg > 0xffff) + is_index_reg = 1; + + mask = reg & 0xf000; + + if (is_index_reg) { /* index registers */ + val2 = reg & 0xffff; + reg = reg >> 16; + nid = reg & 0xff; + ret = regmap_write(rt711->sdw_regmap, reg, ((val2 >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg2, (val2 & 0xff)); + if (ret < 0) + return ret; + + reg3 = RT711_PRIV_DATA_W_H | nid; + ret = regmap_write(rt711->sdw_regmap, reg3, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg4 = reg3 + 0x1000; + reg4 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg4, (val & 0xff)); + if (ret < 0) + return ret; + is_index_reg = 1; + } else if (reg < 0x4fff) { + ret = regmap_write(rt711->sdw_regmap, reg, val); + if (ret < 0) + return ret; + } else if (reg == RT711_FUNC_RESET) { + ret = regmap_write(rt711->sdw_regmap, reg, val); + if (ret < 0) + return ret; + } else if (mask == 0x7000) { + ret = regmap_write(rt711->sdw_regmap, reg, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg2, (val & 0xff)); + if (ret < 0) + return ret; + } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ + reg2 = reg - 0x1000; + reg2 &= ~0x80; + ret = regmap_write(rt711->sdw_regmap, reg2, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + ret = regmap_write(rt711->sdw_regmap, reg, (val & 0xff)); + if (ret < 0) + return ret; + } + + if (reg2 == 0) + dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val); + else if (is_index_reg) + dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n", __func__, + reg, reg2, reg3, reg4, val2, val); + else + dev_dbg(dev, "[%s] %04x %04x <= %04x\n", __func__, reg, reg2, val); + + return 0; +} + +static const struct regmap_config rt711_regmap = { + .reg_bits = 32, + .val_bits = 32, + .readable_reg = rt711_readable_register, /* Readable registers */ + .volatile_reg = rt711_volatile_register, /* volatile register */ + .max_register = 0x75580000, /* Maximum number of register */ + .reg_defaults = rt711_reg_defaults, /* Defaults */ + .num_reg_defaults = ARRAY_SIZE(rt711_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, + .reg_read = rt711_sdw_read, + .reg_write = rt711_sdw_write, +}; + +static const struct regmap_config rt711_sdw_regmap = { + .name = "sdw", + .reg_bits = 32, /* Total register space for SDW */ + .val_bits = 8, /* Total number of bits in register */ + .readable_reg = rt711_readable_register, /* Readable registers */ + .max_register = 0xff01, /* Maximum number of register */ + .cache_type = REGCACHE_NONE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt711_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); + + /* Update the status */ + rt711->status = status; + + if (status == SDW_SLAVE_UNATTACHED) + rt711->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt711->hw_init || rt711->status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt711_io_init(&slave->dev, slave); +} + +static int rt711_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval, i, num_of_ports = 1; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->paging_support = false; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = 0x14; /* BITMAP: 00010100 */ + prop->sink_ports = 0x8; /* BITMAP: 00001000 */ + + nval = hweight32(prop->source_ports); + num_of_ports += nval; + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + num_of_ports += nval; + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* Allocate port_ready based on num_of_ports */ + slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports, + sizeof(*slave->port_ready), + GFP_KERNEL); + if (!slave->port_ready) + return -ENOMEM; + + /* Initialize completion */ + for (i = 0; i < num_of_ports; i++) + init_completion(&slave->port_ready[i]); + + /* set the timeout values */ + prop->clk_stop_timeout = 20; + + return 0; +} + +static int rt711_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); + int ret; + + memcpy(&rt711->params, params, sizeof(*params)); + + ret = rt711_clock_config(&slave->dev); + if (ret < 0) + dev_err(&slave->dev, "Invalid clk config"); + + return ret; +} + +static int rt711_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); + + dev_dbg(&slave->dev, + "%s control_port_stat=%x", __func__, status->control_port); + + if (status->control_port & 0x4) { + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_detect_work, msecs_to_jiffies(250)); + } + + return 0; +} + +static struct sdw_slave_ops rt711_slave_ops = { + .read_prop = rt711_read_prop, + .interrupt_callback = rt711_interrupt_callback, + .update_status = rt711_update_status, + .bus_config = rt711_bus_config, +}; + +static int rt711_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *sdw_regmap, *regmap; + + /* Assign ops */ + slave->ops = &rt711_slave_ops; + + /* Regmap Initialization */ + sdw_regmap = devm_regmap_init_sdw(slave, &rt711_sdw_regmap); + if (!sdw_regmap) + return -EINVAL; + + regmap = devm_regmap_init(&slave->dev, NULL, &slave->dev, &rt711_regmap); + if (!regmap) + return -EINVAL; + + rt711_init(&slave->dev, sdw_regmap, regmap, slave); + + return 0; +} + +static int rt711_sdw_remove(struct sdw_slave *slave) +{ + struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); + + if (rt711 && rt711->hw_init) { + cancel_delayed_work(&rt711->jack_detect_work); + cancel_delayed_work(&rt711->jack_btn_check_work); + cancel_work_sync(&rt711->calibration_work); + } + + return 0; +} + +static const struct sdw_device_id rt711_id[] = { + SDW_SLAVE_ENTRY(0x025d, 0x711, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt711_id); + +static int rt711_dev_suspend(struct device *dev) +{ + struct rt711_priv *rt711 = dev_get_drvdata(dev); + + if (!rt711->hw_init) + return 0; + + regcache_cache_only(rt711->regmap, true); + + return 0; +} + +#define RT711_PROBE_TIMEOUT 2000 + +static int rt711_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = to_sdw_slave_device(dev); + struct rt711_priv *rt711 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt711->hw_init) + return 0; + + time = wait_for_completion_timeout(&slave->enumeration_complete, + msecs_to_jiffies(RT711_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Enumeration not complete, timed out\n"); + return -ETIMEDOUT; + } + + regcache_cache_only(rt711->regmap, false); + regcache_sync_region(rt711->regmap, 0x3000, 0x8fff); + regcache_sync_region(rt711->regmap, 0x75200010, 0x75200091); + + return 0; +} + +static const struct dev_pm_ops rt711_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt711_dev_suspend, rt711_dev_resume) + SET_RUNTIME_PM_OPS(rt711_dev_suspend, rt711_dev_resume, NULL) +}; + +static struct sdw_driver rt711_sdw_driver = { + .driver = { + .name = "rt711", + .owner = THIS_MODULE, + .pm = &rt711_pm, + }, + .probe = rt711_sdw_probe, + .remove = rt711_sdw_remove, + .ops = &rt711_slave_ops, + .id_table = rt711_id, +}; +module_sdw_driver(rt711_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT711 SDW driver"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt711-sdw.h b/sound/soc/codecs/rt711-sdw.h new file mode 100644 index 00000000000000..7d0a2a3c699470 --- /dev/null +++ b/sound/soc/codecs/rt711-sdw.h @@ -0,0 +1,279 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt711-sdw.h -- RT711 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT711_SDW_H__ +#define __RT711_SDW_H__ + +static const struct reg_default rt711_reg_defaults[] = { + { 0x0000, 0x00 }, + { 0x0001, 0x00 }, + { 0x0002, 0x00 }, + { 0x0003, 0x00 }, + { 0x0004, 0x00 }, + { 0x0005, 0x01 }, + { 0x0020, 0x00 }, + { 0x0022, 0x00 }, + { 0x0023, 0x00 }, + { 0x0024, 0x00 }, + { 0x0025, 0x00 }, + { 0x0026, 0x00 }, + { 0x0030, 0x00 }, + { 0x0032, 0x00 }, + { 0x0033, 0x00 }, + { 0x0034, 0x00 }, + { 0x0035, 0x00 }, + { 0x0036, 0x00 }, + { 0x0040, 0x00 }, + { 0x0041, 0x00 }, + { 0x0042, 0x00 }, + { 0x0043, 0x00 }, + { 0x0044, 0x20 }, + { 0x0045, 0x01 }, + { 0x0046, 0x01 }, + { 0x0050, 0x20 }, + { 0x0051, 0x02 }, + { 0x0052, 0x5d }, + { 0x0053, 0x07 }, + { 0x0054, 0x11 }, + { 0x0055, 0x00 }, + { 0x0060, 0x00 }, + { 0x0070, 0x00 }, + { 0x0080, 0xc0 }, + { 0x0088, 0x00 }, + { 0x00e0, 0x00 }, + { 0x00e1, 0x00 }, + { 0x00e2, 0x00 }, + { 0x00e3, 0x00 }, + { 0x00e5, 0x00 }, + { 0x00ee, 0x00 }, + { 0x00ef, 0x00 }, + { 0x00f0, 0x00 }, + { 0x00f1, 0x00 }, + { 0x00f2, 0x00 }, + { 0x00f3, 0x00 }, + { 0x00f4, 0x00 }, + { 0x00f5, 0x00 }, + { 0x00fe, 0x00 }, + { 0x00ff, 0x00 }, + { 0x0100, 0x00 }, + { 0x0101, 0x00 }, + { 0x0102, 0x00 }, + { 0x0103, 0x00 }, + { 0x0104, 0x00 }, + { 0x0105, 0x00 }, + { 0x0120, 0x00 }, + { 0x0122, 0x00 }, + { 0x0123, 0x00 }, + { 0x0124, 0x00 }, + { 0x0125, 0x00 }, + { 0x0126, 0x00 }, + { 0x0127, 0x00 }, + { 0x0130, 0x00 }, + { 0x0132, 0x00 }, + { 0x0133, 0x00 }, + { 0x0134, 0x00 }, + { 0x0135, 0x00 }, + { 0x0136, 0x00 }, + { 0x0137, 0x00 }, + { 0x0200, 0x00 }, + { 0x0201, 0x00 }, + { 0x0202, 0x00 }, + { 0x0203, 0x00 }, + { 0x0204, 0x00 }, + { 0x0205, 0x03 }, + { 0x0220, 0x00 }, + { 0x0222, 0x00 }, + { 0x0223, 0x00 }, + { 0x0224, 0x00 }, + { 0x0225, 0x00 }, + { 0x0226, 0x00 }, + { 0x0227, 0x00 }, + { 0x0230, 0x00 }, + { 0x0232, 0x00 }, + { 0x0233, 0x00 }, + { 0x0234, 0x00 }, + { 0x0235, 0x00 }, + { 0x0236, 0x00 }, + { 0x0237, 0x00 }, + { 0x0300, 0x00 }, + { 0x0301, 0x00 }, + { 0x0302, 0x20 }, + { 0x0303, 0x00 }, + { 0x0304, 0x00 }, + { 0x0305, 0x03 }, + { 0x0320, 0x00 }, + { 0x0322, 0x00 }, + { 0x0323, 0x00 }, + { 0x0324, 0x00 }, + { 0x0325, 0x00 }, + { 0x0326, 0x00 }, + { 0x0327, 0x00 }, + { 0x0330, 0x00 }, + { 0x0332, 0x00 }, + { 0x0333, 0x00 }, + { 0x0334, 0x00 }, + { 0x0335, 0x00 }, + { 0x0336, 0x00 }, + { 0x0337, 0x00 }, + { 0x0400, 0x00 }, + { 0x0401, 0x00 }, + { 0x0402, 0x00 }, + { 0x0403, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x03 }, + { 0x0420, 0x00 }, + { 0x0422, 0x00 }, + { 0x0423, 0x00 }, + { 0x0424, 0x00 }, + { 0x0425, 0x00 }, + { 0x0426, 0x00 }, + { 0x0427, 0x00 }, + { 0x0430, 0x00 }, + { 0x0432, 0x00 }, + { 0x0433, 0x00 }, + { 0x0434, 0x00 }, + { 0x0435, 0x00 }, + { 0x0436, 0x00 }, + { 0x0437, 0x00 }, + { 0x0f00, 0x00 }, + { 0x0f01, 0x00 }, + { 0x0f02, 0x20 }, + { 0x0f03, 0x00 }, + { 0x0f04, 0x00 }, + { 0x0f05, 0x03 }, + { 0x0f06, 0x00 }, + { 0x0f07, 0x00 }, + { 0x0f08, 0x00 }, + { 0x0f09, 0x00 }, + { 0x0f10, 0x00 }, + { 0x0f11, 0x00 }, + { 0x0f12, 0x00 }, + { 0x0f13, 0x00 }, + { 0x0f14, 0x00 }, + { 0x0f15, 0x00 }, + { 0x0f16, 0x00 }, + { 0x0f17, 0x00 }, + { 0x0f18, 0x00 }, + { 0x0f19, 0x00 }, + { 0x0f1a, 0x00 }, + { 0x0f1b, 0x00 }, + { 0x0f1c, 0x00 }, + { 0x0f1d, 0x00 }, + { 0x0f1e, 0x00 }, + { 0x0f1f, 0x00 }, + { 0x0f20, 0x00 }, + { 0x0f22, 0x00 }, + { 0x0f23, 0x00 }, + { 0x0f24, 0x00 }, + { 0x0f25, 0x00 }, + { 0x0f26, 0x00 }, + { 0x0f27, 0x00 }, + { 0x0f30, 0x00 }, + { 0x0f32, 0x00 }, + { 0x0f33, 0x00 }, + { 0x0f34, 0x00 }, + { 0x0f35, 0x00 }, + { 0x0f36, 0x00 }, + { 0x0f37, 0x00 }, + { 0x2012, 0x00 }, + { 0x2013, 0x00 }, + { 0x2014, 0x00 }, + { 0x2015, 0x00 }, + { 0x2016, 0x00 }, + { 0x201a, 0x00 }, + { 0x201b, 0x00 }, + { 0x201c, 0x0c }, + { 0x201d, 0x00 }, + { 0x201e, 0x00 }, + { 0x201f, 0x00 }, + { 0x2020, 0x00 }, + { 0x2021, 0x00 }, + { 0x2022, 0x00 }, + { 0x2023, 0x00 }, + { 0x2024, 0x00 }, + { 0x2025, 0x01 }, + { 0x2026, 0x00 }, + { 0x2027, 0x00 }, + { 0x2029, 0x00 }, + { 0x202a, 0x00 }, + { 0x202d, 0x00 }, + { 0x202e, 0x00 }, + { 0x202f, 0x00 }, + { 0x2030, 0x00 }, + { 0x2031, 0x00 }, + { 0x2032, 0x00 }, + { 0x2033, 0x00 }, + { 0x2034, 0x00 }, + { 0x2201, 0xc7 }, + { 0x2202, 0x0c }, + { 0x2203, 0x22 }, + { 0x2204, 0x04 }, + { 0x2206, 0x00 }, + { 0x2207, 0x00 }, + { 0x2208, 0x00 }, + { 0x2209, 0x00 }, + { 0x220a, 0x00 }, + { 0x220b, 0x00 }, + { 0x220c, 0x00 }, + { 0x220d, 0x04 }, + { 0x220e, 0x00 }, + { 0x220f, 0x00 }, + { 0x2211, 0x01 }, + { 0x2212, 0x00 }, + { 0x2220, 0x00 }, + { 0x2221, 0x00 }, + { 0x2222, 0x00 }, + { 0x2223, 0x00 }, + { 0x2230, 0x00 }, + { 0x2231, 0x2f }, + { 0x2232, 0x80 }, + { 0x2233, 0x00 }, + { 0x2234, 0x00 }, + { 0x2235, 0x00 }, + { 0x2236, 0x00 }, + { 0x2237, 0x00 }, + { 0x2238, 0x00 }, + { 0x2239, 0x00 }, + { 0x2f01, 0x00 }, + { 0x2f02, 0x09 }, + { 0x2f03, 0x00 }, + { 0x2f04, 0x00 }, + { 0x2f05, 0x0b }, + { 0x2f06, 0x01 }, + { 0x2f07, 0xcf }, + { 0x2f08, 0x00 }, + { 0x2f09, 0x00 }, + { 0x2f0a, 0x00 }, + { 0x2f0b, 0x00 }, + { 0x2f0c, 0x00 }, + { 0x2f0d, 0x00 }, + { 0x2f0e, 0x00 }, + { 0x2f0f, 0x00 }, + { 0x3122, 0x00 }, + { 0x3123, 0x00 }, + { 0x7303, 0x57 }, + { 0x8383, 0x57 }, + { 0x7308, 0x97 }, + { 0x8388, 0x97 }, + { 0x7309, 0x97 }, + { 0x8389, 0x97 }, + { 0x7312, 0x00 }, + { 0x8392, 0x00 }, + { 0x7313, 0x00 }, + { 0x8393, 0x00 }, + { 0x7319, 0x00 }, + { 0x8399, 0x00 }, + { 0x7520001a, 0x8003 }, + { 0x75200045, 0x5289 }, + { 0x75200048, 0xd049 }, + { 0x7520004a, 0xa83b }, + { 0x7520006b, 0x5064 }, + { 0x7520006f, 0x058b }, + { 0x75200091, 0x0000 }, +}; + +#endif /* __RT711_SDW_H__ */ diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c new file mode 100644 index 00000000000000..183201f7212f52 --- /dev/null +++ b/sound/soc/codecs/rt711.c @@ -0,0 +1,1225 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt711.c -- rt711 ALSA SoC audio driver +// +// Copyright(c) 2019 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt711.h" + +static int rt711_index_write(struct regmap *regmap, + unsigned int nid, unsigned int reg, unsigned int value) +{ + int ret; + unsigned int addr = ((RT711_PRIV_INDEX_W_H | nid) << 16) | reg; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + pr_err("Failed to set private value: %08x <= %04x %d\n", ret, addr, value); + + return ret; +} + +static unsigned int rt711_index_read(struct regmap *regmap, + unsigned int nid, unsigned int reg, unsigned int *value) +{ + int ret; + unsigned int addr = ((RT711_PRIV_INDEX_W_H | nid) << 16) | reg; + + *value = 0; + ret = regmap_read(regmap, addr, value); + if (ret < 0) + pr_err("Failed to get private value: %08x => %04x %d\n", ret, addr, *value); + + return ret; +} + +static void rt711_reset(struct regmap *regmap) +{ + unsigned int val; + + regmap_write(regmap, RT711_FUNC_RESET, 0); + rt711_index_read(regmap, RT711_VENDOR_REG, RT711_PARA_VERB_CTL, &val); + rt711_index_write(regmap, RT711_VENDOR_REG, + RT711_PARA_VERB_CTL, (val | RT711_HIDDEN_REG_SW_RESET)); +} + +static int rt711_calibration(struct rt711_priv *rt711) +{ + unsigned int val, loop = 0; + struct device *dev; + struct regmap *regmap = rt711->regmap; + int ret = 0; + + mutex_lock(&rt711->calibrate_mutex); + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + dev = regmap_get_device(regmap); + + /* Calibration manual mode */ + rt711_index_read(regmap, RT711_VENDOR_REG, RT711_FSM_CTL, &val); + val &= 0xfffffff0; + rt711_index_write(regmap, RT711_VENDOR_REG, RT711_FSM_CTL, val); + + /* trigger */ + rt711_index_read(regmap, RT711_VENDOR_CALI, + RT711_DAC_DC_CALI_CTL1, &val); + val |= RT711_DAC_DC_CALI_TRIGGER; + rt711_index_write(regmap, RT711_VENDOR_CALI, + RT711_DAC_DC_CALI_CTL1, val); + + /* wait for calibration process */ + rt711_index_read(regmap, RT711_VENDOR_CALI, + RT711_DAC_DC_CALI_CTL1, &val); + + while (val & RT711_DAC_DC_CALI_TRIGGER) { + if (loop >= 500) { + pr_err("%s, calibration time-out!\n", + __func__); + ret = -ETIMEDOUT; + break; + } + loop++; + + usleep_range(10000, 11000); + rt711_index_read(regmap, RT711_VENDOR_CALI, + RT711_DAC_DC_CALI_CTL1, &val); + } + + /* depop mode */ + rt711_index_read(regmap, RT711_VENDOR_REG, RT711_FSM_CTL, &val); + val &= 0xfffffff0; + val |= RT711_DEPOP_CTL; + rt711_index_write(regmap, RT711_VENDOR_REG, RT711_FSM_CTL, val); + + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + mutex_unlock(&rt711->calibrate_mutex); + + dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret); + return ret; +} + +static unsigned int rt711_button_detect(struct rt711_priv *rt711) +{ + unsigned int btn_type = 0, val80, val81; + int ret; + + ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG, + RT711_IRQ_FLAG_TABLE1, &val80); + if (ret < 0) + goto read_error; + ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG, + RT711_IRQ_FLAG_TABLE2, &val81); + if (ret < 0) + goto read_error; + + val80 &= 0x0381; + val81 &= 0xff00; + + switch (val80) { + case 0x0200: + case 0x0100: + case 0x0080: + btn_type |= SND_JACK_BTN_0; + break; + case 0x0001: + btn_type |= SND_JACK_BTN_3; + break; + } + switch (val81) { + case 0x8000: + case 0x4000: + case 0x2000: + btn_type |= SND_JACK_BTN_1; + break; + case 0x1000: + case 0x0800: + case 0x0400: + btn_type |= SND_JACK_BTN_2; + break; + case 0x0200: + case 0x0100: + btn_type |= SND_JACK_BTN_3; + break; + } +read_error: + return btn_type; +} + +static int rt711_headset_detect(struct rt711_priv *rt711) +{ + unsigned int buf, loop = 0; + int ret; + + ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG, + RT711_COMBO_JACK_AUTO_CTL2, &buf); + if (ret < 0) + goto io_error; + + while (loop < 500 && + (buf & RT711_COMBOJACK_AUTO_DET_STATUS) == 0) { + loop++; + + usleep_range(9000, 10000); + ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG, + RT711_COMBO_JACK_AUTO_CTL2, &buf); + if (ret < 0) + goto io_error; + } + + if (loop >= 500) + goto to_error; + + if (buf & RT711_COMBOJACK_AUTO_DET_TRS) + rt711->jack_type = SND_JACK_HEADPHONE; + else if ((buf & RT711_COMBOJACK_AUTO_DET_CTIA) || + (buf & RT711_COMBOJACK_AUTO_DET_OMTP)) + rt711->jack_type = SND_JACK_HEADSET; + + return 0; + +to_error: + ret = -ETIMEDOUT; + pr_err_ratelimited("Time-out error in %s\n", __func__); + return ret; +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static void rt711_jack_detect_handler(struct work_struct *work) +{ + struct rt711_priv *rt711 = + container_of(work, struct rt711_priv, jack_detect_work.work); + int btn_type = 0, ret; + unsigned int jack_status = 0, reg; + + if (!rt711->hs_jack) + return; + + if (!rt711->component->card->instantiated) + return; + + reg = RT711_VERB_GET_PIN_SENSE | RT711_HP_OUT; + ret = regmap_read(rt711->regmap, reg, &jack_status); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (jack_status & (1 << 31)) { + /* jack in */ + if (rt711->jack_type == 0) { + ret = rt711_headset_detect(rt711); + if (ret < 0) + return; + if (rt711->jack_type == SND_JACK_HEADSET) + btn_type = rt711_button_detect(rt711); + } else if (rt711->jack_type == SND_JACK_HEADSET) { + /* jack is already in, report button event */ + btn_type = rt711_button_detect(rt711); + } + } else { + /* jack out */ + rt711->jack_type = 0; + } + + dev_dbg(&rt711->slave->dev, + "in %s, jack_type=0x%x\n", __func__, rt711->jack_type); + dev_dbg(&rt711->slave->dev, + "in %s, btn_type=0x%x\n", __func__, btn_type); + + snd_soc_jack_report(rt711->hs_jack, rt711->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt711->hs_jack, rt711->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt711_btn_check_handler(struct work_struct *work) +{ + struct rt711_priv *rt711 = container_of(work, struct rt711_priv, + jack_btn_check_work.work); + int btn_type = 0, ret; + unsigned int jack_status = 0, reg; + + reg = RT711_VERB_GET_PIN_SENSE | RT711_HP_OUT; + ret = regmap_read(rt711->regmap, reg, &jack_status); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (jack_status & (1 << 31)) { + if (rt711->jack_type == SND_JACK_HEADSET) { + /* jack is already in, report button event */ + btn_type = rt711_button_detect(rt711); + } + } else { + rt711->jack_type = 0; + } + + /* cbj comparator */ + ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG, + RT711_COMBO_JACK_AUTO_CTL2, ®); + if (ret < 0) + goto io_error; + + if ((reg & 0xf0) == 0xf0) + btn_type = 0; + + dev_dbg(&rt711->slave->dev, + "%s, btn_type=0x%x\n", __func__, btn_type); + snd_soc_jack_report(rt711->hs_jack, rt711->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt711->hs_jack, rt711->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt711_jack_init(struct rt711_priv *rt711) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(rt711->component); + + mutex_lock(&rt711->calibrate_mutex); + /* power on */ + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + if (rt711->hs_jack) { + /* unsolicited response & IRQ control */ + regmap_write(rt711->regmap, + RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x82); + regmap_write(rt711->regmap, + RT711_SET_HP_UNSOLICITED_ENABLE, 0x81); + regmap_write(rt711->regmap, + RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x83); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + 0x10, 0x2420); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + 0x19, 0x2e11); + + dev_dbg(&rt711->slave->dev, "in %s enable\n", __func__); + + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_detect_work, msecs_to_jiffies(250)); + } else { + regmap_write(rt711->regmap, + RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x00); + regmap_write(rt711->regmap, + RT711_SET_HP_UNSOLICITED_ENABLE, 0x00); + regmap_write(rt711->regmap, + RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x00); + + dev_dbg(&rt711->slave->dev, "in %s disable\n", __func__); + } + + /* power off */ + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + mutex_unlock(&rt711->calibrate_mutex); +} + +static int rt711_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + + rt711->hs_jack = hs_jack; + + if (!rt711->hw_init) { + dev_dbg(&rt711->slave->dev, + "%s hw_init not ready yet\n", __func__); + return 0; + } + + rt711_jack_init(rt711); + + return 0; +} + +static void rt711_get_gain(struct rt711_priv *rt711, unsigned int addr_h, + unsigned int addr_l, unsigned int val_h, + unsigned int *r_val, unsigned int *l_val) +{ + /* R Channel */ + *r_val = (val_h << 8); + regmap_read(rt711->regmap, addr_l, r_val); + + /* L Channel */ + val_h |= 0x20; + *l_val = (val_h << 8); + regmap_read(rt711->regmap, addr_h, l_val); +} + +/* For Verb-Set Amplifier Gain (Verb ID = 3h) */ +static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned int addr_h, addr_l, val_h, val_ll, val_lr; + unsigned int read_ll, read_rl; + int i; + + /* Can't use update bit function, so read the original value first */ + addr_h = mc->reg; + addr_l = mc->rreg; + if (mc->shift == RT711_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt711_get_gain(rt711, addr_h, addr_l, val_h, &read_rl, &read_ll); + + /* L Channel */ + if (mc->invert) { + /* for mute/unmute */ + val_ll = (mc->max - ucontrol->value.integer.value[0]) + << RT711_MUTE_SFT; + /* keep gain */ + read_ll = read_ll & 0x7f; + val_ll |= read_ll; + } else { + /* for gain */ + val_ll = ((ucontrol->value.integer.value[0]) & 0x7f); + if (val_ll > mc->max) + val_ll = mc->max; + /* keep mute status */ + read_ll = read_ll & (1 << RT711_MUTE_SFT); + val_ll |= read_ll; + } + + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + /* R Channel */ + if (mc->invert) { + /* for mute/unmute */ + val_lr = (mc->max - ucontrol->value.integer.value[1]) + << RT711_MUTE_SFT; + /* keep gain */ + read_rl = read_rl & 0x7f; + val_lr |= read_rl; + } else { + /* for gain */ + val_lr = ((ucontrol->value.integer.value[1]) & 0x7f); + if (val_lr > mc->max) + val_lr = mc->max; + /* keep mute status */ + read_rl = read_rl & (1 << RT711_MUTE_SFT); + val_lr |= read_rl; + } + + for (i = 0; i < 3; i++) { /* retry 3 times at most */ + + if (val_ll == val_lr) { + /* Set both L/R channels at the same time */ + val_h = (1 << mc->shift) | (3 << 4); + regmap_write(rt711->regmap, addr_h, (val_h << 8 | val_ll)); + regmap_write(rt711->regmap, addr_l, (val_h << 8 | val_ll)); + } else { + /* Lch*/ + val_h = (1 << mc->shift) | (1 << 5); + regmap_write(rt711->regmap, addr_h, (val_h << 8 | val_ll)); + + /* Rch */ + val_h = (1 << mc->shift) | (1 << 4); + regmap_write(rt711->regmap, addr_l, (val_h << 8 | val_lr)); + } + /* check result */ + if (mc->shift == RT711_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt711_get_gain(rt711, addr_h, addr_l, val_h, + &read_rl, &read_ll); + if (read_rl == val_lr && read_ll == val_ll) + break; + } + + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + return 0; +} + +static int rt711_set_amp_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int addr_h, addr_l, val_h; + unsigned int read_ll, read_rl; + + /* switch to get command */ + addr_h = mc->reg; + addr_l = mc->rreg; + if (mc->shift == RT711_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt711_get_gain(rt711, addr_h, addr_l, val_h, &read_rl, &read_ll); + + if (mc->invert) { + /* mute/unmute for switch controls */ + read_ll = !((read_ll & 0x80) >> RT711_MUTE_SFT); + read_rl = !((read_rl & 0x80) >> RT711_MUTE_SFT); + } else { + /* for gain volume controls */ + read_ll = read_ll & 0x7f; + read_rl = read_rl & 0x7f; + } + ucontrol->value.integer.value[0] = read_ll; + ucontrol->value.integer.value[1] = read_rl; + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt711_snd_controls[] = { + SOC_DOUBLE_R_EXT_TLV("DAC Surr Playback Volume", RT711_SET_GAIN_DAC2_H, + RT711_SET_GAIN_DAC2_L, RT711_DIR_OUT_SFT, 0x57, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, + out_vol_tlv), + SOC_DOUBLE_R_EXT("ADC 08 Capture Switch", RT711_SET_GAIN_ADC2_H, + RT711_SET_GAIN_ADC2_L, RT711_DIR_IN_SFT, 1, 1, + rt711_set_amp_gain_get, rt711_set_amp_gain_put), + SOC_DOUBLE_R_EXT("ADC 09 Capture Switch", RT711_SET_GAIN_ADC1_H, + RT711_SET_GAIN_ADC1_L, RT711_DIR_IN_SFT, 1, 1, + rt711_set_amp_gain_get, rt711_set_amp_gain_put), + SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume", RT711_SET_GAIN_ADC2_H, + RT711_SET_GAIN_ADC2_L, RT711_DIR_IN_SFT, 0x3f, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, + in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume", RT711_SET_GAIN_ADC1_H, + RT711_SET_GAIN_ADC1_L, RT711_DIR_IN_SFT, 0x3f, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, + in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("AMIC Volume", RT711_SET_GAIN_AMIC_H, + RT711_SET_GAIN_AMIC_L, RT711_DIR_IN_SFT, 3, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DMIC1 Volume", RT711_SET_GAIN_DMIC1_H, + RT711_SET_GAIN_DMIC1_L, RT711_DIR_IN_SFT, 3, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DMIC2 Volume", RT711_SET_GAIN_DMIC2_H, + RT711_SET_GAIN_DMIC2_L, RT711_DIR_IN_SFT, 3, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, + mic_vol_tlv), +}; + +static int rt711_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned int reg, val = 0, nid; + int ret; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + nid = RT711_MIXER_IN1; + else if (strstr(ucontrol->id.name, "ADC 23 Mux")) + nid = RT711_MIXER_IN2; + else + return -EINVAL; + + /* vid = 0xf01 */ + reg = RT711_VERB_SET_CONNECT_SEL | nid; + ret = regmap_read(rt711->regmap, reg, &val); + if (ret < 0) { + dev_err(component->dev, "%s: sdw read failed: %d\n", __func__, ret); + return ret; + } + + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int rt711_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, reg, nid; + int ret; + + if (item[0] >= e->items) + return -EINVAL; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + nid = RT711_MIXER_IN1; + else if (strstr(ucontrol->id.name, "ADC 23 Mux")) + nid = RT711_MIXER_IN2; + else + return -EINVAL; + + /* Verb ID = 0x701h */ + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + reg = RT711_VERB_SET_CONNECT_SEL | nid; + ret = regmap_read(rt711->regmap, reg, &val2); + if (ret < 0) { + dev_err(component->dev, "%s: sdw read failed: %d\n", __func__, ret); + return ret; + } + + if (val == val2) + change = 0; + else + change = 1; + + if (change) { + reg = RT711_VERB_SET_CONNECT_SEL | nid; + regmap_write(rt711->regmap, reg, val); + } + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, NULL); + + return change; +} + +static const char * const adc_mux_text[] = { + "MIC2", + "LINE1", + "LINE2", + "DMIC", +}; + +static SOC_ENUM_SINGLE_DECL( + rt711_adc22_enum, SND_SOC_NOPM, 0, adc_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt711_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text); + +static const struct snd_kcontrol_new rt711_adc22_mux = + SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt711_adc22_enum, + rt711_mux_get, rt711_mux_put); + +static const struct snd_kcontrol_new rt711_adc23_mux = + SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt711_adc23_enum, + rt711_mux_get, rt711_mux_put); + +static int rt711_dac_surround_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned int val_h = (1 << RT711_DIR_OUT_SFT) | (0x3 << 4); + unsigned int val_l; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt711->regmap, + RT711_SET_STREAMID_DAC2, 0x10); + + val_l = 0x00; + regmap_write(rt711->regmap, + RT711_SET_GAIN_HP_H, (val_h << 8 | val_l)); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt711->regmap, + RT711_SET_STREAMID_DAC2, 0x00); + + val_l = (1 << RT711_MUTE_SFT); + regmap_write(rt711->regmap, + RT711_SET_GAIN_HP_H, (val_h << 8 | val_l)); + break; + } + return 0; +} + +static int rt711_adc_09_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt711->regmap, + RT711_SET_STREAMID_ADC1, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt711->regmap, + RT711_SET_STREAMID_ADC1, 0x00); + break; + } + return 0; +} + +static int rt711_adc_08_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt711->regmap, + RT711_SET_STREAMID_ADC2, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt711->regmap, + RT711_SET_STREAMID_ADC2, 0x00); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget rt711_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + + SND_SOC_DAPM_DAC_E("DAC Surround", NULL, SND_SOC_NOPM, 0, 0, + rt711_dac_surround_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC 09", NULL, SND_SOC_NOPM, 0, 0, + rt711_adc_09_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC 08", NULL, SND_SOC_NOPM, 0, 0, + rt711_adc_08_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, + &rt711_adc22_mux), + SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0, + &rt711_adc23_mux), + + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt711_audio_map[] = { + {"DAC Surround", NULL, "DP3RX"}, + {"DP2TX", NULL, "ADC 09"}, + {"DP4TX", NULL, "ADC 08"}, + + {"ADC 09", NULL, "ADC 22 Mux"}, + {"ADC 08", NULL, "ADC 23 Mux"}, + {"ADC 22 Mux", "DMIC", "DMIC1"}, + {"ADC 22 Mux", "LINE1", "LINE1"}, + {"ADC 22 Mux", "LINE2", "LINE2"}, + {"ADC 22 Mux", "MIC2", "MIC2"}, + {"ADC 23 Mux", "DMIC", "DMIC2"}, + {"ADC 23 Mux", "LINE1", "LINE1"}, + {"ADC 23 Mux", "LINE2", "LINE2"}, + {"ADC 23 Mux", "MIC2", "MIC2"}, + + {"HP", NULL, "DAC Surround"}, +}; + +static int rt711_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, + AC_PWRST_D0); + } + break; + + case SND_SOC_BIAS_STANDBY: + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, + AC_PWRST_D3); + break; + + default: + break; + } + + return 0; +} + +static int rt711_probe(struct snd_soc_component *component) +{ + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + + rt711->component = component; + + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_rt711 = { + .probe = rt711_probe, + .set_bias_level = rt711_set_bias_level, + .controls = rt711_snd_controls, + .num_controls = ARRAY_SIZE(rt711_snd_controls), + .dapm_widgets = rt711_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt711_dapm_widgets), + .dapm_routes = rt711_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt711_audio_map), + .set_jack = rt711_set_jack_detect, +}; + +static int rt711_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + struct sdw_stream_data *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + + stream->sdw_stream = (struct sdw_stream_runtime *)sdw_stream; + + /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dai->playback_dma_data = stream; + else + dai->capture_dma_data = stream; + + return 0; +} + +static void rt711_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_stream_data *stream; + + stream = snd_soc_dai_get_dma_data(dai, substream); + snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(stream); +} + +static int rt711_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_data *stream; + int retval, port, num_channels; + unsigned int val = 0; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!stream) + return -EINVAL; + + if (!rt711->slave) + return -EINVAL; + + /* SoundWire specific configuration */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + port = 3; + } else { + direction = SDW_DATA_DIR_TX; + if (dai->id == RT711_AIF1) + port = 4; + else if (dai->id == RT711_AIF2) + port = 2; + else + return -EINVAL; + } + + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + num_channels = params_channels(params); + port_config.ch_mask = (1 << (num_channels)) - 1; + port_config.num = port; + + retval = sdw_stream_add_slave(rt711->slave, &stream_config, + &port_config, 1, stream->sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + if (params_channels(params) <= 16) { + /* bit 3:0 Number of Channel */ + val |= (params_channels(params) - 1); + } else { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + switch (params_width(params)) { + /* bit 6:4 Bits per Sample */ + case 8: + break; + case 16: + val |= (0x1 << 4); + break; + case 20: + val |= (0x2 << 4); + break; + case 24: + val |= (0x3 << 4); + break; + case 32: + val |= (0x4 << 4); + break; + default: + return -EINVAL; + } + + /* 48Khz */ + regmap_write(rt711->regmap, RT711_DAC_FORMAT_H, val); + regmap_write(rt711->regmap, RT711_ADC1_FORMAT_H, val); + regmap_write(rt711->regmap, RT711_ADC2_FORMAT_H, val); + + return retval; +} + +static int rt711_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + struct sdw_stream_data *stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt711->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt711->slave, stream->sdw_stream); + return 0; +} + +#define RT711_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define RT711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static struct snd_soc_dai_ops rt711_ops = { + .hw_params = rt711_pcm_hw_params, + .hw_free = rt711_pcm_hw_free, + .set_sdw_stream = rt711_set_sdw_stream, + .shutdown = rt711_shutdown, +}; + +static struct snd_soc_dai_driver rt711_dai[] = { + { + .name = "rt711-aif1", + .id = RT711_AIF1, + .playback = { + .stream_name = "DP3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT711_STEREO_RATES, + .formats = RT711_FORMATS, + }, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT711_STEREO_RATES, + .formats = RT711_FORMATS, + }, + .ops = &rt711_ops, + }, + { + .name = "rt711-aif2", + .id = RT711_AIF2, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT711_STEREO_RATES, + .formats = RT711_FORMATS, + }, + .ops = &rt711_ops, + } +}; + +/* Bus clock frequency */ +#define RT711_CLK_FREQ_9600000HZ 9600000 +#define RT711_CLK_FREQ_12000000HZ 12000000 +#define RT711_CLK_FREQ_6000000HZ 6000000 +#define RT711_CLK_FREQ_4800000HZ 4800000 +#define RT711_CLK_FREQ_2400000HZ 2400000 +#define RT711_CLK_FREQ_12288000HZ 12288000 + +int rt711_clock_config(struct device *dev) +{ + struct rt711_priv *rt711 = dev_get_drvdata(dev); + unsigned int clk_freq, value; + + clk_freq = (rt711->params.curr_dr_freq >> 1); + + switch (clk_freq) { + case RT711_CLK_FREQ_12000000HZ: + value = 0x0; + break; + case RT711_CLK_FREQ_6000000HZ: + value = 0x1; + break; + case RT711_CLK_FREQ_9600000HZ: + value = 0x2; + break; + case RT711_CLK_FREQ_4800000HZ: + value = 0x3; + break; + case RT711_CLK_FREQ_2400000HZ: + value = 0x4; + break; + case RT711_CLK_FREQ_12288000HZ: + value = 0x5; + break; + default: + return -EINVAL; + } + + regmap_write(rt711->regmap, 0xe0, value); + regmap_write(rt711->regmap, 0xf0, value); + + dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq); + + return 0; +} + +static void rt711_calibration_work(struct work_struct *work) +{ + struct rt711_priv *rt711 = + container_of(work, struct rt711_priv, calibration_work); + + rt711_calibration(rt711); +} + +int rt711_init(struct device *dev, struct regmap *sdw_regmap, + struct regmap *regmap, struct sdw_slave *slave) +{ + struct rt711_priv *rt711; + int ret; + + rt711 = devm_kzalloc(dev, sizeof(*rt711), GFP_KERNEL); + if (!rt711) + return -ENOMEM; + + dev_set_drvdata(dev, rt711); + rt711->slave = slave; + rt711->sdw_regmap = sdw_regmap; + rt711->regmap = regmap; + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt711->hw_init = false; + rt711->first_init = false; + + ret = devm_snd_soc_register_component(dev, + &soc_codec_dev_rt711, + rt711_dai, + ARRAY_SIZE(rt711_dai)); + + dev_dbg(&slave->dev, "%s\n", __func__); + + return ret; +} + +int rt711_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt711_priv *rt711 = dev_get_drvdata(dev); + + if (rt711->hw_init) + return 0; + + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + if (!rt711->first_init) { + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + + rt711->first_init = true; + } + + pm_runtime_get_noresume(&slave->dev); + + rt711_reset(rt711->regmap); + + /* power on */ + regmap_write(rt711->regmap, RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + /* Set Pin Widget */ + regmap_write(rt711->regmap, RT711_SET_PIN_MIC2, 0x25); + regmap_write(rt711->regmap, RT711_SET_PIN_HP, 0xc0); + regmap_write(rt711->regmap, RT711_SET_PIN_DMIC1, 0x20); + regmap_write(rt711->regmap, RT711_SET_PIN_DMIC2, 0x20); + regmap_write(rt711->regmap, RT711_SET_PIN_LINE1, 0x20); + regmap_write(rt711->regmap, RT711_SET_PIN_LINE2, 0x20); + + /* Mute HP/ADC1/ADC2 */ + regmap_write(rt711->regmap, RT711_SET_GAIN_HP_H, 0xa080); + regmap_write(rt711->regmap, RT711_SET_GAIN_HP_H, 0x9080); + regmap_write(rt711->regmap, RT711_SET_GAIN_ADC2_H, 0x6080); + regmap_write(rt711->regmap, RT711_SET_GAIN_ADC2_H, 0x5080); + regmap_write(rt711->regmap, RT711_SET_GAIN_ADC1_H, 0x6080); + regmap_write(rt711->regmap, RT711_SET_GAIN_ADC1_H, 0x5080); + + /* Set Configuration Default */ + regmap_write(rt711->regmap, 0x4f12, 0x91); + regmap_write(rt711->regmap, 0x4e12, 0xd6); + regmap_write(rt711->regmap, 0x4d12, 0x11); + regmap_write(rt711->regmap, 0x4c12, 0x20); + regmap_write(rt711->regmap, 0x4f13, 0x91); + regmap_write(rt711->regmap, 0x4e13, 0xd6); + regmap_write(rt711->regmap, 0x4d13, 0x11); + regmap_write(rt711->regmap, 0x4c13, 0x21); + regmap_write(rt711->regmap, 0x4c21, 0xf0); + regmap_write(rt711->regmap, 0x4d21, 0x11); + regmap_write(rt711->regmap, 0x4e21, 0x11); + regmap_write(rt711->regmap, 0x4f21, 0x01); + + /* Data port arrangement */ + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + RT711_TX_RX_MUX_CTL, 0x0154); + + /* Set index */ + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + RT711_DIGITAL_MISC_CTRL4, 0x201b); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + RT711_COMBO_JACK_AUTO_CTL1, 0x5089); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + RT711_VREFOUT_CTL, 0x5064); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + RT711_INLINE_CMD_CTL, 0xd249); + + /* Finish Initial Settings, set power to D3 */ + regmap_write(rt711->regmap, RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + + INIT_DELAYED_WORK(&rt711->jack_detect_work, + rt711_jack_detect_handler); + INIT_DELAYED_WORK(&rt711->jack_btn_check_work, + rt711_btn_check_handler); + mutex_init(&rt711->calibrate_mutex); + INIT_WORK(&rt711->calibration_work, rt711_calibration_work); + schedule_work(&rt711->calibration_work); + + /* + * if set_jack callback occurred early than io_init, + * we set up the jack detection function now + */ + if (rt711->hs_jack) + rt711_jack_init(rt711); + + /* Mark Slave initialization complete */ + rt711->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + return 0; +} + +MODULE_DESCRIPTION("ASoC RT711 SDW driver"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt711.h b/sound/soc/codecs/rt711.h new file mode 100644 index 00000000000000..795803db7acd13 --- /dev/null +++ b/sound/soc/codecs/rt711.h @@ -0,0 +1,212 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt711.h -- RT711 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT711_H__ +#define __RT711_H__ + +#include + +extern const struct dev_pm_ops rt711_runtime_pm; + +struct rt711_priv { + struct regmap *regmap; + struct regmap *sdw_regmap; + struct snd_soc_component *component; + struct sdw_slave *slave; + enum sdw_slave_status status; + struct sdw_bus_params params; + bool hw_init; + bool first_init; + struct snd_soc_jack *hs_jack; + struct delayed_work jack_detect_work; + struct delayed_work jack_btn_check_work; + struct work_struct calibration_work; + struct mutex calibrate_mutex; /* for headset calibration */ + int jack_type; +}; + +struct sdw_stream_data { + struct sdw_stream_runtime *sdw_stream; +}; + +/* NID */ +#define RT711_AUDIO_FUNCTION_GROUP 0x01 +#define RT711_DAC_OUT2 0x03 +#define RT711_ADC_IN1 0x09 +#define RT711_ADC_IN2 0x08 +#define RT711_DMIC1 0x12 +#define RT711_DMIC2 0x13 +#define RT711_MIC2 0x19 +#define RT711_LINE1 0x1a +#define RT711_LINE2 0x1b +#define RT711_BEEP 0x1d +#define RT711_VENDOR_REG 0x20 +#define RT711_HP_OUT 0x21 +#define RT711_MIXER_IN1 0x22 +#define RT711_MIXER_IN2 0x23 +#define RT711_INLINE_CMD 0x55 +#define RT711_VENDOR_CALI 0x58 +#define RT711_VENDOR_IMS_DRE 0x5b + +/* Index (NID:20h) */ +#define RT711_DAC_DC_CALI_CTL1 0x00 +#define RT711_PARA_VERB_CTL 0x1a +#define RT711_COMBO_JACK_AUTO_CTL1 0x45 +#define RT711_COMBO_JACK_AUTO_CTL2 0x46 +#define RT711_INLINE_CMD_CTL 0x48 +#define RT711_DIGITAL_MISC_CTRL4 0x4a +#define RT711_VREFOUT_CTL 0x6b +#define RT711_FSM_CTL 0x6f +#define RT711_IRQ_FLAG_TABLE1 0x80 +#define RT711_IRQ_FLAG_TABLE2 0x81 +#define RT711_IRQ_FLAG_TABLE3 0x82 +#define RT711_TX_RX_MUX_CTL 0x91 + +/* Index (NID:5bh) */ +#define RT711_IMS_DIGITAL_CTL1 0x00 +#define RT711_HP_IMS_RESULT_L 0x20 +#define RT711_HP_IMS_RESULT_R 0x21 + +/* Verb */ +#define RT711_VERB_SET_CONNECT_SEL 0x3100 +#define RT711_VERB_SET_EAPD_BTLENABLE 0x3c00 +#define RT711_VERB_GET_CONNECT_SEL 0xb100 +#define RT711_VERB_SET_POWER_STATE 0x3500 +#define RT711_VERB_SET_CHANNEL_STREAMID 0x3600 +#define RT711_VERB_SET_PIN_WIDGET_CONTROL 0x3700 +#define RT711_VERB_SET_UNSOLICITED_ENABLE 0x3800 +#define RT711_SET_AMP_GAIN_MUTE_H 0x7300 +#define RT711_SET_AMP_GAIN_MUTE_L 0x8380 +#define RT711_VERB_GET_POWER_STATE 0xb500 +#define RT711_VERB_GET_CHANNEL_STREAMID 0xb600 +#define RT711_VERB_GET_PIN_SENSE 0xb900 +#define RT711_FUNC_RESET 0xff01 + +#define RT711_READ_HDA_3 0x2012 +#define RT711_READ_HDA_2 0x2013 +#define RT711_READ_HDA_1 0x2014 +#define RT711_READ_HDA_0 0x2015 +#define RT711_PRIV_INDEX_W_H 0x7500 +#define RT711_PRIV_INDEX_W_L 0x8580 +#define RT711_PRIV_DATA_W_H 0x7400 +#define RT711_PRIV_DATA_W_L 0x8480 +#define RT711_PRIV_INDEX_R_H 0x9d00 +#define RT711_PRIV_INDEX_R_L 0xad80 +#define RT711_PRIV_DATA_R_H 0x9c00 +#define RT711_PRIV_DATA_R_L 0xac80 +#define RT711_DAC_FORMAT_H 0x7203 +#define RT711_DAC_FORMAT_L 0x8283 +#define RT711_ADC1_FORMAT_H 0x7209 +#define RT711_ADC1_FORMAT_L 0x8289 +#define RT711_ADC2_FORMAT_H 0x7208 +#define RT711_ADC2_FORMAT_L 0x8288 + +#define RT711_SET_AUDIO_POWER_STATE\ + (RT711_VERB_SET_POWER_STATE | RT711_AUDIO_FUNCTION_GROUP) +#define RT711_GET_AUDIO_POWER_STATE\ + (RT711_VERB_GET_POWER_STATE | RT711_AUDIO_FUNCTION_GROUP) +#define RT711_SET_PIN_DMIC1\ + (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_DMIC1) +#define RT711_SET_PIN_DMIC2\ + (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_DMIC2) +#define RT711_SET_PIN_HP\ + (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_HP_OUT) +#define RT711_SET_PIN_MIC2\ + (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_MIC2) +#define RT711_SET_PIN_LINE1\ + (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_LINE1) +#define RT711_SET_PIN_LINE2\ + (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_LINE2) +#define RT711_SET_MIC2_UNSOLICITED_ENABLE\ + (RT711_VERB_SET_UNSOLICITED_ENABLE | RT711_MIC2) +#define RT711_SET_HP_UNSOLICITED_ENABLE\ + (RT711_VERB_SET_UNSOLICITED_ENABLE | RT711_HP_OUT) +#define RT711_SET_INLINE_UNSOLICITED_ENABLE\ + (RT711_VERB_SET_UNSOLICITED_ENABLE | RT711_INLINE_CMD) +#define RT711_SET_STREAMID_DAC2\ + (RT711_VERB_SET_CHANNEL_STREAMID | RT711_DAC_OUT2) +#define RT711_SET_STREAMID_ADC1\ + (RT711_VERB_SET_CHANNEL_STREAMID | RT711_ADC_IN1) +#define RT711_SET_STREAMID_ADC2\ + (RT711_VERB_SET_CHANNEL_STREAMID | RT711_ADC_IN2) +#define RT711_GET_STREAMID_DAC2\ + (RT711_VERB_GET_CHANNEL_STREAMID | RT711_DAC_OUT2) +#define RT711_GET_STREAMID_ADC1\ + (RT711_VERB_GET_CHANNEL_STREAMID | RT711_ADC_IN1) +#define RT711_GET_STREAMID_ADC2\ + (RT711_VERB_GET_CHANNEL_STREAMID | RT711_ADC_IN2) +#define RT711_SET_GAIN_DAC2_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_DAC_OUT2) +#define RT711_SET_GAIN_DAC2_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_DAC_OUT2) +#define RT711_SET_GAIN_ADC1_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_ADC_IN1) +#define RT711_SET_GAIN_ADC1_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_ADC_IN1) +#define RT711_SET_GAIN_ADC2_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_ADC_IN2) +#define RT711_SET_GAIN_ADC2_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_ADC_IN2) +#define RT711_SET_GAIN_AMIC_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_MIC2) +#define RT711_SET_GAIN_AMIC_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_MIC2) +#define RT711_SET_GAIN_DMIC1_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_DMIC1) +#define RT711_SET_GAIN_DMIC1_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_DMIC1) +#define RT711_SET_GAIN_DMIC2_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_DMIC2) +#define RT711_SET_GAIN_DMIC2_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_DMIC2) +#define RT711_SET_GAIN_HP_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_HP_OUT) +#define RT711_SET_GAIN_HP_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_HP_OUT) + +/* DAC DC offset calibration control-1 (0x00)(NID:20h) */ +#define RT711_DAC_DC_CALI_TRIGGER (0x1 << 15) + +/* Parameter & Verb control (0x1a)(NID:20h) */ +#define RT711_HIDDEN_REG_SW_RESET (0x1 << 14) + +/* combo jack auto switch control 2 (0x46)(NID:20h) */ +#define RT711_COMBOJACK_AUTO_DET_STATUS (0x1 << 11) +#define RT711_COMBOJACK_AUTO_DET_TRS (0x1 << 10) +#define RT711_COMBOJACK_AUTO_DET_CTIA (0x1 << 9) +#define RT711_COMBOJACK_AUTO_DET_OMTP (0x1 << 8) + +/* FSM control (0x6f)(NID:20h) */ +#define RT711_CALI_CTL (0x0 << 0) +#define RT711_COMBOJACK_CTL (0x1 << 0) +#define RT711_IMS_CTL (0x2 << 0) +#define RT711_DEPOP_CTL (0x3 << 0) + +/* Impedance Sense Digital Control 1 (0x00)(NID:5bh) */ +#define RT711_TRIGGER_IMS (0x1 << 15) +#define RT711_IMS_EN (0x1 << 6) + +#define RT711_EAPD_HIGH 0x2 +#define RT711_EAPD_LOW 0x0 +#define RT711_MUTE_SFT 7 +/* set input/output mapping to payload[14][15] separately */ +#define RT711_DIR_IN_SFT 6 +#define RT711_DIR_OUT_SFT 7 + +enum { + RT711_AIF1, + RT711_AIF2, + RT711_AIFS, +}; + +int rt711_io_init(struct device *dev, struct sdw_slave *slave); +int rt711_init(struct device *dev, struct regmap *sdw_regmap, + struct regmap *regmap, struct sdw_slave *slave); + +int rt711_jack_detect(struct rt711_priv *rt711, bool *hp, bool *mic); +int rt711_clock_config(struct device *dev); +#endif /* __RT711_H__ */ From ab51c26893028c8fe0205fb2104b20c59e6716e7 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Wed, 21 Aug 2019 17:40:49 +0800 Subject: [PATCH 029/110] ASoC: codecs: rt715: add SoundWire support The driver for this capture device was tested by Realtek and Intel using the Realtek 3-in-1 add-on boards connected to CometLake and IceLake platforms Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Signed-off-by: Jack Yu --- sound/soc/codecs/Kconfig | 9 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt715-sdw.c | 530 +++++++++++++++++++++ sound/soc/codecs/rt715-sdw.h | 276 +++++++++++ sound/soc/codecs/rt715.c | 864 +++++++++++++++++++++++++++++++++++ sound/soc/codecs/rt715.h | 205 +++++++++ 6 files changed, 1886 insertions(+) create mode 100644 sound/soc/codecs/rt715-sdw.c create mode 100644 sound/soc/codecs/rt715-sdw.h create mode 100644 sound/soc/codecs/rt715.c create mode 100644 sound/soc/codecs/rt715.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 64eab6c1bcbecd..036dc714ac1dc8 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1080,6 +1080,15 @@ config SND_SOC_RT711_SDW select SND_SOC_RT711 select REGMAP_SOUNDWIRE +config SND_SOC_RT715 + tristate + +config SND_SOC_RT715_SDW + tristate "Realtek RT715 Codec - SDW" + depends on SOUNDWIRE + select SND_SOC_RT715 + select REGMAP_SOUNDWIRE + #Freescale sgtl5000 codec config SND_SOC_SGTL5000 tristate "Freescale SGTL5000 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index ada517d0c81e53..2fed4c17a07dfa 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -281,6 +281,7 @@ snd-soc-zx-aud96p22-objs := zx_aud96p22.o snd-soc-rt700-objs := rt700.o rt700-sdw.o snd-soc-rt1308-sdw-objs := rt1308-sdw.o snd-soc-rt711-objs := rt711.o rt711-sdw.o +snd-soc-rt715-objs := rt715.o rt715-sdw.o # Amp snd-soc-max9877-objs := max9877.o @@ -574,6 +575,7 @@ obj-$(CONFIG_SND_SOC_ZX_AUD96P22) += snd-soc-zx-aud96p22.o obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o obj-$(CONFIG_SND_SOC_RT1308_SDW) += snd-soc-rt1308-sdw.o obj-$(CONFIG_SND_SOC_RT711) += snd-soc-rt711.o +obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o # Amp obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c new file mode 100644 index 00000000000000..cd1ab562fd64c5 --- /dev/null +++ b/sound/soc/codecs/rt715-sdw.c @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * rt715-sdw.c -- rt715 ALSA SoC audio driver + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + * + * ALC715 ASoC Codec Driver based Intel Dummy SdW codec driver + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rt715.h" +#include "rt715-sdw.h" + +static bool rt715_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x02e0: + case 0x02f0: + case 0x04e0: + case 0x04f0: + case 0x06e0: + case 0x06f0: + case 0x00e0 ... 0x00e5: + case 0x00ee ... 0x00ef: + case 0x00f0 ... 0x00f5: + case 0x00fe ... 0x00ff: + case 0x2000 ... 0x2027: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2200 ... 0x2204: + case 0x2206 ... 0x2212: + case 0x2220 ... 0x2223: + case 0x2230 ... 0x2239: + case 0x22f0 ... 0x22f3: + case 0x3000 ... 0x3fff: + case 0x7000 ... 0x7fff: + case 0x8300 ... 0x83ff: + case 0x9c00 ... 0x9cff: + case 0xb900 ... 0xb9ff: + case 0x75200039: + return true; + default: + return false; + } +} + +static bool rt715_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00e5: + case 0x00f0: + case 0x00f3: + case 0x00f5: + case 0x2009: + case 0x2016: + case 0x201b: + case 0x201c: + case 0x201d: + case 0x201f: + case 0x2023: + case 0x2230: + case 0x200b ... 0x200e: /* i2c read */ + case 0x2012 ... 0x2015: /* HD-A read */ + case 0x202d ... 0x202f: /* BRA */ + case 0x2201 ... 0x2212: /* i2c debug */ + case 0x2220 ... 0x2223: /* decoded HD-A */ + case 0x9c00 ... 0x9cff: + case 0xb900 ... 0xb9ff: + case 0x75200039: + return true; + default: + return false; + } +} + +static int rt715_sdw_read(void *context, unsigned int reg, unsigned int *val) +{ + struct device *dev = context; + struct rt715_priv *rt715 = dev_get_drvdata(dev); + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2; + unsigned int is_hda_reg = 1, is_index_reg = 0; + int ret; + + if (reg > 0xffff) + is_index_reg = 1; + + mask = reg & 0xf000; + + if (is_index_reg) { /* index registers */ + val2 = reg & 0xffff; + reg = reg >> 16; + nid = reg & 0xff; + ret = regmap_write(rt715->sdw_regmap, reg, ((val2 >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, (val2 & 0xff)); + if (ret < 0) + return ret; + + reg3 = RT715_PRIV_DATA_R_H | nid; + ret = regmap_write(rt715->sdw_regmap, reg3, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg4 = reg3 + 0x1000; + reg4 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg4, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0x3000) { + reg += 0x8000; + ret = regmap_write(rt715->sdw_regmap, reg, *val); + if (ret < 0) + return ret; + } else if (mask == 0x7000) { + reg += 0x2000; + reg |= 0x800; + ret = regmap_write(rt715->sdw_regmap, reg, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, (*val & 0xff)); + if (ret < 0) + return ret; + } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ + reg2 = reg - 0x1000; + reg2 &= ~0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + ret = regmap_write(rt715->sdw_regmap, reg, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0x9000) { + ret = regmap_write(rt715->sdw_regmap, reg, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0xb000) { + ret = regmap_write(rt715->sdw_regmap, reg, *val); + if (ret < 0) + return ret; + } else { + ret = regmap_read(rt715->sdw_regmap, reg, val); + if (ret < 0) + return ret; + is_hda_reg = 0; + } + + if (is_hda_reg || is_index_reg) { + sdw_data_3 = 0; + sdw_data_2 = 0; + sdw_data_1 = 0; + sdw_data_0 = 0; + ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_3, &sdw_data_3); + if (ret < 0) + return ret; + ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_2, &sdw_data_2); + if (ret < 0) + return ret; + ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_1, &sdw_data_1); + if (ret < 0) + return ret; + ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_0, &sdw_data_0); + if (ret < 0) + return ret; + *val = ((sdw_data_3 & 0xff) << 24) | ((sdw_data_2 & 0xff) << 16) | + ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); + } + + if (is_hda_reg == 0) + dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val); + else if (is_index_reg) + dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n", __func__, + reg, reg2, reg3, reg4, *val); + else + dev_dbg(dev, "[%s] %04x %04x => %08x\n", __func__, reg, reg2, *val); + + return 0; +} + +static int rt715_sdw_write(void *context, unsigned int reg, unsigned int val) +{ + struct device *dev = context; + struct rt715_priv *rt715 = dev_get_drvdata(dev); + unsigned int reg2 = 0, reg3, reg4, nid, mask, val2; + unsigned int is_index_reg = 0; + int ret; + + if (reg > 0xffff) + is_index_reg = 1; + + mask = reg & 0xf000; + + if (is_index_reg) { /* index registers */ + val2 = reg & 0xffff; + reg = reg >> 16; + nid = reg & 0xff; + ret = regmap_write(rt715->sdw_regmap, reg, ((val2 >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, (val2 & 0xff)); + if (ret < 0) + return ret; + + reg3 = RT715_PRIV_DATA_W_H | nid; + ret = regmap_write(rt715->sdw_regmap, reg3, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg4 = reg3 + 0x1000; + reg4 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg4, (val & 0xff)); + if (ret < 0) + return ret; + is_index_reg = 1; + } else if (reg < 0x4fff) { + ret = regmap_write(rt715->sdw_regmap, reg, val); + if (ret < 0) + return ret; + } else if (reg == RT715_FUNC_RESET) { + ret = regmap_write(rt715->sdw_regmap, reg, val); + if (ret < 0) + return ret; + } else if (mask == 0x7000) { + ret = regmap_write(rt715->sdw_regmap, reg, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, (val & 0xff)); + if (ret < 0) + return ret; + } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ + reg2 = reg - 0x1000; + reg2 &= ~0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + ret = regmap_write(rt715->sdw_regmap, reg, (val & 0xff)); + if (ret < 0) + return ret; + } + + if (reg2 == 0) + dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val); + else if (is_index_reg) + dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n", __func__, + reg, reg2, reg3, reg4, val2, val); + else + dev_dbg(dev, "[%s] %04x %04x <= %04x\n", __func__, reg, reg2, val); + + return 0; +} + +static const struct regmap_config rt715_regmap = { + .reg_bits = 32, + .val_bits = 32, + .readable_reg = rt715_readable_register, /* Readable registers */ + .volatile_reg = rt715_volatile_register, /* volatile register */ + .max_register = 0x75200039, /* Maximum number of register */ + .reg_defaults = rt715_reg_defaults, /* Defaults */ + .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, + .reg_read = rt715_sdw_read, + .reg_write = rt715_sdw_write, +}; + +const struct regmap_config rt715_sdw_regmap = { + .name = "sdw", + .reg_bits = 32, /* Total register space for SDW */ + .val_bits = 8, /* Total number of bits in register */ + .readable_reg = rt715_readable_register, /* Readable registers */ + .max_register = 0xff01, /* Maximum number of register */ + .cache_type = REGCACHE_NONE, + .use_single_read = true, + .use_single_write = true, +}; + +int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload, + unsigned int *sdw_addr_h, unsigned int *sdw_data_h, + unsigned int *sdw_addr_l, unsigned int *sdw_data_l) +{ + unsigned int offset_h, offset_l, e_verb; + + if (((verb & 0xff) != 0) || verb == 0xf00) { /* 12 bits command */ + if (verb == 0x7ff) /* special case */ + offset_h = 0; + else + offset_h = 0x3000; + + if (verb & 0x800) /* get command */ + e_verb = (verb - 0xf00) | 0x80; + else /* set command */ + e_verb = (verb - 0x700); + + *sdw_data_h = payload; /* 7 bits payload */ + *sdw_addr_l = *sdw_data_l = 0; + } else { /* 4 bits command */ + if ((verb & 0x800) == 0x800) { /* read */ + offset_h = 0x9000; + offset_l = 0xa000; + } else { /* write */ + offset_h = 0x7000; + offset_l = 0x8000; + } + e_verb = verb >> 8; + *sdw_data_h = (payload >> 8); /* 16 bits payload [15:8] */ + *sdw_addr_l = (e_verb << 8) | nid | 0x80; /* 0x80: valid bit */ + *sdw_addr_l += offset_l; + *sdw_data_l = payload & 0xff; + } + + *sdw_addr_h = (e_verb << 8) | nid; + *sdw_addr_h += offset_h; + + return 0; +} +EXPORT_SYMBOL(hda_to_sdw); + +static int rt715_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev); + + /* Update the status */ + rt715->status = status; + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt715->hw_init || rt715->status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt715_io_init(&slave->dev, slave); +} + +static int rt715_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval, i, num_of_ports = 1; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->paging_support = false; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = 0x50; /* BITMAP: 01010000 */ + prop->sink_ports = 0x0; /* BITMAP: 00000000 */ + + nval = hweight32(prop->source_ports); + num_of_ports += nval; + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + dpn = prop->src_dpn_prop; + i = 0; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + num_of_ports += nval; + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + dpn = prop->sink_dpn_prop; + i = 0; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* Allocate port_ready based on num_of_ports */ + slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports, + sizeof(*slave->port_ready), + GFP_KERNEL); + if (!slave->port_ready) + return -ENOMEM; + + /* Initialize completion */ + for (i = 0; i < num_of_ports; i++) + init_completion(&slave->port_ready[i]); + + /* set the timeout values */ + prop->clk_stop_timeout = 20; + + return 0; +} + +static int rt715_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev); + int ret; + + memcpy(&rt715->params, params, sizeof(*params)); + + ret = rt715_clock_config(&slave->dev); + if (ret < 0) + dev_err(&slave->dev, "Invalid clk config"); + + return 0; +} + +static struct sdw_slave_ops rt715_slave_ops = { + .read_prop = rt715_read_prop, + .update_status = rt715_update_status, + .bus_config = rt715_bus_config, +}; + +static int rt715_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *sdw_regmap, *regmap; + + /* Assign ops */ + slave->ops = &rt715_slave_ops; + + /* Regmap Initialization */ + sdw_regmap = devm_regmap_init_sdw(slave, &rt715_sdw_regmap); + if (!sdw_regmap) + return -EINVAL; + + regmap = devm_regmap_init(&slave->dev, NULL, &slave->dev, &rt715_regmap); + if (!regmap) + return -EINVAL; + + rt715_init(&slave->dev, sdw_regmap, regmap, slave); + + return 0; +} + +static const struct sdw_device_id rt715_id[] = { + SDW_SLAVE_ENTRY(0x025d, 0x715, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt715_id); + +static int rt715_dev_suspend(struct device *dev) +{ + struct rt715_priv *rt715 = dev_get_drvdata(dev); + + if (!rt715->hw_init) + return 0; + + regcache_cache_only(rt715->regmap, true); + + return 0; +} + +#define RT715_PROBE_TIMEOUT 2000 + +static int rt715_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = to_sdw_slave_device(dev); + struct rt715_priv *rt715 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt715->hw_init) + return 0; + + time = wait_for_completion_timeout(&slave->enumeration_complete, + msecs_to_jiffies(RT715_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Enumeration not complete, timed out\n"); + return -ETIMEDOUT; + } + + regcache_cache_only(rt715->regmap, false); + regcache_sync_region(rt715->regmap, 0x3000, 0x8fff); + regcache_sync_region(rt715->regmap, 0x75200039, 0x75200039); + + return 0; +} + +static const struct dev_pm_ops rt715_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume) + SET_RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL) +}; + +static struct sdw_driver rt715_sdw_driver = { + .driver = { + .name = "rt715", + .owner = THIS_MODULE, + .pm = &rt715_pm, + }, + .probe = rt715_sdw_probe, + .ops = &rt715_slave_ops, + .id_table = rt715_id, +}; +module_sdw_driver(rt715_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT715 driver SDW"); +MODULE_AUTHOR("Jack Yu "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt715-sdw.h b/sound/soc/codecs/rt715-sdw.h new file mode 100644 index 00000000000000..1834127ecfcaab --- /dev/null +++ b/sound/soc/codecs/rt715-sdw.h @@ -0,0 +1,276 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt715-sdw.h -- RT715 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT715_SDW_H__ +#define __RT715_SDW_H__ + +static const struct reg_default rt715_reg_defaults[] = { + { 0x0000, 0x00 }, + { 0x0001, 0x00 }, + { 0x0002, 0x00 }, + { 0x0003, 0x00 }, + { 0x0004, 0x00 }, + { 0x0005, 0x01 }, + { 0x0020, 0x00 }, + { 0x0022, 0x00 }, + { 0x0023, 0x00 }, + { 0x0024, 0x00 }, + { 0x0025, 0x00 }, + { 0x0026, 0x00 }, + { 0x0030, 0x00 }, + { 0x0032, 0x00 }, + { 0x0033, 0x00 }, + { 0x0034, 0x00 }, + { 0x0035, 0x00 }, + { 0x0036, 0x00 }, + { 0x0040, 0x00 }, + { 0x0041, 0x00 }, + { 0x0042, 0x00 }, + { 0x0043, 0x00 }, + { 0x0044, 0x20 }, + { 0x0045, 0x01 }, + { 0x0046, 0x00 }, + { 0x0050, 0x20 }, + { 0x0051, 0x02 }, + { 0x0052, 0x5d }, + { 0x0053, 0x07 }, + { 0x0054, 0x15 }, + { 0x0055, 0x00 }, + { 0x0060, 0x00 }, + { 0x0070, 0x00 }, + { 0x0080, 0x00 }, + { 0x00e0, 0x00 }, + { 0x00e1, 0x00 }, + { 0x00e2, 0x00 }, + { 0x00e3, 0x00 }, + { 0x00e4, 0x00 }, + { 0x00e5, 0x00 }, + { 0x00ee, 0x00 }, + { 0x00ef, 0x00 }, + { 0x00f0, 0x00 }, + { 0x00f1, 0x00 }, + { 0x00f2, 0x00 }, + { 0x00f3, 0x00 }, + { 0x00f4, 0x00 }, + { 0x00f5, 0x00 }, + { 0x00fe, 0x00 }, + { 0x00ff, 0x00 }, + { 0x0200, 0x00 }, + { 0x0201, 0x00 }, + { 0x0202, 0x20 }, + { 0x0203, 0x00 }, + { 0x0204, 0x00 }, + { 0x0205, 0x00 }, + { 0x0220, 0x00 }, + { 0x0221, 0x00 }, + { 0x0222, 0x00 }, + { 0x0223, 0x00 }, + { 0x0224, 0x00 }, + { 0x0225, 0x00 }, + { 0x0226, 0x00 }, + { 0x0227, 0x00 }, + { 0x0230, 0x00 }, + { 0x0231, 0x00 }, + { 0x0232, 0x00 }, + { 0x0233, 0x00 }, + { 0x0234, 0x00 }, + { 0x0235, 0x00 }, + { 0x0236, 0x00 }, + { 0x0237, 0x00 }, + { 0x02e0, 0x00 }, + { 0x02f0, 0x00 }, + { 0x0400, 0x00 }, + { 0x0401, 0x00 }, + { 0x0402, 0x20 }, + { 0x0403, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x00 }, + { 0x0420, 0x00 }, + { 0x0421, 0x00 }, + { 0x0422, 0x00 }, + { 0x0423, 0x00 }, + { 0x0424, 0x00 }, + { 0x0425, 0x00 }, + { 0x0426, 0x00 }, + { 0x0427, 0x00 }, + { 0x0430, 0x00 }, + { 0x0431, 0x00 }, + { 0x0432, 0x00 }, + { 0x0433, 0x00 }, + { 0x0434, 0x00 }, + { 0x0435, 0x00 }, + { 0x0436, 0x00 }, + { 0x0437, 0x00 }, + { 0x04e0, 0x00 }, + { 0x04f0, 0x00 }, + { 0x0600, 0x00 }, + { 0x0601, 0x00 }, + { 0x0602, 0x20 }, + { 0x0603, 0x00 }, + { 0x0604, 0x00 }, + { 0x0605, 0x00 }, + { 0x0620, 0x00 }, + { 0x0621, 0x00 }, + { 0x0622, 0x00 }, + { 0x0623, 0x00 }, + { 0x0624, 0x00 }, + { 0x0625, 0x00 }, + { 0x0626, 0x00 }, + { 0x0627, 0x00 }, + { 0x0630, 0x00 }, + { 0x0631, 0x00 }, + { 0x0632, 0x00 }, + { 0x0633, 0x00 }, + { 0x0634, 0x00 }, + { 0x0635, 0x00 }, + { 0x0636, 0x00 }, + { 0x0637, 0x00 }, + { 0x06e0, 0x00 }, + { 0x06f0, 0x00 }, + { 0x0f00, 0x00 }, + { 0x0f01, 0x00 }, + { 0x0f02, 0x00 }, + { 0x0f03, 0x00 }, + { 0x0f04, 0x00 }, + { 0x0f05, 0xff }, + { 0x0f06, 0x00 }, + { 0x0f07, 0x00 }, + { 0x0f08, 0x00 }, + { 0x0f09, 0x00 }, + { 0x0f0a, 0x00 }, + { 0x0f0b, 0x00 }, + { 0x0f0c, 0x00 }, + { 0x0f0d, 0x00 }, + { 0x0f0e, 0x00 }, + { 0x0f0f, 0x00 }, + { 0x0f10, 0x00 }, + { 0x0f11, 0x00 }, + { 0x0f12, 0x00 }, + { 0x0f13, 0x00 }, + { 0x0f14, 0x00 }, + { 0x0f15, 0xff }, + { 0x0f16, 0x00 }, + { 0x0f17, 0x00 }, + { 0x0f18, 0x00 }, + { 0x0f19, 0x00 }, + { 0x0f1a, 0x00 }, + { 0x0f1b, 0x00 }, + { 0x0f1c, 0x00 }, + { 0x0f1d, 0x00 }, + { 0x0f1e, 0x00 }, + { 0x0f1f, 0x00 }, + { 0x0f20, 0x00 }, + { 0x0f21, 0x00 }, + { 0x0f22, 0x00 }, + { 0x0f23, 0x00 }, + { 0x0f24, 0x00 }, + { 0x0f25, 0x00 }, + { 0x0f26, 0x00 }, + { 0x0f27, 0x00 }, + { 0x0f30, 0x00 }, + { 0x0f31, 0x00 }, + { 0x0f32, 0x00 }, + { 0x0f33, 0x00 }, + { 0x0f34, 0x00 }, + { 0x0f35, 0x00 }, + { 0x0f36, 0x00 }, + { 0x0f37, 0x00 }, + { 0x2000, 0x00 }, + { 0x2001, 0x00 }, + { 0x2002, 0x00 }, + { 0x2003, 0x00 }, + { 0x2004, 0x00 }, + { 0x2005, 0x00 }, + { 0x2006, 0x00 }, + { 0x2007, 0x00 }, + { 0x2008, 0x00 }, + { 0x2009, 0x03 }, + { 0x200a, 0x00 }, + { 0x200b, 0x00 }, + { 0x200c, 0x00 }, + { 0x200d, 0x00 }, + { 0x200e, 0x00 }, + { 0x200f, 0x00 }, + { 0x2010, 0x00 }, + { 0x2011, 0x00 }, + { 0x2012, 0x00 }, + { 0x2013, 0x00 }, + { 0x2014, 0x00 }, + { 0x2015, 0x00 }, + { 0x2016, 0x00 }, + { 0x201a, 0x00 }, + { 0x201b, 0x00 }, + { 0x201c, 0x00 }, + { 0x201d, 0x00 }, + { 0x201e, 0x00 }, + { 0x201f, 0x00 }, + { 0x2020, 0x00 }, + { 0x2021, 0x00 }, + { 0x2022, 0x00 }, + { 0x2023, 0x00 }, + { 0x2024, 0x00 }, + { 0x2025, 0x01 }, + { 0x2026, 0x00 }, + { 0x2027, 0x00 }, + { 0x2029, 0x00 }, + { 0x202a, 0x00 }, + { 0x202d, 0x00 }, + { 0x202e, 0x00 }, + { 0x202f, 0x00 }, + { 0x2030, 0x00 }, + { 0x2031, 0x00 }, + { 0x2032, 0x00 }, + { 0x2033, 0x00 }, + { 0x2034, 0x00 }, + { 0x2200, 0x00 }, + { 0x2201, 0x00 }, + { 0x2202, 0x00 }, + { 0x2203, 0x00 }, + { 0x2204, 0x00 }, + { 0x2206, 0x00 }, + { 0x2207, 0x00 }, + { 0x2208, 0x00 }, + { 0x2209, 0x00 }, + { 0x220a, 0x00 }, + { 0x220b, 0x00 }, + { 0x220c, 0x00 }, + { 0x220d, 0x00 }, + { 0x220e, 0x00 }, + { 0x220f, 0x00 }, + { 0x2210, 0x00 }, + { 0x2211, 0x00 }, + { 0x2212, 0x00 }, + { 0x2220, 0x00 }, + { 0x2221, 0x00 }, + { 0x2222, 0x00 }, + { 0x2223, 0x00 }, + { 0x2230, 0x00 }, + { 0x2231, 0x0f }, + { 0x2232, 0x00 }, + { 0x2233, 0x00 }, + { 0x2234, 0x00 }, + { 0x2235, 0x00 }, + { 0x2236, 0x00 }, + { 0x2237, 0x00 }, + { 0x2238, 0x00 }, + { 0x2239, 0x00 }, + { 0x22f0, 0x00 }, + { 0x22f1, 0x00 }, + { 0x22f2, 0x00 }, + { 0x22f3, 0x00 }, + { 0x7307, 0x97 }, + { 0x8387, 0x97 }, + { 0x7308, 0x97 }, + { 0x8388, 0x97 }, + { 0x7309, 0x97 }, + { 0x8389, 0x97 }, + { 0x7327, 0x97 }, + { 0x83a7, 0x97 }, + { 0x75200039, 0xa500 }, +}; + +#endif /* __RT715_H__ */ diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c new file mode 100644 index 00000000000000..0ac8868012d83d --- /dev/null +++ b/sound/soc/codecs/rt715.c @@ -0,0 +1,864 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * rt715.c -- rt715 ALSA SoC audio driver + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + * + * ALC715 ASoC Codec Driver based Intel Dummy SdW codec driver + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt715.h" + +static int rt715_index_write(struct regmap *regmap, unsigned int reg, + unsigned int value) +{ + int ret; + unsigned int addr = ((RT715_PRIV_INDEX_W_H) << 16) | reg; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) { + pr_err("Failed to set private value: %08x <= %04x %d\n", ret, + addr, value); + } + + return ret; +} + +static unsigned int rt715_index_read(struct regmap *regmap, unsigned int reg, + unsigned int *value) +{ + int ret; + unsigned int addr = ((RT715_PRIV_INDEX_W_H) << 16) | reg; + + *value = 0; + ret = regmap_read(regmap, addr, value); + if (ret < 0) { + pr_err("Failed to get private value: %08x => %04x %d\n", ret, + addr, *value); + } + + return ret; +} + +static void rt715_get_gain(struct rt715_priv *rt715, unsigned int addr_h, + unsigned int addr_l, unsigned int val_h, + unsigned int *r_val, unsigned int *l_val) +{ + /* R Channel */ + *r_val = (val_h << 8); + regmap_read(rt715->regmap, addr_l, r_val); + + /* L Channel */ + val_h |= 0x20; + *l_val = (val_h << 8); + regmap_read(rt715->regmap, addr_h, l_val); +} + +/* For Verb-Set Amplifier Gain (Verb ID = 3h) */ +static int rt715_set_amp_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + unsigned int addr_h, addr_l, val_h, val_ll, val_lr; + unsigned int read_ll, read_rl; + int i; + + /* Can't use update bit function, so read the original value first */ + addr_h = mc->reg; + addr_l = mc->rreg; + if (mc->shift == RT715_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll); + + /* L Channel */ + if (mc->invert) { + /* for mute */ + val_ll = (mc->max - ucontrol->value.integer.value[0]) << 7; + /* keep gain */ + read_ll = read_ll & 0x7f; + val_ll |= read_ll; + } else { + /* for gain */ + val_ll = ((ucontrol->value.integer.value[0]) & 0x7f); + if (val_ll > mc->max) + val_ll = mc->max; + /* keep mute status */ + read_ll = read_ll & 0x80; + val_ll |= read_ll; + } + + /* R Channel */ + if (mc->invert) { + regmap_write(rt715->regmap, + RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + /* for mute */ + val_lr = (mc->max - ucontrol->value.integer.value[1]) << 7; + /* keep gain */ + read_rl = read_rl & 0x7f; + val_lr |= read_rl; + } else { + /* for gain */ + val_lr = ((ucontrol->value.integer.value[1]) & 0x7f); + if (val_lr > mc->max) + val_lr = mc->max; + /* keep mute status */ + read_rl = read_rl & 0x80; + val_lr |= read_rl; + } + + for (i = 0; i < 3; i++) { /* retry 3 times at most */ + + if (val_ll == val_lr) { + /* Set both L/R channels at the same time */ + val_h = (1 << mc->shift) | (3 << 4); + regmap_write(rt715->regmap, addr_h, (val_h << 8 | val_ll)); + regmap_write(rt715->regmap, addr_l, (val_h << 8 | val_ll)); + } else { + /* Lch*/ + val_h = (1 << mc->shift) | (1 << 5); + regmap_write(rt715->regmap, addr_h, (val_h << 8 | val_ll)); + + /* Rch */ + val_h = (1 << mc->shift) | (1 << 4); + regmap_write(rt715->regmap, addr_l, (val_h << 8 | val_lr)); + } + /* check result */ + if (mc->shift == RT715_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt715_get_gain(rt715, addr_h, addr_l, val_h, + &read_rl, &read_ll); + if (read_rl == val_lr && read_ll == val_ll) + break; + } + /* D0:power on state, D3: power saving mode */ + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt715->regmap, + RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + return 0; +} + +static int rt715_set_amp_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int addr_h, addr_l, val_h; + unsigned int read_ll, read_rl; + + addr_h = mc->reg; + addr_l = mc->rreg; + if (mc->shift == RT715_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll); + + if (mc->invert) { + /* for mute status */ + read_ll = !((read_ll & 0x80) >> RT715_MUTE_SFT); + read_rl = !((read_rl & 0x80) >> RT715_MUTE_SFT); + } else { + /* for gain */ + read_ll = read_ll & 0x7f; + read_rl = read_rl & 0x7f; + } + ucontrol->value.integer.value[0] = read_ll; + ucontrol->value.integer.value[1] = read_rl; + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +#define SOC_DOUBLE_R_EXT(xname, reg_left, reg_right, xshift, xmax, xinvert,\ + xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = snd_soc_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \ + xmax, xinvert) } + +static const struct snd_kcontrol_new rt715_snd_controls[] = { + /* Capture switch */ + SOC_DOUBLE_R_EXT("ADC 07 Capture Switch", RT715_SET_GAIN_MIC_ADC_H, + RT715_SET_GAIN_MIC_ADC_L, RT715_DIR_IN_SFT, 1, 1, + rt715_set_amp_gain_get, rt715_set_amp_gain_put), + SOC_DOUBLE_R_EXT("ADC 08 Capture Switch", RT715_SET_GAIN_LINE_ADC_H, + RT715_SET_GAIN_LINE_ADC_L, RT715_DIR_IN_SFT, 1, 1, + rt715_set_amp_gain_get, rt715_set_amp_gain_put), + SOC_DOUBLE_R_EXT("ADC 09 Capture Switch", RT715_SET_GAIN_MIX_ADC_H, + RT715_SET_GAIN_MIX_ADC_L, RT715_DIR_IN_SFT, 1, 1, + rt715_set_amp_gain_get, rt715_set_amp_gain_put), + SOC_DOUBLE_R_EXT("ADC 27 Capture Switch", RT715_SET_GAIN_MIX_ADC2_H, + RT715_SET_GAIN_MIX_ADC2_L, RT715_DIR_IN_SFT, 1, 1, + rt715_set_amp_gain_get, rt715_set_amp_gain_put), + /* Volume Control */ + SOC_DOUBLE_R_EXT_TLV("ADC 07 Capture Volume", RT715_SET_GAIN_MIC_ADC_H, + RT715_SET_GAIN_MIC_ADC_L, RT715_DIR_IN_SFT, 0x3f, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume", RT715_SET_GAIN_LINE_ADC_H, + RT715_SET_GAIN_LINE_ADC_L, RT715_DIR_IN_SFT, 0x3f, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume", RT715_SET_GAIN_MIX_ADC_H, + RT715_SET_GAIN_MIX_ADC_L, RT715_DIR_IN_SFT, 0x3f, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("ADC 27 Capture Volume", RT715_SET_GAIN_MIX_ADC2_H, + RT715_SET_GAIN_MIX_ADC2_L, RT715_DIR_IN_SFT, 0x3f, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + in_vol_tlv), + /* MIC Boost Control */ + SOC_DOUBLE_R_EXT_TLV("DMIC1 Boost", RT715_SET_GAIN_DMIC1_H, + RT715_SET_GAIN_DMIC1_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DMIC2 Boost", RT715_SET_GAIN_DMIC2_H, + RT715_SET_GAIN_DMIC2_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DMIC3 Boost", RT715_SET_GAIN_DMIC3_H, + RT715_SET_GAIN_DMIC3_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DMIC4 Boost", RT715_SET_GAIN_DMIC4_H, + RT715_SET_GAIN_DMIC4_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("MIC1 Boost", RT715_SET_GAIN_MIC1_H, + RT715_SET_GAIN_MIC1_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("MIC2 Boost", RT715_SET_GAIN_MIC2_H, + RT715_SET_GAIN_MIC2_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("LINE1 Boost", RT715_SET_GAIN_LINE1_H, + RT715_SET_GAIN_LINE1_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("LINE2 Boost", RT715_SET_GAIN_LINE2_H, + RT715_SET_GAIN_LINE2_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), +}; + +static int rt715_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int reg, val, ret; + + /* nid = e->reg, vid = 0xf01 */ + reg = RT715_VERB_SET_CONNECT_SEL | e->reg; + ret = regmap_read(rt715->regmap, reg, &val); + if (ret < 0) { + dev_err(component->dev, "%s: sdw read failed: %d\n", __func__, ret); + return ret; + } + + /* + * The first two indices of ADC Mux 24/25 are routed to the same + * hardware source. ie, ADC Mux 24 0/1 will both connect to MIC2. + * To have a unique set of inputs, we skip the index1 of the muxes. + */ + if ((e->reg == RT715_MUX_IN3 || e->reg == RT715_MUX_IN4) && (val > 0)) + val -= 1; + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int rt715_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, reg, ret; + + if (item[0] >= e->items) + return -EINVAL; + + /* Verb ID = 0x701h, nid = e->reg */ + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + reg = RT715_VERB_SET_CONNECT_SEL | e->reg; + ret = regmap_read(rt715->regmap, reg, &val2); + if (ret < 0) { + dev_err(component->dev, "%s: sdw read failed: %d\n", __func__, ret); + return ret; + } + + if (val == val2) + change = 0; + else + change = 1; + + if (change) { + reg = RT715_VERB_SET_CONNECT_SEL | e->reg; + regmap_write(rt715->regmap, reg, val); + } + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, NULL); + + return change; +} + +static const char * const adc_22_23_mux_text[] = { + "MIC1", + "MIC2", + "LINE1", + "LINE2", + "DMIC1", + "DMIC2", + "DMIC3", + "DMIC4", +}; + +/** + * Due to mux design for nid 24 (MUX_IN3)/25 (MUX_IN4), connection index 0 and + * 1 will be connected to the same dmic source, therefore we skip index 1 to + * avoid misunderstanding on usage of dapm routing. + */ +static const unsigned int rt715_adc_24_25_values[] = { + 0, + 2, + 3, + 4, + 5, +}; + +static const char * const adc_24_mux_text[] = { + "MIC2", + "DMIC1", + "DMIC2", + "DMIC3", + "DMIC4", +}; + +static const char * const adc_25_mux_text[] = { + "MIC1", + "DMIC1", + "DMIC2", + "DMIC3", + "DMIC4", +}; + +static SOC_ENUM_SINGLE_DECL( + rt715_adc22_enum, RT715_MUX_IN1, 0, adc_22_23_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt715_adc23_enum, RT715_MUX_IN2, 0, adc_22_23_mux_text); + +static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc24_enum, + RT715_MUX_IN3, 0, 0xf, + adc_24_mux_text, rt715_adc_24_25_values); +static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc25_enum, + RT715_MUX_IN4, 0, 0xf, + adc_25_mux_text, rt715_adc_24_25_values); + +static const struct snd_kcontrol_new rt715_adc22_mux = + SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt715_adc22_enum, + rt715_mux_get, rt715_mux_put); + +static const struct snd_kcontrol_new rt715_adc23_mux = + SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt715_adc23_enum, + rt715_mux_get, rt715_mux_put); + +static const struct snd_kcontrol_new rt715_adc24_mux = + SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt715_adc24_enum, + rt715_mux_get, rt715_mux_put); + +static const struct snd_kcontrol_new rt715_adc25_mux = + SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt715_adc25_enum, + rt715_mux_get, rt715_mux_put); + +static const struct snd_soc_dapm_widget rt715_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("DMIC3"), + SND_SOC_DAPM_INPUT("DMIC4"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + SND_SOC_DAPM_ADC("ADC 07", NULL, RT715_SET_STREAMID_MIC_ADC, 4, 0), + SND_SOC_DAPM_ADC("ADC 08", NULL, RT715_SET_STREAMID_LINE_ADC, 4, 0), + SND_SOC_DAPM_ADC("ADC 09", NULL, RT715_SET_STREAMID_MIX_ADC, 4, 0), + SND_SOC_DAPM_ADC("ADC 27", NULL, RT715_SET_STREAMID_MIX_ADC2, 4, 0), + SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc22_mux), + SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc23_mux), + SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc24_mux), + SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc25_mux), + SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt715_audio_map[] = { + {"DP6TX", NULL, "ADC 09"}, + {"DP6TX", NULL, "ADC 08"}, + {"DP4TX", NULL, "ADC 07"}, + {"DP4TX", NULL, "ADC 27"}, + {"ADC 09", NULL, "ADC 22 Mux"}, + {"ADC 08", NULL, "ADC 23 Mux"}, + {"ADC 07", NULL, "ADC 24 Mux"}, + {"ADC 27", NULL, "ADC 25 Mux"}, + {"ADC 22 Mux", "MIC1", "MIC1"}, + {"ADC 22 Mux", "MIC2", "MIC2"}, + {"ADC 22 Mux", "LINE1", "LINE1"}, + {"ADC 22 Mux", "LINE2", "LINE2"}, + {"ADC 22 Mux", "DMIC1", "DMIC1"}, + {"ADC 22 Mux", "DMIC2", "DMIC2"}, + {"ADC 22 Mux", "DMIC3", "DMIC3"}, + {"ADC 22 Mux", "DMIC4", "DMIC4"}, + {"ADC 23 Mux", "MIC1", "MIC1"}, + {"ADC 23 Mux", "MIC2", "MIC2"}, + {"ADC 23 Mux", "LINE1", "LINE1"}, + {"ADC 23 Mux", "LINE2", "LINE2"}, + {"ADC 23 Mux", "DMIC1", "DMIC1"}, + {"ADC 23 Mux", "DMIC2", "DMIC2"}, + {"ADC 23 Mux", "DMIC3", "DMIC3"}, + {"ADC 23 Mux", "DMIC4", "DMIC4"}, + {"ADC 24 Mux", "MIC2", "MIC2"}, + {"ADC 24 Mux", "DMIC1", "DMIC1"}, + {"ADC 24 Mux", "DMIC2", "DMIC2"}, + {"ADC 24 Mux", "DMIC3", "DMIC3"}, + {"ADC 24 Mux", "DMIC4", "DMIC4"}, + {"ADC 25 Mux", "MIC1", "MIC1"}, + {"ADC 25 Mux", "DMIC1", "DMIC1"}, + {"ADC 25 Mux", "DMIC2", "DMIC2"}, + {"ADC 25 Mux", "DMIC3", "DMIC3"}, + {"ADC 25 Mux", "DMIC4", "DMIC4"}, +}; + +static int rt715_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { + regmap_write(rt715->regmap, + RT715_SET_AUDIO_POWER_STATE, + AC_PWRST_D0); + } + break; + + case SND_SOC_BIAS_STANDBY: + regmap_write(rt715->regmap, + RT715_SET_AUDIO_POWER_STATE, + AC_PWRST_D3); + break; + + default: + break; + } + dapm->bias_level = level; + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_rt715 = { + .set_bias_level = rt715_set_bias_level, + .controls = rt715_snd_controls, + .num_controls = ARRAY_SIZE(rt715_snd_controls), + .dapm_widgets = rt715_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt715_dapm_widgets), + .dapm_routes = rt715_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt715_audio_map), +}; + +static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + + struct sdw_stream_data *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + + stream->sdw_stream = (struct sdw_stream_runtime *)sdw_stream; + + /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dai->playback_dma_data = stream; + else + dai->capture_dma_data = stream; + + return 0; +} + +static void rt715_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + +{ + struct sdw_stream_data *stream; + + stream = snd_soc_dai_get_dma_data(dai, substream); + snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(stream); +} + +static int rt715_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_data *stream; + int retval, port, num_channels; + unsigned int val = 0; + + stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!stream) + return -EINVAL; + + if (!rt715->slave) + return -EINVAL; + + switch (dai->id) { + case RT715_AIF1: + direction = SDW_DATA_DIR_TX; + port = 6; + rt715_index_write(rt715->regmap, RT715_SDW_INPUT_SEL, 0xa500); + break; + case RT715_AIF2: + direction = SDW_DATA_DIR_TX; + port = 4; + rt715_index_write(rt715->regmap, RT715_SDW_INPUT_SEL, 0xa000); + break; + default: + dev_err(component->dev, "Invalid DAI id %d\n", dai->id); + return -EINVAL; + } + + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + num_channels = params_channels(params); + port_config.ch_mask = (1 << (num_channels)) - 1; + port_config.num = port; + + retval = sdw_stream_add_slave(rt715->slave, &stream_config, + &port_config, 1, stream->sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + switch (params_rate(params)) { + /* bit 14 0:48K 1:44.1K */ + /* bit 15 Stream Type 0:PCM 1:Non-PCM, should always be PCM */ + case 44100: + val |= 0x40 << 8; + break; + case 48000: + val |= 0x0 << 8; + break; + default: + dev_err(component->dev, "Unsupported sample rate %d\n", + params_rate(params)); + return -EINVAL; + } + + if (params_channels(params) <= 16) { + /* bit 3:0 Number of Channel */ + val |= (params_channels(params) - 1); + } else { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + switch (params_width(params)) { + /* bit 6:4 Bits per Sample */ + case 8: + break; + case 16: + val |= (0x1 << 4); + break; + case 20: + val |= (0x2 << 4); + break; + case 24: + val |= (0x3 << 4); + break; + case 32: + val |= (0x4 << 4); + break; + default: + return -EINVAL; + } + + regmap_write(rt715->regmap, RT715_MIC_ADC_FORMAT_H, val); + regmap_write(rt715->regmap, RT715_MIC_LINE_FORMAT_H, val); + regmap_write(rt715->regmap, RT715_MIX_ADC_FORMAT_H, val); + regmap_write(rt715->regmap, RT715_MIX_ADC2_FORMAT_H, val); + + return retval; +} + +static int rt715_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + struct sdw_stream_data *stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt715->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt715->slave, stream->sdw_stream); + return 0; +} + +#define RT715_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define RT715_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static struct snd_soc_dai_ops rt715_ops = { + .hw_params = rt715_pcm_hw_params, + .hw_free = rt715_pcm_hw_free, + .set_sdw_stream = rt715_set_sdw_stream, + .shutdown = rt715_shutdown, +}; + +static struct snd_soc_dai_driver rt715_dai[] = { + { + .name = "rt715-aif1", + .id = RT715_AIF1, + .capture = { + .stream_name = "DP6 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT715_STEREO_RATES, + .formats = RT715_FORMATS, + }, + .ops = &rt715_ops, + }, + { + .name = "rt715-aif2", + .id = RT715_AIF2, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT715_STEREO_RATES, + .formats = RT715_FORMATS, + }, + .ops = &rt715_ops, + }, +}; + +/* Bus clock frequency */ +#define RT715_CLK_FREQ_9600000HZ 9600000 +#define RT715_CLK_FREQ_12000000HZ 12000000 +#define RT715_CLK_FREQ_6000000HZ 6000000 +#define RT715_CLK_FREQ_4800000HZ 4800000 +#define RT715_CLK_FREQ_2400000HZ 2400000 +#define RT715_CLK_FREQ_12288000HZ 12288000 + +int rt715_clock_config(struct device *dev) +{ + struct rt715_priv *rt715 = dev_get_drvdata(dev); + unsigned int clk_freq, value; + + clk_freq = (rt715->params.curr_dr_freq >> 1); + + switch (clk_freq) { + case RT715_CLK_FREQ_12000000HZ: + value = 0x0; + break; + case RT715_CLK_FREQ_6000000HZ: + value = 0x1; + break; + case RT715_CLK_FREQ_9600000HZ: + value = 0x2; + break; + case RT715_CLK_FREQ_4800000HZ: + value = 0x3; + break; + case RT715_CLK_FREQ_2400000HZ: + value = 0x4; + break; + case RT715_CLK_FREQ_12288000HZ: + value = 0x5; + break; + default: + return -EINVAL; + } + + regmap_write(rt715->regmap, 0xe0, value); + regmap_write(rt715->regmap, 0xf0, value); + + return 0; +} + +int rt715_init(struct device *dev, struct regmap *sdw_regmap, + struct regmap *regmap, struct sdw_slave *slave) +{ + struct rt715_priv *rt715; + int ret; + + rt715 = devm_kzalloc(dev, sizeof(*rt715), GFP_KERNEL); + if (!rt715) + return -ENOMEM; + + dev_set_drvdata(dev, rt715); + rt715->slave = slave; + rt715->regmap = regmap; + rt715->sdw_regmap = sdw_regmap; + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt715->hw_init = false; + rt715->first_init = false; + + ret = devm_snd_soc_register_component(dev, + &soc_codec_dev_rt715, + rt715_dai, + ARRAY_SIZE(rt715_dai)); + + return ret; +} + +int rt715_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt715_priv *rt715 = dev_get_drvdata(dev); + + if (rt715->hw_init) + return 0; + + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + if (!rt715->first_init) { + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + + rt715->first_init = true; + } + + pm_runtime_get_noresume(&slave->dev); + + /* Mute nid=08h/09h */ + regmap_write(rt715->regmap, RT715_SET_GAIN_LINE_ADC_H, 0xb080); + regmap_write(rt715->regmap, RT715_SET_GAIN_MIX_ADC_H, 0xb080); + /* Mute nid=07h/27h */ + regmap_write(rt715->regmap, RT715_SET_GAIN_MIC_ADC_H, 0xb080); + regmap_write(rt715->regmap, RT715_SET_GAIN_MIX_ADC2_H, 0xb080); + + /* Set Pin Widget */ + regmap_write(rt715->regmap, RT715_SET_PIN_DMIC1, 0x20); + regmap_write(rt715->regmap, RT715_SET_PIN_DMIC2, 0x20); + /* Set Converter Stream */ + regmap_write(rt715->regmap, RT715_SET_STREAMID_LINE_ADC, 0x10); + regmap_write(rt715->regmap, RT715_SET_STREAMID_MIX_ADC, 0x10); + regmap_write(rt715->regmap, RT715_SET_STREAMID_MIC_ADC, 0x10); + regmap_write(rt715->regmap, RT715_SET_STREAMID_MIX_ADC2, 0x10); + /* Set Configuration Default */ + regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT1, 0xd0); + regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT2, 0x11); + regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT3, 0xa1); + regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT4, 0x81); + regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT1, 0xd1); + regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT2, 0x11); + regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT3, 0xa1); + regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT4, 0x81); + + /* Finish Initial Settings, set power to D3 */ + regmap_write(rt715->regmap, RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + + /* Mark Slave initialization complete */ + rt715->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + return 0; +} + +MODULE_DESCRIPTION("ASoC rt715 driver"); +MODULE_DESCRIPTION("ASoC rt715 driver SDW"); +MODULE_AUTHOR("Jack Yu "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h new file mode 100644 index 00000000000000..846d6a0116d9d0 --- /dev/null +++ b/sound/soc/codecs/rt715.h @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt715.h -- RT715 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT715_H__ +#define __RT715_H__ + +#include + +struct rt715_priv { + struct regmap *regmap; + struct regmap *sdw_regmap; + struct snd_soc_codec *codec; + struct sdw_slave *slave; + int dbg_nid; + int dbg_vid; + int dbg_payload; + enum sdw_slave_status status; + struct sdw_bus_params params; + bool hw_init; + bool first_init; +}; + +struct sdw_stream_data { + struct sdw_stream_runtime *sdw_stream; +}; + +/* NID */ +#define RT715_AUDIO_FUNCTION_GROUP 0x01 +#define RT715_MIC_ADC 0x07 +#define RT715_LINE_ADC 0x08 +#define RT715_MIX_ADC 0x09 +#define RT715_DMIC1 0x12 +#define RT715_DMIC2 0x13 +#define RT715_MIC1 0x18 +#define RT715_MIC2 0x19 +#define RT715_LINE1 0x1a +#define RT715_LINE2 0x1b +#define RT715_DMIC3 0x1d +#define RT715_DMIC4 0x29 +#define RT715_VENDOR_REGISTERS 0x20 +#define RT715_MUX_IN1 0x22 +#define RT715_MUX_IN2 0x23 +#define RT715_MUX_IN3 0x24 +#define RT715_MUX_IN4 0x25 +#define RT715_MIX_ADC2 0x27 +#define RT715_INLINE_CMD 0x55 + +/* Index (NID:20h) */ +#define RT715_SDW_INPUT_SEL 0x39 +#define RT715_EXT_DMIC_CLK_CTRL2 0x54 + +/* Verb */ +#define RT715_VERB_SET_CONNECT_SEL 0x3100 +#define RT715_VERB_GET_CONNECT_SEL 0xb100 +#define RT715_VERB_SET_EAPD_BTLENABLE 0x3c00 +#define RT715_VERB_SET_POWER_STATE 0x3500 +#define RT715_VERB_SET_CHANNEL_STREAMID 0x3600 +#define RT715_VERB_SET_PIN_WIDGET_CONTROL 0x3700 +#define RT715_VERB_SET_CONFIG_DEFAULT1 0x4c00 +#define RT715_VERB_SET_CONFIG_DEFAULT2 0x4d00 +#define RT715_VERB_SET_CONFIG_DEFAULT3 0x4e00 +#define RT715_VERB_SET_CONFIG_DEFAULT4 0x4f00 +#define RT715_VERB_SET_UNSOLICITED_ENABLE 0x3800 +#define RT715_SET_AMP_GAIN_MUTE_H 0x7300 +#define RT715_SET_AMP_GAIN_MUTE_L 0x8380 +#define RT715_READ_HDA_3 0x2012 +#define RT715_READ_HDA_2 0x2013 +#define RT715_READ_HDA_1 0x2014 +#define RT715_READ_HDA_0 0x2015 +#define RT715_PRIV_INDEX_W_H 0x7520 +#define RT715_PRIV_INDEX_W_L 0x85a0 +#define RT715_PRIV_DATA_W_H 0x7420 +#define RT715_PRIV_DATA_W_L 0x84a0 +#define RT715_PRIV_INDEX_R_H 0x9d20 +#define RT715_PRIV_INDEX_R_L 0xada0 +#define RT715_PRIV_DATA_R_H 0x9c20 +#define RT715_PRIV_DATA_R_L 0xaca0 +#define RT715_MIC_ADC_FORMAT_H 0x7207 +#define RT715_MIC_ADC_FORMAT_L 0x8287 +#define RT715_MIC_LINE_FORMAT_H 0x7208 +#define RT715_MIC_LINE_FORMAT_L 0x8288 +#define RT715_MIX_ADC_FORMAT_H 0x7209 +#define RT715_MIX_ADC_FORMAT_L 0x8289 +#define RT715_MIX_ADC2_FORMAT_H 0x7227 +#define RT715_MIX_ADC2_FORMAT_L 0x82a7 +#define RT715_FUNC_RESET 0xff01 + +#define RT715_SET_AUDIO_POWER_STATE\ + (RT715_VERB_SET_POWER_STATE | RT715_AUDIO_FUNCTION_GROUP) +#define RT715_SET_PIN_DMIC1\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC1) +#define RT715_SET_PIN_DMIC2\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC2) +#define RT715_SET_PIN_DMIC3\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC3) +#define RT715_SET_PIN_DMIC4\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC4) +#define RT715_SET_PIN_MIC1\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_MIC1) +#define RT715_SET_PIN_MIC2\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_MIC2) +#define RT715_SET_PIN_LINE1\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_LINE1) +#define RT715_SET_PIN_LINE2\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_LINE2) +#define RT715_SET_MIC1_UNSOLICITED_ENABLE\ + (RT715_VERB_SET_UNSOLICITED_ENABLE | RT715_MIC1) +#define RT715_SET_MIC2_UNSOLICITED_ENABLE\ + (RT715_VERB_SET_UNSOLICITED_ENABLE | RT715_MIC2) +#define RT715_SET_STREAMID_MIC_ADC\ + (RT715_VERB_SET_CHANNEL_STREAMID | RT715_MIC_ADC) +#define RT715_SET_STREAMID_LINE_ADC\ + (RT715_VERB_SET_CHANNEL_STREAMID | RT715_LINE_ADC) +#define RT715_SET_STREAMID_MIX_ADC\ + (RT715_VERB_SET_CHANNEL_STREAMID | RT715_MIX_ADC) +#define RT715_SET_STREAMID_MIX_ADC2\ + (RT715_VERB_SET_CHANNEL_STREAMID | RT715_MIX_ADC2) +#define RT715_SET_GAIN_MIC_ADC_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIC_ADC) +#define RT715_SET_GAIN_MIC_ADC_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIC_ADC) +#define RT715_SET_GAIN_LINE_ADC_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_LINE_ADC) +#define RT715_SET_GAIN_LINE_ADC_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_LINE_ADC) +#define RT715_SET_GAIN_MIX_ADC_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIX_ADC) +#define RT715_SET_GAIN_MIX_ADC_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIX_ADC) +#define RT715_SET_GAIN_MIX_ADC2_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIX_ADC2) +#define RT715_SET_GAIN_MIX_ADC2_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIX_ADC2) +#define RT715_SET_GAIN_DMIC1_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC1) +#define RT715_SET_GAIN_DMIC1_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC1) +#define RT715_SET_GAIN_DMIC2_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC2) +#define RT715_SET_GAIN_DMIC2_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC2) +#define RT715_SET_GAIN_DMIC3_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC3) +#define RT715_SET_GAIN_DMIC3_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC3) +#define RT715_SET_GAIN_DMIC4_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC4) +#define RT715_SET_GAIN_DMIC4_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC4) +#define RT715_SET_GAIN_MIC1_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIC1) +#define RT715_SET_GAIN_MIC1_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIC1) +#define RT715_SET_GAIN_MIC2_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIC2) +#define RT715_SET_GAIN_MIC2_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIC2) +#define RT715_SET_GAIN_LINE1_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_LINE1) +#define RT715_SET_GAIN_LINE1_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_LINE1) +#define RT715_SET_GAIN_LINE2_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_LINE2) +#define RT715_SET_GAIN_LINE2_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_LINE2) +#define RT715_SET_DMIC1_CONFIG_DEFAULT1\ + (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC1) +#define RT715_SET_DMIC2_CONFIG_DEFAULT1\ + (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC2) +#define RT715_SET_DMIC1_CONFIG_DEFAULT2\ + (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC1) +#define RT715_SET_DMIC2_CONFIG_DEFAULT2\ + (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC2) +#define RT715_SET_DMIC1_CONFIG_DEFAULT3\ + (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC1) +#define RT715_SET_DMIC2_CONFIG_DEFAULT3\ + (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC2) +#define RT715_SET_DMIC1_CONFIG_DEFAULT4\ + (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC1) +#define RT715_SET_DMIC2_CONFIG_DEFAULT4\ + (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC2) + +#define RT715_MUTE_SFT 7 +#define RT715_DIR_IN_SFT 6 +#define RT715_DIR_OUT_SFT 7 + +enum { + RT715_AIF1, + RT715_AIF2, + RT715_AIFS, +}; + +int rt715_io_init(struct device *dev, struct sdw_slave *slave); +int rt715_init(struct device *dev, struct regmap *sdw_regmap, + struct regmap *regmap, struct sdw_slave *slave); + +int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload, + unsigned int *sdw_addr_h, unsigned int *sdw_data_h, + unsigned int *sdw_addr_l, unsigned int *sdw_data_l); +int rt715_clock_config(struct device *dev); +#endif /* __RT715_H__ */ From 74b8b4ef71b6962d5d0394dab67dd9458b131895 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 18 Apr 2019 16:20:05 -0500 Subject: [PATCH 030/110] ASoC: codecs: Add DEBUG to Makefile Don't push upstream Signed-off-by: Pierre-Louis Bossart --- sound/soc/codecs/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 2fed4c17a07dfa..823de4b00f1640 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -1,3 +1,5 @@ +ccflags-y += -DDEBUG + # SPDX-License-Identifier: GPL-2.0 snd-soc-88pm860x-objs := 88pm860x-codec.o snd-soc-ab8500-codec-objs := ab8500-codec.o From b2d8fab8183aaf9bd0234e9c690024989328da41 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Fri, 18 Oct 2019 17:02:29 +0800 Subject: [PATCH 031/110] ASoC: codecs: rt715: rt715_sdw_regmap can be static Fixes: 340dea861e4c ("ASoC: codecs: rt715: add SoundWire support") Signed-off-by: kbuild test robot --- sound/soc/codecs/rt715-sdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index cd1ab562fd64c5..58f5e31131385d 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -284,7 +284,7 @@ static const struct regmap_config rt715_regmap = { .reg_write = rt715_sdw_write, }; -const struct regmap_config rt715_sdw_regmap = { +static const struct regmap_config rt715_sdw_regmap = { .name = "sdw", .reg_bits = 32, /* Total register space for SDW */ .val_bits = 8, /* Total number of bits in register */ From 05effd6b5c08f8d9dc96607cc6d2aed0211bff67 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 23 Oct 2019 13:43:46 +0800 Subject: [PATCH 032/110] ASoC: rt711: changes 32bits address mapping of index defined registers to 24bits It takes too many times to dump the registers when using 32 bits mapping. We change it to 24 bits to map the Realtek index defined registers. And this patch fixes some coding style. Signed-off-by: Shuming Fan --- sound/soc/codecs/rt711-sdw.c | 135 +++++++++++++++++++---------------- sound/soc/codecs/rt711-sdw.h | 14 ++-- sound/soc/codecs/rt711.c | 132 ++++++++++++++++++---------------- 3 files changed, 153 insertions(+), 128 deletions(-) diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index b044d12a515dd2..404a4b6ad4a904 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -37,17 +37,17 @@ static bool rt711_readable_register(struct device *dev, unsigned int reg) case 0x8300 ... 0x83ff: case 0x9c00 ... 0x9cff: case 0xb900 ... 0xb9ff: - case 0x7520001a: - case 0x75200045: - case 0x75200046: - case 0x75200048: - case 0x7520004a: - case 0x7520006b: - case 0x7520006f: - case 0x75200080: - case 0x75200081: - case 0x75200091: - case 0x75580000: + case 0x75201a: + case 0x752045: + case 0x752046: + case 0x752048: + case 0x75204a: + case 0x75206b: + case 0x75206f: + case 0x752080: + case 0x752081: + case 0x752091: + case 0x755800: return true; default: return false; @@ -72,11 +72,11 @@ static bool rt711_volatile_register(struct device *dev, unsigned int reg) case 0x9c00 ... 0x9cff: case 0xb900 ... 0xb9ff: case 0xff01: - case 0x7520001a: - case 0x75200046: - case 0x75200080: - case 0x75200081: - case 0x75580000: + case 0x75201a: + case 0x752046: + case 0x752080: + case 0x752081: + case 0x755800: return true; default: return false; @@ -98,20 +98,21 @@ static int rt711_sdw_read(void *context, unsigned int reg, unsigned int *val) mask = reg & 0xf000; if (is_index_reg) { /* index registers */ - val2 = reg & 0xffff; - reg = reg >> 16; + val2 = reg & 0xff; + reg = reg >> 8; nid = reg & 0xff; - ret = regmap_write(rt711->sdw_regmap, reg, ((val2 >> 8) & 0xff)); + ret = regmap_write(rt711->sdw_regmap, reg, 0); if (ret < 0) return ret; reg2 = reg + 0x1000; reg2 |= 0x80; - ret = regmap_write(rt711->sdw_regmap, reg2, (val2 & 0xff)); + ret = regmap_write(rt711->sdw_regmap, reg2, val2); if (ret < 0) return ret; reg3 = RT711_PRIV_DATA_R_H | nid; - ret = regmap_write(rt711->sdw_regmap, reg3, ((*val >> 8) & 0xff)); + ret = regmap_write(rt711->sdw_regmap, + reg3, ((*val >> 8) & 0xff)); if (ret < 0) return ret; reg4 = reg3 + 0x1000; @@ -127,7 +128,8 @@ static int rt711_sdw_read(void *context, unsigned int reg, unsigned int *val) } else if (mask == 0x7000) { reg += 0x2000; reg |= 0x800; - ret = regmap_write(rt711->sdw_regmap, reg, ((*val >> 8) & 0xff)); + ret = regmap_write(rt711->sdw_regmap, + reg, ((*val >> 8) & 0xff)); if (ret < 0) return ret; reg2 = reg + 0x1000; @@ -138,14 +140,16 @@ static int rt711_sdw_read(void *context, unsigned int reg, unsigned int *val) } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ reg2 = reg - 0x1000; reg2 &= ~0x80; - ret = regmap_write(rt711->sdw_regmap, reg2, ((*val >> 8) & 0xff)); + ret = regmap_write(rt711->sdw_regmap, + reg2, ((*val >> 8) & 0xff)); if (ret < 0) return ret; ret = regmap_write(rt711->sdw_regmap, reg, (*val & 0xff)); if (ret < 0) return ret; } else if (mask == 0x9000) { - ret = regmap_write(rt711->sdw_regmap, reg, ((*val >> 8) & 0xff)); + ret = regmap_write(rt711->sdw_regmap, + reg, ((*val >> 8) & 0xff)); if (ret < 0) return ret; reg2 = reg + 0x1000; @@ -169,29 +173,35 @@ static int rt711_sdw_read(void *context, unsigned int reg, unsigned int *val) sdw_data_2 = 0; sdw_data_1 = 0; sdw_data_0 = 0; - ret = regmap_read(rt711->sdw_regmap, RT711_READ_HDA_3, &sdw_data_3); + ret = regmap_read(rt711->sdw_regmap, + RT711_READ_HDA_3, &sdw_data_3); if (ret < 0) return ret; - ret = regmap_read(rt711->sdw_regmap, RT711_READ_HDA_2, &sdw_data_2); + ret = regmap_read(rt711->sdw_regmap, + RT711_READ_HDA_2, &sdw_data_2); if (ret < 0) return ret; - ret = regmap_read(rt711->sdw_regmap, RT711_READ_HDA_1, &sdw_data_1); + ret = regmap_read(rt711->sdw_regmap, + RT711_READ_HDA_1, &sdw_data_1); if (ret < 0) return ret; - ret = regmap_read(rt711->sdw_regmap, RT711_READ_HDA_0, &sdw_data_0); + ret = regmap_read(rt711->sdw_regmap, + RT711_READ_HDA_0, &sdw_data_0); if (ret < 0) return ret; - *val = ((sdw_data_3 & 0xff) << 24) | ((sdw_data_2 & 0xff) << 16) | - ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); + *val = ((sdw_data_3 & 0xff) << 24) | + ((sdw_data_2 & 0xff) << 16) | + ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); } if (is_hda_reg == 0) dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val); else if (is_index_reg) - dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n", __func__, - reg, reg2, reg3, reg4, *val); + dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n", + __func__, reg, reg2, reg3, reg4, *val); else - dev_dbg(dev, "[%s] %04x %04x => %08x\n", __func__, reg, reg2, *val); + dev_dbg(dev, "[%s] %04x %04x => %08x\n", + __func__, reg, reg2, *val); return 0; } @@ -210,20 +220,21 @@ static int rt711_sdw_write(void *context, unsigned int reg, unsigned int val) mask = reg & 0xf000; if (is_index_reg) { /* index registers */ - val2 = reg & 0xffff; - reg = reg >> 16; + val2 = reg & 0xff; + reg = reg >> 8; nid = reg & 0xff; - ret = regmap_write(rt711->sdw_regmap, reg, ((val2 >> 8) & 0xff)); + ret = regmap_write(rt711->sdw_regmap, reg, 0); if (ret < 0) return ret; reg2 = reg + 0x1000; reg2 |= 0x80; - ret = regmap_write(rt711->sdw_regmap, reg2, (val2 & 0xff)); + ret = regmap_write(rt711->sdw_regmap, reg2, val2); if (ret < 0) return ret; reg3 = RT711_PRIV_DATA_W_H | nid; - ret = regmap_write(rt711->sdw_regmap, reg3, ((val >> 8) & 0xff)); + ret = regmap_write(rt711->sdw_regmap, + reg3, ((val >> 8) & 0xff)); if (ret < 0) return ret; reg4 = reg3 + 0x1000; @@ -241,7 +252,8 @@ static int rt711_sdw_write(void *context, unsigned int reg, unsigned int val) if (ret < 0) return ret; } else if (mask == 0x7000) { - ret = regmap_write(rt711->sdw_regmap, reg, ((val >> 8) & 0xff)); + ret = regmap_write(rt711->sdw_regmap, + reg, ((val >> 8) & 0xff)); if (ret < 0) return ret; reg2 = reg + 0x1000; @@ -252,7 +264,8 @@ static int rt711_sdw_write(void *context, unsigned int reg, unsigned int val) } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ reg2 = reg - 0x1000; reg2 &= ~0x80; - ret = regmap_write(rt711->sdw_regmap, reg2, ((val >> 8) & 0xff)); + ret = regmap_write(rt711->sdw_regmap, + reg2, ((val >> 8) & 0xff)); if (ret < 0) return ret; ret = regmap_write(rt711->sdw_regmap, reg, (val & 0xff)); @@ -263,20 +276,21 @@ static int rt711_sdw_write(void *context, unsigned int reg, unsigned int val) if (reg2 == 0) dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val); else if (is_index_reg) - dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n", __func__, - reg, reg2, reg3, reg4, val2, val); + dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n", + __func__, reg, reg2, reg3, reg4, val2, val); else - dev_dbg(dev, "[%s] %04x %04x <= %04x\n", __func__, reg, reg2, val); + dev_dbg(dev, "[%s] %04x %04x <= %04x\n", + __func__, reg, reg2, val); return 0; } static const struct regmap_config rt711_regmap = { - .reg_bits = 32, + .reg_bits = 24, .val_bits = 32, .readable_reg = rt711_readable_register, /* Readable registers */ .volatile_reg = rt711_volatile_register, /* volatile register */ - .max_register = 0x75580000, /* Maximum number of register */ + .max_register = 0x755800, /* Maximum number of register */ .reg_defaults = rt711_reg_defaults, /* Defaults */ .num_reg_defaults = ARRAY_SIZE(rt711_reg_defaults), .cache_type = REGCACHE_RBTREE, @@ -298,7 +312,7 @@ static const struct regmap_config rt711_sdw_regmap = { }; static int rt711_update_status(struct sdw_slave *slave, - enum sdw_slave_status status) + enum sdw_slave_status status) { struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); @@ -330,14 +344,14 @@ static int rt711_read_prop(struct sdw_slave *slave) prop->paging_support = false; /* first we need to allocate memory for set bits in port lists */ - prop->source_ports = 0x14; /* BITMAP: 00010100 */ - prop->sink_ports = 0x8; /* BITMAP: 00001000 */ + prop->source_ports = 0x14; /* BITMAP: 00010100 */ + prop->sink_ports = 0x8; /* BITMAP: 00001000 */ nval = hweight32(prop->source_ports); num_of_ports += nval; prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, - sizeof(*prop->src_dpn_prop), - GFP_KERNEL); + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); if (!prop->src_dpn_prop) return -ENOMEM; @@ -356,8 +370,8 @@ static int rt711_read_prop(struct sdw_slave *slave) nval = hweight32(prop->sink_ports); num_of_ports += nval; prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, - sizeof(*prop->sink_dpn_prop), - GFP_KERNEL); + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); if (!prop->sink_dpn_prop) return -ENOMEM; @@ -374,8 +388,8 @@ static int rt711_read_prop(struct sdw_slave *slave) /* Allocate port_ready based on num_of_ports */ slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports, - sizeof(*slave->port_ready), - GFP_KERNEL); + sizeof(*slave->port_ready), + GFP_KERNEL); if (!slave->port_ready) return -ENOMEM; @@ -390,7 +404,7 @@ static int rt711_read_prop(struct sdw_slave *slave) } static int rt711_bus_config(struct sdw_slave *slave, - struct sdw_bus_params *params) + struct sdw_bus_params *params) { struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); int ret; @@ -405,7 +419,7 @@ static int rt711_bus_config(struct sdw_slave *slave, } static int rt711_interrupt_callback(struct sdw_slave *slave, - struct sdw_slave_intr_status *status) + struct sdw_slave_intr_status *status) { struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); @@ -428,7 +442,7 @@ static struct sdw_slave_ops rt711_slave_ops = { }; static int rt711_sdw_probe(struct sdw_slave *slave, - const struct sdw_device_id *id) + const struct sdw_device_id *id) { struct regmap *sdw_regmap, *regmap; @@ -440,7 +454,8 @@ static int rt711_sdw_probe(struct sdw_slave *slave, if (!sdw_regmap) return -EINVAL; - regmap = devm_regmap_init(&slave->dev, NULL, &slave->dev, &rt711_regmap); + regmap = devm_regmap_init(&slave->dev, NULL, + &slave->dev, &rt711_regmap); if (!regmap) return -EINVAL; @@ -492,7 +507,7 @@ static int rt711_dev_resume(struct device *dev) return 0; time = wait_for_completion_timeout(&slave->enumeration_complete, - msecs_to_jiffies(RT711_PROBE_TIMEOUT)); + msecs_to_jiffies(RT711_PROBE_TIMEOUT)); if (!time) { dev_err(&slave->dev, "Enumeration not complete, timed out\n"); return -ETIMEDOUT; @@ -500,7 +515,7 @@ static int rt711_dev_resume(struct device *dev) regcache_cache_only(rt711->regmap, false); regcache_sync_region(rt711->regmap, 0x3000, 0x8fff); - regcache_sync_region(rt711->regmap, 0x75200010, 0x75200091); + regcache_sync_region(rt711->regmap, 0x752010, 0x752091); return 0; } diff --git a/sound/soc/codecs/rt711-sdw.h b/sound/soc/codecs/rt711-sdw.h index 7d0a2a3c699470..10cd6e2cc063a1 100644 --- a/sound/soc/codecs/rt711-sdw.h +++ b/sound/soc/codecs/rt711-sdw.h @@ -267,13 +267,13 @@ static const struct reg_default rt711_reg_defaults[] = { { 0x8393, 0x00 }, { 0x7319, 0x00 }, { 0x8399, 0x00 }, - { 0x7520001a, 0x8003 }, - { 0x75200045, 0x5289 }, - { 0x75200048, 0xd049 }, - { 0x7520004a, 0xa83b }, - { 0x7520006b, 0x5064 }, - { 0x7520006f, 0x058b }, - { 0x75200091, 0x0000 }, + { 0x75201a, 0x8003 }, + { 0x752045, 0x5289 }, + { 0x752048, 0xd049 }, + { 0x75204a, 0xa83b }, + { 0x75206b, 0x5064 }, + { 0x75206f, 0x058b }, + { 0x752091, 0x0000 }, }; #endif /* __RT711_SDW_H__ */ diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index 183201f7212f52..11fab034e9735c 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -41,11 +41,12 @@ static int rt711_index_write(struct regmap *regmap, unsigned int nid, unsigned int reg, unsigned int value) { int ret; - unsigned int addr = ((RT711_PRIV_INDEX_W_H | nid) << 16) | reg; + unsigned int addr = ((RT711_PRIV_INDEX_W_H | nid) << 8) | reg; ret = regmap_write(regmap, addr, value); if (ret < 0) - pr_err("Failed to set private value: %08x <= %04x %d\n", ret, addr, value); + pr_err("Failed to set private value: %06x <= %04x ret=%d\n", + addr, value, ret); return ret; } @@ -54,12 +55,13 @@ static unsigned int rt711_index_read(struct regmap *regmap, unsigned int nid, unsigned int reg, unsigned int *value) { int ret; - unsigned int addr = ((RT711_PRIV_INDEX_W_H | nid) << 16) | reg; + unsigned int addr = ((RT711_PRIV_INDEX_W_H | nid) << 8) | reg; *value = 0; ret = regmap_read(regmap, addr, value); if (ret < 0) - pr_err("Failed to get private value: %08x => %04x %d\n", ret, addr, *value); + pr_err("Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); return ret; } @@ -71,7 +73,7 @@ static void rt711_reset(struct regmap *regmap) regmap_write(regmap, RT711_FUNC_RESET, 0); rt711_index_read(regmap, RT711_VENDOR_REG, RT711_PARA_VERB_CTL, &val); rt711_index_write(regmap, RT711_VENDOR_REG, - RT711_PARA_VERB_CTL, (val | RT711_HIDDEN_REG_SW_RESET)); + RT711_PARA_VERB_CTL, (val | RT711_HIDDEN_REG_SW_RESET)); } static int rt711_calibration(struct rt711_priv *rt711) @@ -184,7 +186,7 @@ static int rt711_headset_detect(struct rt711_priv *rt711) int ret; ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG, - RT711_COMBO_JACK_AUTO_CTL2, &buf); + RT711_COMBO_JACK_AUTO_CTL2, &buf); if (ret < 0) goto io_error; @@ -402,8 +404,8 @@ static int rt711_set_jack_detect(struct snd_soc_component *component, } static void rt711_get_gain(struct rt711_priv *rt711, unsigned int addr_h, - unsigned int addr_l, unsigned int val_h, - unsigned int *r_val, unsigned int *l_val) + unsigned int addr_l, unsigned int val_h, + unsigned int *r_val, unsigned int *l_val) { /* R Channel */ *r_val = (val_h << 8); @@ -417,7 +419,7 @@ static void rt711_get_gain(struct rt711_priv *rt711, unsigned int addr_h, /* For Verb-Set Amplifier Gain (Verb ID = 3h) */ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_context *dapm = @@ -459,7 +461,7 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) regmap_write(rt711->regmap, - RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); /* R Channel */ if (mc->invert) { @@ -484,16 +486,20 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, if (val_ll == val_lr) { /* Set both L/R channels at the same time */ val_h = (1 << mc->shift) | (3 << 4); - regmap_write(rt711->regmap, addr_h, (val_h << 8 | val_ll)); - regmap_write(rt711->regmap, addr_l, (val_h << 8 | val_ll)); + regmap_write(rt711->regmap, + addr_h, (val_h << 8 | val_ll)); + regmap_write(rt711->regmap, + addr_l, (val_h << 8 | val_ll)); } else { /* Lch*/ val_h = (1 << mc->shift) | (1 << 5); - regmap_write(rt711->regmap, addr_h, (val_h << 8 | val_ll)); + regmap_write(rt711->regmap, + addr_h, (val_h << 8 | val_ll)); /* Rch */ val_h = (1 << mc->shift) | (1 << 4); - regmap_write(rt711->regmap, addr_l, (val_h << 8 | val_lr)); + regmap_write(rt711->regmap, + addr_l, (val_h << 8 | val_lr)); } /* check result */ if (mc->shift == RT711_DIR_OUT_SFT) /* output */ @@ -502,19 +508,19 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, val_h = 0x0; rt711_get_gain(rt711, addr_h, addr_l, val_h, - &read_rl, &read_ll); + &read_rl, &read_ll); if (read_rl == val_lr && read_ll == val_ll) break; } if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) regmap_write(rt711->regmap, - RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); return 0; } static int rt711_set_amp_gain_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); @@ -553,40 +559,42 @@ static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); static const struct snd_kcontrol_new rt711_snd_controls[] = { - SOC_DOUBLE_R_EXT_TLV("DAC Surr Playback Volume", RT711_SET_GAIN_DAC2_H, - RT711_SET_GAIN_DAC2_L, RT711_DIR_OUT_SFT, 0x57, 0, - rt711_set_amp_gain_get, rt711_set_amp_gain_put, - out_vol_tlv), - SOC_DOUBLE_R_EXT("ADC 08 Capture Switch", RT711_SET_GAIN_ADC2_H, - RT711_SET_GAIN_ADC2_L, RT711_DIR_IN_SFT, 1, 1, - rt711_set_amp_gain_get, rt711_set_amp_gain_put), - SOC_DOUBLE_R_EXT("ADC 09 Capture Switch", RT711_SET_GAIN_ADC1_H, - RT711_SET_GAIN_ADC1_L, RT711_DIR_IN_SFT, 1, 1, - rt711_set_amp_gain_get, rt711_set_amp_gain_put), - SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume", RT711_SET_GAIN_ADC2_H, - RT711_SET_GAIN_ADC2_L, RT711_DIR_IN_SFT, 0x3f, 0, - rt711_set_amp_gain_get, rt711_set_amp_gain_put, - in_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume", RT711_SET_GAIN_ADC1_H, - RT711_SET_GAIN_ADC1_L, RT711_DIR_IN_SFT, 0x3f, 0, - rt711_set_amp_gain_get, rt711_set_amp_gain_put, - in_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("AMIC Volume", RT711_SET_GAIN_AMIC_H, - RT711_SET_GAIN_AMIC_L, RT711_DIR_IN_SFT, 3, 0, - rt711_set_amp_gain_get, rt711_set_amp_gain_put, - mic_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("DMIC1 Volume", RT711_SET_GAIN_DMIC1_H, - RT711_SET_GAIN_DMIC1_L, RT711_DIR_IN_SFT, 3, 0, - rt711_set_amp_gain_get, rt711_set_amp_gain_put, - mic_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("DMIC2 Volume", RT711_SET_GAIN_DMIC2_H, - RT711_SET_GAIN_DMIC2_L, RT711_DIR_IN_SFT, 3, 0, - rt711_set_amp_gain_get, rt711_set_amp_gain_put, - mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DAC Surr Playback Volume", + RT711_SET_GAIN_DAC2_H, RT711_SET_GAIN_DAC2_L, + RT711_DIR_OUT_SFT, 0x57, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, out_vol_tlv), + SOC_DOUBLE_R_EXT("ADC 08 Capture Switch", + RT711_SET_GAIN_ADC2_H, RT711_SET_GAIN_ADC2_L, + RT711_DIR_IN_SFT, 1, 1, + rt711_set_amp_gain_get, rt711_set_amp_gain_put), + SOC_DOUBLE_R_EXT("ADC 09 Capture Switch", + RT711_SET_GAIN_ADC1_H, RT711_SET_GAIN_ADC1_L, + RT711_DIR_IN_SFT, 1, 1, + rt711_set_amp_gain_get, rt711_set_amp_gain_put), + SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume", + RT711_SET_GAIN_ADC2_H, RT711_SET_GAIN_ADC2_L, + RT711_DIR_IN_SFT, 0x3f, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume", + RT711_SET_GAIN_ADC1_H, RT711_SET_GAIN_ADC1_L, + RT711_DIR_IN_SFT, 0x3f, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("AMIC Volume", + RT711_SET_GAIN_AMIC_H, RT711_SET_GAIN_AMIC_L, + RT711_DIR_IN_SFT, 3, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DMIC1 Volume", + RT711_SET_GAIN_DMIC1_H, RT711_SET_GAIN_DMIC1_L, + RT711_DIR_IN_SFT, 3, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DMIC2 Volume", + RT711_SET_GAIN_DMIC2_H, RT711_SET_GAIN_DMIC2_L, + RT711_DIR_IN_SFT, 3, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, mic_vol_tlv), }; static int rt711_mux_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); @@ -605,7 +613,8 @@ static int rt711_mux_get(struct snd_kcontrol *kcontrol, reg = RT711_VERB_SET_CONNECT_SEL | nid; ret = regmap_read(rt711->regmap, reg, &val); if (ret < 0) { - dev_err(component->dev, "%s: sdw read failed: %d\n", __func__, ret); + dev_err(component->dev, "%s: sdw read failed: %d\n", + __func__, ret); return ret; } @@ -615,7 +624,7 @@ static int rt711_mux_get(struct snd_kcontrol *kcontrol, } static int rt711_mux_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); @@ -643,7 +652,8 @@ static int rt711_mux_put(struct snd_kcontrol *kcontrol, reg = RT711_VERB_SET_CONNECT_SEL | nid; ret = regmap_read(rt711->regmap, reg, &val2); if (ret < 0) { - dev_err(component->dev, "%s: sdw read failed: %d\n", __func__, ret); + dev_err(component->dev, "%s: sdw read failed: %d\n", + __func__, ret); return ret; } @@ -658,7 +668,7 @@ static int rt711_mux_put(struct snd_kcontrol *kcontrol, } snd_soc_dapm_mux_update_power(dapm, kcontrol, - item[0], e, NULL); + item[0], e, NULL); return change; } @@ -871,7 +881,7 @@ static int rt711_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, } static void rt711_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { struct sdw_stream_data *stream; @@ -881,8 +891,8 @@ static void rt711_shutdown(struct snd_pcm_substream *substream, } static int rt711_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); @@ -926,7 +936,7 @@ static int rt711_pcm_hw_params(struct snd_pcm_substream *substream, port_config.num = port; retval = sdw_stream_add_slave(rt711->slave, &stream_config, - &port_config, 1, stream->sdw_stream); + &port_config, 1, stream->sdw_stream); if (retval) { dev_err(dai->dev, "Unable to configure port\n"); return retval; @@ -970,7 +980,7 @@ static int rt711_pcm_hw_params(struct snd_pcm_substream *substream, } static int rt711_pcm_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); @@ -1084,7 +1094,7 @@ static void rt711_calibration_work(struct work_struct *work) } int rt711_init(struct device *dev, struct regmap *sdw_regmap, - struct regmap *regmap, struct sdw_slave *slave) + struct regmap *regmap, struct sdw_slave *slave) { struct rt711_priv *rt711; int ret; @@ -1106,9 +1116,9 @@ int rt711_init(struct device *dev, struct regmap *sdw_regmap, rt711->first_init = false; ret = devm_snd_soc_register_component(dev, - &soc_codec_dev_rt711, - rt711_dai, - ARRAY_SIZE(rt711_dai)); + &soc_codec_dev_rt711, + rt711_dai, + ARRAY_SIZE(rt711_dai)); dev_dbg(&slave->dev, "%s\n", __func__); From 551a1ddd1747f92aa5c10b53adb71c6d9dd96e56 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 23 Oct 2019 13:44:08 +0800 Subject: [PATCH 033/110] ASoC: rt700: changes 32bits address mapping of index defined registers to 24bits It takes too many times to dump the registers when using 32 bits mapping. We change it to 24 bits to map the Realtek index defined registers. And this patch fixes some coding style. Signed-off-by: Shuming Fan --- sound/soc/codecs/rt700-sdw.c | 127 ++++++++++++++++++++--------------- sound/soc/codecs/rt700-sdw.h | 10 +-- sound/soc/codecs/rt700.c | 114 ++++++++++++++++--------------- 3 files changed, 137 insertions(+), 114 deletions(-) diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 2ec16a5b9015aa..30977b9c3c08d1 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -38,14 +38,14 @@ static bool rt700_readable_register(struct device *dev, unsigned int reg) case 0x8300 ... 0x83ff: case 0x9c00 ... 0x9cff: case 0xb900 ... 0xb9ff: - case 0x7520001a: - case 0x75200045: - case 0x75200046: - case 0x75200048: - case 0x7520004a: - case 0x7520006b: - case 0x75200080: - case 0x75200081: + case 0x75201a: + case 0x752045: + case 0x752046: + case 0x752048: + case 0x75204a: + case 0x75206b: + case 0x752080: + case 0x752081: return true; default: return false; @@ -72,10 +72,10 @@ static bool rt700_volatile_register(struct device *dev, unsigned int reg) case 0x9c00 ... 0x9cff: case 0xb900 ... 0xb9ff: case 0xff01: - case 0x7520001a: - case 0x75200046: - case 0x75200080: - case 0x75200081: + case 0x75201a: + case 0x752046: + case 0x752080: + case 0x752081: return true; default: return false; @@ -97,20 +97,21 @@ static int rt700_sdw_read(void *context, unsigned int reg, unsigned int *val) mask = reg & 0xf000; if (is_index_reg) { /* index registers */ - val2 = reg & 0xffff; - reg = reg >> 16; + val2 = reg & 0xff; + reg = reg >> 8; nid = reg & 0xff; - ret = regmap_write(rt700->sdw_regmap, reg, ((val2 >> 8) & 0xff)); + ret = regmap_write(rt700->sdw_regmap, reg, 0); if (ret < 0) return ret; reg2 = reg + 0x1000; reg2 |= 0x80; - ret = regmap_write(rt700->sdw_regmap, reg2, (val2 & 0xff)); + ret = regmap_write(rt700->sdw_regmap, reg2, val2); if (ret < 0) return ret; reg3 = RT700_PRIV_DATA_R_H | nid; - ret = regmap_write(rt700->sdw_regmap, reg3, ((*val >> 8) & 0xff)); + ret = regmap_write(rt700->sdw_regmap, + reg3, ((*val >> 8) & 0xff)); if (ret < 0) return ret; reg4 = reg3 + 0x1000; @@ -126,7 +127,8 @@ static int rt700_sdw_read(void *context, unsigned int reg, unsigned int *val) } else if (mask == 0x7000) { reg += 0x2000; reg |= 0x800; - ret = regmap_write(rt700->sdw_regmap, reg, ((*val >> 8) & 0xff)); + ret = regmap_write(rt700->sdw_regmap, + reg, ((*val >> 8) & 0xff)); if (ret < 0) return ret; reg2 = reg + 0x1000; @@ -137,14 +139,16 @@ static int rt700_sdw_read(void *context, unsigned int reg, unsigned int *val) } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ reg2 = reg - 0x1000; reg2 &= ~0x80; - ret = regmap_write(rt700->sdw_regmap, reg2, ((*val >> 8) & 0xff)); + ret = regmap_write(rt700->sdw_regmap, + reg2, ((*val >> 8) & 0xff)); if (ret < 0) return ret; ret = regmap_write(rt700->sdw_regmap, reg, (*val & 0xff)); if (ret < 0) return ret; } else if (mask == 0x9000) { - ret = regmap_write(rt700->sdw_regmap, reg, ((*val >> 8) & 0xff)); + ret = regmap_write(rt700->sdw_regmap, + reg, ((*val >> 8) & 0xff)); if (ret < 0) return ret; reg2 = reg + 0x1000; @@ -168,29 +172,35 @@ static int rt700_sdw_read(void *context, unsigned int reg, unsigned int *val) sdw_data_2 = 0; sdw_data_1 = 0; sdw_data_0 = 0; - ret = regmap_read(rt700->sdw_regmap, RT700_READ_HDA_3, &sdw_data_3); + ret = regmap_read(rt700->sdw_regmap, + RT700_READ_HDA_3, &sdw_data_3); if (ret < 0) return ret; - ret = regmap_read(rt700->sdw_regmap, RT700_READ_HDA_2, &sdw_data_2); + ret = regmap_read(rt700->sdw_regmap, + RT700_READ_HDA_2, &sdw_data_2); if (ret < 0) return ret; - ret = regmap_read(rt700->sdw_regmap, RT700_READ_HDA_1, &sdw_data_1); + ret = regmap_read(rt700->sdw_regmap, + RT700_READ_HDA_1, &sdw_data_1); if (ret < 0) return ret; - ret = regmap_read(rt700->sdw_regmap, RT700_READ_HDA_0, &sdw_data_0); + ret = regmap_read(rt700->sdw_regmap, + RT700_READ_HDA_0, &sdw_data_0); if (ret < 0) return ret; - *val = ((sdw_data_3 & 0xff) << 24) | ((sdw_data_2 & 0xff) << 16) | - ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); + *val = ((sdw_data_3 & 0xff) << 24) | + ((sdw_data_2 & 0xff) << 16) | + ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); } if (is_hda_reg == 0) dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val); else if (is_index_reg) - dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n", __func__, - reg, reg2, reg3, reg4, *val); + dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n", + __func__, reg, reg2, reg3, reg4, *val); else - dev_dbg(dev, "[%s] %04x %04x => %08x\n", __func__, reg, reg2, *val); + dev_dbg(dev, "[%s] %04x %04x => %08x\n", + __func__, reg, reg2, *val); return 0; } @@ -209,20 +219,21 @@ static int rt700_sdw_write(void *context, unsigned int reg, unsigned int val) mask = reg & 0xf000; if (is_index_reg) { /* index registers */ - val2 = reg & 0xffff; - reg = reg >> 16; + val2 = reg & 0xff; + reg = reg >> 8; nid = reg & 0xff; - ret = regmap_write(rt700->sdw_regmap, reg, ((val2 >> 8) & 0xff)); + ret = regmap_write(rt700->sdw_regmap, reg, 0); if (ret < 0) return ret; reg2 = reg + 0x1000; reg2 |= 0x80; - ret = regmap_write(rt700->sdw_regmap, reg2, (val2 & 0xff)); + ret = regmap_write(rt700->sdw_regmap, reg2, val2); if (ret < 0) return ret; reg3 = RT700_PRIV_DATA_W_H | nid; - ret = regmap_write(rt700->sdw_regmap, reg3, ((val >> 8) & 0xff)); + ret = regmap_write(rt700->sdw_regmap, + reg3, ((val >> 8) & 0xff)); if (ret < 0) return ret; reg4 = reg3 + 0x1000; @@ -240,7 +251,8 @@ static int rt700_sdw_write(void *context, unsigned int reg, unsigned int val) if (ret < 0) return ret; } else if (mask == 0x7000) { - ret = regmap_write(rt700->sdw_regmap, reg, ((val >> 8) & 0xff)); + ret = regmap_write(rt700->sdw_regmap, + reg, ((val >> 8) & 0xff)); if (ret < 0) return ret; reg2 = reg + 0x1000; @@ -251,7 +263,8 @@ static int rt700_sdw_write(void *context, unsigned int reg, unsigned int val) } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ reg2 = reg - 0x1000; reg2 &= ~0x80; - ret = regmap_write(rt700->sdw_regmap, reg2, ((val >> 8) & 0xff)); + ret = regmap_write(rt700->sdw_regmap, + reg2, ((val >> 8) & 0xff)); if (ret < 0) return ret; ret = regmap_write(rt700->sdw_regmap, reg, (val & 0xff)); @@ -262,20 +275,21 @@ static int rt700_sdw_write(void *context, unsigned int reg, unsigned int val) if (reg2 == 0) dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val); else if (is_index_reg) - dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n", __func__, - reg, reg2, reg3, reg4, val2, val); + dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n", + __func__, reg, reg2, reg3, reg4, val2, val); else - dev_dbg(dev, "[%s] %04x %04x <= %04x\n", __func__, reg, reg2, val); + dev_dbg(dev, "[%s] %04x %04x <= %04x\n", + __func__, reg, reg2, val); return 0; } static const struct regmap_config rt700_regmap = { - .reg_bits = 32, + .reg_bits = 24, .val_bits = 32, .readable_reg = rt700_readable_register, /* Readable registers */ .volatile_reg = rt700_volatile_register, /* volatile register */ - .max_register = 0x75580000, /* Maximum number of register */ + .max_register = 0x755800, /* Maximum number of register */ .reg_defaults = rt700_reg_defaults, /* Defaults */ .num_reg_defaults = ARRAY_SIZE(rt700_reg_defaults), .cache_type = REGCACHE_RBTREE, @@ -297,7 +311,7 @@ static const struct regmap_config rt700_sdw_regmap = { }; static int rt700_update_status(struct sdw_slave *slave, - enum sdw_slave_status status) + enum sdw_slave_status status) { struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); @@ -329,14 +343,14 @@ static int rt700_read_prop(struct sdw_slave *slave) prop->paging_support = false; /* first we need to allocate memory for set bits in port lists */ - prop->source_ports = 0x14; /* BITMAP: 00010100 */ - prop->sink_ports = 0xA; /* BITMAP: 00001010 */ + prop->source_ports = 0x14; /* BITMAP: 00010100 */ + prop->sink_ports = 0xA; /* BITMAP: 00001010 */ nval = hweight32(prop->source_ports); num_of_ports += nval; prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, - sizeof(*prop->src_dpn_prop), - GFP_KERNEL); + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); if (!prop->src_dpn_prop) return -ENOMEM; @@ -355,8 +369,8 @@ static int rt700_read_prop(struct sdw_slave *slave) nval = hweight32(prop->sink_ports); num_of_ports += nval; prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, - sizeof(*prop->sink_dpn_prop), - GFP_KERNEL); + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); if (!prop->sink_dpn_prop) return -ENOMEM; @@ -373,8 +387,8 @@ static int rt700_read_prop(struct sdw_slave *slave) /* Allocate port_ready based on num_of_ports */ slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports, - sizeof(*slave->port_ready), - GFP_KERNEL); + sizeof(*slave->port_ready), + GFP_KERNEL); if (!slave->port_ready) return -ENOMEM; @@ -389,7 +403,7 @@ static int rt700_read_prop(struct sdw_slave *slave) } static int rt700_bus_config(struct sdw_slave *slave, - struct sdw_bus_params *params) + struct sdw_bus_params *params) { struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); int ret; @@ -404,7 +418,7 @@ static int rt700_bus_config(struct sdw_slave *slave, } static int rt700_interrupt_callback(struct sdw_slave *slave, - struct sdw_slave_intr_status *status) + struct sdw_slave_intr_status *status) { struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); @@ -431,7 +445,7 @@ static struct sdw_slave_ops rt700_slave_ops = { }; static int rt700_sdw_probe(struct sdw_slave *slave, - const struct sdw_device_id *id) + const struct sdw_device_id *id) { struct regmap *sdw_regmap, *regmap; @@ -443,7 +457,8 @@ static int rt700_sdw_probe(struct sdw_slave *slave, if (!sdw_regmap) return -EINVAL; - regmap = devm_regmap_init(&slave->dev, NULL, &slave->dev, &rt700_regmap); + regmap = devm_regmap_init(&slave->dev, NULL, + &slave->dev, &rt700_regmap); if (!regmap) return -EINVAL; @@ -494,7 +509,7 @@ static int rt700_dev_resume(struct device *dev) return 0; time = wait_for_completion_timeout(&slave->enumeration_complete, - msecs_to_jiffies(RT700_PROBE_TIMEOUT)); + msecs_to_jiffies(RT700_PROBE_TIMEOUT)); if (!time) { dev_err(&slave->dev, "Enumeration not complete, timed out\n"); return -ETIMEDOUT; @@ -502,7 +517,7 @@ static int rt700_dev_resume(struct device *dev) regcache_cache_only(rt700->regmap, false); regcache_sync_region(rt700->regmap, 0x3000, 0x8fff); - regcache_sync_region(rt700->regmap, 0x75200010, 0x7520006b); + regcache_sync_region(rt700->regmap, 0x752010, 0x75206b); return 0; } diff --git a/sound/soc/codecs/rt700-sdw.h b/sound/soc/codecs/rt700-sdw.h index 2f5301b0bd8dff..4ad0dcfd16fdb7 100644 --- a/sound/soc/codecs/rt700-sdw.h +++ b/sound/soc/codecs/rt700-sdw.h @@ -325,11 +325,11 @@ static const struct reg_default rt700_reg_defaults[] = { { 0x8393, 0x0000 }, { 0x7319, 0x0000 }, { 0x8399, 0x0000 }, - { 0x7520001a, 0x8003 }, - { 0x75200045, 0x5289 }, - { 0x75200048, 0xd049 }, - { 0x7520004a, 0xa83b }, - { 0x7520006b, 0x5064 }, + { 0x75201a, 0x8003 }, + { 0x752045, 0x5289 }, + { 0x752048, 0xd049 }, + { 0x75204a, 0xa83b }, + { 0x75206b, 0x5064 }, }; #endif /* __RT700_H__ */ diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 2bfa9b180e431d..d110e4526409a9 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -38,28 +38,30 @@ #include "rt700.h" static int rt700_index_write(struct regmap *regmap, - unsigned int reg, unsigned int value) + unsigned int reg, unsigned int value) { int ret; - unsigned int addr = (RT700_PRIV_INDEX_W_H << 16) | reg; + unsigned int addr = (RT700_PRIV_INDEX_W_H << 8) | reg; ret = regmap_write(regmap, addr, value); if (ret < 0) - pr_err("Failed to set private value: %08x <= %04x %d\n", ret, addr, value); + pr_err("Failed to set private value: %06x <= %04x ret=%d\n", + addr, value, ret); return ret; } static int rt700_index_read(struct regmap *regmap, - unsigned int reg, unsigned int *value) + unsigned int reg, unsigned int *value) { int ret; - unsigned int addr = (RT700_PRIV_INDEX_W_H << 16) | reg; + unsigned int addr = (RT700_PRIV_INDEX_W_H << 8) | reg; *value = 0; ret = regmap_read(regmap, addr, value); if (ret < 0) - pr_err("Failed to get private value: %08x => %04x %d\n", ret, addr, *value); + pr_err("Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); return ret; } @@ -115,7 +117,7 @@ static int rt700_headset_detect(struct rt700_priv *rt700) int ret; ret = rt700_index_read(rt700->regmap, - RT700_COMBO_JACK_AUTO_CTL2, &buf); + RT700_COMBO_JACK_AUTO_CTL2, &buf); if (ret < 0) goto io_error; @@ -328,8 +330,8 @@ static int rt700_set_jack_detect(struct snd_soc_component *component, } static void rt700_get_gain(struct rt700_priv *rt700, unsigned int addr_h, - unsigned int addr_l, unsigned int val_h, - unsigned int *r_val, unsigned int *l_val) + unsigned int addr_l, unsigned int val_h, + unsigned int *r_val, unsigned int *l_val) { /* R Channel */ *r_val = (val_h << 8); @@ -343,7 +345,7 @@ static void rt700_get_gain(struct rt700_priv *rt700, unsigned int addr_h, /* For Verb-Set Amplifier Gain (Verb ID = 3h) */ static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_context *dapm = @@ -384,7 +386,7 @@ static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) regmap_write(rt700->regmap, - RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); /* R Channel */ if (mc->invert) { @@ -407,16 +409,20 @@ static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, if (val_ll == val_lr) { /* Set both L/R channels at the same time */ val_h = (1 << mc->shift) | (3 << 4); - regmap_write(rt700->regmap, addr_h, (val_h << 8 | val_ll)); - regmap_write(rt700->regmap, addr_l, (val_h << 8 | val_ll)); + regmap_write(rt700->regmap, + addr_h, (val_h << 8 | val_ll)); + regmap_write(rt700->regmap, + addr_l, (val_h << 8 | val_ll)); } else { /* Lch*/ val_h = (1 << mc->shift) | (1 << 5); - regmap_write(rt700->regmap, addr_h, (val_h << 8 | val_ll)); + regmap_write(rt700->regmap, + addr_h, (val_h << 8 | val_ll)); /* Rch */ val_h = (1 << mc->shift) | (1 << 4); - regmap_write(rt700->regmap, addr_l, (val_h << 8 | val_lr)); + regmap_write(rt700->regmap, + addr_l, (val_h << 8 | val_lr)); } /* check result */ if (mc->shift == RT700_DIR_OUT_SFT) /* output */ @@ -425,19 +431,19 @@ static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, val_h = 0x0; rt700_get_gain(rt700, addr_h, addr_l, val_h, - &read_rl, &read_ll); + &read_rl, &read_ll); if (read_rl == val_lr && read_ll == val_ll) break; } if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) regmap_write(rt700->regmap, - RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); return 0; } static int rt700_set_amp_gain_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); @@ -475,32 +481,34 @@ static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); static const struct snd_kcontrol_new rt700_snd_controls[] = { - SOC_DOUBLE_R_EXT_TLV("DAC Front Playback Volume", RT700_SET_GAIN_DAC1_H, - RT700_SET_GAIN_DAC1_L, RT700_DIR_OUT_SFT, 0x57, 0, - rt700_set_amp_gain_get, rt700_set_amp_gain_put, - out_vol_tlv), - SOC_DOUBLE_R_EXT("ADC 08 Capture Switch", RT700_SET_GAIN_ADC2_H, - RT700_SET_GAIN_ADC2_L, RT700_DIR_IN_SFT, 1, 1, - rt700_set_amp_gain_get, rt700_set_amp_gain_put), - SOC_DOUBLE_R_EXT("ADC 09 Capture Switch", RT700_SET_GAIN_ADC1_H, - RT700_SET_GAIN_ADC1_L, RT700_DIR_IN_SFT, 1, 1, - rt700_set_amp_gain_get, rt700_set_amp_gain_put), - SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume", RT700_SET_GAIN_ADC2_H, - RT700_SET_GAIN_ADC2_L, RT700_DIR_IN_SFT, 0x3f, 0, - rt700_set_amp_gain_get, rt700_set_amp_gain_put, - in_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume", RT700_SET_GAIN_ADC1_H, - RT700_SET_GAIN_ADC1_L, RT700_DIR_IN_SFT, 0x3f, 0, - rt700_set_amp_gain_get, rt700_set_amp_gain_put, - in_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("AMIC Volume", RT700_SET_GAIN_AMIC_H, - RT700_SET_GAIN_AMIC_L, RT700_DIR_IN_SFT, 3, 0, - rt700_set_amp_gain_get, rt700_set_amp_gain_put, - mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DAC Front Playback Volume", + RT700_SET_GAIN_DAC1_H, RT700_SET_GAIN_DAC1_L, + RT700_DIR_OUT_SFT, 0x57, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, out_vol_tlv), + SOC_DOUBLE_R_EXT("ADC 08 Capture Switch", + RT700_SET_GAIN_ADC2_H, RT700_SET_GAIN_ADC2_L, + RT700_DIR_IN_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT("ADC 09 Capture Switch", + RT700_SET_GAIN_ADC1_H, RT700_SET_GAIN_ADC1_L, + RT700_DIR_IN_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume", + RT700_SET_GAIN_ADC2_H, RT700_SET_GAIN_ADC2_L, + RT700_DIR_IN_SFT, 0x3f, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume", + RT700_SET_GAIN_ADC1_H, RT700_SET_GAIN_ADC1_L, + RT700_DIR_IN_SFT, 0x3f, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("AMIC Volume", + RT700_SET_GAIN_AMIC_H, RT700_SET_GAIN_AMIC_L, + RT700_DIR_IN_SFT, 3, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, mic_vol_tlv), }; static int rt700_mux_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); @@ -529,7 +537,7 @@ static int rt700_mux_get(struct snd_kcontrol *kcontrol, } static int rt700_mux_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); @@ -572,7 +580,7 @@ static int rt700_mux_put(struct snd_kcontrol *kcontrol, } snd_soc_dapm_mux_update_power(dapm, kcontrol, - item[0], e, NULL); + item[0], e, NULL); return change; } @@ -608,7 +616,7 @@ static SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new rt700_hp_mux = SOC_DAPM_ENUM_EXT("HP Mux", rt700_hp_enum, - rt700_mux_get, rt700_mux_put); + rt700_mux_get, rt700_mux_put); static int rt700_dac_front_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -867,7 +875,7 @@ static int rt700_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, } static void rt700_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { struct sdw_stream_data *stream; @@ -877,8 +885,8 @@ static void rt700_shutdown(struct snd_pcm_substream *substream, } static int rt700_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); @@ -929,7 +937,7 @@ static int rt700_pcm_hw_params(struct snd_pcm_substream *substream, port_config.num = port; retval = sdw_stream_add_slave(rt700->slave, &stream_config, - &port_config, 1, stream->sdw_stream); + &port_config, 1, stream->sdw_stream); if (retval) { dev_err(dai->dev, "Unable to configure port\n"); return retval; @@ -972,7 +980,7 @@ static int rt700_pcm_hw_params(struct snd_pcm_substream *substream, } static int rt700_pcm_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); @@ -1085,7 +1093,7 @@ int rt700_clock_config(struct device *dev) } int rt700_init(struct device *dev, struct regmap *sdw_regmap, - struct regmap *regmap, struct sdw_slave *slave) + struct regmap *regmap, struct sdw_slave *slave) { struct rt700_priv *rt700; @@ -1108,9 +1116,9 @@ int rt700_init(struct device *dev, struct regmap *sdw_regmap, rt700->first_init = false; ret = devm_snd_soc_register_component(dev, - &soc_codec_dev_rt700, - rt700_dai, - ARRAY_SIZE(rt700_dai)); + &soc_codec_dev_rt700, + rt700_dai, + ARRAY_SIZE(rt700_dai)); dev_dbg(&slave->dev, "%s\n", __func__); From d885c092c95eaf2acca0e96c5dcb2785a26409c4 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 23 Oct 2019 13:44:23 +0800 Subject: [PATCH 034/110] ASoC: rt1308-sdw: fix some coding style Signed-off-by: Shuming Fan --- sound/soc/codecs/rt1308-sdw.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index 5730f59ae08022..fac0fa20843eac 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -129,15 +129,15 @@ static int rt1308_read_prop(struct sdw_slave *slave) prop->paging_support = true; /* first we need to allocate memory for set bits in port lists */ - prop->source_ports = 0x00; /* BITMAP: 00010100 (not enable yet) */ - prop->sink_ports = 0x2; /* BITMAP: 00000010 */ + prop->source_ports = 0x00; /* BITMAP: 00010100 (not enable yet) */ + prop->sink_ports = 0x2; /* BITMAP: 00000010 */ /* for sink */ nval = hweight32(prop->sink_ports); num_of_ports += nval; prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, - sizeof(*prop->sink_dpn_prop), - GFP_KERNEL); + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); if (!prop->sink_dpn_prop) return -ENOMEM; @@ -154,8 +154,8 @@ static int rt1308_read_prop(struct sdw_slave *slave) /* Allocate port_ready based on num_of_ports */ slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports, - sizeof(*slave->port_ready), - GFP_KERNEL); + sizeof(*slave->port_ready), + GFP_KERNEL); if (!slave->port_ready) return -ENOMEM; @@ -258,7 +258,7 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) } static int rt1308_update_status(struct sdw_slave *slave, - enum sdw_slave_status status) + enum sdw_slave_status status) { struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev); @@ -280,7 +280,7 @@ static int rt1308_update_status(struct sdw_slave *slave, } static int rt1308_bus_config(struct sdw_slave *slave, - struct sdw_bus_params *params) + struct sdw_bus_params *params) { struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev); int ret; @@ -295,7 +295,7 @@ static int rt1308_bus_config(struct sdw_slave *slave, } static int rt1308_interrupt_callback(struct sdw_slave *slave, - struct sdw_slave_intr_status *status) + struct sdw_slave_intr_status *status) { dev_dbg(&slave->dev, "%s control_port_stat=%x", __func__, status->control_port); @@ -469,7 +469,7 @@ static int rt1308_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, } static void rt1308_sdw_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { struct sdw_stream_data *stream; @@ -528,7 +528,7 @@ static int rt1308_sdw_hw_params(struct snd_pcm_substream *substream, } static int rt1308_sdw_pcm_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct rt1308_sdw_priv *rt1308 = @@ -590,7 +590,7 @@ static struct snd_soc_dai_driver rt1308_sdw_dai[] = { }; static int rt1308_sdw_init(struct device *dev, struct regmap *regmap, - struct sdw_slave *slave) + struct sdw_slave *slave) { struct rt1308_sdw_priv *rt1308; int ret; @@ -611,9 +611,9 @@ static int rt1308_sdw_init(struct device *dev, struct regmap *regmap, rt1308->first_init = false; ret = devm_snd_soc_register_component(dev, - &soc_component_sdw_rt1308, - rt1308_sdw_dai, - ARRAY_SIZE(rt1308_sdw_dai)); + &soc_component_sdw_rt1308, + rt1308_sdw_dai, + ARRAY_SIZE(rt1308_sdw_dai)); dev_dbg(&slave->dev, "%s\n", __func__); @@ -621,7 +621,7 @@ static int rt1308_sdw_init(struct device *dev, struct regmap *regmap, } static int rt1308_sdw_probe(struct sdw_slave *slave, - const struct sdw_device_id *id) + const struct sdw_device_id *id) { struct regmap *regmap; @@ -668,7 +668,7 @@ static int rt1308_dev_resume(struct device *dev) return 0; time = wait_for_completion_timeout(&slave->enumeration_complete, - msecs_to_jiffies(RT1308_PROBE_TIMEOUT)); + msecs_to_jiffies(RT1308_PROBE_TIMEOUT)); if (!time) { dev_err(&slave->dev, "Enumeration not complete, timed out\n"); return -ETIMEDOUT; From e76945dcfdd90c0647d907accf844b1a72cfe18d Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Thu, 24 Oct 2019 13:29:54 +0800 Subject: [PATCH 035/110] Resume correct register setting for dmic recording after suspend. Signed-off-by: Jack Yu --- sound/soc/codecs/rt715-sdw.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index 58f5e31131385d..66e7d09ef263c7 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -74,7 +74,6 @@ static bool rt715_volatile_register(struct device *dev, unsigned int reg) case 0x2220 ... 0x2223: /* decoded HD-A */ case 0x9c00 ... 0x9cff: case 0xb900 ... 0xb9ff: - case 0x75200039: return true; default: return false; From 07d8a39d5d07d03f5c3ce51ed791e9a60d1215c8 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 24 Oct 2019 20:50:50 +0800 Subject: [PATCH 036/110] ASoC: rt711: add JD2 configuration In dell project, the JD source changes to JD2. Therefore, the codec driver add the JD2 configuration in default. The application circuit also changes to JD2 as JD source. Signed-off-by: Shuming Fan --- sound/soc/codecs/rt711-sdw.c | 2 + sound/soc/codecs/rt711-sdw.h | 2 + sound/soc/codecs/rt711.c | 76 +++++++++++++++++++++++++++--------- sound/soc/codecs/rt711.h | 19 ++++++++- 4 files changed, 80 insertions(+), 19 deletions(-) diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index 404a4b6ad4a904..5223bc17d00ca4 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -37,6 +37,8 @@ static bool rt711_readable_register(struct device *dev, unsigned int reg) case 0x8300 ... 0x83ff: case 0x9c00 ... 0x9cff: case 0xb900 ... 0xb9ff: + case 0x752009: + case 0x752011: case 0x75201a: case 0x752045: case 0x752046: diff --git a/sound/soc/codecs/rt711-sdw.h b/sound/soc/codecs/rt711-sdw.h index 10cd6e2cc063a1..43b2b984b29cb9 100644 --- a/sound/soc/codecs/rt711-sdw.h +++ b/sound/soc/codecs/rt711-sdw.h @@ -267,6 +267,8 @@ static const struct reg_default rt711_reg_defaults[] = { { 0x8393, 0x00 }, { 0x7319, 0x00 }, { 0x8399, 0x00 }, + { 0x752009, 0x1029 }, + { 0x752011, 0x007a }, { 0x75201a, 0x8003 }, { 0x752045, 0x5289 }, { 0x752048, 0xd049 }, diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index 11fab034e9735c..96f9fe21d9a469 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -51,7 +51,7 @@ static int rt711_index_write(struct regmap *regmap, return ret; } -static unsigned int rt711_index_read(struct regmap *regmap, +static int rt711_index_read(struct regmap *regmap, unsigned int nid, unsigned int reg, unsigned int *value) { int ret; @@ -66,14 +66,28 @@ static unsigned int rt711_index_read(struct regmap *regmap, return ret; } -static void rt711_reset(struct regmap *regmap) +static int rt711_index_update_bits(struct regmap *regmap, unsigned int nid, + unsigned int reg, unsigned int mask, unsigned int val) { - unsigned int val; + unsigned int tmp, orig; + int ret; + + ret = rt711_index_read(regmap, nid, reg, &orig); + if (ret < 0) + return ret; + + tmp = orig & ~mask; + tmp |= val & mask; + return rt711_index_write(regmap, nid, reg, tmp); +} + +static void rt711_reset(struct regmap *regmap) +{ regmap_write(regmap, RT711_FUNC_RESET, 0); - rt711_index_read(regmap, RT711_VENDOR_REG, RT711_PARA_VERB_CTL, &val); - rt711_index_write(regmap, RT711_VENDOR_REG, - RT711_PARA_VERB_CTL, (val | RT711_HIDDEN_REG_SW_RESET)); + rt711_index_update_bits(regmap, RT711_VENDOR_REG, + RT711_PARA_VERB_CTL, RT711_HIDDEN_REG_SW_RESET, + RT711_HIDDEN_REG_SW_RESET); } static int rt711_calibration(struct rt711_priv *rt711) @@ -90,16 +104,13 @@ static int rt711_calibration(struct rt711_priv *rt711) dev = regmap_get_device(regmap); /* Calibration manual mode */ - rt711_index_read(regmap, RT711_VENDOR_REG, RT711_FSM_CTL, &val); - val &= 0xfffffff0; - rt711_index_write(regmap, RT711_VENDOR_REG, RT711_FSM_CTL, val); + rt711_index_update_bits(regmap, RT711_VENDOR_REG, RT711_FSM_CTL, + 0xf, 0x0); /* trigger */ - rt711_index_read(regmap, RT711_VENDOR_CALI, - RT711_DAC_DC_CALI_CTL1, &val); - val |= RT711_DAC_DC_CALI_TRIGGER; - rt711_index_write(regmap, RT711_VENDOR_CALI, - RT711_DAC_DC_CALI_CTL1, val); + rt711_index_update_bits(regmap, RT711_VENDOR_CALI, + RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER, + RT711_DAC_DC_CALI_TRIGGER); /* wait for calibration process */ rt711_index_read(regmap, RT711_VENDOR_CALI, @@ -120,10 +131,8 @@ static int rt711_calibration(struct rt711_priv *rt711) } /* depop mode */ - rt711_index_read(regmap, RT711_VENDOR_REG, RT711_FSM_CTL, &val); - val &= 0xfffffff0; - val |= RT711_DEPOP_CTL; - rt711_index_write(regmap, RT711_VENDOR_REG, RT711_FSM_CTL, val); + rt711_index_update_bits(regmap, RT711_VENDOR_REG, + RT711_FSM_CTL, 0xf, RT711_DEPOP_CTL); regmap_write(rt711->regmap, RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); @@ -363,6 +372,25 @@ static void rt711_jack_init(struct rt711_priv *rt711) rt711_index_write(rt711->regmap, RT711_VENDOR_REG, 0x19, 0x2e11); + switch (rt711->jd_src) { + case RT711_JD1: + /* default settings was already for JD1 */ + break; + case RT711_JD2: + rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG, + RT711_JD_CTL2, RT711_JD2_2PORT_200K_DECODE_HP | + RT711_HP_JD_SEL_JD2, + RT711_JD2_2PORT_200K_DECODE_HP | + RT711_HP_JD_SEL_JD2); + rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG, + RT711_CC_DET1, RT711_HP_JD_FINAL_RESULT_CTL_JD12, + RT711_HP_JD_FINAL_RESULT_CTL_JD12); + break; + default: + dev_warn(rt711->component->dev, "Wrong JD source\n"); + break; + } + dev_dbg(&rt711->slave->dev, "in %s enable\n", __func__); mod_delayed_work(system_power_efficient_wq, @@ -1093,6 +1121,14 @@ static void rt711_calibration_work(struct work_struct *work) rt711_calibration(rt711); } +static int rt711_parse_dt(struct rt711_priv *rt711, struct device *dev) +{ + device_property_read_u32(dev, "realtek,jd-src", + &rt711->jd_src); + + return 0; +} + int rt711_init(struct device *dev, struct regmap *sdw_regmap, struct regmap *regmap, struct sdw_slave *slave) { @@ -1115,6 +1151,10 @@ int rt711_init(struct device *dev, struct regmap *sdw_regmap, rt711->hw_init = false; rt711->first_init = false; + /* JD source uses JD2 in default */ + rt711->jd_src = RT711_JD2; + rt711_parse_dt(rt711, &slave->dev); + ret = devm_snd_soc_register_component(dev, &soc_codec_dev_rt711, rt711_dai, diff --git a/sound/soc/codecs/rt711.h b/sound/soc/codecs/rt711.h index 795803db7acd13..ac7509bfa32d8b 100644 --- a/sound/soc/codecs/rt711.h +++ b/sound/soc/codecs/rt711.h @@ -26,7 +26,7 @@ struct rt711_priv { struct delayed_work jack_btn_check_work; struct work_struct calibration_work; struct mutex calibrate_mutex; /* for headset calibration */ - int jack_type; + int jack_type, jd_src; }; struct sdw_stream_data { @@ -54,6 +54,8 @@ struct sdw_stream_data { /* Index (NID:20h) */ #define RT711_DAC_DC_CALI_CTL1 0x00 +#define RT711_JD_CTL2 0x09 +#define RT711_CC_DET1 0x11 #define RT711_PARA_VERB_CTL 0x1a #define RT711_COMBO_JACK_AUTO_CTL1 0x45 #define RT711_COMBO_JACK_AUTO_CTL2 0x46 @@ -171,6 +173,15 @@ struct sdw_stream_data { /* DAC DC offset calibration control-1 (0x00)(NID:20h) */ #define RT711_DAC_DC_CALI_TRIGGER (0x1 << 15) +/* jack detect control 2 (0x09)(NID:20h) */ +#define RT711_JD2_2PORT_200K_DECODE_HP (0x1 << 13) +#define RT711_HP_JD_SEL_JD1 (0x0 << 1) +#define RT711_HP_JD_SEL_JD2 (0x1 << 1) + +/* CC DET1 (0x11)(NID:20h) */ +#define RT711_HP_JD_FINAL_RESULT_CTL_JD12 (0x1 << 10) +#define RT711_HP_JD_FINAL_RESULT_CTL_CCDET (0x0 << 10) + /* Parameter & Verb control (0x1a)(NID:20h) */ #define RT711_HIDDEN_REG_SW_RESET (0x1 << 14) @@ -203,6 +214,12 @@ enum { RT711_AIFS, }; +enum rt711_jd_src { + RT711_JD_NULL, + RT711_JD1, + RT711_JD2 +}; + int rt711_io_init(struct device *dev, struct sdw_slave *slave); int rt711_init(struct device *dev, struct regmap *sdw_regmap, struct regmap *regmap, struct sdw_slave *slave); From 81f964ddd2269de5da83e993e3e46db47750487c Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Fri, 25 Oct 2019 10:16:19 +0800 Subject: [PATCH 037/110] ASoC: codec:rt715-sdw:Modify register mapping of index from 32bits to 24bits. Signed-off-by: Jack Yu --- sound/soc/codecs/rt715-sdw.c | 94 ++++++++++++---------- sound/soc/codecs/rt715-sdw.h | 2 +- sound/soc/codecs/rt715.c | 151 +++++++++++++++++++---------------- sound/soc/codecs/rt715.h | 16 ++++ 4 files changed, 154 insertions(+), 109 deletions(-) diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index 66e7d09ef263c7..c9b1328365abe0 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -45,7 +45,7 @@ static bool rt715_readable_register(struct device *dev, unsigned int reg) case 0x8300 ... 0x83ff: case 0x9c00 ... 0x9cff: case 0xb900 ... 0xb9ff: - case 0x75200039: + case 0x752039: return true; default: return false; @@ -95,20 +95,21 @@ static int rt715_sdw_read(void *context, unsigned int reg, unsigned int *val) mask = reg & 0xf000; if (is_index_reg) { /* index registers */ - val2 = reg & 0xffff; - reg = reg >> 16; + val2 = reg & 0xff; + reg = reg >> 8; nid = reg & 0xff; - ret = regmap_write(rt715->sdw_regmap, reg, ((val2 >> 8) & 0xff)); + ret = regmap_write(rt715->sdw_regmap, reg, 0); if (ret < 0) return ret; reg2 = reg + 0x1000; reg2 |= 0x80; - ret = regmap_write(rt715->sdw_regmap, reg2, (val2 & 0xff)); + ret = regmap_write(rt715->sdw_regmap, reg2, val2); if (ret < 0) return ret; reg3 = RT715_PRIV_DATA_R_H | nid; - ret = regmap_write(rt715->sdw_regmap, reg3, ((*val >> 8) & 0xff)); + ret = regmap_write(rt715->sdw_regmap, reg3, + ((*val >> 8) & 0xff)); if (ret < 0) return ret; reg4 = reg3 + 0x1000; @@ -124,7 +125,8 @@ static int rt715_sdw_read(void *context, unsigned int reg, unsigned int *val) } else if (mask == 0x7000) { reg += 0x2000; reg |= 0x800; - ret = regmap_write(rt715->sdw_regmap, reg, ((*val >> 8) & 0xff)); + ret = regmap_write(rt715->sdw_regmap, reg, + ((*val >> 8) & 0xff)); if (ret < 0) return ret; reg2 = reg + 0x1000; @@ -135,14 +137,16 @@ static int rt715_sdw_read(void *context, unsigned int reg, unsigned int *val) } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ reg2 = reg - 0x1000; reg2 &= ~0x80; - ret = regmap_write(rt715->sdw_regmap, reg2, ((*val >> 8) & 0xff)); + ret = regmap_write(rt715->sdw_regmap, reg2, + ((*val >> 8) & 0xff)); if (ret < 0) return ret; ret = regmap_write(rt715->sdw_regmap, reg, (*val & 0xff)); if (ret < 0) return ret; } else if (mask == 0x9000) { - ret = regmap_write(rt715->sdw_regmap, reg, ((*val >> 8) & 0xff)); + ret = regmap_write(rt715->sdw_regmap, reg, + ((*val >> 8) & 0xff)); if (ret < 0) return ret; reg2 = reg + 0x1000; @@ -166,20 +170,25 @@ static int rt715_sdw_read(void *context, unsigned int reg, unsigned int *val) sdw_data_2 = 0; sdw_data_1 = 0; sdw_data_0 = 0; - ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_3, &sdw_data_3); + ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_3, + &sdw_data_3); if (ret < 0) return ret; - ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_2, &sdw_data_2); + ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_2, + &sdw_data_2); if (ret < 0) return ret; - ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_1, &sdw_data_1); + ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_1, + &sdw_data_1); if (ret < 0) return ret; - ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_0, &sdw_data_0); + ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_0, + &sdw_data_0); if (ret < 0) return ret; - *val = ((sdw_data_3 & 0xff) << 24) | ((sdw_data_2 & 0xff) << 16) | - ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); + *val = ((sdw_data_3 & 0xff) << 24) | + ((sdw_data_2 & 0xff) << 16) | + ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); } if (is_hda_reg == 0) @@ -188,7 +197,8 @@ static int rt715_sdw_read(void *context, unsigned int reg, unsigned int *val) dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n", __func__, reg, reg2, reg3, reg4, *val); else - dev_dbg(dev, "[%s] %04x %04x => %08x\n", __func__, reg, reg2, *val); + dev_dbg(dev, "[%s] %04x %04x => %08x\n", + __func__, reg, reg2, *val); return 0; } @@ -207,20 +217,21 @@ static int rt715_sdw_write(void *context, unsigned int reg, unsigned int val) mask = reg & 0xf000; if (is_index_reg) { /* index registers */ - val2 = reg & 0xffff; - reg = reg >> 16; + val2 = reg & 0xff; + reg = reg >> 8; nid = reg & 0xff; - ret = regmap_write(rt715->sdw_regmap, reg, ((val2 >> 8) & 0xff)); + ret = regmap_write(rt715->sdw_regmap, reg, 0); if (ret < 0) return ret; reg2 = reg + 0x1000; reg2 |= 0x80; - ret = regmap_write(rt715->sdw_regmap, reg2, (val2 & 0xff)); + ret = regmap_write(rt715->sdw_regmap, reg2, val2); if (ret < 0) return ret; reg3 = RT715_PRIV_DATA_W_H | nid; - ret = regmap_write(rt715->sdw_regmap, reg3, ((val >> 8) & 0xff)); + ret = regmap_write(rt715->sdw_regmap, reg3, + ((val >> 8) & 0xff)); if (ret < 0) return ret; reg4 = reg3 + 0x1000; @@ -238,7 +249,8 @@ static int rt715_sdw_write(void *context, unsigned int reg, unsigned int val) if (ret < 0) return ret; } else if (mask == 0x7000) { - ret = regmap_write(rt715->sdw_regmap, reg, ((val >> 8) & 0xff)); + ret = regmap_write(rt715->sdw_regmap, reg, + ((val >> 8) & 0xff)); if (ret < 0) return ret; reg2 = reg + 0x1000; @@ -249,7 +261,8 @@ static int rt715_sdw_write(void *context, unsigned int reg, unsigned int val) } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ reg2 = reg - 0x1000; reg2 &= ~0x80; - ret = regmap_write(rt715->sdw_regmap, reg2, ((val >> 8) & 0xff)); + ret = regmap_write(rt715->sdw_regmap, reg2, + ((val >> 8) & 0xff)); if (ret < 0) return ret; ret = regmap_write(rt715->sdw_regmap, reg, (val & 0xff)); @@ -260,20 +273,21 @@ static int rt715_sdw_write(void *context, unsigned int reg, unsigned int val) if (reg2 == 0) dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val); else if (is_index_reg) - dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n", __func__, - reg, reg2, reg3, reg4, val2, val); + dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n", + __func__, reg, reg2, reg3, reg4, val2, val); else - dev_dbg(dev, "[%s] %04x %04x <= %04x\n", __func__, reg, reg2, val); + dev_dbg(dev, "[%s] %04x %04x <= %04x\n", + __func__, reg, reg2, val); return 0; } static const struct regmap_config rt715_regmap = { - .reg_bits = 32, + .reg_bits = 24, .val_bits = 32, .readable_reg = rt715_readable_register, /* Readable registers */ .volatile_reg = rt715_volatile_register, /* volatile register */ - .max_register = 0x75200039, /* Maximum number of register */ + .max_register = 0x752039, /* Maximum number of register */ .reg_defaults = rt715_reg_defaults, /* Defaults */ .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults), .cache_type = REGCACHE_RBTREE, @@ -287,7 +301,6 @@ static const struct regmap_config rt715_sdw_regmap = { .name = "sdw", .reg_bits = 32, /* Total register space for SDW */ .val_bits = 8, /* Total number of bits in register */ - .readable_reg = rt715_readable_register, /* Readable registers */ .max_register = 0xff01, /* Maximum number of register */ .cache_type = REGCACHE_NONE, .use_single_read = true, @@ -336,7 +349,7 @@ int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload, EXPORT_SYMBOL(hda_to_sdw); static int rt715_update_status(struct sdw_slave *slave, - enum sdw_slave_status status) + enum sdw_slave_status status) { struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev); @@ -364,14 +377,14 @@ static int rt715_read_prop(struct sdw_slave *slave) prop->paging_support = false; /* first we need to allocate memory for set bits in port lists */ - prop->source_ports = 0x50; /* BITMAP: 01010000 */ + prop->source_ports = 0x50;/* BITMAP: 01010000 */ prop->sink_ports = 0x0; /* BITMAP: 00000000 */ nval = hweight32(prop->source_ports); num_of_ports += nval; prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, - sizeof(*prop->src_dpn_prop), - GFP_KERNEL); + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); if (!prop->src_dpn_prop) return -ENOMEM; @@ -389,8 +402,8 @@ static int rt715_read_prop(struct sdw_slave *slave) nval = hweight32(prop->sink_ports); num_of_ports += nval; prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, - sizeof(*prop->sink_dpn_prop), - GFP_KERNEL); + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); if (!prop->sink_dpn_prop) return -ENOMEM; @@ -406,8 +419,8 @@ static int rt715_read_prop(struct sdw_slave *slave) /* Allocate port_ready based on num_of_ports */ slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports, - sizeof(*slave->port_ready), - GFP_KERNEL); + sizeof(*slave->port_ready), + GFP_KERNEL); if (!slave->port_ready) return -ENOMEM; @@ -422,7 +435,7 @@ static int rt715_read_prop(struct sdw_slave *slave) } static int rt715_bus_config(struct sdw_slave *slave, - struct sdw_bus_params *params) + struct sdw_bus_params *params) { struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev); int ret; @@ -455,7 +468,8 @@ static int rt715_sdw_probe(struct sdw_slave *slave, if (!sdw_regmap) return -EINVAL; - regmap = devm_regmap_init(&slave->dev, NULL, &slave->dev, &rt715_regmap); + regmap = devm_regmap_init(&slave->dev, NULL, &slave->dev, + &rt715_regmap); if (!regmap) return -EINVAL; @@ -502,7 +516,7 @@ static int rt715_dev_resume(struct device *dev) regcache_cache_only(rt715->regmap, false); regcache_sync_region(rt715->regmap, 0x3000, 0x8fff); - regcache_sync_region(rt715->regmap, 0x75200039, 0x75200039); + regcache_sync_region(rt715->regmap, 0x752039, 0x752039); return 0; } diff --git a/sound/soc/codecs/rt715-sdw.h b/sound/soc/codecs/rt715-sdw.h index 1834127ecfcaab..0bc0efada86296 100644 --- a/sound/soc/codecs/rt715-sdw.h +++ b/sound/soc/codecs/rt715-sdw.h @@ -270,7 +270,7 @@ static const struct reg_default rt715_reg_defaults[] = { { 0x8389, 0x97 }, { 0x7327, 0x97 }, { 0x83a7, 0x97 }, - { 0x75200039, 0xa500 }, + { 0x752039, 0xa500 }, }; #endif /* __RT715_H__ */ diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index 0ac8868012d83d..63e9465a0e9a52 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -42,7 +42,7 @@ static int rt715_index_write(struct regmap *regmap, unsigned int reg, unsigned int value) { int ret; - unsigned int addr = ((RT715_PRIV_INDEX_W_H) << 16) | reg; + unsigned int addr = ((RT715_PRIV_INDEX_W_H) << 8) | reg; ret = regmap_write(regmap, addr, value); if (ret < 0) { @@ -57,7 +57,7 @@ static unsigned int rt715_index_read(struct regmap *regmap, unsigned int reg, unsigned int *value) { int ret; - unsigned int addr = ((RT715_PRIV_INDEX_W_H) << 16) | reg; + unsigned int addr = ((RT715_PRIV_INDEX_W_H) << 8) | reg; *value = 0; ret = regmap_read(regmap, addr, value); @@ -70,8 +70,8 @@ static unsigned int rt715_index_read(struct regmap *regmap, unsigned int reg, } static void rt715_get_gain(struct rt715_priv *rt715, unsigned int addr_h, - unsigned int addr_l, unsigned int val_h, - unsigned int *r_val, unsigned int *l_val) + unsigned int addr_l, unsigned int val_h, + unsigned int *r_val, unsigned int *l_val) { /* R Channel */ *r_val = (val_h << 8); @@ -85,7 +85,7 @@ static void rt715_get_gain(struct rt715_priv *rt715, unsigned int addr_h, /* For Verb-Set Amplifier Gain (Verb ID = 3h) */ static int rt715_set_amp_gain_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_context *dapm = @@ -148,16 +148,19 @@ static int rt715_set_amp_gain_put(struct snd_kcontrol *kcontrol, if (val_ll == val_lr) { /* Set both L/R channels at the same time */ val_h = (1 << mc->shift) | (3 << 4); - regmap_write(rt715->regmap, addr_h, (val_h << 8 | val_ll)); - regmap_write(rt715->regmap, addr_l, (val_h << 8 | val_ll)); + regmap_write(rt715->regmap, addr_h, + (val_h << 8 | val_ll)); + regmap_write(rt715->regmap, addr_l, + (val_h << 8 | val_ll)); } else { /* Lch*/ val_h = (1 << mc->shift) | (1 << 5); - regmap_write(rt715->regmap, addr_h, (val_h << 8 | val_ll)); - + regmap_write(rt715->regmap, addr_h, + (val_h << 8 | val_ll)); /* Rch */ val_h = (1 << mc->shift) | (1 << 4); - regmap_write(rt715->regmap, addr_l, (val_h << 8 | val_lr)); + regmap_write(rt715->regmap, addr_l, + (val_h << 8 | val_lr)); } /* check result */ if (mc->shift == RT715_DIR_OUT_SFT) /* output */ @@ -173,7 +176,7 @@ static int rt715_set_amp_gain_put(struct snd_kcontrol *kcontrol, /* D0:power on state, D3: power saving mode */ if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) regmap_write(rt715->regmap, - RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3); return 0; } @@ -226,71 +229,71 @@ static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); static const struct snd_kcontrol_new rt715_snd_controls[] = { /* Capture switch */ SOC_DOUBLE_R_EXT("ADC 07 Capture Switch", RT715_SET_GAIN_MIC_ADC_H, - RT715_SET_GAIN_MIC_ADC_L, RT715_DIR_IN_SFT, 1, 1, - rt715_set_amp_gain_get, rt715_set_amp_gain_put), + RT715_SET_GAIN_MIC_ADC_L, RT715_DIR_IN_SFT, 1, 1, + rt715_set_amp_gain_get, rt715_set_amp_gain_put), SOC_DOUBLE_R_EXT("ADC 08 Capture Switch", RT715_SET_GAIN_LINE_ADC_H, - RT715_SET_GAIN_LINE_ADC_L, RT715_DIR_IN_SFT, 1, 1, - rt715_set_amp_gain_get, rt715_set_amp_gain_put), + RT715_SET_GAIN_LINE_ADC_L, RT715_DIR_IN_SFT, 1, 1, + rt715_set_amp_gain_get, rt715_set_amp_gain_put), SOC_DOUBLE_R_EXT("ADC 09 Capture Switch", RT715_SET_GAIN_MIX_ADC_H, - RT715_SET_GAIN_MIX_ADC_L, RT715_DIR_IN_SFT, 1, 1, - rt715_set_amp_gain_get, rt715_set_amp_gain_put), + RT715_SET_GAIN_MIX_ADC_L, RT715_DIR_IN_SFT, 1, 1, + rt715_set_amp_gain_get, rt715_set_amp_gain_put), SOC_DOUBLE_R_EXT("ADC 27 Capture Switch", RT715_SET_GAIN_MIX_ADC2_H, - RT715_SET_GAIN_MIX_ADC2_L, RT715_DIR_IN_SFT, 1, 1, - rt715_set_amp_gain_get, rt715_set_amp_gain_put), + RT715_SET_GAIN_MIX_ADC2_L, RT715_DIR_IN_SFT, 1, 1, + rt715_set_amp_gain_get, rt715_set_amp_gain_put), /* Volume Control */ SOC_DOUBLE_R_EXT_TLV("ADC 07 Capture Volume", RT715_SET_GAIN_MIC_ADC_H, - RT715_SET_GAIN_MIC_ADC_L, RT715_DIR_IN_SFT, 0x3f, 0, - rt715_set_amp_gain_get, rt715_set_amp_gain_put, - in_vol_tlv), + RT715_SET_GAIN_MIC_ADC_L, RT715_DIR_IN_SFT, 0x3f, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + in_vol_tlv), SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume", RT715_SET_GAIN_LINE_ADC_H, - RT715_SET_GAIN_LINE_ADC_L, RT715_DIR_IN_SFT, 0x3f, 0, - rt715_set_amp_gain_get, rt715_set_amp_gain_put, - in_vol_tlv), + RT715_SET_GAIN_LINE_ADC_L, RT715_DIR_IN_SFT, 0x3f, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + in_vol_tlv), SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume", RT715_SET_GAIN_MIX_ADC_H, - RT715_SET_GAIN_MIX_ADC_L, RT715_DIR_IN_SFT, 0x3f, 0, - rt715_set_amp_gain_get, rt715_set_amp_gain_put, - in_vol_tlv), + RT715_SET_GAIN_MIX_ADC_L, RT715_DIR_IN_SFT, 0x3f, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + in_vol_tlv), SOC_DOUBLE_R_EXT_TLV("ADC 27 Capture Volume", RT715_SET_GAIN_MIX_ADC2_H, - RT715_SET_GAIN_MIX_ADC2_L, RT715_DIR_IN_SFT, 0x3f, 0, - rt715_set_amp_gain_get, rt715_set_amp_gain_put, - in_vol_tlv), + RT715_SET_GAIN_MIX_ADC2_L, RT715_DIR_IN_SFT, 0x3f, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + in_vol_tlv), /* MIC Boost Control */ SOC_DOUBLE_R_EXT_TLV("DMIC1 Boost", RT715_SET_GAIN_DMIC1_H, - RT715_SET_GAIN_DMIC1_L, RT715_DIR_IN_SFT, 3, 0, - rt715_set_amp_gain_get, rt715_set_amp_gain_put, - mic_vol_tlv), + RT715_SET_GAIN_DMIC1_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), SOC_DOUBLE_R_EXT_TLV("DMIC2 Boost", RT715_SET_GAIN_DMIC2_H, - RT715_SET_GAIN_DMIC2_L, RT715_DIR_IN_SFT, 3, 0, - rt715_set_amp_gain_get, rt715_set_amp_gain_put, - mic_vol_tlv), + RT715_SET_GAIN_DMIC2_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), SOC_DOUBLE_R_EXT_TLV("DMIC3 Boost", RT715_SET_GAIN_DMIC3_H, - RT715_SET_GAIN_DMIC3_L, RT715_DIR_IN_SFT, 3, 0, - rt715_set_amp_gain_get, rt715_set_amp_gain_put, - mic_vol_tlv), + RT715_SET_GAIN_DMIC3_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), SOC_DOUBLE_R_EXT_TLV("DMIC4 Boost", RT715_SET_GAIN_DMIC4_H, - RT715_SET_GAIN_DMIC4_L, RT715_DIR_IN_SFT, 3, 0, - rt715_set_amp_gain_get, rt715_set_amp_gain_put, - mic_vol_tlv), + RT715_SET_GAIN_DMIC4_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), SOC_DOUBLE_R_EXT_TLV("MIC1 Boost", RT715_SET_GAIN_MIC1_H, - RT715_SET_GAIN_MIC1_L, RT715_DIR_IN_SFT, 3, 0, - rt715_set_amp_gain_get, rt715_set_amp_gain_put, - mic_vol_tlv), + RT715_SET_GAIN_MIC1_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), SOC_DOUBLE_R_EXT_TLV("MIC2 Boost", RT715_SET_GAIN_MIC2_H, - RT715_SET_GAIN_MIC2_L, RT715_DIR_IN_SFT, 3, 0, - rt715_set_amp_gain_get, rt715_set_amp_gain_put, - mic_vol_tlv), + RT715_SET_GAIN_MIC2_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), SOC_DOUBLE_R_EXT_TLV("LINE1 Boost", RT715_SET_GAIN_LINE1_H, - RT715_SET_GAIN_LINE1_L, RT715_DIR_IN_SFT, 3, 0, - rt715_set_amp_gain_get, rt715_set_amp_gain_put, - mic_vol_tlv), + RT715_SET_GAIN_LINE1_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), SOC_DOUBLE_R_EXT_TLV("LINE2 Boost", RT715_SET_GAIN_LINE2_H, - RT715_SET_GAIN_LINE2_L, RT715_DIR_IN_SFT, 3, 0, - rt715_set_amp_gain_get, rt715_set_amp_gain_put, - mic_vol_tlv), + RT715_SET_GAIN_LINE2_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), }; static int rt715_mux_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); @@ -302,7 +305,8 @@ static int rt715_mux_get(struct snd_kcontrol *kcontrol, reg = RT715_VERB_SET_CONNECT_SEL | e->reg; ret = regmap_read(rt715->regmap, reg, &val); if (ret < 0) { - dev_err(component->dev, "%s: sdw read failed: %d\n", __func__, ret); + dev_err(component->dev, "%s: sdw read failed: %d\n", + __func__, ret); return ret; } @@ -319,7 +323,7 @@ static int rt715_mux_get(struct snd_kcontrol *kcontrol, } static int rt715_mux_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); @@ -339,7 +343,8 @@ static int rt715_mux_put(struct snd_kcontrol *kcontrol, reg = RT715_VERB_SET_CONNECT_SEL | e->reg; ret = regmap_read(rt715->regmap, reg, &val2); if (ret < 0) { - dev_err(component->dev, "%s: sdw read failed: %d\n", __func__, ret); + dev_err(component->dev, "%s: sdw read failed: %d\n", + __func__, ret); return ret; } @@ -354,7 +359,7 @@ static int rt715_mux_put(struct snd_kcontrol *kcontrol, } snd_soc_dapm_mux_update_power(dapm, kcontrol, - item[0], e, NULL); + item[0], e, NULL); return change; } @@ -551,7 +556,7 @@ static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, } static void rt715_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { struct sdw_stream_data *stream; @@ -562,8 +567,8 @@ static void rt715_shutdown(struct snd_pcm_substream *substream, } static int rt715_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); @@ -608,7 +613,7 @@ static int rt715_pcm_hw_params(struct snd_pcm_substream *substream, port_config.num = port; retval = sdw_stream_add_slave(rt715->slave, &stream_config, - &port_config, 1, stream->sdw_stream); + &port_config, 1, stream->sdw_stream); if (retval) { dev_err(dai->dev, "Unable to configure port\n"); return retval; @@ -667,7 +672,7 @@ static int rt715_pcm_hw_params(struct snd_pcm_substream *substream, } static int rt715_pcm_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); @@ -786,9 +791,9 @@ int rt715_init(struct device *dev, struct regmap *sdw_regmap, rt715->first_init = false; ret = devm_snd_soc_register_component(dev, - &soc_codec_dev_rt715, - rt715_dai, - ARRAY_SIZE(rt715_dai)); + &soc_codec_dev_rt715, + rt715_dai, + ARRAY_SIZE(rt715_dai)); return ret; } @@ -831,6 +836,8 @@ int rt715_io_init(struct device *dev, struct sdw_slave *slave) /* Set Pin Widget */ regmap_write(rt715->regmap, RT715_SET_PIN_DMIC1, 0x20); regmap_write(rt715->regmap, RT715_SET_PIN_DMIC2, 0x20); + regmap_write(rt715->regmap, RT715_SET_PIN_DMIC3, 0x20); + regmap_write(rt715->regmap, RT715_SET_PIN_DMIC4, 0x20); /* Set Converter Stream */ regmap_write(rt715->regmap, RT715_SET_STREAMID_LINE_ADC, 0x10); regmap_write(rt715->regmap, RT715_SET_STREAMID_MIX_ADC, 0x10); @@ -845,6 +852,14 @@ int rt715_io_init(struct device *dev, struct sdw_slave *slave) regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT2, 0x11); regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT3, 0xa1); regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT4, 0x81); + regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT1, 0xd0); + regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT2, 0x11); + regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT3, 0xa1); + regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT4, 0x81); + regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT1, 0xd1); + regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT2, 0x11); + regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT3, 0xa1); + regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT4, 0x81); /* Finish Initial Settings, set power to D3 */ regmap_write(rt715->regmap, RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3); diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h index 846d6a0116d9d0..52d24cfb581d1b 100644 --- a/sound/soc/codecs/rt715.h +++ b/sound/soc/codecs/rt715.h @@ -183,6 +183,22 @@ struct sdw_stream_data { (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC1) #define RT715_SET_DMIC2_CONFIG_DEFAULT4\ (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC2) +#define RT715_SET_DMIC3_CONFIG_DEFAULT1\ + (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC3) +#define RT715_SET_DMIC4_CONFIG_DEFAULT1\ + (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC4) +#define RT715_SET_DMIC3_CONFIG_DEFAULT2\ + (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC3) +#define RT715_SET_DMIC4_CONFIG_DEFAULT2\ + (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC4) +#define RT715_SET_DMIC3_CONFIG_DEFAULT3\ + (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC3) +#define RT715_SET_DMIC4_CONFIG_DEFAULT3\ + (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC4) +#define RT715_SET_DMIC3_CONFIG_DEFAULT4\ + (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC3) +#define RT715_SET_DMIC4_CONFIG_DEFAULT4\ + (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC4) #define RT715_MUTE_SFT 7 #define RT715_DIR_IN_SFT 6 From e828fe38d01543eba9a8251ed0ea4c84a188a3a3 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 28 Oct 2019 17:26:53 +0800 Subject: [PATCH 038/110] ASoC: rt700: fix pop noise while stopping playback Signed-off-by: Shuming Fan --- sound/soc/codecs/rt700.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index d110e4526409a9..73911494711f14 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -717,6 +717,7 @@ static int rt700_hpo_mux_event(struct snd_soc_dapm_widget *w, val_l = (1 << RT700_MUTE_SFT); regmap_write(rt700->regmap, RT700_SET_GAIN_HP_H, (val_h << 8 | val_l)); + usleep_range(50000, 55000); break; } return 0; From f54f8e0b39727ccdd3830fde93b6b0b86bfeb167 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 28 Oct 2019 17:27:06 +0800 Subject: [PATCH 039/110] ASoC: rt711: fix pop noise while stopping playback Signed-off-by: Shuming Fan --- sound/soc/codecs/rt711.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index 96f9fe21d9a469..19bd118ae6964d 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -383,7 +383,8 @@ static void rt711_jack_init(struct rt711_priv *rt711) RT711_JD2_2PORT_200K_DECODE_HP | RT711_HP_JD_SEL_JD2); rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG, - RT711_CC_DET1, RT711_HP_JD_FINAL_RESULT_CTL_JD12, + RT711_CC_DET1, + RT711_HP_JD_FINAL_RESULT_CTL_JD12, RT711_HP_JD_FINAL_RESULT_CTL_JD12); break; default: @@ -741,12 +742,13 @@ static int rt711_dac_surround_event(struct snd_soc_dapm_widget *w, RT711_SET_GAIN_HP_H, (val_h << 8 | val_l)); break; case SND_SOC_DAPM_PRE_PMD: - regmap_write(rt711->regmap, - RT711_SET_STREAMID_DAC2, 0x00); - val_l = (1 << RT711_MUTE_SFT); regmap_write(rt711->regmap, RT711_SET_GAIN_HP_H, (val_h << 8 | val_l)); + usleep_range(50000, 55000); + + regmap_write(rt711->regmap, + RT711_SET_STREAMID_DAC2, 0x00); break; } return 0; From 1e7791631f78800e7df71cb600c70184ae9b7443 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 28 Oct 2019 17:27:21 +0800 Subject: [PATCH 040/110] ASoC: rt1308-sdw: fix DC offset loading from EFUSE and increase DAC volume Fix the PVDD DC calibration parameters doesn't be loaded correctly. The DAC volume gain adjusts to the 0xe7 value which HW AE suggests. Signed-off-by: Shuming Fan --- sound/soc/codecs/rt1308-sdw.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index fac0fa20843eac..96afc7aa8291d9 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -175,6 +175,8 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) { struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); int ret = 0; + unsigned int efuse_m_btl_l, efuse_m_btl_r, tmp; + unsigned int efuse_c_btl_l, efuse_c_btl_r; if (rt1308->hw_init) return 0; @@ -209,7 +211,7 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) /* read efuse */ regmap_write(rt1308->regmap, 0xc360, 0x01); - regmap_write(rt1308->regmap, 0xc360, 0x80); + regmap_write(rt1308->regmap, 0xc361, 0x80); regmap_write(rt1308->regmap, 0xc7f0, 0x04); regmap_write(rt1308->regmap, 0xc7f1, 0xfe); msleep(100); @@ -217,6 +219,27 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) msleep(20); regmap_write(rt1308->regmap, 0xc240, 0x10); + regmap_read(rt1308->regmap, 0xc861, &tmp); + efuse_m_btl_l = tmp; + regmap_read(rt1308->regmap, 0xc860, &tmp); + efuse_m_btl_l = efuse_m_btl_l | (tmp << 8); + regmap_read(rt1308->regmap, 0xc863, &tmp); + efuse_c_btl_l = tmp; + regmap_read(rt1308->regmap, 0xc862, &tmp); + efuse_c_btl_l = efuse_c_btl_l | (tmp << 8); + regmap_read(rt1308->regmap, 0xc871, &tmp); + efuse_m_btl_r = tmp; + regmap_read(rt1308->regmap, 0xc870, &tmp); + efuse_m_btl_r = efuse_m_btl_r | (tmp << 8); + regmap_read(rt1308->regmap, 0xc873, &tmp); + efuse_c_btl_r = tmp; + regmap_read(rt1308->regmap, 0xc872, &tmp); + efuse_c_btl_r = efuse_c_btl_r | (tmp << 8); + dev_info(&slave->dev, "%s m_btl_l=0x%x, m_btl_r=0x%x\n", __func__, + efuse_m_btl_l, efuse_m_btl_r); + dev_info(&slave->dev, "%s c_btl_l=0x%x, c_btl_r=0x%x\n", __func__, + efuse_c_btl_l, efuse_c_btl_r); + /* initial settings */ regmap_write(rt1308->regmap, 0xc103, 0xc0); regmap_write(rt1308->regmap, 0xc030, 0x17); @@ -241,8 +264,8 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) regmap_write(rt1308->regmap, 0xc0a1, 0x71); regmap_write(rt1308->regmap, 0xc210, 0x00); regmap_write(rt1308->regmap, 0xc070, 0x00); - regmap_write(rt1308->regmap, 0xc100, 0xaf); - regmap_write(rt1308->regmap, 0xc101, 0xaf); + regmap_write(rt1308->regmap, 0xc100, 0xe7); + regmap_write(rt1308->regmap, 0xc101, 0xe7); regmap_write(rt1308->regmap, 0xc310, 0x24); /* Mark Slave initialization complete */ From 5579fbd3aa32cfeeb5262fb3c106a9ea75ac0670 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Tue, 29 Oct 2019 11:28:03 +0800 Subject: [PATCH 041/110] ASoC: codec:rt715-sdw: Modify some ret values regarding to warning and add return handle for regmap_read. Signed-off-by: Jack Yu --- sound/soc/codecs/rt715.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index 63e9465a0e9a52..40e006b0fc6138 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -73,14 +73,19 @@ static void rt715_get_gain(struct rt715_priv *rt715, unsigned int addr_h, unsigned int addr_l, unsigned int val_h, unsigned int *r_val, unsigned int *l_val) { + int ret; /* R Channel */ *r_val = (val_h << 8); - regmap_read(rt715->regmap, addr_l, r_val); + ret = regmap_read(rt715->regmap, addr_l, r_val); + if (ret < 0) + pr_err("Failed to get R channel gain.\n"); /* L Channel */ val_h |= 0x20; *l_val = (val_h << 8); - regmap_read(rt715->regmap, addr_h, l_val); + ret = regmap_read(rt715->regmap, addr_h, l_val); + if (ret < 0) + pr_err("Failed to get L channel gain.\n"); } /* For Verb-Set Amplifier Gain (Verb ID = 3h) */ @@ -299,7 +304,8 @@ static int rt715_mux_get(struct snd_kcontrol *kcontrol, snd_soc_dapm_kcontrol_component(kcontrol); struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int reg, val, ret; + unsigned int reg, val; + int ret; /* nid = e->reg, vid = 0xf01 */ reg = RT715_VERB_SET_CONNECT_SEL | e->reg; @@ -332,7 +338,8 @@ static int rt715_mux_put(struct snd_kcontrol *kcontrol, struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int *item = ucontrol->value.enumerated.item; - unsigned int val, val2 = 0, change, reg, ret; + unsigned int val, val2 = 0, change, reg; + int ret; if (item[0] >= e->items) return -EINVAL; From 1cc00e5a4a474c197abc9ba5ec7ceff21246495f Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 30 Oct 2019 18:34:28 +0800 Subject: [PATCH 042/110] ASoC: rt1308-sdw: output gain enhancement The HW engineer changes HV_Gain voltage setting to enhance the output gain. And he also adjusts the DAC volume gain. Signed-off-by: Shuming Fan --- sound/soc/codecs/rt1308-sdw.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index 96afc7aa8291d9..266a3d1076547a 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -264,9 +264,9 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) regmap_write(rt1308->regmap, 0xc0a1, 0x71); regmap_write(rt1308->regmap, 0xc210, 0x00); regmap_write(rt1308->regmap, 0xc070, 0x00); - regmap_write(rt1308->regmap, 0xc100, 0xe7); - regmap_write(rt1308->regmap, 0xc101, 0xe7); - regmap_write(rt1308->regmap, 0xc310, 0x24); + regmap_write(rt1308->regmap, 0xc100, 0xd7); + regmap_write(rt1308->regmap, 0xc101, 0xd7); + regmap_write(rt1308->regmap, 0xc300, 0x09); /* Mark Slave initialization complete */ rt1308->hw_init = true; From 66b736cfd0b84fff993f1dd0a1e8e5bde43d6fa7 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 4 Nov 2019 14:10:01 +0800 Subject: [PATCH 043/110] ASoC: rt711: move rt711_parse_dt to rt711_probe Intel add device property on machine driver. So we can't parse device before machine driver is probed. Signed-off-by: Bard Liao --- sound/soc/codecs/rt711.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index 19bd118ae6964d..7190642534397d 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -869,10 +869,19 @@ static int rt711_set_bias_level(struct snd_soc_component *component, return 0; } +static int rt711_parse_dt(struct rt711_priv *rt711, struct device *dev) +{ + device_property_read_u32(dev, "realtek,jd-src", + &rt711->jd_src); + + return 0; +} + static int rt711_probe(struct snd_soc_component *component) { struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + rt711_parse_dt(rt711, &rt711->slave->dev); rt711->component = component; return 0; @@ -1123,14 +1132,6 @@ static void rt711_calibration_work(struct work_struct *work) rt711_calibration(rt711); } -static int rt711_parse_dt(struct rt711_priv *rt711, struct device *dev) -{ - device_property_read_u32(dev, "realtek,jd-src", - &rt711->jd_src); - - return 0; -} - int rt711_init(struct device *dev, struct regmap *sdw_regmap, struct regmap *regmap, struct sdw_slave *slave) { @@ -1155,7 +1156,6 @@ int rt711_init(struct device *dev, struct regmap *sdw_regmap, /* JD source uses JD2 in default */ rt711->jd_src = RT711_JD2; - rt711_parse_dt(rt711, &slave->dev); ret = devm_snd_soc_register_component(dev, &soc_codec_dev_rt711, From aad24cf23627150fc1de0d23ccb69cdf8b879b09 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 4 Nov 2019 12:56:51 -0600 Subject: [PATCH 044/110] ASoC: codecs: rt1308-sdw.c: wait for initialization_complete Make sure the resume steps are delayed until all settings have been restored after the device becomes enumerated and .update_status is called. Signed-off-by: Pierre-Louis Bossart --- sound/soc/codecs/rt1308-sdw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index 266a3d1076547a..ba6350366b24b4 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -690,10 +690,10 @@ static int rt1308_dev_resume(struct device *dev) if (!rt1308->hw_init) return 0; - time = wait_for_completion_timeout(&slave->enumeration_complete, + time = wait_for_completion_timeout(&slave->initialization_complete, msecs_to_jiffies(RT1308_PROBE_TIMEOUT)); if (!time) { - dev_err(&slave->dev, "Enumeration not complete, timed out\n"); + dev_err(&slave->dev, "Initialization not complete, timed out\n"); return -ETIMEDOUT; } From f3ac2fda6651106f3cfb0e86fd870e1f8b595908 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 4 Nov 2019 12:56:52 -0600 Subject: [PATCH 045/110] ASoC: codecs: rt700-sdw.c: wait for initialization_complete Make sure the resume steps are delayed until all settings have been restored after the device becomes enumerated and .update_status is called. Signed-off-by: Pierre-Louis Bossart --- sound/soc/codecs/rt700-sdw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 30977b9c3c08d1..5388b14be5f050 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -508,10 +508,10 @@ static int rt700_dev_resume(struct device *dev) if (!rt700->hw_init) return 0; - time = wait_for_completion_timeout(&slave->enumeration_complete, + time = wait_for_completion_timeout(&slave->initialization_complete, msecs_to_jiffies(RT700_PROBE_TIMEOUT)); if (!time) { - dev_err(&slave->dev, "Enumeration not complete, timed out\n"); + dev_err(&slave->dev, "Initialization not complete, timed out\n"); return -ETIMEDOUT; } From a441b9eb46e3a2e6b86c0ac24ad23b2f1feb7fef Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 4 Nov 2019 12:56:52 -0600 Subject: [PATCH 046/110] ASoC: codecs: rt711-sdw.c: wait for initialization_complete Make sure the resume steps are delayed until all settings have been restored after the device becomes enumerated and .update_status is called. Signed-off-by: Pierre-Louis Bossart --- sound/soc/codecs/rt711-sdw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index 5223bc17d00ca4..7121fe720654fd 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -508,10 +508,10 @@ static int rt711_dev_resume(struct device *dev) if (!rt711->hw_init) return 0; - time = wait_for_completion_timeout(&slave->enumeration_complete, + time = wait_for_completion_timeout(&slave->initialization_complete, msecs_to_jiffies(RT711_PROBE_TIMEOUT)); if (!time) { - dev_err(&slave->dev, "Enumeration not complete, timed out\n"); + dev_err(&slave->dev, "Initialization not complete, timed out\n"); return -ETIMEDOUT; } From 0020b7f6bc9fed1ed82deb4d4360da06e0556582 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 4 Nov 2019 12:56:53 -0600 Subject: [PATCH 047/110] ASoC: codecs: rt715-sdw.c: wait for initialization_complete Make sure the resume steps are delayed until all settings have been restored after the device becomes enumerated and .update_status is called Signed-off-by: Pierre-Louis Bossart --- sound/soc/codecs/rt715-sdw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index c9b1328365abe0..8ec9ef09f6c679 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -507,10 +507,10 @@ static int rt715_dev_resume(struct device *dev) if (!rt715->hw_init) return 0; - time = wait_for_completion_timeout(&slave->enumeration_complete, + time = wait_for_completion_timeout(&slave->initialization_complete, msecs_to_jiffies(RT715_PROBE_TIMEOUT)); if (!time) { - dev_err(&slave->dev, "Enumeration not complete, timed out\n"); + dev_err(&slave->dev, "Initialization not complete, timed out\n"); return -ETIMEDOUT; } From 97ba5a26ac54fdb7927a1cc60d0ea570771c1b6b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Sat, 24 Aug 2019 15:52:40 -0500 Subject: [PATCH 048/110] ASoC: Intel: common: soc-acpi: declare new tables for SoundWire We cannot really lump SoundWire-based configurations into the same tables since the mechanisms to identify boards is based on link configurations and _ADR instead of _HID for I2S, so define new tables Signed-off-by: Pierre-Louis Bossart --- include/sound/soc-acpi-intel-match.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h index 20c0bee3b959db..ab6f75a86611dc 100644 --- a/include/sound/soc-acpi-intel-match.h +++ b/include/sound/soc-acpi-intel-match.h @@ -31,6 +31,12 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_ehl_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_sdw_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_sdw_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_sdw_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_sdw_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[]; + /* * generic table used for HDA codec-based platforms, possibly with * additional ACPI-enumerated codecs From 5b3ea6f1b0909e79f4ee80e1608a6362ad3b0891 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 20 Jun 2019 17:47:01 +0800 Subject: [PATCH 049/110] ASoC: Intel: common: add match tables for ICL w/ SoundWire The two configurations are with the Realtek 3-in-1 board requiring all 4 links to be enabled, or basic configuration with the on-board RT700 using link0. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- .../soc/intel/common/soc-acpi-intel-icl-match.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-icl-match.c b/sound/soc/intel/common/soc-acpi-intel-icl-match.c index 38977669b57653..63b6197a605606 100644 --- a/sound/soc/intel/common/soc-acpi-intel-icl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-icl-match.c @@ -33,5 +33,22 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[] = { }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_icl_machines); +struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_sdw_machines[] = { + { + .link_mask = 0xF, /* 4 active links required */ + .drv_name = "sdw_rt711_rt1308_rt715", + .sof_fw_filename = "sof-icl.ri", + .sof_tplg_filename = "sof-icl-rt711-rt1308-rt715.tplg", + }, + { + .link_mask = 0x1, /* rt700 connected on link0 */ + .drv_name = "sdw_rt700", + .sof_fw_filename = "sof-icl.ri", + .sof_tplg_filename = "sof-icl-rt700.tplg", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_icl_sdw_machines); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel Common ACPI Match module"); From d3d8b5570e8e81b354dc3f59eea3624a4a254942 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Mon, 22 Jul 2019 17:20:53 +0800 Subject: [PATCH 050/110] ASoC: Intel: common: add match tables for CNL/CFL/CML w/ SoundWire The two configurations are with the Realtek 3-in-1 board requiring all 4 links to be enabled, or basic configuration with the on-board RT700 using link1. For now we only have definitions for CML, CNL and CFL are just placeholders. Signed-off-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- .../intel/common/soc-acpi-intel-cfl-match.c | 5 ++++ .../intel/common/soc-acpi-intel-cml-match.c | 23 +++++++++++++++++++ .../intel/common/soc-acpi-intel-cnl-match.c | 5 ++++ 3 files changed, 33 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-cfl-match.c b/sound/soc/intel/common/soc-acpi-intel-cfl-match.c index d6fd2026d0b863..ff9d6938b9f668 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cfl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cfl-match.c @@ -14,5 +14,10 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_machines[] = { }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cfl_machines); +struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_sdw_machines[] = { + {} +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cfl_sdw_machines); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-cml-match.c b/sound/soc/intel/common/soc-acpi-intel-cml-match.c index 5d08ae0667380b..58dbd6dac460d8 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cml-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cml-match.c @@ -52,5 +52,28 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[] = { }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cml_machines); +struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_sdw_machines[] = { + { + .link_mask = 0xF, /* 4 active links required */ + .drv_name = "sdw_rt711_rt1308_rt715", + .sof_fw_filename = "sof-cml.ri", + .sof_tplg_filename = "sof-cml-rt711-rt1308-rt715.tplg", + }, + { + .link_mask = 0xB, /* 3 active links required */ + .drv_name = "sdw_rt711_rt1308_rt715", + .sof_fw_filename = "sof-cml.ri", + .sof_tplg_filename = "sof-cml-rt711-rt1308-mono-rt715.tplg", + }, + { + .link_mask = 0x2, /* RT700 connected on Link1 */ + .drv_name = "sdw_rt700", + .sof_fw_filename = "sof-cml.ri", + .sof_tplg_filename = "sof-cml-rt700.tplg", + }, + {} +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cml_sdw_machines); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c index 27588841c8b005..828980d5630d46 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -27,5 +27,10 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines); +struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_sdw_machines[] = { + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_sdw_machines); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel Common ACPI Match module"); From 74743fe9007de60c8ae26013980fd14fccec3d38 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 4 Sep 2019 10:01:58 -0500 Subject: [PATCH 051/110] ASoC: Intel: common: add match tables for TGL w/ SoundWire RT711 is in SoundWire mode on link0 and RT1308 on SSP2. Signed-off-by: Pierre-Louis Bossart --- .../soc/intel/common/soc-acpi-intel-tgl-match.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c index 57a6298d6dca31..e74f428192ccc7 100644 --- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c @@ -12,13 +12,25 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = { { .id = "10EC1308", - .drv_name = "tgl_rt1308", + .drv_name = "rt711_rt1308", + .link_mask = 0x1, /* RT711 on SoundWire link0 */ .sof_fw_filename = "sof-tgl.ri", - .sof_tplg_filename = "sof-tgl-rt1308.tplg", + .sof_tplg_filename = "sof-tgl-rt711-rt1308.tplg", }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_machines); +struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[] = { + { + .link_mask = 0x1, /* this will only enable rt711 for now */ + .drv_name = "sdw_rt711_rt1308_rt715", + .sof_fw_filename = "sof-tgl.ri", + .sof_tplg_filename = "sof-tgl-rt711-rt1308-rt715.tplg", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_sdw_machines); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel Common ACPI Match module"); From f7a6a033fccf30b325a82d45bee643cec492805f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 4 Sep 2019 10:05:06 -0500 Subject: [PATCH 052/110] ASoC: SOF: Intel: reference SoundWire machine lists Use static tables to automatically select the relevant configurations. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/sof-pci-dev.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index bbeffd932de7fd..94af36149e6914 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -103,6 +103,7 @@ static const struct sof_dev_desc tng_desc = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE) static const struct sof_dev_desc cnl_desc = { .machines = snd_soc_acpi_intel_cnl_machines, + .alt_machines = snd_soc_acpi_intel_cnl_sdw_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -121,6 +122,7 @@ static const struct sof_dev_desc cnl_desc = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE) static const struct sof_dev_desc cfl_desc = { .machines = snd_soc_acpi_intel_cfl_machines, + .alt_machines = snd_soc_acpi_intel_cfl_sdw_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -141,6 +143,7 @@ static const struct sof_dev_desc cfl_desc = { static const struct sof_dev_desc cml_desc = { .machines = snd_soc_acpi_intel_cml_machines, + .alt_machines = snd_soc_acpi_intel_cml_sdw_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -159,6 +162,7 @@ static const struct sof_dev_desc cml_desc = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE) static const struct sof_dev_desc icl_desc = { .machines = snd_soc_acpi_intel_icl_machines, + .alt_machines = snd_soc_acpi_intel_icl_sdw_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -177,6 +181,7 @@ static const struct sof_dev_desc icl_desc = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE) static const struct sof_dev_desc tgl_desc = { .machines = snd_soc_acpi_intel_tgl_machines, + .alt_machines = snd_soc_acpi_intel_tgl_sdw_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, From 6ec22a859a8360dfda453293ed10f975cf858e1b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 10 Apr 2019 20:58:52 -0500 Subject: [PATCH 053/110] ASoC: Intel: boards: add sdw_rt700 machine driver This machine driver supports CML and ICL RVP boards in SoundWire mode only. These boards need to be reworked to support the SoundWire link and the BIOS advanced config menu also needs to select SoundWire for links 0 (ICL) or 1 (CML). Unlike a lot of machine drivers, we use different DAI links for capture and playback since SoundWire PDIs can do capture OR playback, not both simultaneously. Signed-off-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 15 ++ sound/soc/intel/boards/Makefile | 3 +- sound/soc/intel/boards/sdw_rt700.c | 319 +++++++++++++++++++++++++++++ 3 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/boards/sdw_rt700.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 7815080e5e7eb1..fc2c98ef76e561 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -498,4 +498,19 @@ config SND_SOC_INTEL_TGL_RT1308_MACH endif ## SND_SOC_SOF_TIGERLAKE +if SND_SOC_SOF_INTEL_SOUNDWIRE + +config SND_SOC_INTEL_SOUNDWIRE_RT700_MACH + tristate "SoundWire with RT700 codec" + depends on SOUNDWIRE && ACPI + select SND_SOC_RT700_SDW + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI if SND_SOC_SOF_HDA_LINK + help + Add support for Intel SoundWire-based platforms connected to RT700 + on link 0 or 1 + If unsure select "N" + +endif + endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 2c2eaaf7e83e4d..f8a89f6b269feb 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -31,7 +31,7 @@ snd-soc-skl_hda_dsp-objs := skl_hda_dsp_generic.o skl_hda_dsp_common.o hda_dsp_c snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o snd-soc-tgl-rt1308-objs := tgl_rt1308.o hda_dsp_common.o - +snd-soc-sdw-rt700-objs := sdw_rt700.o hda_dsp_common.o obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -64,3 +64,4 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max9 obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o obj-$(CONFIG_SND_SOC_INTEL_TGL_RT1308_MACH) += snd-soc-tgl-rt1308.o +obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT700_MACH) += snd-soc-sdw-rt700.o diff --git a/sound/soc/intel/boards/sdw_rt700.c b/sound/soc/intel/boards/sdw_rt700.c new file mode 100644 index 00000000000000..fd46f0b18b149e --- /dev/null +++ b/sound/soc/intel/boards/sdw_rt700.c @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2016-19 Intel Corporation + +/* + * sdw_rt700 - ASOC Machine driver for Intel SoundWire platforms + * connected to ALC700 device + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/hdac_hdmi.h" +#include "hda_dsp_common.h" + +struct mc_private { + struct list_head hdmi_pcm_list; + bool common_hdmi_codec_drv; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) +static struct snd_soc_jack hdmi[3]; + +struct hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +static int hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + /* dai_link id is 1:1 mapped to the PCM device */ + pcm->device = rtd->dai_link->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +#define NAME_SIZE 32 +static int card_late_probe(struct snd_soc_card *card) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(card); + struct hdmi_pcm *pcm; + struct snd_soc_component *component = NULL; + int err, i = 0; + char jack_name[NAME_SIZE]; + + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct hdmi_pcm, + head); + component = pcm->codec_dai->component; + + if (ctx->common_hdmi_codec_drv) + return hda_dsp_hdmi_build_controls(card, component); + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &hdmi[i], + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &hdmi[i]); + if (err < 0) + return err; + + i++; + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} +#else +static int card_late_probe(struct snd_soc_card *card) +{ + return 0; +} +#endif + +static const struct snd_soc_dapm_widget widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +static const struct snd_soc_dapm_route map[] = { + /*Headphones*/ + { "Headphones", NULL, "HP" }, + { "Speaker", NULL, "SPK" }, + { "MIC2", NULL, "AMIC" }, +}; + +static const struct snd_kcontrol_new controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), + SOC_DAPM_PIN_SWITCH("AMIC"), + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +SND_SOC_DAILINK_DEF(sdw0_pin2, + DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin2"))); +SND_SOC_DAILINK_DEF(sdw0_pin3, + DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin3"))); +SND_SOC_DAILINK_DEF(sdw0_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("sdw:0:25d:700:0", "rt700-aif1"))); + +SND_SOC_DAILINK_DEF(sdw1_pin2, + DAILINK_COMP_ARRAY(COMP_CPU("SDW1 Pin2"))); +SND_SOC_DAILINK_DEF(sdw1_pin3, + DAILINK_COMP_ARRAY(COMP_CPU("SDW1 Pin3"))); +SND_SOC_DAILINK_DEF(sdw1_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("sdw:1:25d:700:0", "rt700-aif1"))); + +SND_SOC_DAILINK_DEF(dmic_pin, + DAILINK_COMP_ARRAY(COMP_CPU("DMIC01 Pin"))); + +SND_SOC_DAILINK_DEF(dmic16k_pin, + DAILINK_COMP_ARRAY(COMP_CPU("DMIC16k Pin"))); + +SND_SOC_DAILINK_DEF(dmic_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi"))); + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) +SND_SOC_DAILINK_DEF(idisp1_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp1 Pin"))); +SND_SOC_DAILINK_DEF(idisp1_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi1"))); + +SND_SOC_DAILINK_DEF(idisp2_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp2 Pin"))); +SND_SOC_DAILINK_DEF(idisp2_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi2"))); + +SND_SOC_DAILINK_DEF(idisp3_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp3 Pin"))); +SND_SOC_DAILINK_DEF(idisp3_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi3"))); +#endif + +SND_SOC_DAILINK_DEF(platform, + DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:1f.3"))); + +struct snd_soc_dai_link dailink[] = { + { + .name = "SDW0-Playback", + .id = 0, + .no_pcm = 1, + .dpcm_playback = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw0_pin2, sdw0_codec, platform), + }, + { + .name = "SDW0-Capture", + .id = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw0_pin3, sdw0_codec, platform), + }, + { + .name = "dmic01", + .id = 2, + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(dmic_pin, dmic_codec, platform), + }, + { + .name = "dmic16k", + .id = 3, + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(dmic16k_pin, dmic_codec, platform), + }, +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) + { + .name = "iDisp1", + .id = 4, + .init = hdmi_init, + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(idisp1_pin, idisp1_codec, platform), + }, + { + .name = "iDisp2", + .id = 5, + .init = hdmi_init, + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(idisp2_pin, idisp2_codec, platform), + }, + { + .name = "iDisp3", + .id = 6, + .init = hdmi_init, + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(idisp3_pin, idisp3_codec, platform), + }, +#endif +}; + +/* SoC card */ +static struct snd_soc_card card_sdw_rt700 = { + .name = "sdw-rt700", + .dai_link = dailink, + .num_links = ARRAY_SIZE(dailink), + .controls = controls, + .num_controls = ARRAY_SIZE(controls), + .dapm_widgets = widgets, + .num_dapm_widgets = ARRAY_SIZE(widgets), + .dapm_routes = map, + .num_dapm_routes = ARRAY_SIZE(map), + .late_probe = card_late_probe, +}; + +static int mc_probe(struct platform_device *pdev) +{ + struct mc_private *ctx; + struct snd_soc_acpi_mach *mach; + const char *platform_name; + struct snd_soc_card *card = &card_sdw_rt700; + const char *board; + int ret; + + dev_dbg(&pdev->dev, "Entry %s\n", __func__); + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); +#endif + + board = dmi_get_system_info(DMI_BOARD_NAME); + if (strstr(board, "CometLake U DDR4 HR")) { + dailink[0].name = "SDW1-Playback"; + dailink[0].codecs = sdw1_codec; + dailink[0].num_codecs = ARRAY_SIZE(sdw1_codec); + dailink[0].cpus = sdw1_pin2; + dailink[0].num_cpus = ARRAY_SIZE(sdw1_pin2); + + dailink[1].name = "SDW1-Capture"; + dailink[1].codecs = sdw1_codec; + dailink[1].num_codecs = ARRAY_SIZE(sdw1_codec); + dailink[1].cpus = sdw1_pin3; + dailink[1].num_cpus = ARRAY_SIZE(sdw1_pin3); + } + + card->dev = &pdev->dev; + + /* override platform name, if required */ + mach = (&pdev->dev)->platform_data; + platform_name = mach->mach_params.platform; + + ret = snd_soc_fixup_dai_links_platform_name(card, platform_name); + if (ret) + return ret; + + ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv; + + snd_soc_card_set_drvdata(card, ctx); + + /* Register the card */ + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { + dev_err(card->dev, "snd_soc_register_card failed %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, card); + + return ret; +} + +static struct platform_driver sdw_rt700_driver = { + .driver = { + .name = "sdw_rt700", + .pm = &snd_soc_pm_ops, + }, + .probe = mc_probe, +}; + +module_platform_driver(sdw_rt700_driver); + +MODULE_DESCRIPTION("ASoC SoundWire RT700 Machine driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_AUTHOR("Pierre-Louis Bossart "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sdw_rt700"); From 7da16ae0cf63c5436db1232aa5061984f83e4293 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 26 Aug 2019 15:07:50 -0500 Subject: [PATCH 054/110] ASoC: Intel: boards: add sdw_rt711_rt1308_rt715 3-in-1 config support Add configuration with 4 SoundWire links connected to RT711, RT1308 (2x) and RT715, using the '3-in-1' test card. This topology will also be used on actual form-factors. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 12 + sound/soc/intel/boards/Makefile | 2 + .../soc/intel/boards/sdw_rt711_rt1308_rt715.c | 461 ++++++++++++++++++ 3 files changed, 475 insertions(+) create mode 100644 sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index fc2c98ef76e561..bcea5755c9edb3 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -511,6 +511,18 @@ config SND_SOC_INTEL_SOUNDWIRE_RT700_MACH on link 0 or 1 If unsure select "N" +config SND_SOC_INTEL_SOUNDWIRE_RT711_RT1308_RT715_MACH + tristate "SoundWire with RT711, RT1308 and RT715" + depends on SOUNDWIRE && ACPI + select SND_SOC_RT711_SDW + select SND_SOC_RT1308_SDW + select SND_SOC_RT715_SDW + select SND_SOC_HDAC_HDMI if SND_SOC_SOF_HDA_LINK + help + Add support for Intel SoundWire-based platforms connected to RT711, + RT1308 and RT715 + If unsure select "N". + endif endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index f8a89f6b269feb..09679f0a39569f 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -32,6 +32,7 @@ snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o snd-soc-tgl-rt1308-objs := tgl_rt1308.o hda_dsp_common.o snd-soc-sdw-rt700-objs := sdw_rt700.o hda_dsp_common.o +snd-soc-sdw-rt711-rt1308-rt715-objs := sdw_rt711_rt1308_rt715.o hda_dsp_common.o obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -65,3 +66,4 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ss obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o obj-$(CONFIG_SND_SOC_INTEL_TGL_RT1308_MACH) += snd-soc-tgl-rt1308.o obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT700_MACH) += snd-soc-sdw-rt700.o +obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT711_RT1308_RT715_MACH) += snd-soc-sdw-rt711-rt1308-rt715.o diff --git a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c new file mode 100644 index 00000000000000..b594e0a9effc46 --- /dev/null +++ b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c @@ -0,0 +1,461 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Intel Corporation + +/* + * sdw_rt711_rt1308_rt715 - ASOC Machine driver for Intel SoundWire platforms + * connected to 3 Realtek devices + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/hdac_hdmi.h" +#include "hda_dsp_common.h" + +/* comment out this define for mono configurations */ +#define ENABLE_RT1308_SDW2 + +#define MAX_NO_PROPS 2 + +extern struct bus_type sdw_bus_type; + +enum { + SOF_RT711_JD_SRC_JD1 = 1, + SOF_RT711_JD_SRC_JD2 = 2, +}; + +#define SOF_RT711_JDSRC(quirk) ((quirk) & GENMASK(1, 0)) + +static unsigned long sof_rt711_rt1308_rt715_quirk = SOF_RT711_JD_SRC_JD1; + +struct mc_private { + struct list_head hdmi_pcm_list; + bool common_hdmi_codec_drv; + struct snd_soc_jack sdw_headset; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) +static struct snd_soc_jack hdmi[3]; + +struct hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +static int hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + /* dai_link id is 1:1 mapped to the PCM device */ + pcm->device = rtd->dai_link->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +#define NAME_SIZE 32 +static int card_late_probe(struct snd_soc_card *card) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(card); + struct hdmi_pcm *pcm; + struct snd_soc_component *component = NULL; + int err, i = 0; + char jack_name[NAME_SIZE]; + + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct hdmi_pcm, + head); + component = pcm->codec_dai->component; + + if (ctx->common_hdmi_codec_drv) + return hda_dsp_hdmi_build_controls(card, component); + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &hdmi[i], + NULL, 0); + + if (err) + return err; + + err = snd_jack_add_new_kctl(hdmi[i].jack, + jack_name, SND_JACK_AVOUT); + if (err) + dev_warn(component->dev, "failed creating Jack kctl\n"); + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &hdmi[i]); + if (err < 0) + return err; + + i++; + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} +#else +static int card_late_probe(struct snd_soc_card *card) +{ + return 0; +} +#endif + +static struct snd_soc_jack_pin sdw_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int headset_init(struct snd_soc_pcm_runtime *rtd) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_jack *jack; + int ret; + + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &ctx->sdw_headset, + sdw_jack_pins, + ARRAY_SIZE(sdw_jack_pins)); + if (ret) { + dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n", + ret); + return ret; + } + + jack = &ctx->sdw_headset; + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); + + ret = snd_soc_component_set_jack(component, jack, NULL); + + if (ret) + dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n", + ret); + + return ret; +} + +static int sof_rt711_rt1308_rt715_quirk_cb(const struct dmi_system_id *id) +{ + sof_rt711_rt1308_rt715_quirk = (unsigned long)id->driver_data; + return 1; +} + +static const struct dmi_system_id sof_sdw_rt711_rt1308_rt715_quirk_table[] = { + { + .callback = sof_rt711_rt1308_rt715_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + }, + .driver_data = (void *)(SOF_RT711_JD_SRC_JD2), + }, + {} +}; + +/* + * Note this MUST be called before snd_soc_register_card(), so that the props + * are in place before the codec component driver's probe function parses them. + */ +static int sof_rt711_add_codec_device_props(const char *sdw_dev_name) +{ + struct property_entry props[MAX_NO_PROPS] = {}; + struct device *sdw_dev; + int ret, cnt = 0; + + sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_dev_name); + if (!sdw_dev) + return -EPROBE_DEFER; + + if (SOF_RT711_JDSRC(sof_rt711_rt1308_rt715_quirk)) { + props[cnt++] = PROPERTY_ENTRY_U32( + "realtek,jd-src", + SOF_RT711_JDSRC(sof_rt711_rt1308_rt715_quirk)); + } + + ret = device_add_properties(sdw_dev, props); + put_device(sdw_dev); + + return ret; +} + +static const struct snd_soc_dapm_widget widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +static const struct snd_soc_dapm_route map[] = { + /* Headphones */ + { "Headphone", NULL, "rt711 HP" }, + { "rt711 MIC2", NULL, "Headset Mic" }, + /* Speakers */ + { "Speaker", NULL, "rt1308-1 SPOL" }, + { "Speaker", NULL, "rt1308-1 SPOR" }, +#ifdef ENABLE_RT1308_SDW2 + { "Speaker", NULL, "rt1308-2 SPOL" }, + { "Speaker", NULL, "rt1308-2 SPOR" }, +#endif +}; + +static const struct snd_kcontrol_new controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +SND_SOC_DAILINK_DEF(sdw0_pin2, + DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin2"))); +SND_SOC_DAILINK_DEF(sdw0_pin3, + DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin3"))); +SND_SOC_DAILINK_DEF(sdw0_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("sdw:0:25d:711:0", "rt711-aif1"))); + +SND_SOC_DAILINK_DEF(sdw1_pin2, + DAILINK_COMP_ARRAY(COMP_CPU("SDW1 Pin2"))); +SND_SOC_DAILINK_DEF(sdw1_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("sdw:1:25d:1308:0", "rt1308-aif"))); + +#ifdef ENABLE_RT1308_SDW2 +SND_SOC_DAILINK_DEF(sdw2_pin2, + DAILINK_COMP_ARRAY(COMP_CPU("SDW2 Pin2"))); +SND_SOC_DAILINK_DEF(sdw2_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("sdw:2:25d:1308:0", "rt1308-aif"))); +#endif + +SND_SOC_DAILINK_DEF(sdw3_pin2, + DAILINK_COMP_ARRAY(COMP_CPU("SDW3 Pin2"))); +SND_SOC_DAILINK_DEF(sdw3_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("sdw:3:25d:715:0", "rt715-aif2"))); + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) +SND_SOC_DAILINK_DEF(idisp1_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp1 Pin"))); +SND_SOC_DAILINK_DEF(idisp1_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi1"))); + +SND_SOC_DAILINK_DEF(idisp2_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp2 Pin"))); +SND_SOC_DAILINK_DEF(idisp2_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi2"))); + +SND_SOC_DAILINK_DEF(idisp3_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp3 Pin"))); +SND_SOC_DAILINK_DEF(idisp3_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi3"))); +#endif + +SND_SOC_DAILINK_DEF(platform, + DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:1f.3"))); + +static struct snd_soc_codec_conf codec_conf[] = { + { + .dev_name = "sdw:0:25d:711:0", + .name_prefix = "rt711", + }, + { + .dev_name = "sdw:1:25d:1308:0", + .name_prefix = "rt1308-1", + }, +#ifdef ENABLE_RT1308_SDW2 + { + .dev_name = "sdw:2:25d:1308:0", + .name_prefix = "rt1308-2", + }, +#endif + { + .dev_name = "sdw:3:25d:715:0", + .name_prefix = "rt715", + }, + +}; + +struct snd_soc_dai_link dailink[] = { + { + .name = "SDW0-Playback", + .id = 0, + .init = headset_init, + .no_pcm = 1, + .dpcm_playback = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw0_pin2, sdw0_codec, platform), + }, + { + .name = "SDW0-Capture", + .id = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw0_pin3, sdw0_codec, platform), + }, + { + .name = "SDW1-Playback", + .id = 2, + .no_pcm = 1, + .dpcm_playback = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw1_pin2, sdw1_codec, platform), + }, +#ifdef ENABLE_RT1308_SDW2 + { + .name = "SDW2-Playback", + .id = 3, + .no_pcm = 1, + .dpcm_playback = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw2_pin2, sdw2_codec, platform), + }, +#endif + { + .name = "SDW3-Capture", + .id = 4, + .no_pcm = 1, + .dpcm_capture = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw3_pin2, sdw3_codec, platform), + }, + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) + { + .name = "iDisp1", + .id = 5, + .init = hdmi_init, + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(idisp1_pin, idisp1_codec, platform), + }, + { + .name = "iDisp2", + .id = 6, + .init = hdmi_init, + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(idisp2_pin, idisp2_codec, platform), + }, + { + .name = "iDisp3", + .id = 7, + .init = hdmi_init, + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(idisp3_pin, idisp3_codec, platform), + }, +#endif +}; + +/* SoC card */ +static struct snd_soc_card card_rt700_rt1308_rt715 = { + .name = "sdw-rt711-1308-715", + .dai_link = dailink, + .num_links = ARRAY_SIZE(dailink), + .controls = controls, + .num_controls = ARRAY_SIZE(controls), + .dapm_widgets = widgets, + .num_dapm_widgets = ARRAY_SIZE(widgets), + .dapm_routes = map, + .num_dapm_routes = ARRAY_SIZE(map), + .late_probe = card_late_probe, + .codec_conf = codec_conf, + .num_configs = ARRAY_SIZE(codec_conf), +}; + +static int mc_probe(struct platform_device *pdev) +{ + struct mc_private *ctx; + struct snd_soc_acpi_mach *mach; + const char *platform_name; + struct snd_soc_card *card = &card_rt700_rt1308_rt715; + int ret; + + dev_dbg(&pdev->dev, "Entry %s\n", __func__); + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + dmi_check_system(sof_sdw_rt711_rt1308_rt715_quirk_table); + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); +#endif + + card->dev = &pdev->dev; + + /* override platform name, if required */ + mach = (&pdev->dev)->platform_data; + platform_name = mach->mach_params.platform; + + ret = snd_soc_fixup_dai_links_platform_name(card, platform_name); + if (ret) + return ret; + + ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv; + + snd_soc_card_set_drvdata(card, ctx); + + sof_rt711_add_codec_device_props("sdw:0:25d:711:0"); + /* Register the card */ + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { + dev_err(card->dev, "snd_soc_register_card failed %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, card); + + return ret; +} + +static struct platform_driver sdw_rt711_rt1308_rt715_driver = { + .driver = { + .name = "sdw_rt711_rt1308_rt715", + .pm = &snd_soc_pm_ops, + }, + .probe = mc_probe, +}; + +module_platform_driver(sdw_rt711_rt1308_rt715_driver); + +MODULE_DESCRIPTION("ASoC SoundWire RT711/1308/715 Machine driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_AUTHOR("Pierre-Louis Bossart "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sdw_rt711_rt1308_rt715"); From 2b5ede1abadf4d842ca00ad13bdec0b7b688a44f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 17 Sep 2019 18:29:27 -0500 Subject: [PATCH 055/110] ASoC: Intel: boards: sdw_rt711: add machine driver Simpler subset of rt711_rt1308_rt715. The RT711 is assumed to be located on Link0. Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 9 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/sdw_rt711.c | 276 +++++++++++++++++++++++++++++ 3 files changed, 287 insertions(+) create mode 100644 sound/soc/intel/boards/sdw_rt711.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index bcea5755c9edb3..57320a96ccc232 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -511,6 +511,15 @@ config SND_SOC_INTEL_SOUNDWIRE_RT700_MACH on link 0 or 1 If unsure select "N" +config SND_SOC_INTEL_SOUNDWIRE_RT711_MACH + tristate "SoundWire with RT711 codec" + depends on SOUNDWIRE && ACPI + select SND_SOC_RT711_SDW + help + Add support for Intel SoundWire-based platforms connected to RT711 + on link 0 or 1 + If unsure select "N" + config SND_SOC_INTEL_SOUNDWIRE_RT711_RT1308_RT715_MACH tristate "SoundWire with RT711, RT1308 and RT715" depends on SOUNDWIRE && ACPI diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 09679f0a39569f..edbc871487904f 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -32,6 +32,7 @@ snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o snd-soc-tgl-rt1308-objs := tgl_rt1308.o hda_dsp_common.o snd-soc-sdw-rt700-objs := sdw_rt700.o hda_dsp_common.o +snd-soc-sdw-rt711-objs := sdw_rt711.o hda_dsp_common.o snd-soc-sdw-rt711-rt1308-rt715-objs := sdw_rt711_rt1308_rt715.o hda_dsp_common.o obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o @@ -66,4 +67,5 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ss obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o obj-$(CONFIG_SND_SOC_INTEL_TGL_RT1308_MACH) += snd-soc-tgl-rt1308.o obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT700_MACH) += snd-soc-sdw-rt700.o +obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT711_MACH) += snd-soc-sdw-rt711.o obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT711_RT1308_RT715_MACH) += snd-soc-sdw-rt711-rt1308-rt715.o diff --git a/sound/soc/intel/boards/sdw_rt711.c b/sound/soc/intel/boards/sdw_rt711.c new file mode 100644 index 00000000000000..09da46b150992f --- /dev/null +++ b/sound/soc/intel/boards/sdw_rt711.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Intel Corporation + +/* + * sdw_rt711 - ASOC Machine driver for Intel SoundWire platforms + * connected to ALC711 device + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/hdac_hdmi.h" +#include "hda_dsp_common.h" + +struct mc_private { + struct list_head hdmi_pcm_list; + bool common_hdmi_codec_drv; + struct snd_soc_jack sdw_headset; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) +static struct snd_soc_jack hdmi[3]; + +struct hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +static int hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + /* dai_link id is 1:1 mapped to the PCM device */ + pcm->device = rtd->dai_link->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +#define NAME_SIZE 32 +static int card_late_probe(struct snd_soc_card *card) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(card); + struct hdmi_pcm *pcm; + struct snd_soc_component *component = NULL; + int err, i = 0; + char jack_name[NAME_SIZE]; + + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct hdmi_pcm, + head); + component = pcm->codec_dai->component; + + if (ctx->common_hdmi_codec_drv) + return hda_dsp_hdmi_build_controls(card, component); + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &hdmi[i], + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &hdmi[i]); + if (err < 0) + return err; + + i++; + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} +#else +static int card_late_probe(struct snd_soc_card *card) +{ + return 0; +} +#endif + +static struct snd_soc_jack_pin sdw_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int headset_init(struct snd_soc_pcm_runtime *rtd) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_jack *jack; + int ret; + + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &ctx->sdw_headset, + sdw_jack_pins, + ARRAY_SIZE(sdw_jack_pins)); + if (ret) { + dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n", + ret); + return ret; + } + + jack = &ctx->sdw_headset; + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); + + ret = snd_soc_component_set_jack(component, jack, NULL); + + if (ret) + dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n", + ret); + + return ret; +} + +static const struct snd_soc_dapm_widget widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +static const struct snd_soc_dapm_route map[] = { + /*Headphones*/ + { "Headphone", NULL, "HP" }, + { "MIC2", NULL, "Headset Mic" }, +}; + +static const struct snd_kcontrol_new controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +SND_SOC_DAILINK_DEF(sdw0_pin2, + DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin2"))); +SND_SOC_DAILINK_DEF(sdw0_pin3, + DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin3"))); +SND_SOC_DAILINK_DEF(sdw0_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("sdw:0:25d:711:0", "rt711-aif1"))); + +SND_SOC_DAILINK_DEF(platform, + DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:1f.3"))); + +struct snd_soc_dai_link dailink[] = { + { + .name = "SDW0-Playback", + .id = 0, + .init = headset_init, + .no_pcm = 1, + .dpcm_playback = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw0_pin2, sdw0_codec, platform), + }, + { + .name = "SDW0-Capture", + .id = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw0_pin3, sdw0_codec, platform), + }, +}; + +/* SoC card */ +static struct snd_soc_card card_sdw_rt711 = { + .name = "sdw-rt711", + .dai_link = dailink, + .num_links = ARRAY_SIZE(dailink), + .controls = controls, + .num_controls = ARRAY_SIZE(controls), + .dapm_widgets = widgets, + .num_dapm_widgets = ARRAY_SIZE(widgets), + .dapm_routes = map, + .num_dapm_routes = ARRAY_SIZE(map), + .late_probe = card_late_probe, +}; + +static int mc_probe(struct platform_device *pdev) +{ + struct mc_private *ctx; + struct snd_soc_acpi_mach *mach; + const char *platform_name; + struct snd_soc_card *card = &card_sdw_rt711; + int ret; + + dev_dbg(&pdev->dev, "Entry %s\n", __func__); + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); +#endif + + card->dev = &pdev->dev; + + /* override platform name, if required */ + mach = (&pdev->dev)->platform_data; + platform_name = mach->mach_params.platform; + + ret = snd_soc_fixup_dai_links_platform_name(card, platform_name); + if (ret) + return ret; + + ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv; + + snd_soc_card_set_drvdata(card, ctx); + + /* Register the card */ + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { + dev_err(card->dev, "snd_soc_register_card failed %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, card); + + return ret; +} + +static struct platform_driver sdw_rt711_driver = { + .driver = { + .name = "sdw_rt711", + .pm = &snd_soc_pm_ops, + }, + .probe = mc_probe, +}; + +module_platform_driver(sdw_rt711_driver); + +MODULE_DESCRIPTION("ASoC SoundWire RT711 Machine driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_AUTHOR("Pierre-Louis Bossart "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sdw_rt711"); From add880497d9a3014f8c3b4eed91bc089fd4affec Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 25 Sep 2019 17:29:19 -0500 Subject: [PATCH 056/110] ASoC: Intel: boards: SoundWire rt711 + I2S RT1308 configuration This is the baseline for TGL RVP Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 11 +- sound/soc/intel/boards/Makefile | 4 +- .../{tgl_rt1308.c => sdw_rt711_i2s_rt1308.c} | 227 ++++++++++++------ 3 files changed, 165 insertions(+), 77 deletions(-) rename sound/soc/intel/boards/{tgl_rt1308.c => sdw_rt711_i2s_rt1308.c} (54%) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 57320a96ccc232..b8472a5c928b14 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -481,22 +481,23 @@ config SND_SOC_INTEL_SOF_CML_RT1011_RT5682_MACH endif ## SND_SOC_SOF_COMETLAKE_LP && SND_SOC_SOF_HDA_LINK -if SND_SOC_SOF_TIGERLAKE +if SND_SOC_SOF_TIGERLAKE && SND_SOC_SOF_INTEL_SOUNDWIRE -config SND_SOC_INTEL_TGL_RT1308_MACH - tristate "TGL with RT1308 in I2S Mode" +config SND_SOC_INTEL_TGL_RT711_RT1308_MACH + tristate "TGL with RT711 in SDW mode and RT1308 in I2S Mode" depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST + select SND_SOC_RT711_SDW select SND_SOC_RT1308 select SND_SOC_DMIC select SND_SOC_HDAC_HDMI if SND_SOC_SOF_HDA_LINK help This adds support for ASoC machine driver for Tigerlake platforms - with RT1308 I2S audio codec. + with SoundWire RT711 and RT1308 I2S audio codec. Say Y if you have such a device. If unsure select "N". -endif ## SND_SOC_SOF_TIGERLAKE +endif ## SND_SOC_SOF_TIGERLAKE && SND_SOC_SOF_INTEL_SOUNDWIRE if SND_SOC_SOF_INTEL_SOUNDWIRE diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index edbc871487904f..14d96a49454d36 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -30,7 +30,7 @@ snd-soc-skl_rt286-objs := skl_rt286.o snd-soc-skl_hda_dsp-objs := skl_hda_dsp_generic.o skl_hda_dsp_common.o hda_dsp_common.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o -snd-soc-tgl-rt1308-objs := tgl_rt1308.o hda_dsp_common.o +snd-soc-tgl-rt711-rt1308-objs := sdw_rt711_i2s_rt1308.o hda_dsp_common.o snd-soc-sdw-rt700-objs := sdw_rt700.o hda_dsp_common.o snd-soc-sdw-rt711-objs := sdw_rt711.o hda_dsp_common.o snd-soc-sdw-rt711-rt1308-rt715-objs := sdw_rt711_rt1308_rt715.o hda_dsp_common.o @@ -65,7 +65,7 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o -obj-$(CONFIG_SND_SOC_INTEL_TGL_RT1308_MACH) += snd-soc-tgl-rt1308.o +obj-$(CONFIG_SND_SOC_INTEL_TGL_RT711_RT1308_MACH) += snd-soc-tgl-rt711-rt1308.o obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT700_MACH) += snd-soc-sdw-rt700.o obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT711_MACH) += snd-soc-sdw-rt711.o obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT711_RT1308_RT715_MACH) += snd-soc-sdw-rt711-rt1308-rt715.o diff --git a/sound/soc/intel/boards/tgl_rt1308.c b/sound/soc/intel/boards/sdw_rt711_i2s_rt1308.c similarity index 54% rename from sound/soc/intel/boards/tgl_rt1308.c rename to sound/soc/intel/boards/sdw_rt711_i2s_rt1308.c index c9e3c479b27cd6..5d8ae497a5e4ac 100644 --- a/sound/soc/intel/boards/tgl_rt1308.c +++ b/sound/soc/intel/boards/sdw_rt711_i2s_rt1308.c @@ -3,45 +3,50 @@ // Copyright(c) 2019 Intel Corporation. All rights reserved. /* - * tgl_rt1308.c - ASoc Machine driver for Intel platforms - * with RT1308 codec. + * tgl_rt711_rt1308.c - ASoc Machine driver for Intel platforms + * with RT711 codec over SoundWire and RT1308 amplifier over I2S */ #include +#include #include +#include +#include +#include +#include +#include +#include #include -#include - -#include +#include #include #include #include #include #include - #include "../../codecs/rt1308.h" #include "../../codecs/hdac_hdmi.h" #include "hda_dsp_common.h" -struct tgl_card_private { +struct mc_private { struct list_head hdmi_pcm_list; bool common_hdmi_codec_drv; + struct snd_soc_jack sdw_headset; }; #if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) -static struct snd_soc_jack tgl_hdmi[4]; +static struct snd_soc_jack hdmi[4]; -struct tgl_hdmi_pcm { +struct hdmi_pcm { struct list_head head; struct snd_soc_dai *codec_dai; int device; }; -static int tgl_hdmi_init(struct snd_soc_pcm_runtime *rtd) +static int hdmi_init(struct snd_soc_pcm_runtime *rtd) { - struct tgl_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *dai = rtd->codec_dai; - struct tgl_hdmi_pcm *pcm; + struct hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); if (!pcm) @@ -57,15 +62,15 @@ static int tgl_hdmi_init(struct snd_soc_pcm_runtime *rtd) } #define NAME_SIZE 32 -static int tgl_card_late_probe(struct snd_soc_card *card) +static int card_late_probe(struct snd_soc_card *card) { - struct tgl_card_private *ctx = snd_soc_card_get_drvdata(card); - struct tgl_hdmi_pcm *pcm; + struct mc_private *ctx = snd_soc_card_get_drvdata(card); + struct hdmi_pcm *pcm; struct snd_soc_component *component = NULL; int err, i = 0; char jack_name[NAME_SIZE]; - pcm = list_first_entry(&ctx->hdmi_pcm_list, struct tgl_hdmi_pcm, + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct hdmi_pcm, head); component = pcm->codec_dai->component; @@ -77,14 +82,14 @@ static int tgl_card_late_probe(struct snd_soc_card *card) snprintf(jack_name, sizeof(jack_name), "HDMI/DP, pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, - SND_JACK_AVOUT, &tgl_hdmi[i], + SND_JACK_AVOUT, &hdmi[i], NULL, 0); if (err) return err; err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, - &tgl_hdmi[i]); + &hdmi[i]); if (err < 0) return err; @@ -97,14 +102,61 @@ static int tgl_card_late_probe(struct snd_soc_card *card) return hdac_hdmi_jack_port_init(component, &card->dapm); } #else -static int tgl_card_late_probe(struct snd_soc_card *card) +static int card_late_probe(struct snd_soc_card *card) { return 0; } #endif -static int tgl_rt1308_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static struct snd_soc_jack_pin sdw_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int headset_init(struct snd_soc_pcm_runtime *rtd) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_jack *jack; + int ret; + + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &ctx->sdw_headset, + sdw_jack_pins, + ARRAY_SIZE(sdw_jack_pins)); + if (ret) { + dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n", + ret); + return ret; + } + + jack = &ctx->sdw_headset; + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); + + ret = snd_soc_component_set_jack(component, jack, NULL); + + if (ret) + dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n", + ret); + + return ret; +} + +static int rt1308_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; @@ -136,27 +188,39 @@ static int tgl_rt1308_hw_params(struct snd_pcm_substream *substream, } /* machine stream operations */ -static struct snd_soc_ops tgl_rt1308_ops = { - .hw_params = tgl_rt1308_hw_params, +static struct snd_soc_ops rt1308_ops = { + .hw_params = rt1308_hw_params, }; -static const struct snd_soc_dapm_widget tgl_rt1308_dapm_widgets[] = { +static const struct snd_soc_dapm_widget widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_SPK("Speakers", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), }; -static const struct snd_kcontrol_new tgl_rt1308_controls[] = { - SOC_DAPM_PIN_SWITCH("Speakers"), -}; +static const struct snd_soc_dapm_route map[] = { + { "Headphone", NULL, "HP" }, + { "MIC2", NULL, "Headset Mic" }, -static const struct snd_soc_dapm_route tgl_rt1308_dapm_routes[] = { { "Speakers", NULL, "SPOL" }, { "Speakers", NULL, "SPOR" }, + { "DMic", NULL, "SoC DMIC"}, +}; - /* digital mics */ - {"DMic", NULL, "SoC DMIC"}, +static const struct snd_kcontrol_new controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Speakers"), }; +SND_SOC_DAILINK_DEF(sdw0_pin2, + DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin2"))); +SND_SOC_DAILINK_DEF(sdw0_pin3, + DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin3"))); +SND_SOC_DAILINK_DEF(sdw0_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("sdw:0:25d:711:0", "rt711-aif1"))); + SND_SOC_DAILINK_DEF(ssp2_pin, DAILINK_COMP_ARRAY(COMP_CPU("SSP2 Pin"))); @@ -173,6 +237,7 @@ SND_SOC_DAILINK_DEF(dmic_codec, SND_SOC_DAILINK_DEF(dmic16k, DAILINK_COMP_ARRAY(COMP_CPU("DMIC16k Pin"))); +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) SND_SOC_DAILINK_DEF(idisp1_pin, DAILINK_COMP_ARRAY(COMP_CPU("iDisp1 Pin"))); SND_SOC_DAILINK_DEF(idisp1_codec, @@ -192,20 +257,38 @@ SND_SOC_DAILINK_DEF(idisp4_pin, DAILINK_COMP_ARRAY(COMP_CPU("iDisp4 Pin"))); SND_SOC_DAILINK_DEF(idisp4_codec, DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi4"))); +#endif -static struct snd_soc_dai_link tgl_rt1308_dailink[] = { +struct snd_soc_dai_link dailink[] = { + { + .name = "SDW0-Playback", + .id = 0, + .init = headset_init, + .no_pcm = 1, + .dpcm_playback = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw0_pin2, sdw0_codec, platform), + }, + { + .name = "SDW0-Capture", + .id = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw0_pin3, sdw0_codec, platform), + }, { .name = "SSP2-Codec", - .id = 0, + .id = 2, .no_pcm = 1, - .ops = &tgl_rt1308_ops, + .ops = &rt1308_ops, .dpcm_playback = 1, .nonatomic = true, SND_SOC_DAILINK_REG(ssp2_pin, ssp2_codec, platform), }, { .name = "dmic01", - .id = 1, + .id = 3, .ignore_suspend = 1, .dpcm_capture = 1, .no_pcm = 1, @@ -213,7 +296,7 @@ static struct snd_soc_dai_link tgl_rt1308_dailink[] = { }, { .name = "dmic16k", - .id = 2, + .id = 4, .ignore_suspend = 1, .dpcm_capture = 1, .no_pcm = 1, @@ -222,32 +305,32 @@ static struct snd_soc_dai_link tgl_rt1308_dailink[] = { #if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) { .name = "iDisp1", - .id = 3, - .init = tgl_hdmi_init, + .id = 5, + .init = hdmi_init, .dpcm_playback = 1, .no_pcm = 1, SND_SOC_DAILINK_REG(idisp1_pin, idisp1_codec, platform), }, { .name = "iDisp2", - .id = 4, - .init = tgl_hdmi_init, + .id = 6, + .init = hdmi_init, .dpcm_playback = 1, .no_pcm = 1, SND_SOC_DAILINK_REG(idisp2_pin, idisp2_codec, platform), }, { .name = "iDisp3", - .id = 5, - .init = tgl_hdmi_init, + .id = 7, + .init = hdmi_init, .dpcm_playback = 1, .no_pcm = 1, SND_SOC_DAILINK_REG(idisp3_pin, idisp3_codec, platform), }, { .name = "iDisp4", - .id = 6, - .init = tgl_hdmi_init, + .id = 8, + .init = hdmi_init, .dpcm_playback = 1, .no_pcm = 1, SND_SOC_DAILINK_REG(idisp4_pin, idisp4_codec, platform), @@ -255,41 +338,45 @@ static struct snd_soc_dai_link tgl_rt1308_dailink[] = { #endif }; -/* audio machine driver */ -static struct snd_soc_card tgl_rt1308_card = { - .name = "tgl_rt1308", - .owner = THIS_MODULE, - .dai_link = tgl_rt1308_dailink, - .num_links = ARRAY_SIZE(tgl_rt1308_dailink), - .controls = tgl_rt1308_controls, - .num_controls = ARRAY_SIZE(tgl_rt1308_controls), - .dapm_widgets = tgl_rt1308_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(tgl_rt1308_dapm_widgets), - .dapm_routes = tgl_rt1308_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(tgl_rt1308_dapm_routes), - .late_probe = tgl_card_late_probe, +/* SoC card */ +static struct snd_soc_card card_rt711_rt1308 = { + .name = "rt711-rt1308", + .dai_link = dailink, + .num_links = ARRAY_SIZE(dailink), + .controls = controls, + .num_controls = ARRAY_SIZE(controls), + .dapm_widgets = widgets, + .num_dapm_widgets = ARRAY_SIZE(widgets), + .dapm_routes = map, + .num_dapm_routes = ARRAY_SIZE(map), + .late_probe = card_late_probe, }; -static int tgl_rt1308_probe(struct platform_device *pdev) +static int mc_probe(struct platform_device *pdev) { + struct mc_private *ctx; struct snd_soc_acpi_mach *mach; - struct tgl_card_private *ctx; - struct snd_soc_card *card = &tgl_rt1308_card; + const char *platform_name; + struct snd_soc_card *card = &card_rt711_rt1308; int ret; - card->dev = &pdev->dev; + dev_dbg(&pdev->dev, "Entry %s\n", __func__); ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; - if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) - INIT_LIST_HEAD(&ctx->hdmi_pcm_list); +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); +#endif + + card->dev = &pdev->dev; + /* override platform name, if required */ mach = (&pdev->dev)->platform_data; + platform_name = mach->mach_params.platform; - ret = snd_soc_fixup_dai_links_platform_name(card, - mach->mach_params.platform); + ret = snd_soc_fixup_dai_links_platform_name(card, platform_name); if (ret) return ret; @@ -300,17 +387,17 @@ static int tgl_rt1308_probe(struct platform_device *pdev) return devm_snd_soc_register_card(&pdev->dev, card); } -static struct platform_driver tgl_rt1308_driver = { +static struct platform_driver rt711_rt1308_driver = { .driver = { - .name = "tgl_rt1308", + .name = "rt711_rt1308", .pm = &snd_soc_pm_ops, }, - .probe = tgl_rt1308_probe, + .probe = mc_probe, }; -module_platform_driver(tgl_rt1308_driver); +module_platform_driver(rt711_rt1308_driver); MODULE_AUTHOR("Xiuli Pan"); -MODULE_DESCRIPTION("ASoC Intel(R) Tiger Lake + RT1308 Machine driver"); +MODULE_DESCRIPTION("ASoC SoundWire RT711 + RT1308 Machine driver"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:tgl_rt1308"); +MODULE_ALIAS("platform:rt711_rt1308"); From fbeb8e0053d7d74b186f5f2617048583131b1132 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 24 Jun 2019 11:52:23 -0500 Subject: [PATCH 057/110] NOT FOR UPSTREAM: Add debug for boards Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 14d96a49454d36..fe56cab93104d4 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -1,3 +1,5 @@ +ccflags-y += -DDEBUG + # SPDX-License-Identifier: GPL-2.0 snd-soc-sst-haswell-objs := haswell.o snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o From 993570a6de6b8eb4b6b6ba17800e4d44fba8dd89 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 5 Nov 2019 14:50:11 +0800 Subject: [PATCH 058/110] ASoC: intel: sdw_rt711_rt1308_rt715: remove ENABLE_RT1308_SDW2 flag We can use quick to decide if we need 2nd rt1308 or not. Signed-off-by: Bard Liao --- .../soc/intel/boards/sdw_rt711_rt1308_rt715.c | 62 ++++++++++++------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c index b594e0a9effc46..919f77287ed06e 100644 --- a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c +++ b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c @@ -26,7 +26,6 @@ #include "hda_dsp_common.h" /* comment out this define for mono configurations */ -#define ENABLE_RT1308_SDW2 #define MAX_NO_PROPS 2 @@ -38,6 +37,7 @@ enum { }; #define SOF_RT711_JDSRC(quirk) ((quirk) & GENMASK(1, 0)) +#define SOF_SDW_MONO_SPK BIT(2) static unsigned long sof_rt711_rt1308_rt715_quirk = SOF_RT711_JD_SRC_JD1; @@ -186,7 +186,8 @@ static const struct dmi_system_id sof_sdw_rt711_rt1308_rt715_quirk_table[] = { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), }, - .driver_data = (void *)(SOF_RT711_JD_SRC_JD2), + .driver_data = (void *)(SOF_RT711_JD_SRC_JD2 | + SOF_SDW_MONO_SPK), }, {} }; @@ -230,10 +231,11 @@ static const struct snd_soc_dapm_route map[] = { /* Speakers */ { "Speaker", NULL, "rt1308-1 SPOL" }, { "Speaker", NULL, "rt1308-1 SPOR" }, -#ifdef ENABLE_RT1308_SDW2 +}; + +static const struct snd_soc_dapm_route second_speaker_map[] = { { "Speaker", NULL, "rt1308-2 SPOL" }, { "Speaker", NULL, "rt1308-2 SPOR" }, -#endif }; static const struct snd_kcontrol_new controls[] = { @@ -242,6 +244,20 @@ static const struct snd_kcontrol_new controls[] = { SOC_DAPM_PIN_SWITCH("Speaker"), }; +static int second_spk_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_dapm_add_routes(&card->dapm, second_speaker_map, + ARRAY_SIZE(second_speaker_map)); + + if (ret) + dev_err(rtd->dev, "second Speaker map addition failed: %d\n", + ret); + return ret; +} + SND_SOC_DAILINK_DEF(sdw0_pin2, DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin2"))); SND_SOC_DAILINK_DEF(sdw0_pin3, @@ -254,12 +270,10 @@ SND_SOC_DAILINK_DEF(sdw1_pin2, SND_SOC_DAILINK_DEF(sdw1_codec, DAILINK_COMP_ARRAY(COMP_CODEC("sdw:1:25d:1308:0", "rt1308-aif"))); -#ifdef ENABLE_RT1308_SDW2 SND_SOC_DAILINK_DEF(sdw2_pin2, DAILINK_COMP_ARRAY(COMP_CPU("SDW2 Pin2"))); SND_SOC_DAILINK_DEF(sdw2_codec, DAILINK_COMP_ARRAY(COMP_CODEC("sdw:2:25d:1308:0", "rt1308-aif"))); -#endif SND_SOC_DAILINK_DEF(sdw3_pin2, DAILINK_COMP_ARRAY(COMP_CPU("SDW3 Pin2"))); @@ -295,16 +309,14 @@ static struct snd_soc_codec_conf codec_conf[] = { .dev_name = "sdw:1:25d:1308:0", .name_prefix = "rt1308-1", }, -#ifdef ENABLE_RT1308_SDW2 - { - .dev_name = "sdw:2:25d:1308:0", - .name_prefix = "rt1308-2", - }, -#endif { .dev_name = "sdw:3:25d:715:0", .name_prefix = "rt715", }, + { + .dev_name = "sdw:2:25d:1308:0", + .name_prefix = "rt1308-2", + }, }; @@ -334,16 +346,6 @@ struct snd_soc_dai_link dailink[] = { .nonatomic = true, SND_SOC_DAILINK_REG(sdw1_pin2, sdw1_codec, platform), }, -#ifdef ENABLE_RT1308_SDW2 - { - .name = "SDW2-Playback", - .id = 3, - .no_pcm = 1, - .dpcm_playback = 1, - .nonatomic = true, - SND_SOC_DAILINK_REG(sdw2_pin2, sdw2_codec, platform), - }, -#endif { .name = "SDW3-Capture", .id = 4, @@ -379,6 +381,15 @@ struct snd_soc_dai_link dailink[] = { SND_SOC_DAILINK_REG(idisp3_pin, idisp3_codec, platform), }, #endif + { + .name = "SDW2-Playback", + .id = 3, + .init = second_spk_init, + .no_pcm = 1, + .dpcm_playback = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw2_pin2, sdw2_codec, platform), + }, }; /* SoC card */ @@ -432,6 +443,13 @@ static int mc_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, ctx); sof_rt711_add_codec_device_props("sdw:0:25d:711:0"); + + if (sof_rt711_rt1308_rt715_quirk & SOF_SDW_MONO_SPK) { + /* Remove rt1308-2 codec from dailink and codec_conf */ + card->num_links--; + card->num_configs--; + } + /* Register the card */ ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { From 87a98d15cf7b4a2458c3c28bd6a269263e043e55 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Mon, 19 Aug 2019 16:43:06 +0800 Subject: [PATCH 059/110] soundwire: intel: add prepare support in sdw dai driver The existing code does not expose a prepare operation, which is very much needed to deal with underflow and resume operations. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 492684ba08c324..c50aebda7f4d52 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -697,6 +697,21 @@ static int intel_hw_params(struct snd_pcm_substream *substream, return ret; } +static int intel_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_cdns_dma_data *dma; + + dma = snd_soc_dai_get_dma_data(dai, substream); + if (!dma) { + dev_err(dai->dev, "failed to get dma data in %s", + __func__); + return -EIO; + } + + return sdw_prepare_stream(dma->stream); +} + static int intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -743,6 +758,7 @@ static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, static const struct snd_soc_dai_ops intel_pcm_dai_ops = { .hw_params = intel_hw_params, + .prepare = intel_prepare, .hw_free = intel_hw_free, .shutdown = intel_shutdown, .set_sdw_stream = intel_pcm_set_sdw_stream, @@ -750,6 +766,7 @@ static const struct snd_soc_dai_ops intel_pcm_dai_ops = { static const struct snd_soc_dai_ops intel_pdm_dai_ops = { .hw_params = intel_hw_params, + .prepare = intel_prepare, .hw_free = intel_hw_free, .shutdown = intel_shutdown, .set_sdw_stream = intel_pdm_set_sdw_stream, From a104febb1ab053cd9773f935102dd88c9906606f Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Mon, 19 Aug 2019 16:48:46 +0800 Subject: [PATCH 060/110] soundwire: intel: add trigger support in sdw dai driver The existing code does not expose a trigger callback, which is very much required for streaming. The SoundWire stream is enabled and disabled in trigger function. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index c50aebda7f4d52..862dde9a9b869e 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -712,6 +712,43 @@ static int intel_prepare(struct snd_pcm_substream *substream, return sdw_prepare_stream(dma->stream); } +static int intel_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct sdw_cdns_dma_data *dma; + int ret; + + dma = snd_soc_dai_get_dma_data(dai, substream); + if (!dma) { + dev_err(dai->dev, "failed to get dma data in %s", __func__); + return -EIO; + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + ret = sdw_enable_stream(dma->stream); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + ret = sdw_disable_stream(dma->stream); + break; + + default: + ret = -EINVAL; + break; + } + + if (ret) + dev_err(dai->dev, + "%s trigger %d failed: %d", + __func__, cmd, ret); + return ret; +} + static int intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -759,6 +796,7 @@ static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, static const struct snd_soc_dai_ops intel_pcm_dai_ops = { .hw_params = intel_hw_params, .prepare = intel_prepare, + .trigger = intel_trigger, .hw_free = intel_hw_free, .shutdown = intel_shutdown, .set_sdw_stream = intel_pcm_set_sdw_stream, @@ -767,6 +805,7 @@ static const struct snd_soc_dai_ops intel_pcm_dai_ops = { static const struct snd_soc_dai_ops intel_pdm_dai_ops = { .hw_params = intel_hw_params, .prepare = intel_prepare, + .trigger = intel_trigger, .hw_free = intel_hw_free, .shutdown = intel_shutdown, .set_sdw_stream = intel_pdm_set_sdw_stream, From 5e96ee91c03f8cfff12974fa81b88d54afca1b94 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Mon, 19 Aug 2019 16:55:01 +0800 Subject: [PATCH 061/110] soundwire: intel: add sdw_stream_setup helper for .startup callback The sdw stream is allocated and stored in dai to share the sdw runtime information. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 65 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 862dde9a9b869e..af24fa048addca 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -615,6 +615,69 @@ static int intel_post_bank_switch(struct sdw_bus *bus) * DAI routines */ +static int sdw_stream_setup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sdw_stream_runtime *sdw_stream = NULL; + char *name; + int i, ret; + + name = kzalloc(32, GFP_KERNEL); + if (!name) + return -ENOMEM; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snprintf(name, 32, "%s-Playback", dai->name); + else + snprintf(name, 32, "%s-Capture", dai->name); + + sdw_stream = sdw_alloc_stream(name); + if (!sdw_stream) { + dev_err(dai->dev, "alloc stream failed for DAI %s", dai->name); + ret = -ENOMEM; + goto error; + } + + /* Set stream pointer on CPU DAI */ + ret = snd_soc_dai_set_sdw_stream(dai, sdw_stream, substream->stream); + if (ret < 0) { + dev_err(dai->dev, "failed to set stream pointer on cpu dai %s", + dai->name); + goto release_stream; + } + + /* Set stream pointer on all CODEC DAIs */ + for (i = 0; i < rtd->num_codecs; i++) { + ret = snd_soc_dai_set_sdw_stream(rtd->codec_dais[i], sdw_stream, + substream->stream); + if (ret < 0) { + dev_err(dai->dev, "failed to set stream pointer on codec dai %s", + rtd->codec_dais[i]->name); + goto release_stream; + } + } + + return 0; + +release_stream: + sdw_release_stream(sdw_stream); +error: + kfree(name); + return ret; +} + +static int intel_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + /* + * TODO: add pm_runtime support here, the startup callback + * will make sure the IP is 'active' + */ + + return sdw_stream_setup(substream, dai); +} + static int intel_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -794,6 +857,7 @@ static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, } static const struct snd_soc_dai_ops intel_pcm_dai_ops = { + .startup = intel_startup, .hw_params = intel_hw_params, .prepare = intel_prepare, .trigger = intel_trigger, @@ -803,6 +867,7 @@ static const struct snd_soc_dai_ops intel_pcm_dai_ops = { }; static const struct snd_soc_dai_ops intel_pdm_dai_ops = { + .startup = intel_startup, .hw_params = intel_hw_params, .prepare = intel_prepare, .trigger = intel_trigger, From a5a307b85e8806bcf17aa5705bb22a7be9226b47 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 23 Oct 2019 14:05:33 -0500 Subject: [PATCH 062/110] soundwire: intel: free all resources on hw_free() Make sure all calls to the SoundWire stream API are done and involve callback Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 40 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index af24fa048addca..cad1c0b64ee3ed 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -548,6 +548,25 @@ static int intel_params_stream(struct sdw_intel *sdw, return -EIO; } +static int intel_free_stream(struct sdw_intel *sdw, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, + int link_id) +{ + struct sdw_intel_link_res *res = sdw->link_res; + struct sdw_intel_stream_free_data free_data; + + free_data.substream = substream; + free_data.dai = dai; + free_data.link_id = link_id; + + if (res->ops && res->ops->free_stream && res->dev) + return res->ops->free_stream(res->dev, + &free_data); + + return 0; +} + /* * bank switch routines */ @@ -816,6 +835,7 @@ static int intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_cdns_dma_data *dma; int ret; @@ -823,12 +843,28 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) if (!dma) return -EIO; + ret = sdw_deprepare_stream(dma->stream); + if (ret) { + dev_err(dai->dev, "sdw_deprepare_stream: failed %d", ret); + return ret; + } + ret = sdw_stream_remove_master(&cdns->bus, dma->stream); - if (ret < 0) + if (ret < 0) { dev_err(dai->dev, "remove master from stream %s failed: %d\n", dma->stream->name, ret); + return ret; + } - return ret; + ret = intel_free_stream(sdw, substream, dai, sdw->instance); + if (ret < 0) { + dev_err(dai->dev, "intel_free_stream: failed %d", ret); + return ret; + } + + sdw_release_stream(dma->stream); + + return 0; } static void intel_shutdown(struct snd_pcm_substream *substream, From 23fc9056ed5077e57aa0585cc5e6cf97abf5b43c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 23 Oct 2019 14:38:00 -0500 Subject: [PATCH 063/110] soundwire: intel_init: add implementation of sdw_intel_enable_irq() This function is required to enable all interrupts across all links. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel_init.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 42f7ae034beabf..14ffe9ce29292c 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -137,6 +137,30 @@ sdw_intel_scan_controller(struct sdw_intel_acpi_info *info) return 0; } +#define HDA_DSP_REG_ADSPIC2 (0x10) +#define HDA_DSP_REG_ADSPIS2 (0x14) +#define HDA_DSP_REG_ADSPIC2_SNDW BIT(5) + +/** + * sdw_intel_enable_irq() - enable/disable Intel SoundWire IRQ + * @mmio_base: The mmio base of the control register + * @enable: true if enable + */ +void sdw_intel_enable_irq(void __iomem *mmio_base, bool enable) +{ + u32 val; + + val = readl(mmio_base + HDA_DSP_REG_ADSPIC2); + + if (enable) + val |= HDA_DSP_REG_ADSPIC2_SNDW; + else + val &= ~HDA_DSP_REG_ADSPIC2_SNDW; + + writel(val, mmio_base + HDA_DSP_REG_ADSPIC2); +} +EXPORT_SYMBOL(sdw_intel_enable_irq); + static struct sdw_intel_ctx *sdw_intel_probe_controller(struct sdw_intel_res *res) { From ed04f8e6416330a591422ebdeabc2eaff82b4da4 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 24 Sep 2019 16:16:46 +0800 Subject: [PATCH 064/110] soundwire: intel/cadence: fix timeouts in MSI mode The existing code uses one pair of interrupt handler/thread per link but at the hardware level the interrupt is shared. This works fine for legacy PCI interrupts, but leads to timeouts in MSI (Message-Signaled Interrupt) mode, likely due to edges being lost. This patch unifies interrupt handling for all links with a single threaded IRQ handler. The handler is simplified to the bare minimum of detecting a SoundWire interrupt, and the thread takes care of dealing with interrupt sources. This partition follows the model used for the SOF IPC on HDaudio platforms, where similar timeout issues were noticed and doing all the interrupt handling/clearing in the thread improved reliability/stability. Validation results with 4 links active in parallel show a night-and-day improvement with no timeouts noticed even during stress tests. Latency and quality of service are not affected by the change - mostly because events on a SoundWire link are throttled by the bus frame rate (typically 8..48kHz). Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.c | 17 ++++++----- drivers/soundwire/cadence_master.h | 4 +++ drivers/soundwire/intel.c | 17 +---------- drivers/soundwire/intel.h | 2 ++ drivers/soundwire/intel_init.c | 45 ++++++++++++++++++++++++++++- include/linux/soundwire/sdw_intel.h | 2 ++ 6 files changed, 62 insertions(+), 25 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index fed21e2b22774d..362fb6e49bfe09 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "bus.h" #include "cadence_master.h" @@ -745,7 +746,7 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id) CDNS_MCP_INT_SLAVE_MASK, 0); int_status &= ~CDNS_MCP_INT_SLAVE_MASK; - ret = IRQ_WAKE_THREAD; + schedule_work(&cdns->work); } cdns_writel(cdns, CDNS_MCP_INTSTAT, int_status); @@ -754,13 +755,14 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id) EXPORT_SYMBOL(sdw_cdns_irq); /** - * sdw_cdns_thread() - Cadence irq thread handler - * @irq: irq number - * @dev_id: irq context + * To update slave status in a work since we will need to handle + * other interrupts eg. CDNS_MCP_INT_RX_WL during the update slave + * process. */ -irqreturn_t sdw_cdns_thread(int irq, void *dev_id) +static void cdns_update_slave_status_work(struct work_struct *work) { - struct sdw_cdns *cdns = dev_id; + struct sdw_cdns *cdns = + container_of(work, struct sdw_cdns, work); u32 slave0, slave1; dev_dbg_ratelimited(cdns->dev, "Slave status change\n"); @@ -777,9 +779,7 @@ irqreturn_t sdw_cdns_thread(int irq, void *dev_id) cdns_updatel(cdns, CDNS_MCP_INTMASK, CDNS_MCP_INT_SLAVE_MASK, CDNS_MCP_INT_SLAVE_MASK); - return IRQ_HANDLED; } -EXPORT_SYMBOL(sdw_cdns_thread); /* * init routines @@ -1187,6 +1187,7 @@ int sdw_cdns_probe(struct sdw_cdns *cdns) init_completion(&cdns->tx_complete); cdns->bus.port_ops = &cdns_port_ops; + INIT_WORK(&cdns->work, cdns_update_slave_status_work); return 0; } EXPORT_SYMBOL(sdw_cdns_probe); diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 001457cbe5ad2e..0f108fd31fd943 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -126,6 +126,10 @@ struct sdw_cdns { bool link_up; unsigned int msg_count; + + struct work_struct work; + + struct list_head list; }; #define bus_to_cdns(_bus) container_of(_bus, struct sdw_cdns, bus) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index cad1c0b64ee3ed..c918a26d0772a3 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1095,6 +1095,7 @@ static int intel_master_probe(struct sdw_master_device *md, void *link_ctx) sdw->cdns.msg_count = 0; sdw->cdns.bus.dev = &md->dev; sdw->cdns.bus.link_id = md->link_id; + sdw->link_res->cdns = &sdw->cdns; sdw_cdns_probe(&sdw->cdns); @@ -1119,21 +1120,7 @@ static int intel_master_probe(struct sdw_master_device *md, void *link_ctx) return 0; } - /* Acquire IRQ */ - ret = request_threaded_irq(sdw->link_res->irq, - sdw_cdns_irq, sdw_cdns_thread, - IRQF_SHARED, KBUILD_MODNAME, &sdw->cdns); - if (ret < 0) { - dev_err(sdw->cdns.dev, "unable to grab IRQ %d, disabling device\n", - sdw->link_res->irq); - goto err_init; - } - return 0; - -err_init: - sdw_delete_bus_master(&sdw->cdns.bus); - return ret; } static int intel_master_startup(struct sdw_master_device *md) @@ -1190,7 +1177,6 @@ static int intel_master_startup(struct sdw_master_device *md) err_interrupt: sdw_cdns_enable_interrupt(&sdw->cdns, false); err_init: - free_irq(sdw->link_res->irq, sdw); sdw_delete_bus_master(&sdw->cdns.bus); return ret; } @@ -1204,7 +1190,6 @@ static int intel_master_remove(struct sdw_master_device *md) if (!sdw->cdns.bus.prop.hw_disabled) { intel_debugfs_exit(sdw); sdw_cdns_enable_interrupt(&sdw->cdns, false); - free_irq(sdw->link_res->irq, sdw); snd_soc_unregister_component(sdw->cdns.dev); } sdw_delete_bus_master(&sdw->cdns.bus); diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index cfab2f00214d25..2ae4fa7486861d 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -25,6 +25,8 @@ struct sdw_intel_link_res { int irq; const struct sdw_intel_ops *ops; struct device *dev; + struct sdw_cdns *cdns; + struct list_head list; }; #define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1) diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 14ffe9ce29292c..ed2a83989c72ff 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -11,8 +11,10 @@ #include #include #include +#include #include #include +#include "cadence_master.h" #include "intel.h" #define SDW_LINK_TYPE 4 /* from Intel ACPI documentation */ @@ -161,6 +163,32 @@ void sdw_intel_enable_irq(void __iomem *mmio_base, bool enable) } EXPORT_SYMBOL(sdw_intel_enable_irq); +static irqreturn_t sdw_intel_irq(int irq, void *dev_id) +{ + struct sdw_intel_ctx *ctx = dev_id; + u32 int_status; + + int_status = readl(ctx->mmio_base + HDA_DSP_REG_ADSPIS2); + if (int_status & HDA_DSP_REG_ADSPIC2_SNDW) { + sdw_intel_enable_irq(ctx->mmio_base, false); + return IRQ_WAKE_THREAD; + } + + return IRQ_NONE; +} + +static irqreturn_t sdw_intel_thread(int irq, void *dev_id) +{ + struct sdw_intel_ctx *ctx = dev_id; + struct sdw_intel_link_res *link; + + list_for_each_entry(link, &ctx->link_list, list) + sdw_cdns_irq(irq, link->cdns); + + sdw_intel_enable_irq(ctx->mmio_base, true); + return IRQ_HANDLED; +} + static struct sdw_intel_ctx *sdw_intel_probe_controller(struct sdw_intel_res *res) { @@ -171,6 +199,7 @@ static struct sdw_intel_ctx u32 link_mask; int count; int i; + int ret; if (!res) return NULL; @@ -196,10 +225,13 @@ static struct sdw_intel_ctx ctx->mmio_base = res->mmio_base; ctx->link_mask = res->link_mask; ctx->handle = res->handle; + ctx->irq = res->irq; link = ctx->links; link_mask = ctx->link_mask; + INIT_LIST_HEAD(&ctx->link_list); + /* Create SDW Master devices */ for (i = 0; i < count; i++, link++) { if (link_mask && !(link_mask & BIT(i))) @@ -220,12 +252,22 @@ static struct sdw_intel_ctx + (SDW_LINK_SIZE * i); link->shim = res->mmio_base + SDW_SHIM_BASE; link->alh = res->mmio_base + SDW_ALH_BASE; - link->irq = res->irq; link->ops = res->ops; link->dev = res->dev; /* let the SoundWire master driver to its probe */ md->driver->probe(md, link); + + list_add_tail(&link->list, &ctx->link_list); + } + + ret = request_threaded_irq(ctx->irq, + sdw_intel_irq, sdw_intel_thread, + IRQF_SHARED, KBUILD_MODNAME, ctx); + if (ret < 0) { + dev_err(&adev->dev, "unable to grab IRQ %d, disabling device\n", + res->irq); + goto err; } return ctx; @@ -373,6 +415,7 @@ EXPORT_SYMBOL(sdw_intel_startup); */ void sdw_intel_exit(struct sdw_intel_ctx *ctx) { + free_irq(ctx->irq, ctx); sdw_intel_cleanup(ctx); kfree(ctx); } diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h index 36e2b522a7494a..373947003cea01 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -94,7 +94,9 @@ struct sdw_intel_ctx { void __iomem *mmio_base; u32 link_mask; acpi_handle handle; + int irq; struct sdw_intel_link_res *links; + struct list_head link_list; }; /* From 6908c9d2391b3bb1ff6a495e80c2cb77fbf72914 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 1 Aug 2019 19:53:15 -0500 Subject: [PATCH 065/110] soundwire: fix race between driver probe and update_status callback The driver probe takes care of basic initialization and is invoked when a Slave becomes attached, after a match between the Slave DevID registers and ACPI/DT entries. The update_status callback is invoked when a Slave state changes, e.g. when it is assigned a non-zero Device Number and it reports with an ATTACHED/ALERT state. The state change detection is usually hardware-based and based on the SoundWire frame rate (e.g. double-digit microseconds) while the probe is a pure software operation, which may involve a kernel module load. In corner cases, it's possible that the state changes before the probe completes. This patch suggests the use of wait_for_completion to avoid races on startup, so that the update_status callback does not rely on invalid pointers/data structures. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 24 +++++++++++++++++++++--- drivers/soundwire/bus.h | 1 + drivers/soundwire/bus_type.c | 5 +++++ drivers/soundwire/slave.c | 2 ++ 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 4b22ee996a6566..903aee25880069 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -961,10 +961,28 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) static int sdw_update_slave_status(struct sdw_slave *slave, enum sdw_slave_status status) { - if (slave->ops && slave->ops->update_status) - return slave->ops->update_status(slave, status); + unsigned long time; - return 0; + if (!slave->ops || !slave->ops->update_status) + return 0; + + if (!slave->probed) { + /* + * the slave status update is typically handled in an + * interrupt thread, which can race with the driver + * probe, e.g. when a module needs to be loaded. + * + * make sure the probe is complete before updating + * status. + */ + time = wait_for_completion_timeout(&slave->probe_complete, + msecs_to_jiffies(DEFAULT_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Probe not complete, timed out\n"); + return -ETIMEDOUT; + } + } + return slave->ops->update_status(slave, status); } /** diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index cb482da914da94..acb8d11a4c84fe 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -5,6 +5,7 @@ #define __SDW_BUS_H #define DEFAULT_BANK_SWITCH_TIMEOUT 3000 +#define DEFAULT_PROBE_TIMEOUT 2000 #if IS_ENABLED(CONFIG_ACPI) int sdw_acpi_find_slaves(struct sdw_bus *bus); diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index fd234f63bf2f4e..ac484384bd6e75 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -118,6 +118,11 @@ static int sdw_slave_drv_probe(struct device *dev) slave->bus->clk_stop_timeout = max_t(u32, slave->bus->clk_stop_timeout, slave->prop.clk_stop_timeout); + slave->probed = true; + complete(&slave->probe_complete); + + dev_dbg(dev, "probe complete\n"); + return 0; } diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index c87267f12a3b97..81b94cd3985e73 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -52,6 +52,8 @@ static int sdw_slave_add(struct sdw_bus *bus, slave->bus = bus; slave->status = SDW_SLAVE_UNATTACHED; slave->dev_num = 0; + init_completion(&slave->probe_complete); + slave->probed = false; mutex_lock(&bus->bus_lock); list_add_tail(&slave->node, &bus->slaves); From cc7582652370b70a8ebb43d3d7ee1d5dab96c85f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 10 Jun 2019 19:09:14 -0500 Subject: [PATCH 066/110] soundwire: bus: add PM/no-PM versions of read/write functions Add support for pm_runtime with the appropriate error checks for sdw_write/read functions, e.g. when pm_runtime is not supported. Also expose internal functions without pm_runtime support, which are required to perform any sort of suspend/resume operation, as well as any enumeration tasks. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 68 +++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 903aee25880069..7f431677f1527c 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -317,6 +317,46 @@ int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave, return 0; } +/* + * Read/Write IO functions. + * no_pm versions can only be called by the bus, e.g. while enumerating or + * handling suspend-resume sequences. + * all clients need to use the pm versions + */ + +static int +sdw_nread_no_pm(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) +{ + struct sdw_msg msg; + int ret; + + ret = sdw_fill_msg(&msg, slave, addr, count, + slave->dev_num, SDW_MSG_FLAG_READ, val); + if (ret < 0) + return ret; + + return sdw_transfer(slave->bus, &msg); +} + +static int +sdw_nwrite_no_pm(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) +{ + struct sdw_msg msg; + int ret; + + ret = sdw_fill_msg(&msg, slave, addr, count, + slave->dev_num, SDW_MSG_FLAG_WRITE, val); + if (ret < 0) + return ret; + + return sdw_transfer(slave->bus, &msg); +} + +int sdw_write_no_pm(struct sdw_slave *slave, u32 addr, u8 value) +{ + return sdw_nwrite_no_pm(slave, addr, 1, &value); +} + /** * sdw_nread() - Read "n" contiguous SDW Slave registers * @slave: SDW Slave @@ -326,19 +366,17 @@ int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave, */ int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) { - struct sdw_msg msg; int ret; - ret = sdw_fill_msg(&msg, slave, addr, count, - slave->dev_num, SDW_MSG_FLAG_READ, val); - if (ret < 0) - return ret; - ret = pm_runtime_get_sync(slave->bus->dev); - if (ret < 0) + if (ret < 0 && ret != -EACCES) { + pm_runtime_put_noidle(slave->bus->dev); return ret; + } + + ret = sdw_nread_no_pm(slave, addr, count, val); - ret = sdw_transfer(slave->bus, &msg); + pm_runtime_mark_last_busy(slave->bus->dev); pm_runtime_put(slave->bus->dev); return ret; @@ -354,19 +392,17 @@ EXPORT_SYMBOL(sdw_nread); */ int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) { - struct sdw_msg msg; int ret; - ret = sdw_fill_msg(&msg, slave, addr, count, - slave->dev_num, SDW_MSG_FLAG_WRITE, val); - if (ret < 0) - return ret; - ret = pm_runtime_get_sync(slave->bus->dev); - if (ret < 0) + if (ret < 0 && ret != -EACCES) { + pm_runtime_put_noidle(slave->bus->dev); return ret; + } + + ret = sdw_nwrite_no_pm(slave, addr, count, val); - ret = sdw_transfer(slave->bus, &msg); + pm_runtime_mark_last_busy(slave->bus->dev); pm_runtime_put(slave->bus->dev); return ret; From 9dd34d91add6cca166ac93eed30ee565929359d2 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 26 Sep 2019 16:19:00 -0500 Subject: [PATCH 067/110] soundwire: bus: write Slave Device Number without runtime_pm While handling the Device0, we can safely use sdw_write_no_pm. This move will also helps us track that all other usages of sdw_write() happen when the Slave is already enumerated. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 7f431677f1527c..0e761efd32f9cc 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -513,7 +513,7 @@ static int sdw_assign_device_num(struct sdw_slave *slave) slave->dev_num = 0; } - ret = sdw_write(slave, SDW_SCP_DEVNUMBER, dev_num); + ret = sdw_write_no_pm(slave, SDW_SCP_DEVNUMBER, dev_num); if (ret < 0) { dev_err(&slave->dev, "Program device_num %d failed: %d\n", dev_num, ret); From a8e28a9e856891b6dfaa9168c385255d4dc6ed64 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 20 Sep 2019 14:08:57 -0500 Subject: [PATCH 068/110] soundwire: intel: add helpers for link power down and shim wake These routines are required for power management Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index c918a26d0772a3..fe3a8b3b2c04a1 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -362,6 +362,59 @@ static int intel_shim_init(struct sdw_intel *sdw) return ret; } +static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) +{ + void __iomem *shim = sdw->link_res->shim; + unsigned int link_id = sdw->instance; + u16 wake_en, wake_sts; + + if (wake_enable) { + /* Enable the wakeup */ + intel_writew(shim, SDW_SHIM_WAKEEN, + (SDW_SHIM_WAKEEN_ENABLE << link_id)); + } else { + /* Disable the wake up interrupt */ + wake_en = intel_readw(shim, SDW_SHIM_WAKEEN); + wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id); + intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); + + /* Clear wake status */ + wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); + wake_sts |= (SDW_SHIM_WAKEEN_ENABLE << link_id); + intel_writew(shim, SDW_SHIM_WAKESTS_STATUS, wake_sts); + } +} + +static int intel_link_power_down(struct sdw_intel *sdw) +{ + int link_control, spa_mask, cpa_mask, ret; + unsigned int link_id = sdw->instance; + void __iomem *shim = sdw->link_res->shim; + u16 ioctl; + + /* Glue logic */ + ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); + ioctl |= SDW_SHIM_IOCTL_BKE; + ioctl |= SDW_SHIM_IOCTL_COE; + intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); + + ioctl &= ~(SDW_SHIM_IOCTL_MIF); + intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); + + /* Link power down sequence */ + link_control = intel_readl(shim, SDW_SHIM_LCTL); + spa_mask = ~(SDW_SHIM_LCTL_SPA << link_id); + cpa_mask = (SDW_SHIM_LCTL_CPA << link_id); + link_control &= spa_mask; + + ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); + if (ret < 0) + return ret; + + sdw->cdns.link_up = false; + return 0; +} + /* * PDI routines */ From 34b38756fecfdae173de0b7b3216c6b5e6c07f57 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 20 Sep 2019 14:21:18 -0500 Subject: [PATCH 069/110] soundwire: intel: Add basic power management support Implement suspend/resume capabilities (not runtime_pm for now) The resume part is essentially a full-blown re-enumeration. When S0ix is supported, we will select clock stop mode when the ACPI target state is S0, and tear down the link for S3. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 75 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index fe3a8b3b2c04a1..aeef0c0fc443e6 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1253,10 +1253,85 @@ static int intel_master_remove(struct sdw_master_device *md) return 0; } +/* + * PM calls + */ + +#ifdef CONFIG_PM + +static int intel_suspend(struct device *dev) +{ + struct sdw_cdns *cdns = dev_get_drvdata(dev); + struct sdw_intel *sdw = cdns_to_intel(cdns); + int ret; + + if (cdns->bus.prop.hw_disabled) { + dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", + cdns->bus.link_id); + return 0; + } + + ret = sdw_cdns_enable_interrupt(cdns, false); + if (ret < 0) { + dev_err(dev, "cannot disable interrupts on suspend\n"); + return ret; + } + + ret = intel_link_power_down(sdw); + if (ret) { + dev_err(dev, "Link power down failed: %d", ret); + return ret; + } + + intel_shim_wake(sdw, false); + + return 0; +} + +static int intel_resume(struct device *dev) +{ + struct sdw_cdns *cdns = dev_get_drvdata(dev); + struct sdw_intel *sdw = cdns_to_intel(cdns); + int ret; + + if (cdns->bus.prop.hw_disabled) { + dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", + cdns->bus.link_id); + return 0; + } + + ret = intel_init(sdw); + if (ret) { + dev_err(dev, "%s failed: %d", __func__, ret); + return ret; + } + + ret = sdw_cdns_enable_interrupt(cdns, true); + if (ret < 0) { + dev_err(dev, "cannot enable interrupts during resume\n"); + return ret; + } + + ret = sdw_cdns_exit_reset(cdns); + if (ret < 0) { + dev_err(dev, "unable to exit bus reset sequence during resume\n"); + return ret; + } + + return ret; +} + +#endif + +static const struct dev_pm_ops intel_pm = { + SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume) +}; + struct sdw_md_driver intel_sdw_driver = { .driver = { .name = "intel-sdw", .owner = THIS_MODULE, + .pm = &intel_pm, }, .probe = intel_master_probe, .startup = intel_master_startup, From 01f46cf3b225a56a9f50e98fc6131db81bbaa8fa Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 20 Sep 2019 14:51:39 -0500 Subject: [PATCH 070/110] soundwire: intel: add pm_runtime support Add basic hooks in DAI .startup and .shutdown callbacks. The SoundWire IP should be powered between those two calls. The power dependencies between SoundWire and DSP are handled with the parent/child relationship, before the SoundWire master device becomes active the parent device will become active and power-up the shared rails. For now the strategy is to rely on complete enumeration when the device becomes active, so the code is a copy/paste of the sequence for system suspend/resume. In future patches, the strategy will optionally be to rely on clock stop if the enumeration time is prohibitive or when the devices connected to a link can signal a wake. A module parameter is added to make integration of new Slave devices easier, to e.g. keep the device active or prevent clock-stop. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 108 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 4 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index aeef0c0fc443e6..6505813487326a 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,20 @@ #include "bus.h" #include "intel.h" +/* + * 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) + +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)"); + /* Intel SHIM Registers Definition */ #define SDW_SHIM_LCAP 0x0 #define SDW_SHIM_LCTL 0x4 @@ -742,10 +757,16 @@ static int sdw_stream_setup(struct snd_pcm_substream *substream, static int intel_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - /* - * TODO: add pm_runtime support here, the startup callback - * will make sure the IP is 'active' - */ + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + int ret; + + ret = pm_runtime_get_sync(cdns->dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(cdns->dev, + "pm_runtime_get_sync failed in %s, ret %d\n", + __func__, ret); + pm_runtime_put_noidle(cdns->dev); + } return sdw_stream_setup(substream, dai); } @@ -924,6 +945,7 @@ static void intel_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct sdw_cdns_dma_data *dma; + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); dma = snd_soc_dai_get_dma_data(dai, substream); if (!dma) @@ -931,6 +953,9 @@ static void intel_shutdown(struct snd_pcm_substream *substream, snd_soc_dai_set_dma_data(dai, substream, NULL); kfree(dma); + + pm_runtime_mark_last_busy(cdns->dev); + pm_runtime_put_autosuspend(cdns->dev); } static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, @@ -1180,6 +1205,7 @@ static int intel_master_startup(struct sdw_master_device *md) { struct sdw_cdns_stream_config config; struct sdw_intel *sdw; + int link_flags; int ret; sdw = md->pdata; @@ -1225,6 +1251,17 @@ static int intel_master_startup(struct sdw_master_device *md) intel_debugfs_init(sdw); + /* Enable runtime PM */ + link_flags = md_flags >> (sdw->cdns.bus.link_id * 8); + if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) { + pm_runtime_set_autosuspend_delay(&md->dev, 3000); + pm_runtime_use_autosuspend(&md->dev); + pm_runtime_mark_last_busy(&md->dev); + + pm_runtime_set_active(&md->dev); + pm_runtime_enable(&md->dev); + } + return 0; err_interrupt: @@ -1288,6 +1325,35 @@ static int intel_suspend(struct device *dev) return 0; } +static int intel_suspend_runtime(struct device *dev) +{ + struct sdw_cdns *cdns = dev_get_drvdata(dev); + struct sdw_intel *sdw = cdns_to_intel(cdns); + int ret; + + if (cdns->bus.prop.hw_disabled) { + dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", + cdns->bus.link_id); + return 0; + } + + ret = sdw_cdns_enable_interrupt(cdns, false); + if (ret < 0) { + dev_err(dev, "cannot disable interrupts on suspend\n"); + return ret; + } + + ret = intel_link_power_down(sdw); + if (ret) { + dev_err(dev, "Link power down failed: %d", ret); + return ret; + } + + intel_shim_wake(sdw, false); + + return 0; +} + static int intel_resume(struct device *dev) { struct sdw_cdns *cdns = dev_get_drvdata(dev); @@ -1321,10 +1387,44 @@ static int intel_resume(struct device *dev) return ret; } +static int intel_resume_runtime(struct device *dev) +{ + struct sdw_cdns *cdns = dev_get_drvdata(dev); + struct sdw_intel *sdw = cdns_to_intel(cdns); + int ret; + + if (cdns->bus.prop.hw_disabled) { + dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", + cdns->bus.link_id); + return 0; + } + + ret = intel_init(sdw); + if (ret) { + dev_err(dev, "%s failed: %d", __func__, ret); + return ret; + } + + ret = sdw_cdns_enable_interrupt(cdns, true); + if (ret < 0) { + dev_err(dev, "cannot enable interrupts during resume\n"); + return ret; + } + + ret = sdw_cdns_exit_reset(cdns); + if (ret < 0) { + dev_err(dev, "unable to exit bus reset sequence during resume\n"); + return ret; + } + + return ret; +} + #endif static const struct dev_pm_ops intel_pm = { SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume) + SET_RUNTIME_PM_OPS(intel_suspend_runtime, intel_resume_runtime, NULL) }; struct sdw_md_driver intel_sdw_driver = { From 92011c39e722ccb82e2f09d17130167f5a68ecf3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 11 Oct 2019 17:15:41 -0500 Subject: [PATCH 071/110] soundwire: intel: reset pm_runtime status during system resume The system resume does the entire bus re-initialization and brings it to full-power. If the device was pm_runtime suspended, there is no need to run the pm_runtime resume sequence after the system runtime. Follow the documentation from runtime_pm.rst, and conditionally disable, set_active and re-enable the device on system resume. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 30 ++++++++++++++++++++++++++++++ include/linux/soundwire/sdw.h | 1 + 2 files changed, 31 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 6505813487326a..c3cd7d2d5a3426 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1298,6 +1298,7 @@ static int intel_master_remove(struct sdw_master_device *md) static int intel_suspend(struct device *dev) { + struct sdw_master_device *md = to_sdw_master_device(dev); struct sdw_cdns *cdns = dev_get_drvdata(dev); struct sdw_intel *sdw = cdns_to_intel(cdns); int ret; @@ -1308,6 +1309,20 @@ static int intel_suspend(struct device *dev) return 0; } + if (pm_runtime_status_suspended(dev)) { + dev_dbg(dev, + "%s: pm_runtime status: suspended\n", + __func__); + + /* + * keep track of the state for the system resume, where + * we will need to reset the pm_runtime status to active + */ + md->pm_runtime_suspended = true; + + return 0; + } + ret = sdw_cdns_enable_interrupt(cdns, false); if (ret < 0) { dev_err(dev, "cannot disable interrupts on suspend\n"); @@ -1356,6 +1371,7 @@ static int intel_suspend_runtime(struct device *dev) static int intel_resume(struct device *dev) { + struct sdw_master_device *md = to_sdw_master_device(dev); struct sdw_cdns *cdns = dev_get_drvdata(dev); struct sdw_intel *sdw = cdns_to_intel(cdns); int ret; @@ -1366,6 +1382,20 @@ static int intel_resume(struct device *dev) return 0; } + if (md->pm_runtime_suspended) { + dev_dbg(dev, + "%s: pm_runtime status was suspended, forcing active\n", + __func__); + + /* follow required sequence from runtime_pm.rst */ + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_enable(dev); + + md->pm_runtime_suspended = false; + } + ret = intel_init(sdw); if (ret) { dev_err(dev, "%s failed: %d", __func__, ret); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 17ec786d3a1531..c06c515abad9b5 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -583,6 +583,7 @@ struct sdw_master_device { struct device dev; int link_id; struct sdw_md_driver *driver; + bool pm_runtime_suspended; void *pdata; /* core does not touch */ }; From 657246654545420126b9ddcedbf593b7edc2771b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 26 Sep 2019 18:26:11 -0500 Subject: [PATCH 072/110] soundwire: bus: add helper to reset Slave status to UNATTACHED When resuming, we need to re-enumerate and restart from UNATTACHED. This will help implement a more robust state machine avoiding race conditions on resume. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 24 ++++++++++++++++++++++++ drivers/soundwire/bus.h | 2 ++ 2 files changed, 26 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 0e761efd32f9cc..17c744c0ff4caa 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1107,3 +1107,27 @@ int sdw_handle_slave_status(struct sdw_bus *bus, return ret; } EXPORT_SYMBOL(sdw_handle_slave_status); + +void sdw_clear_slave_status(struct sdw_bus *bus) +{ + struct sdw_slave *slave; + int i; + + /* Check all non-zero devices */ + for (i = 1; i <= SDW_MAX_DEVICES; i++) { + mutex_lock(&bus->bus_lock); + if (test_bit(i, bus->assigned) == false) { + mutex_unlock(&bus->bus_lock); + continue; + } + mutex_unlock(&bus->bus_lock); + + slave = sdw_get_slave(bus, i); + if (!slave) + continue; + + if (slave->status != SDW_SLAVE_UNATTACHED) + sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED); + } +} +EXPORT_SYMBOL(sdw_clear_slave_status); diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index acb8d11a4c84fe..cdbd11292cc323 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -165,4 +165,6 @@ sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val) return sdw_write(slave, addr, tmp); } +void sdw_clear_slave_status(struct sdw_bus *bus); + #endif /* __SDW_BUS_H */ From c6e53c3dada861b0dd7d9dd7ba8459151f0640c1 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 26 Sep 2019 18:32:52 -0500 Subject: [PATCH 073/110] soundwire: intel: call helper to reset Slave states on resume This helps make sure they are all UNATTACHED. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index c3cd7d2d5a3426..36e09e273eda67 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1435,6 +1435,9 @@ static int intel_resume_runtime(struct device *dev) return ret; } + /* make sure all Slaves are tagged as UNATTACHED */ + sdw_clear_slave_status(&sdw->cdns.bus); + ret = sdw_cdns_enable_interrupt(cdns, true); if (ret < 0) { dev_err(dev, "cannot enable interrupts during resume\n"); From b9beb3b2effd21e35ae096de4707cf9afa229347 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 26 Sep 2019 18:37:00 -0500 Subject: [PATCH 074/110] soundwire: bus: check first if Slaves become UNATTACHED Before checking for the presence of Device0, we first need to clean-up the internal state of Slaves that are no longer attached. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 17c744c0ff4caa..65521c2ed2e1ca 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1033,6 +1033,24 @@ int sdw_handle_slave_status(struct sdw_bus *bus, struct sdw_slave *slave; int i, ret = 0; + /* first check if any Slaves fell off the bus */ + for (i = 1; i <= SDW_MAX_DEVICES; i++) { + mutex_lock(&bus->bus_lock); + if (test_bit(i, bus->assigned) == false) { + mutex_unlock(&bus->bus_lock); + continue; + } + mutex_unlock(&bus->bus_lock); + + slave = sdw_get_slave(bus, i); + if (!slave) + continue; + + if (status[i] == SDW_SLAVE_UNATTACHED && + slave->status != SDW_SLAVE_UNATTACHED) + sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED); + } + if (status[0] == SDW_SLAVE_ATTACHED) { dev_dbg(bus->dev, "Slave attached, programming device number\n"); ret = sdw_program_device_num(bus); From 8e37222b3a0f92e948faf5c184eb846a11f04f9a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 26 Sep 2019 18:47:35 -0500 Subject: [PATCH 075/110] soundwire: add enumeration_complete signaling First add the init_completion() and complete() calls to test their symmetry. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 19 +++++++++++++++++++ drivers/soundwire/slave.c | 1 + 2 files changed, 20 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 65521c2ed2e1ca..b18110726273e5 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -637,6 +637,25 @@ static void sdw_modify_slave_status(struct sdw_slave *slave, enum sdw_slave_status status) { mutex_lock(&slave->bus->bus_lock); + + dev_vdbg(&slave->dev, + "%s: changing status slave %d status %d new status %d\n", + __func__, slave->dev_num, slave->status, status); + + if (status == SDW_SLAVE_UNATTACHED) { + dev_dbg(&slave->dev, + "%s: initializing completion for Slave %d\n", + __func__, slave->dev_num); + + init_completion(&slave->enumeration_complete); + + } else if (status == SDW_SLAVE_ATTACHED) { + dev_dbg(&slave->dev, + "%s: signaling completion for Slave %d\n", + __func__, slave->dev_num); + + complete(&slave->enumeration_complete); + } slave->status = status; mutex_unlock(&slave->bus->bus_lock); } diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index 81b94cd3985e73..5c6c744e071328 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -51,6 +51,7 @@ static int sdw_slave_add(struct sdw_bus *bus, slave->dev.type = &sdw_slave_type; slave->bus = bus; slave->status = SDW_SLAVE_UNATTACHED; + init_completion(&slave->enumeration_complete); slave->dev_num = 0; init_completion(&slave->probe_complete); slave->probed = false; From ba0197f5922278c503449db6f63add81b6a23e96 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 4 Nov 2019 12:47:01 -0600 Subject: [PATCH 076/110] soundwire: bus: add initialization_complete signaling init_completion() is called when the Slave device becomes unattached, as done with enumeration_complete. The difference with the enumeration_complete case is that complete() is signaled when the Slave device is fully initialized after the .update_status() callback is called. A Slave device driver can decide to wait on either of the two complete() cases, depending on its initialization code and requirements. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 8 ++++++++ drivers/soundwire/slave.c | 1 + 2 files changed, 9 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index b18110726273e5..93856747b93412 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -648,6 +648,7 @@ static void sdw_modify_slave_status(struct sdw_slave *slave, __func__, slave->dev_num); init_completion(&slave->enumeration_complete); + init_completion(&slave->initialization_complete); } else if (status == SDW_SLAVE_ATTACHED) { dev_dbg(&slave->dev, @@ -1050,6 +1051,7 @@ int sdw_handle_slave_status(struct sdw_bus *bus, { enum sdw_slave_status prev_status; struct sdw_slave *slave; + bool attached_initializing; int i, ret = 0; /* first check if any Slaves fell off the bus */ @@ -1095,6 +1097,8 @@ int sdw_handle_slave_status(struct sdw_bus *bus, if (!slave) continue; + attached_initializing = false; + switch (status[i]) { case SDW_SLAVE_UNATTACHED: if (slave->status == SDW_SLAVE_UNATTACHED) @@ -1121,6 +1125,8 @@ int sdw_handle_slave_status(struct sdw_bus *bus, if (prev_status == SDW_SLAVE_ALERT) break; + attached_initializing = true; + ret = sdw_initialize_slave(slave); if (ret) dev_err(bus->dev, @@ -1139,6 +1145,8 @@ int sdw_handle_slave_status(struct sdw_bus *bus, if (ret) dev_err(slave->bus->dev, "Update Slave status failed:%d\n", ret); + if (attached_initializing) + complete(&slave->initialization_complete); } return ret; diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index 5c6c744e071328..543f4ddd9de0fb 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -52,6 +52,7 @@ static int sdw_slave_add(struct sdw_bus *bus, slave->bus = bus; slave->status = SDW_SLAVE_UNATTACHED; init_completion(&slave->enumeration_complete); + init_completion(&slave->initialization_complete); slave->dev_num = 0; init_completion(&slave->probe_complete); slave->probed = false; From 12c720ed413ebfce92e6c5de265cb0e82c0950d3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Oct 2019 21:05:13 -0500 Subject: [PATCH 077/110] soundwire: intel: disable pm_runtime when removing a master Prevent race conditions between remove and resume by disabling pm_runtime. Note that this only takes care of pm_runtime at the Master level, the same precautions are needed when removing a Slave device. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 36e09e273eda67..f0f9a62525228d 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1275,6 +1275,8 @@ static int intel_master_remove(struct sdw_master_device *md) { struct sdw_intel *sdw; + pm_runtime_disable(&md->dev); + sdw = md->pdata; if (!sdw->cdns.bus.prop.hw_disabled) { From 5211c36c74f014a8c6fa85cd67cf902965c7c9c1 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Oct 2019 21:02:08 -0500 Subject: [PATCH 078/110] soundwire: bus: disable pm_runtime in sdw_slave_delete Before removing the slave device, disable pm_runtime to prevent any race condition with the resume being executed after the bus and slave devices are removed. Since this pm_runtime_disable() is handled in common routines, implementations of Slave drivers do not need to call it in their .remove() routine. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 93856747b93412..7d02b0ae68e2e7 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -113,6 +113,8 @@ static int sdw_delete_slave(struct device *dev, void *data) struct sdw_slave *slave = to_sdw_slave_device(dev); struct sdw_bus *bus = slave->bus; + pm_runtime_disable(dev); + sdw_slave_debugfs_exit(slave); mutex_lock(&bus->bus_lock); From 00ec1c6f6d27a29889b4fdf3ec9f15a2e2b483d0 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Sat, 12 Oct 2019 14:38:06 -0500 Subject: [PATCH 079/110] soundwire: stream: update state machine and add state checks The state machine and notes don't accurately explain or allow transitions from STREAM_DEPREPARED and STREAM_DISABLED. Add more explanations and allow for more transitions as a result of a trigger_stop(), trigger_suspend() and prepare(), depending on the ALSA/ASoC layer behavior defined by the INFO_RESUME and INFO_PAUSE flags. Also add basic checks to help debug inconsistent states and illegal state machine transitions. Signed-off-by: Pierre-Louis Bossart --- Documentation/driver-api/soundwire/stream.rst | 63 +++++++++++++------ drivers/soundwire/stream.c | 38 +++++++++++ 2 files changed, 83 insertions(+), 18 deletions(-) diff --git a/Documentation/driver-api/soundwire/stream.rst b/Documentation/driver-api/soundwire/stream.rst index 5351bd2f34a870..9b7418ff8d59aa 100644 --- a/Documentation/driver-api/soundwire/stream.rst +++ b/Documentation/driver-api/soundwire/stream.rst @@ -156,22 +156,27 @@ Below shows the SoundWire stream states and state transition diagram. :: +-----------+ +------------+ +----------+ +----------+ | ALLOCATED +---->| CONFIGURED +---->| PREPARED +---->| ENABLED | | STATE | | STATE | | STATE | | STATE | - +-----------+ +------------+ +----------+ +----+-----+ - ^ - | - | - v - +----------+ +------------+ +----+-----+ + +-----------+ +------------+ +---+--+---+ +----+-----+ + ^ ^ ^ + | | | + __| |___________ | + | | | + v | v + +----------+ +-----+------+ +-+--+-----+ | RELEASED |<----------+ DEPREPARED |<-------+ DISABLED | | STATE | | STATE | | STATE | +----------+ +------------+ +----------+ -NOTE: State transition between prepare and deprepare is supported in Spec -but not in the software (subsystem) +NOTE: State transitions between ``SDW_STREAM_ENABLED`` and +``SDW_STREAM_DISABLED`` are only relevant when then INFO_PAUSE flag is +supported at the ALSA/ASoC level. Likewise the transition between +``SDW_DISABLED_STATE`` and ``SDW_PREPARED_STATE`` depends on the +INFO_RESUME flag. -NOTE2: Stream state transition checks need to be handled by caller -framework, for example ALSA/ASoC. No checks for stream transition exist in -SoundWire subsystem. +NOTE2: The framework implements basic state transition checks, but +does not e.g. check if a transition from DISABLED to ENABLED is valid +on a specific platform. Such tests need to be added at the ALSA/ASoC +level. Stream State Operations ----------------------- @@ -246,6 +251,9 @@ SDW_STREAM_PREPARED Prepare state of stream. Operations performed before entering in this state: + (0) Steps 1 and 2 are omitted in the case of a resume operation, + where the bus bandwidth is known. + (1) Bus parameters such as bandwidth, frame shape, clock frequency, are computed based on current stream as well as already active stream(s) on Bus. Re-computation is required to accommodate current @@ -270,13 +278,15 @@ Prepare state of stream. Operations performed before entering in this state: After all above operations are successful, stream state is set to ``SDW_STREAM_PREPARED``. -Bus implements below API for PREPARE state which needs to be called once per -stream. From ASoC DPCM framework, this stream state is linked to -.prepare() operation. +Bus implements below API for PREPARE state which needs to be called +once per stream. From ASoC DPCM framework, this stream state is linked +to .prepare() operation. Since the .trigger() operations may not +follow the .prepare(), a direct transitions from +``SDW_STREAM_PREPARED`` to ``SDW_STREAM_DEPREPARED`` is allowed. .. code-block:: c - int sdw_prepare_stream(struct sdw_stream_runtime * stream); + int sdw_prepare_stream(struct sdw_stream_runtime * stream, bool resume); SDW_STREAM_ENABLED @@ -332,6 +342,14 @@ Bus implements below API for DISABLED state which needs to be called once per stream. From ASoC DPCM framework, this stream state is linked to .trigger() stop operation. +When the INFO_PAUSE flag is supported, a direct transition to +``SDW_STREAM_ENABLED`` is allowed. + +For resume operations where ASoC will use the .prepare() callback, the +stream can transition from ``SDW_STREAM_DISABLED`` to +``SDW_STREAM_PREPARED``, with all required settings restored but +without updating the bandwidth and bit allocation. + .. code-block:: c int sdw_disable_stream(struct sdw_stream_runtime * stream); @@ -353,9 +371,18 @@ state: After all above operations are successful, stream state is set to ``SDW_STREAM_DEPREPARED``. -Bus implements below API for DEPREPARED state which needs to be called once -per stream. From ASoC DPCM framework, this stream state is linked to -.trigger() stop operation. +Bus implements below API for DEPREPARED state which needs to be called +once per stream. ALSA/ASoC do not have a concept of 'deprepare', and +the mapping from this stream state to ALSA/ASoC operation may be +implementation specific. + +When the INFO_PAUSE flag is supported, the stream state is linked to +the .hw_free() operation - the stream is not deprepared on a +TRIGGER_STOP. + +Other implementations may transition to the ``SDW_STREAM_DEPREPARED`` +state on TRIGGER_STOP, should they require a transition through the +``SDW_STREAM_PREPARED`` state. .. code-block:: c diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index e69f94a8c3a865..0a074d445b8d99 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1553,10 +1553,21 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); + if (stream->state != SDW_STREAM_CONFIGURED && + stream->state != SDW_STREAM_DEPREPARED && + stream->state != SDW_STREAM_DISABLED) { + pr_err("%s: %s: inconsistent state state %d\n", + __func__, stream->name, stream->state); + ret = -EINVAL; + goto state_err; + } + ret = _sdw_prepare_stream(stream); + if (ret < 0) pr_err("Prepare for stream:%s failed: %d\n", stream->name, ret); +state_err: sdw_release_bus_lock(stream); return ret; } @@ -1621,10 +1632,19 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); + if (stream->state != SDW_STREAM_PREPARED && + stream->state != SDW_STREAM_DISABLED) { + pr_err("%s: %s: inconsistent state state %d\n", + __func__, stream->name, stream->state); + ret = -EINVAL; + goto state_err; + } + ret = _sdw_enable_stream(stream); if (ret < 0) pr_err("Enable for stream:%s failed: %d\n", stream->name, ret); +state_err: sdw_release_bus_lock(stream); return ret; } @@ -1697,10 +1717,18 @@ int sdw_disable_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); + if (stream->state != SDW_STREAM_ENABLED) { + pr_err("%s: %s: inconsistent state state %d\n", + __func__, stream->name, stream->state); + ret = -EINVAL; + goto state_err; + } + ret = _sdw_disable_stream(stream); if (ret < 0) pr_err("Disable for stream:%s failed: %d\n", stream->name, ret); +state_err: sdw_release_bus_lock(stream); return ret; } @@ -1755,10 +1783,20 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream) } sdw_acquire_bus_lock(stream); + + if (stream->state != SDW_STREAM_PREPARED && + stream->state != SDW_STREAM_DISABLED) { + pr_err("%s: %s: inconsistent state state %d\n", + __func__, stream->name, stream->state); + ret = -EINVAL; + goto state_err; + } + ret = _sdw_deprepare_stream(stream); if (ret < 0) pr_err("De-prepare for stream:%d failed: %d\n", ret, ret); +state_err: sdw_release_bus_lock(stream); return ret; } From bcced8687218901db4bb6d71bc6fa8dd65e774ed Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 20 Sep 2019 14:48:26 +0800 Subject: [PATCH 080/110] soundwire: stream: only prepare stream when it is configured. We don't need to prepare the stream again if the stream is already prepared. sdw_prepare_stream() could be called multiple times without calling sdw_deprepare_stream(). We call sdw_prepare_stream() in the prepare dai ops and sdw_deprepare_stream() in the hw_free dai ops. If an xrun happens, sdw_prepare_stream() will be called but sdw_deprepare_stream() will not, which results in an imbalance and an invalid total bandwidth. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/stream.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 0a074d445b8d99..38375c9124e423 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1544,7 +1544,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) */ int sdw_prepare_stream(struct sdw_stream_runtime *stream) { - int ret = 0; + int ret; if (!stream) { pr_err("SoundWire: Handle not found for stream\n"); @@ -1553,6 +1553,11 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); + if (stream->state == SDW_STREAM_PREPARED) { + ret = 0; + goto state_err; + } + if (stream->state != SDW_STREAM_CONFIGURED && stream->state != SDW_STREAM_DEPREPARED && stream->state != SDW_STREAM_DISABLED) { From 81f3108596368a223321dae7b903320db2b1f13b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 15 Oct 2019 09:43:47 -0500 Subject: [PATCH 081/110] soundwire: stream: do not update parameters during DISABLED-PREPARED transition After a system suspend, the ALSA/ASoC core will invoke the .prepare() callback and a TRIGGER_START when INFO_RESUME is not supported. Likewise, when an underflow occurs, the .prepare callback will be invoked. In both cases, the stream can be in DISABLED mode, and will transition into the PREPARED mode. We however don't want the bus bandwidth to be recomputed. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/stream.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 38375c9124e423..3447ef7d55e32e 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1460,7 +1460,8 @@ static void sdw_release_bus_lock(struct sdw_stream_runtime *stream) } } -static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) +static int _sdw_prepare_stream(struct sdw_stream_runtime *stream, + bool update_params) { struct sdw_master_runtime *m_rt; struct sdw_bus *bus = NULL; @@ -1480,6 +1481,9 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) return -EINVAL; } + if (!update_params) + goto program_params; + /* Increment cumulative bus bandwidth */ /* TODO: Update this during Device-Device support */ bus->params.bandwidth += m_rt->stream->params.rate * @@ -1495,6 +1499,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) } } +program_params: /* Program params */ ret = sdw_program_params(bus); if (ret < 0) { @@ -1544,6 +1549,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) */ int sdw_prepare_stream(struct sdw_stream_runtime *stream) { + bool update_params = true; int ret; if (!stream) { @@ -1567,7 +1573,16 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream) goto state_err; } - ret = _sdw_prepare_stream(stream); + /* + * when the stream is DISABLED, this means sdw_prepare_stream() + * is called as a result of an underflow or a resume operation. + * In this case, the bus parameters shall not be recomputed, but + * still need to be re-applied + */ + if (stream->state == SDW_STREAM_DISABLED) + update_params = false; + + ret = _sdw_prepare_stream(stream, update_params); if (ret < 0) pr_err("Prepare for stream:%s failed: %d\n", stream->name, ret); From 237ddcc46fd010f6e446cfaf391d935d8ed4355b Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 14 Oct 2019 17:35:52 +0800 Subject: [PATCH 082/110] soundwire: intel: reinitialize IP+DSP in .prepare() The .prepare() callback is invoked for normal streaming, underflows or during the system resume transition. In the latter case, the context for the ALH PDIs is lost, and the DSP is not initialized properly either, but the bus parameters don't need to be recomputed. To avoid keeping track of state variables, it's simpler to just reinitialize the SHIM/ALH/Cadence/DSP settings in .prepare. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 40 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index f0f9a62525228d..ec6c58635a9937 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -117,6 +117,8 @@ struct sdw_intel { struct sdw_cdns cdns; int instance; struct sdw_intel_link_res *link_res; + struct snd_pcm_hw_params *hw_params; + struct sdw_cdns_pdi *pdi; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; #endif @@ -813,6 +815,8 @@ static int intel_hw_params(struct snd_pcm_substream *substream, intel_pdi_alh_configure(sdw, pdi); sdw_cdns_config_stream(cdns, ch, dir, pdi); + sdw->pdi = pdi; + sdw->hw_params = params; /* Inform DSP about PDI stream number */ ret = intel_params_stream(sdw, substream, dai, params, @@ -856,7 +860,11 @@ static int intel_hw_params(struct snd_pcm_substream *substream, static int intel_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_cdns_dma_data *dma; + int ch, dir; + int ret; dma = snd_soc_dai_get_dma_data(dai, substream); if (!dma) { @@ -865,7 +873,35 @@ static int intel_prepare(struct snd_pcm_substream *substream, return -EIO; } - return sdw_prepare_stream(dma->stream); + /* + * .prepare() is called after system resume, where we need to + * reinitialize the SHIM/ALH/Cadence IP. To avoid dealing with + * complicated state machines, we just re-initialize in all + * cases since there are no side effects. + */ + + /* configure stream */ + ch = params_channels(sdw->hw_params); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + dir = SDW_DATA_DIR_RX; + else + dir = SDW_DATA_DIR_TX; + + intel_pdi_shim_configure(sdw, sdw->pdi); + intel_pdi_alh_configure(sdw, sdw->pdi); + sdw_cdns_config_stream(cdns, ch, dir, sdw->pdi); + + /* Inform DSP about PDI stream number */ + ret = intel_params_stream(sdw, substream, dai, sdw->hw_params, + sdw->instance, + sdw->pdi->intel_alh_id); + if (ret) + goto err; + + ret = sdw_prepare_stream(dma->stream); + +err: + return ret; } static int intel_trigger(struct snd_pcm_substream *substream, int cmd, @@ -936,6 +972,8 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) return ret; } + sdw->hw_params = NULL; + sdw->pdi = NULL; sdw_release_stream(dma->stream); return 0; From 7802c0902d472e951c064f4961968a10edf12635 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Sat, 12 Oct 2019 12:32:57 -0500 Subject: [PATCH 083/110] [HACK] add traces to debug aplay suspend/resume issue Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 52 +++++++++++++++++++++++++++++++++++--- drivers/soundwire/stream.c | 19 ++++++++++++++ sound/soc/Makefile | 2 ++ sound/soc/sof/pcm.c | 7 +++++ sound/soc/sof/pm.c | 18 +++++++++++++ 5 files changed, 95 insertions(+), 3 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index ec6c58635a9937..927b32b46d2614 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -762,6 +762,8 @@ static int intel_startup(struct snd_pcm_substream *substream, struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); int ret; + dev_err(dai->dev, "%s: %s: start\n", __func__, dai->name); + ret = pm_runtime_get_sync(cdns->dev); if (ret < 0 && ret != -EACCES) { dev_err_ratelimited(cdns->dev, @@ -770,7 +772,11 @@ static int intel_startup(struct snd_pcm_substream *substream, pm_runtime_put_noidle(cdns->dev); } - return sdw_stream_setup(substream, dai); + ret = sdw_stream_setup(substream, dai); + + dev_err(dai->dev, "%s: %s: done\n", __func__, dai->name); + + return ret; } static int intel_hw_params(struct snd_pcm_substream *substream, @@ -787,6 +793,8 @@ static int intel_hw_params(struct snd_pcm_substream *substream, int ret; bool pcm = true; + dev_err(dai->dev, "%s: %s: start\n", __func__, dai->name); + dma = snd_soc_dai_get_dma_data(dai, substream); if (!dma) return -EIO; @@ -854,6 +862,8 @@ static int intel_hw_params(struct snd_pcm_substream *substream, kfree(pconfig); error: + + dev_err(dai->dev, "%s: %s: done\n", __func__, dai->name); return ret; } @@ -866,6 +876,8 @@ static int intel_prepare(struct snd_pcm_substream *substream, int ch, dir; int ret; + dev_err(dai->dev, "%s: %s: start\n", __func__, dai->name); + dma = snd_soc_dai_get_dma_data(dai, substream); if (!dma) { dev_err(dai->dev, "failed to get dma data in %s", @@ -900,6 +912,7 @@ static int intel_prepare(struct snd_pcm_substream *substream, ret = sdw_prepare_stream(dma->stream); + dev_err(dai->dev, "%s: %s: done\n", __func__, dai->name); err: return ret; } @@ -910,6 +923,8 @@ static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct sdw_cdns_dma_data *dma; int ret; + dev_err(dai->dev, "%s: %s: start\n", __func__, dai->name); + dma = snd_soc_dai_get_dma_data(dai, substream); if (!dma) { dev_err(dai->dev, "failed to get dma data in %s", __func__); @@ -917,14 +932,18 @@ static int intel_trigger(struct snd_pcm_substream *substream, int cmd, } switch (cmd) { + case SNDRV_PCM_TRIGGER_RESUME: + dev_err(dai->dev, "%s: %s: resume\n", __func__, dai->name); + /* fallthrough */ case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - case SNDRV_PCM_TRIGGER_RESUME: ret = sdw_enable_stream(dma->stream); break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: + dev_err(dai->dev, "%s: %s: suspend\n", __func__, dai->name); + /* fallthrough */ + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP: ret = sdw_disable_stream(dma->stream); break; @@ -938,6 +957,9 @@ static int intel_trigger(struct snd_pcm_substream *substream, int cmd, dev_err(dai->dev, "%s trigger %d failed: %d", __func__, cmd, ret); + + dev_err(dai->dev, "%s: %s: done\n", __func__, dai->name); + return ret; } @@ -949,6 +971,8 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) struct sdw_cdns_dma_data *dma; int ret; + dev_err(dai->dev, "%s: %s: start\n", __func__, dai->name); + dma = snd_soc_dai_get_dma_data(dai, substream); if (!dma) return -EIO; @@ -976,6 +1000,8 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) sdw->pdi = NULL; sdw_release_stream(dma->stream); + dev_err(dai->dev, "%s: %s: done\n", __func__, dai->name); + return 0; } @@ -985,6 +1011,8 @@ static void intel_shutdown(struct snd_pcm_substream *substream, struct sdw_cdns_dma_data *dma; struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + dev_err(dai->dev, "%s: %s: start\n", __func__, dai->name); + dma = snd_soc_dai_get_dma_data(dai, substream); if (!dma) return; @@ -994,6 +1022,8 @@ static void intel_shutdown(struct snd_pcm_substream *substream, pm_runtime_mark_last_busy(cdns->dev); pm_runtime_put_autosuspend(cdns->dev); + + dev_err(dai->dev, "%s: %s: done\n", __func__, dai->name); } static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, @@ -1349,6 +1379,8 @@ static int intel_suspend(struct device *dev) return 0; } + dev_err(dev, "%s start\n", __func__); + if (pm_runtime_status_suspended(dev)) { dev_dbg(dev, "%s: pm_runtime status: suspended\n", @@ -1377,6 +1409,8 @@ static int intel_suspend(struct device *dev) intel_shim_wake(sdw, false); + dev_err(dev, "%s done\n", __func__); + return 0; } @@ -1392,6 +1426,8 @@ static int intel_suspend_runtime(struct device *dev) return 0; } + dev_err(dev, "%s start\n", __func__); + ret = sdw_cdns_enable_interrupt(cdns, false); if (ret < 0) { dev_err(dev, "cannot disable interrupts on suspend\n"); @@ -1406,6 +1442,8 @@ static int intel_suspend_runtime(struct device *dev) intel_shim_wake(sdw, false); + dev_err(dev, "%s done\n", __func__); + return 0; } @@ -1422,6 +1460,8 @@ static int intel_resume(struct device *dev) return 0; } + dev_err(dev, "%s start\n", __func__); + if (md->pm_runtime_suspended) { dev_dbg(dev, "%s: pm_runtime status was suspended, forcing active\n", @@ -1454,6 +1494,8 @@ static int intel_resume(struct device *dev) return ret; } + dev_err(dev, "%s done\n", __func__); + return ret; } @@ -1469,6 +1511,8 @@ static int intel_resume_runtime(struct device *dev) return 0; } + dev_err(dev, "%s start\n", __func__); + ret = intel_init(sdw); if (ret) { dev_err(dev, "%s failed: %d", __func__, ret); @@ -1490,6 +1534,8 @@ static int intel_resume_runtime(struct device *dev) return ret; } + dev_err(dev, "%s done\n", __func__); + return ret; } diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 3447ef7d55e32e..5f5ae1998cbac1 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1552,6 +1552,8 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream) bool update_params = true; int ret; + pr_err("%s: %s start\n", __func__, stream->name); + if (!stream) { pr_err("SoundWire: Handle not found for stream\n"); return -EINVAL; @@ -1589,6 +1591,9 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream) state_err: sdw_release_bus_lock(stream); + + pr_err("%s: %s: done\n", __func__, stream->name); + return ret; } EXPORT_SYMBOL(sdw_prepare_stream); @@ -1645,6 +1650,8 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream) { int ret; + pr_err("%s: %s: start\n", __func__, stream->name); + if (!stream) { pr_err("SoundWire: Handle not found for stream\n"); return -EINVAL; @@ -1666,6 +1673,8 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream) state_err: sdw_release_bus_lock(stream); + + pr_err("%s: %s: done\n", __func__, stream->name); return ret; } EXPORT_SYMBOL(sdw_enable_stream); @@ -1730,6 +1739,8 @@ int sdw_disable_stream(struct sdw_stream_runtime *stream) { int ret; + pr_err("%s: %s: start\n", __func__, stream->name); + if (!stream) { pr_err("SoundWire: Handle not found for stream\n"); return -EINVAL; @@ -1750,6 +1761,9 @@ int sdw_disable_stream(struct sdw_stream_runtime *stream) state_err: sdw_release_bus_lock(stream); + + pr_err("%s: %s: done\n", __func__, stream->name); + return ret; } EXPORT_SYMBOL(sdw_disable_stream); @@ -1797,6 +1811,8 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream) { int ret; + pr_err("%s: %s: start\n", __func__, stream->name); + if (!stream) { pr_err("SoundWire: Handle not found for stream\n"); return -EINVAL; @@ -1818,6 +1834,9 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream) state_err: sdw_release_bus_lock(stream); + + pr_err("%s: %s: done\n", __func__, stream->name); + return ret; } EXPORT_SYMBOL(sdw_deprepare_stream); diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 861a21b7948443..429add4afe3827 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,3 +1,5 @@ +ccflags-y += -DDEBUG + # SPDX-License-Identifier: GPL-2.0 snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-utils.o soc-dai.o soc-component.o snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 3d5cd1b445ba0a..90e477a9a97b36 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -356,6 +356,9 @@ static int sof_pcm_trigger(struct snd_soc_component *component, return 0; } + dev_dbg(sdev->dev, "pcm: trigger resume stream %d dir %d cmd %d\n", + spcm->pcm.pcm_id, substream->stream, cmd); + /* set up hw_params */ ret = sof_pcm_prepare(component, substream); if (ret < 0) { @@ -389,6 +392,10 @@ static int sof_pcm_trigger(struct snd_soc_component *component, spcm->stream[substream->stream].suspend_ignored = true; return 0; } + + dev_dbg(sdev->dev, "pcm: trigger suspend stream %d dir %d cmd %d\n", + spcm->pcm.pcm_id, substream->stream, cmd); + /* fallthrough */ case SNDRV_PCM_TRIGGER_STOP: stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 560a937e048420..951f9ca9fc69a2 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -17,6 +17,8 @@ static int sof_restore_kcontrols(struct snd_sof_dev *sdev) int ipc_cmd, ctrl_type; int ret = 0; + dev_err(sdev->dev, "%s\n", __func__); + /* restore kcontrol values */ list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { /* reset readback offset for scontrol after resuming */ @@ -69,6 +71,8 @@ static int sof_restore_pipelines(struct snd_sof_dev *sdev) struct sof_ipc_cmd_hdr *hdr; int ret; + dev_err(sdev->dev, "%s\n", __func__); + /* restore pipeline components */ list_for_each_entry_reverse(swidget, &sdev->widget_list, list) { struct sof_ipc_comp_reply r; @@ -202,6 +206,8 @@ static int sof_send_pm_ctx_ipc(struct snd_sof_dev *sdev, int cmd) struct sof_ipc_pm_ctx pm_ctx; struct sof_ipc_reply reply; + dev_err(sdev->dev, "%s\n", __func__); + memset(&pm_ctx, 0, sizeof(pm_ctx)); /* configure ctx save ipc message */ @@ -220,6 +226,7 @@ static int sof_set_hw_params_upon_resume(struct snd_sof_dev *sdev) snd_pcm_state_t state; int dir; + dev_err(sdev->dev, "%s\n", __func__); /* * SOF requires hw_params to be set-up internally upon resume. * So, set the flag to indicate this for those streams that @@ -265,6 +272,8 @@ static int sof_resume(struct device *dev, bool runtime_resume) struct snd_sof_dev *sdev = dev_get_drvdata(dev); int ret; + dev_err(dev, "%s\n", __func__); + /* do nothing if dsp resume callbacks are not set */ if (!sof_ops(sdev)->resume || !sof_ops(sdev)->runtime_resume) return 0; @@ -337,6 +346,8 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) struct snd_sof_dev *sdev = dev_get_drvdata(dev); int ret; + dev_err(dev, "%s\n", __func__); + /* do nothing if dsp suspend callback is not set */ if (!sof_ops(sdev)->suspend) return 0; @@ -393,6 +404,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) int snd_sof_runtime_suspend(struct device *dev) { + dev_err(dev, "%s\n", __func__); return sof_suspend(dev, true); } EXPORT_SYMBOL(snd_sof_runtime_suspend); @@ -401,12 +413,14 @@ int snd_sof_runtime_idle(struct device *dev) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); + dev_err(dev, "%s\n", __func__); return snd_sof_dsp_runtime_idle(sdev); } EXPORT_SYMBOL(snd_sof_runtime_idle); int snd_sof_runtime_resume(struct device *dev) { + dev_err(dev, "%s\n", __func__); return sof_resume(dev, true); } EXPORT_SYMBOL(snd_sof_runtime_resume); @@ -433,6 +447,8 @@ int snd_sof_resume(struct device *dev) struct snd_sof_dev *sdev = dev_get_drvdata(dev); int ret; + dev_err(dev, "%s\n", __func__); + if (sdev->s0_suspend) { /* resume from D0I3 */ dev_dbg(sdev->dev, "DSP will exit from D0i3...\n"); @@ -462,6 +478,8 @@ int snd_sof_suspend(struct device *dev) struct snd_sof_dev *sdev = dev_get_drvdata(dev); int ret; + dev_err(dev, "%s\n", __func__); + if (sdev->s0_suspend) { /* suspend to D0i3 */ dev_dbg(sdev->dev, "DSP is trying to enter D0i3...\n"); From 9020a25ed2962542b70490598b5bd6e090fcb69d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 24 Oct 2019 13:29:07 -0500 Subject: [PATCH 084/110] soundwire: intel: update logs when Master is disabled Fix logs and remove spurious return Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 927b32b46d2614..2426ed3fb338ee 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1260,11 +1260,10 @@ static int intel_master_probe(struct sdw_master_device *md, void *link_ctx) return ret; } - if (sdw->cdns.bus.prop.hw_disabled) { - dev_info(&md->dev, "SoundWire master %d is disabled, ignoring\n", + if (sdw->cdns.bus.prop.hw_disabled) + dev_info(&md->dev, + "SoundWire master %d is disabled, will be ignored\n", sdw->cdns.bus.link_id); - return 0; - } return 0; } @@ -1279,7 +1278,8 @@ static int intel_master_startup(struct sdw_master_device *md) sdw = md->pdata; if (sdw->cdns.bus.prop.hw_disabled) { - dev_info(&md->dev, "SoundWire master %d is disabled, ignoring\n", + dev_info(&md->dev, + "SoundWire master %d is disabled, ignoring\n", sdw->cdns.bus.link_id); return 0; } From e51113c2f78e4b0dc41cf03d573538b25575ffb2 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 24 Oct 2019 13:43:14 -0500 Subject: [PATCH 085/110] soundwire: intel: pm_runtime idle scheduling Add quirk and pm_runtime idle scheduling to let the Master suspend if no Slaves become attached. This can happen when a link is not marked as disabled and has devices exposed in the DSDT, if the power is controlled by sideband means or the link includes a pluggable connector. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 2426ed3fb338ee..5a99b9e07b9a0a 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -29,8 +29,9 @@ * 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 BIT(0) +#define SDW_INTEL_MASTER_DISABLE_CLOCK_STOP BIT(1) +#define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE BIT(2) static int md_flags; module_param_named(sdw_md_flags, md_flags, int, 0444); @@ -1330,6 +1331,22 @@ static int intel_master_startup(struct sdw_master_device *md) pm_runtime_enable(&md->dev); } + /* + * Slave devices have an pm_runtime of '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(&md->dev); + return 0; err_interrupt: @@ -1452,6 +1469,7 @@ static int intel_resume(struct device *dev) struct sdw_master_device *md = to_sdw_master_device(dev); struct sdw_cdns *cdns = dev_get_drvdata(dev); struct sdw_intel *sdw = cdns_to_intel(cdns); + int link_flags; int ret; if (cdns->bus.prop.hw_disabled) { @@ -1474,6 +1492,10 @@ static int intel_resume(struct device *dev) pm_runtime_enable(dev); md->pm_runtime_suspended = false; + + link_flags = md_flags >> (sdw->cdns.bus.link_id * 8); + if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE)) + pm_runtime_idle(&md->dev); } ret = intel_init(sdw); From 8b2493d4d848e178884928e3814968ec9dbe02d2 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Tue, 29 Oct 2019 14:34:46 +0800 Subject: [PATCH 086/110] soundwire: fix jack detection failure when booting up There is a race condition between codec probe and jack status update, which prevents the jack status from being reported if it is already inserted when the bus starts. We need to wait until the codec is probed before checking for the slave->ops, which are initialized in the codec probe routine. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 7d02b0ae68e2e7..ab0c5edfd8ae9b 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1021,9 +1021,6 @@ static int sdw_update_slave_status(struct sdw_slave *slave, { unsigned long time; - if (!slave->ops || !slave->ops->update_status) - return 0; - if (!slave->probed) { /* * the slave status update is typically handled in an @@ -1040,6 +1037,10 @@ static int sdw_update_slave_status(struct sdw_slave *slave, return -ETIMEDOUT; } } + + if (!slave->ops || !slave->ops->update_status) + return 0; + return slave->ops->update_status(slave, status); } From 80851598a60103d23bd91070f4703045c6d3e16c Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 31 Oct 2019 10:16:47 +0800 Subject: [PATCH 087/110] soundwire: intel: clear slave status on intel_resume That will make sure slave is unattached before slave's resume function is called. Signed-off-by: Bard Liao --- drivers/soundwire/intel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 5a99b9e07b9a0a..b6a8415d5bf07c 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1504,6 +1504,9 @@ static int intel_resume(struct device *dev) return ret; } + /* make sure all Slaves are tagged as UNATTACHED */ + sdw_clear_slave_status(&sdw->cdns.bus); + ret = sdw_cdns_enable_interrupt(cdns, true); if (ret < 0) { dev_err(dev, "cannot enable interrupts during resume\n"); From 7f1b2daf3e5c081288af0d4bfd35edb21cb4b411 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 10 Jun 2019 19:31:58 -0500 Subject: [PATCH 088/110] soundwire: cadence_master: handle multiple status reports per Slave When a Slave reports multiple status in the sticky bits, find the latest configuration from the mirror of the PING frame status and update the status directly. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.c | 35 +++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 362fb6e49bfe09..a97552a49babe8 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -673,13 +673,36 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns, /* first check if Slave reported multiple status */ if (set_status > 1) { + u32 val; + dev_warn_ratelimited(cdns->dev, - "Slave reported multiple Status: %d\n", - mask); - /* - * TODO: we need to reread the status here by - * issuing a PING cmd - */ + "Slave %d reported multiple Status: %d\n", + i, mask); + + /* check latest status extracted from PING commands */ + val = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT); + val >>= (i * 2); + + switch (val & 0x3) { + case 0: + status[i] = SDW_SLAVE_UNATTACHED; + break; + case 1: + status[i] = SDW_SLAVE_ATTACHED; + break; + case 2: + status[i] = SDW_SLAVE_ALERT; + break; + case 3: + default: + status[i] = SDW_SLAVE_RESERVED; + break; + } + + dev_warn_ratelimited(cdns->dev, + "Slave %d status updated to %d\n", + i, status[i]); + } } From c04be17f5d68d5fc7266708a66e7fc13bd1c54bb Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 24 Apr 2018 15:28:28 +0530 Subject: [PATCH 089/110] soundwire: Add generic bandwidth allocation algorithm This algorithm computes bus parameters like clock frequency, frame shape and port transport parameters based on active stream(s) running on the bus. Developers can also implement their own .compute_params() callback for specific resource management algorithm, and set if before calling sdw_add_bus_master() Credits: this patch is based on an earlier internal contribution by Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah. All hard-coded values were removed from the initial contribution to use BIOS information instead. FIXME: remove checkpatch report WARNING: Reusing the krealloc arg is almost always a bug + group->rates = krealloc(group->rates, Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/Kconfig | 4 + drivers/soundwire/Makefile | 3 + drivers/soundwire/bus.c | 6 + drivers/soundwire/bus.h | 46 +- .../soundwire/generic_bandwidth_allocation.c | 407 ++++++++++++++++++ drivers/soundwire/intel.c | 3 + drivers/soundwire/stream.c | 12 + include/linux/soundwire/sdw.h | 3 + 8 files changed, 482 insertions(+), 2 deletions(-) create mode 100644 drivers/soundwire/generic_bandwidth_allocation.c diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig index c725d0a8b288b6..eb73c91b7dba87 100644 --- a/drivers/soundwire/Kconfig +++ b/drivers/soundwire/Kconfig @@ -24,6 +24,7 @@ config SOUNDWIRE_CADENCE config SOUNDWIRE_INTEL tristate "Intel SoundWire Master driver" select SOUNDWIRE_CADENCE + select SOUNDWIRE_GENERIC_ALLOCATION depends on ACPI && SND_SOC help SoundWire Intel Master driver. @@ -31,4 +32,7 @@ config SOUNDWIRE_INTEL enable this config option to get the SoundWire support for that device. +config SOUNDWIRE_GENERIC_ALLOCATION + tristate + endif diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index 89b29819dd3ad8..eb37d69cbdca96 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -7,6 +7,9 @@ soundwire-bus-objs := bus_type.o bus.o master.o slave.o mipi_disco.o stream.o obj-$(CONFIG_SOUNDWIRE) += soundwire-bus.o +soundwire-generic-allocation-objs := generic_bandwidth_allocation.o +obj-$(CONFIG_SOUNDWIRE_GENERIC_ALLOCATION) += soundwire-generic-allocation.o + ifdef CONFIG_DEBUG_FS soundwire-bus-objs += debugfs.o endif diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index ab0c5edfd8ae9b..5774d35bfe7df0 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -30,6 +30,12 @@ int sdw_add_bus_master(struct sdw_bus *bus) return -EINVAL; } + if (!bus->compute_params) { + dev_err(bus->dev, + "Bandwidth allocation not configured, compute_parems no set\n"); + return -EINVAL; + } + mutex_init(&bus->msg_lock); mutex_init(&bus->bus_lock); INIT_LIST_HEAD(&bus->slaves); diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index cdbd11292cc323..d2900cb4de18df 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -66,6 +66,7 @@ struct sdw_msg { }; #define SDW_DOUBLE_RATE_FACTOR 2 +#define SDW_STRM_RATE_GROUPING 1 extern int sdw_rows[SDW_FRAME_ROWS]; extern int sdw_cols[SDW_FRAME_COLS]; @@ -151,9 +152,50 @@ int sdw_transfer_defer(struct sdw_bus *bus, struct sdw_msg *msg, int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave, u32 addr, size_t count, u16 dev_num, u8 flags, u8 *buf); +/* Retrieve and return channel count from channel mask */ +static inline int sdw_ch_mask_to_ch(int ch_mask) +{ + int c = 0; + + for (c = 0; ch_mask; ch_mask >>= 1) + c += ch_mask & 1; + + return c; +} + +/* Fill transport parameter data structure */ +static inline void sdw_fill_xport_params(struct sdw_transport_params *params, + int port_num, bool grp_ctrl_valid, + int grp_ctrl, int sample_int, + int off1, int off2, + int hstart, int hstop, + int pack_mode, int lane_ctrl) +{ + params->port_num = port_num; + params->blk_grp_ctrl_valid = grp_ctrl_valid; + params->blk_grp_ctrl = grp_ctrl; + params->sample_interval = sample_int; + params->offset1 = off1; + params->offset2 = off2; + params->hstart = hstart; + params->hstop = hstop; + params->blk_pkg_mode = pack_mode; + params->lane_ctrl = lane_ctrl; +} + +/* Fill port parameter data structure */ +static inline void sdw_fill_port_params(struct sdw_port_params *params, + int port_num, int bps, + int flow_mode, int data_mode) +{ + params->num = port_num; + params->bps = bps; + params->flow_mode = flow_mode; + params->data_mode = data_mode; +} + /* Read-Modify-Write Slave register */ -static inline int -sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val) +static inline int sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val) { int tmp; diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c new file mode 100644 index 00000000000000..dc7ffa987963ac --- /dev/null +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -0,0 +1,407 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2015-2019 Intel Corporation. + +/* + * Bandwidth management algorithm based on 2^n gears + * + */ + +#include +#include +#include +#include +#include +#include "bus.h" + +#define SDW_STRM_RATE_GROUPING 1 + +struct sdw_group_params { + unsigned int rate; + int full_bw; + int payload_bw; + int hwidth; +}; + +struct sdw_group { + unsigned int count; + unsigned int max_size; + unsigned int *rates; +}; + +struct sdw_transport_data { + int hstart; + int hstop; + int block_offset; + int sub_block_offset; +}; + +static void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt, + struct sdw_transport_data *t_data) +{ + struct sdw_slave_runtime *s_rt = NULL; + struct sdw_port_runtime *p_rt; + int port_bo, sample_int; + unsigned int rate, bps, ch = 0; + + port_bo = t_data->block_offset; + + list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { + rate = m_rt->stream->params.rate; + bps = m_rt->stream->params.bps; + sample_int = (m_rt->bus->params.curr_dr_freq / rate); + + list_for_each_entry(p_rt, &s_rt->port_list, port_node) { + ch = sdw_ch_mask_to_ch(p_rt->ch_mask); + + sdw_fill_xport_params(&p_rt->transport_params, + p_rt->num, true, + SDW_BLK_GRP_CNT_1, + sample_int, port_bo, port_bo >> 8, + t_data->hstart, + t_data->hstop, + (SDW_BLK_GRP_CNT_1 * ch), 0x0); + + sdw_fill_port_params(&p_rt->port_params, + p_rt->num, bps, + SDW_PORT_FLOW_MODE_ISOCH, + SDW_PORT_DATA_MODE_NORMAL); + + port_bo += bps * ch; + } + } +} + +static void sdw_compute_master_ports(struct sdw_master_runtime *m_rt, + struct sdw_group_params *params, + int port_bo, int hstop) +{ + struct sdw_transport_data t_data = {0}; + struct sdw_port_runtime *p_rt; + struct sdw_bus *bus = m_rt->bus; + int sample_int, hstart = 0; + unsigned int rate, bps, ch, no_ch; + + rate = m_rt->stream->params.rate; + bps = m_rt->stream->params.bps; + ch = m_rt->ch_count; + sample_int = (bus->params.curr_dr_freq / rate); + + if (rate != params->rate) + return; + + t_data.hstop = hstop; + hstart = hstop - params->hwidth + 1; + t_data.hstart = hstart; + + list_for_each_entry(p_rt, &m_rt->port_list, port_node) { + no_ch = sdw_ch_mask_to_ch(p_rt->ch_mask); + + sdw_fill_xport_params(&p_rt->transport_params, p_rt->num, + true, SDW_BLK_GRP_CNT_1, sample_int, + port_bo, port_bo >> 8, hstart, hstop, + (SDW_BLK_GRP_CNT_1 * no_ch), 0x0); + + sdw_fill_port_params(&p_rt->port_params, + p_rt->num, bps, + SDW_PORT_FLOW_MODE_ISOCH, + SDW_PORT_DATA_MODE_NORMAL); + + /* Check for first entry */ + if (!(p_rt == list_first_entry(&m_rt->port_list, + struct sdw_port_runtime, + port_node))) { + port_bo += bps * ch; + continue; + } + + t_data.hstart = hstart; + t_data.hstop = hstop; + t_data.block_offset = port_bo; + t_data.sub_block_offset = 0; + port_bo += bps * ch; + } + + sdw_compute_slave_ports(m_rt, &t_data); +} + +static void _sdw_compute_port_params(struct sdw_bus *bus, + struct sdw_group_params *params, int count) +{ + struct sdw_master_runtime *m_rt = NULL; + int hstop = bus->params.col - 1; + int block_offset, port_bo, i; + + /* Run loop for all groups to compute transport parameters */ + for (i = 0; i < count; i++) { + port_bo = 1; + block_offset = 1; + + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + sdw_compute_master_ports(m_rt, ¶ms[i], + port_bo, hstop); + + block_offset += m_rt->ch_count * + m_rt->stream->params.bps; + port_bo = block_offset; + } + + hstop = hstop - params[i].hwidth; + } +} + +static int sdw_compute_group_params(struct sdw_bus *bus, + struct sdw_group_params *params, + int *rates, int count) +{ + struct sdw_master_runtime *m_rt = NULL; + int sel_col = bus->params.col; + unsigned int rate, bps, ch; + int i, column_needed = 0; + + /* Calculate bandwidth per group */ + for (i = 0; i < count; i++) { + params[i].rate = rates[i]; + params[i].full_bw = bus->params.curr_dr_freq / params[i].rate; + } + + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + rate = m_rt->stream->params.rate; + bps = m_rt->stream->params.bps; + ch = m_rt->ch_count; + + for (i = 0; i < count; i++) { + if (rate == params[i].rate) + params[i].payload_bw += bps * ch; + } + } + + for (i = 0; i < count; i++) { + params[i].hwidth = (sel_col * + params[i].payload_bw + params[i].full_bw - 1) / + params[i].full_bw; + + column_needed += params[i].hwidth; + } + + if (column_needed > sel_col - 1) + return -EINVAL; + + return 0; +} + +static int sdw_add_element_group_count(struct sdw_group *group, + unsigned int rate) +{ + int num = group->count; + int i; + + for (i = 0; i <= num; i++) { + if (rate == group->rates[i]) + break; + + if (i != num) + continue; + + if (group->count >= group->max_size) { + group->max_size += 1; + group->rates = krealloc(group->rates, + (sizeof(int) * group->max_size), + GFP_KERNEL); + if (!group->rates) + return -ENOMEM; + } + + group->rates[group->count++] = rate; + } + + return 0; +} + +static int sdw_get_group_count(struct sdw_bus *bus, + struct sdw_group *group) +{ + struct sdw_master_runtime *m_rt; + unsigned int rate; + int ret = 0; + + group->count = 0; + group->max_size = SDW_STRM_RATE_GROUPING; + group->rates = kcalloc(group->max_size, sizeof(int), GFP_KERNEL); + if (!group->rates) + return -ENOMEM; + + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + rate = m_rt->stream->params.rate; + if (m_rt == list_first_entry(&bus->m_rt_list, + struct sdw_master_runtime, + bus_node)) { + group->rates[group->count++] = rate; + + } else { + ret = sdw_add_element_group_count(group, rate); + if (ret < 0) + return ret; + } + } + + return ret; +} + +/** + * sdw_compute_port_params: Compute transport and port parameters + * + * @bus: SDW Bus instance + */ +static int sdw_compute_port_params(struct sdw_bus *bus) +{ + struct sdw_group_params *params = NULL; + struct sdw_group group; + int ret; + + ret = sdw_get_group_count(bus, &group); + if (ret < 0) + goto out; + + if (group.count == 0) + goto out; + + params = kcalloc(group.count, sizeof(*params), GFP_KERNEL); + if (!params) { + ret = -ENOMEM; + goto out; + } + + /* Compute transport parameters for grouped streams */ + ret = sdw_compute_group_params(bus, params, + &group.rates[0], group.count); + if (ret < 0) + goto out; + + _sdw_compute_port_params(bus, params, group.count); + +out: + kfree(params); + kfree(group.rates); + + return ret; +} + +static int sdw_select_row_col(struct sdw_bus *bus, int clk_freq) +{ + struct sdw_master_prop *prop = &bus->prop; + int frame_int, frame_freq; + int r, c; + + for (c = 0; c < SDW_FRAME_COLS; c++) { + for (r = 0; r < SDW_FRAME_ROWS; r++) { + if (sdw_rows[r] != prop->default_row || + sdw_cols[c] != prop->default_col) + continue; + + frame_int = sdw_rows[r] * sdw_cols[c]; + frame_freq = clk_freq / frame_int; + + if ((clk_freq - (frame_freq * SDW_FRAME_CTRL_BITS)) < + bus->params.bandwidth) + continue; + + bus->params.row = sdw_rows[r]; + bus->params.col = sdw_cols[c]; + return 0; + } + } + + return -EINVAL; +} + +/** + * sdw_compute_bus_params: Compute bus parameters + * + * @bus: SDW Bus instance + */ +static int sdw_compute_bus_params(struct sdw_bus *bus) +{ + unsigned int max_dr_freq, curr_dr_freq = 0; + struct sdw_master_prop *mstr_prop = NULL; + int i, clk_values, ret; + bool is_gear = false; + u32 *clk_buf; + + mstr_prop = &bus->prop; + if (!mstr_prop) + return -EINVAL; + + if (mstr_prop->num_clk_gears) { + clk_values = mstr_prop->num_clk_gears; + clk_buf = mstr_prop->clk_gears; + is_gear = true; + } else if (mstr_prop->num_clk_freq) { + clk_values = mstr_prop->num_clk_freq; + clk_buf = mstr_prop->clk_freq; + } else { + clk_values = 1; + clk_buf = NULL; + } + + max_dr_freq = mstr_prop->max_clk_freq * SDW_DOUBLE_RATE_FACTOR; + + for (i = 0; i < clk_values; i++) { + if (!clk_buf) + curr_dr_freq = max_dr_freq; + else + curr_dr_freq = (is_gear) ? + (max_dr_freq >> clk_buf[i]) : + clk_buf[i] * SDW_DOUBLE_RATE_FACTOR; + + if (curr_dr_freq <= bus->params.bandwidth) + continue; + + break; + + /* + * TODO: Check all the Slave(s) port(s) audio modes and find + * whether given clock rate is supported with glitchless + * transition. + */ + } + + if (i == clk_values) + return -EINVAL; + + ret = sdw_select_row_col(bus, curr_dr_freq); + if (ret < 0) + return -EINVAL; + + bus->params.curr_dr_freq = curr_dr_freq; + return 0; +} + +/** + * sdw_compute_params: Compute bus, transport and port parameters + * + * @bus: SDW Bus instance + */ +int sdw_compute_params(struct sdw_bus *bus) +{ + int ret; + + /* Computes clock frequency, frame shape and frame frequency */ + ret = sdw_compute_bus_params(bus); + if (ret < 0) { + dev_err(bus->dev, "Compute bus params failed: %d", ret); + return ret; + } + + /* Compute transport and port params */ + ret = sdw_compute_port_params(bus); + if (ret < 0) { + dev_err(bus->dev, "Compute transport params failed: %d", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(sdw_compute_params); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("SoundWire Generic Bandwidth Allocation"); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index b6a8415d5bf07c..499cdd06a942d6 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1255,6 +1255,9 @@ static int intel_master_probe(struct sdw_master_device *md, void *link_ctx) /* set driver data, accessed by snd_soc_dai_set_drvdata() */ dev_set_drvdata(&md->dev, &sdw->cdns); + /* use generic bandwidth allocation algorithm */ + sdw->cdns.bus.compute_params = sdw_compute_params; + ret = sdw_add_bus_master(&sdw->cdns.bus); if (ret) { dev_err(&md->dev, "sdw_add_bus_master fail: %d\n", ret); diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 5f5ae1998cbac1..f2054fcc6821e2 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -26,6 +26,8 @@ int sdw_rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147, 192, 200, 240, 256, 72, 144, 90, 180}; int sdw_cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16}; +EXPORT_SYMBOL(sdw_rows); +EXPORT_SYMBOL(sdw_cols); int sdw_find_col_index(int col) { @@ -1788,6 +1790,16 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) bus->params.bandwidth -= m_rt->stream->params.rate * m_rt->ch_count * m_rt->stream->params.bps; + /* Compute params */ + if (bus->compute_params) { + ret = bus->compute_params(bus); + if (ret < 0) { + dev_err(bus->dev, "Compute params failed: %d", + ret); + return ret; + } + } + /* Program params */ ret = sdw_program_params(bus); if (ret < 0) { diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index c06c515abad9b5..e5b290acba3bfb 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -904,6 +904,9 @@ struct sdw_stream_runtime { struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name); void sdw_release_stream(struct sdw_stream_runtime *stream); + +int sdw_compute_params(struct sdw_bus *bus); + int sdw_stream_add_master(struct sdw_bus *bus, struct sdw_stream_config *stream_config, struct sdw_port_config *port_config, From 16ff8693424cdf784e7a4369a0583904edfcec5a Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 2 Aug 2019 10:14:46 +0800 Subject: [PATCH 090/110] soundwire: dynamic_allocation: set grp_ctrl_valid false Some Realtek codecs don't support grp_ctrl_valid. Set it to false to prevent DPN_BlockCtrl2 reg write failed. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/generic_bandwidth_allocation.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index dc7ffa987963ac..6124c6f8561f32 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -54,7 +54,7 @@ static void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt, ch = sdw_ch_mask_to_ch(p_rt->ch_mask); sdw_fill_xport_params(&p_rt->transport_params, - p_rt->num, true, + p_rt->num, false, SDW_BLK_GRP_CNT_1, sample_int, port_bo, port_bo >> 8, t_data->hstart, @@ -97,7 +97,7 @@ static void sdw_compute_master_ports(struct sdw_master_runtime *m_rt, no_ch = sdw_ch_mask_to_ch(p_rt->ch_mask); sdw_fill_xport_params(&p_rt->transport_params, p_rt->num, - true, SDW_BLK_GRP_CNT_1, sample_int, + false, SDW_BLK_GRP_CNT_1, sample_int, port_bo, port_bo >> 8, hstart, hstop, (SDW_BLK_GRP_CNT_1 * no_ch), 0x0); From d0dee92596148c25d5328e875b064203f1e6f262 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 30 Jul 2019 16:14:18 -0500 Subject: [PATCH 091/110] soundwire: bus: fix device number leak on errors If the programming of the dev_number fails due to an IO error, a new device_number will be assigned, resulting in a leak. Make sure we only assign a device_number once per Slave device. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 39 ++++++++++++++++++++++------------- include/linux/soundwire/sdw.h | 4 +++- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 5774d35bfe7df0..5df0a92eb236db 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -500,26 +500,37 @@ static int sdw_get_device_num(struct sdw_slave *slave) static int sdw_assign_device_num(struct sdw_slave *slave) { int ret, dev_num; + bool new_device = false; + + dev_dbg(slave->bus->dev, "in %s\n", __func__); /* check first if device number is assigned, if so reuse that */ if (!slave->dev_num) { - mutex_lock(&slave->bus->bus_lock); - dev_num = sdw_get_device_num(slave); - mutex_unlock(&slave->bus->bus_lock); - if (dev_num < 0) { - dev_err(slave->bus->dev, "Get dev_num failed: %d\n", - dev_num); - return dev_num; + if (!slave->dev_num_sticky) { + mutex_lock(&slave->bus->bus_lock); + dev_num = sdw_get_device_num(slave); + mutex_unlock(&slave->bus->bus_lock); + if (dev_num < 0) { + dev_err(slave->bus->dev, "Get dev_num failed: %d\n", + dev_num); + return dev_num; + } + slave->dev_num = dev_num; + slave->dev_num_sticky = dev_num; + new_device = true; + } else { + slave->dev_num = slave->dev_num_sticky; } - } else { + } + + if (!new_device) dev_info(slave->bus->dev, - "Slave already registered dev_num:%d\n", + "Slave already registered, reusing dev_num:%d\n", slave->dev_num); - /* Clear the slave->dev_num to transfer message on device 0 */ - dev_num = slave->dev_num; - slave->dev_num = 0; - } + /* Clear the slave->dev_num to transfer message on device 0 */ + dev_num = slave->dev_num; + slave->dev_num = 0; ret = sdw_write_no_pm(slave, SDW_SCP_DEVNUMBER, dev_num); if (ret < 0) { @@ -529,7 +540,7 @@ static int sdw_assign_device_num(struct sdw_slave *slave) } /* After xfer of msg, restore dev_num */ - slave->dev_num = dev_num; + slave->dev_num = slave->dev_num_sticky; return 0; } diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index e5b290acba3bfb..450f279d38143b 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -546,7 +546,8 @@ struct sdw_slave_ops { * @debugfs: Slave debugfs * @node: node for bus list * @port_ready: Port ready completion flag for each Slave port - * @dev_num: Device Number assigned by Bus + * @dev_num: Current Device Number, values can be 0 or dev_num_sticky + * @dev_num_sticky: one-time static 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 @@ -570,6 +571,7 @@ struct sdw_slave { struct list_head node; struct completion *port_ready; u16 dev_num; + u16 dev_num_sticky; bool probed; struct completion probe_complete; struct completion enumeration_complete; From 07ae9503b6d706e35aa11c9b34d82e8174408635 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 19 Aug 2019 12:13:03 -0500 Subject: [PATCH 092/110] soundwire: intel: modify DMAT field for ALH The current value for this field is too low and results in some samples getting dropped and playback being faster than normal. Increase the value to a safer value. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 499cdd06a942d6..be833ac756e923 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -104,7 +104,7 @@ MODULE_PARM_DESC(sdw_md_flags, "SoundWire Intel Master device flags (0x0 all off #define SDW_ALH_STRMZCFG(x) (0x000 + (0x4 * (x))) #define SDW_ALH_NUM_STREAMS 64 -#define SDW_ALH_STRMZCFG_DMAT_VAL 0x3 +#define SDW_ALH_STRMZCFG_DMAT_VAL 0xf #define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0) #define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16) From cf496f1d1bde25b1b4802957c63ca866a6b6cba7 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 19 Aug 2019 12:18:05 -0500 Subject: [PATCH 093/110] soundwire: intel: fix factor of two in MCLK handling Somehow Intel folks were confused, the property is 2x what the mclk frequency actually is, as checked with the actual bus frequency. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index be833ac756e923..a10b36963e214e 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1182,6 +1182,7 @@ static int sdw_master_read_intel_prop(struct sdw_bus *bus) "intel-sdw-ip-clock", &prop->mclk_freq); + prop->mclk_freq /= 2; fwnode_property_read_u32(link, "intel-quirk-mask", &quirk_mask); From f6ec28985c785e83f1ecfe9f826e59caee16015c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jun 2019 12:29:42 -0500 Subject: [PATCH 094/110] soundwire: cadence_master: log register write info useful for debug, but can be verbose so only enable if strictly needed. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index a97552a49babe8..d7ecd6ffcbccfe 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -196,6 +196,7 @@ static inline u32 cdns_readl(struct sdw_cdns *cdns, int offset) static inline void cdns_writel(struct sdw_cdns *cdns, int offset, u32 value) { + dev_vdbg(cdns->dev, "%s %x %x\n", __func__, offset, value); writel(value, cdns->registers + offset); } From 26770ec1e2a7769141e695bd4f4a5345eadcc956 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 10 Jun 2019 19:21:39 -0500 Subject: [PATCH 095/110] add more traces to bus code Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 5df0a92eb236db..b500922999f1a7 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -502,6 +502,9 @@ static int sdw_assign_device_num(struct sdw_slave *slave) int ret, dev_num; bool new_device = false; + dev_dbg(slave->bus->dev, "in %s\n", __func__); + + dev_dbg(slave->bus->dev, "in %s\n", __func__); /* check first if device number is assigned, if so reuse that */ @@ -532,6 +535,7 @@ static int sdw_assign_device_num(struct sdw_slave *slave) dev_num = slave->dev_num; slave->dev_num = 0; + dev_dbg(slave->bus->dev, "in %s, writing dev_num %d\n", __func__, dev_num); ret = sdw_write_no_pm(slave, SDW_SCP_DEVNUMBER, dev_num); if (ret < 0) { dev_err(&slave->dev, "Program device_num %d failed: %d\n", @@ -649,6 +653,8 @@ static int sdw_program_device_num(struct sdw_bus *bus) } while (ret == 0 && count < (SDW_MAX_DEVICES * 2)); + dev_err(bus->dev, "End of %s ret %d\n", __func__, ret); + return ret; } @@ -1136,14 +1142,17 @@ int sdw_handle_slave_status(struct sdw_bus *bus, break; case SDW_SLAVE_ATTACHED: - if (slave->status == SDW_SLAVE_ATTACHED) + if (slave->status == SDW_SLAVE_ATTACHED) { + dev_err(bus->dev, "Slave %d status attached, was already attached, ignoring\n", i); break; - + } prev_status = slave->status; sdw_modify_slave_status(slave, SDW_SLAVE_ATTACHED); - if (prev_status == SDW_SLAVE_ALERT) + if (prev_status == SDW_SLAVE_ALERT) { + dev_err(bus->dev, "Slave %d status attached, was alert, ignoring\n", i); break; + } attached_initializing = true; @@ -1161,6 +1170,7 @@ int sdw_handle_slave_status(struct sdw_bus *bus, break; } + dev_err(bus->dev, "Updating Slave %d status\n", i); ret = sdw_update_slave_status(slave, status[i]); if (ret) dev_err(slave->bus->dev, From 5c9af85afdfe4e0181ec517bdbb2e1ba7307d94a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 1 Aug 2019 18:31:34 -0500 Subject: [PATCH 096/110] add traces for bus Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index b500922999f1a7..ec4796fcbde1ae 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1080,6 +1080,8 @@ int sdw_handle_slave_status(struct sdw_bus *bus, bool attached_initializing; int i, ret = 0; + dev_dbg(bus->dev, "%s: start\n", __func__); + /* first check if any Slaves fell off the bus */ for (i = 1; i <= SDW_MAX_DEVICES; i++) { mutex_lock(&bus->bus_lock); @@ -1175,10 +1177,16 @@ int sdw_handle_slave_status(struct sdw_bus *bus, if (ret) dev_err(slave->bus->dev, "Update Slave status failed:%d\n", ret); + if (attached_initializing) complete(&slave->initialization_complete); + + dev_err(bus->dev, "%s: Updating Slave %d status done\n", + __func__, i); } + dev_dbg(bus->dev, "%s: end\n", __func__); + return ret; } EXPORT_SYMBOL(sdw_handle_slave_status); From 383045b275878d8234fa2a64325874396aa264a6 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 11 Sep 2019 20:18:19 -0500 Subject: [PATCH 097/110] [HACK] soundwire: add dynamic DEBUG in makefile Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index eb37d69cbdca96..76a5c52b12b412 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -2,6 +2,7 @@ # # Makefile for soundwire core # +ccflags-y += -DDEBUG #Bus Objs soundwire-bus-objs := bus_type.o bus.o master.o slave.o mipi_disco.o stream.o From 4956261bbf896c77f0d9e20daae960bad1ed90bf Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 5 Nov 2019 10:44:49 +0800 Subject: [PATCH 098/110] ALSA: HDA: intel-dsp-config: add DMI info for Dell laptop The laptop doesn't use DMIC, but use SOF. Signed-off-by: Bard Liao --- sound/hda/intel-dsp-config.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c index be1df80ed01337..6e6e8662534d8a 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/intel-dsp-config.c @@ -204,6 +204,12 @@ static const struct config_entry config_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Google"), } }, + { + .ident = "Dell laptop", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + } + }, {} } }, From 64983a3fa69248a7c19c5f5fb6333d0f09c733ac Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 7 Nov 2019 16:34:27 +0800 Subject: [PATCH 099/110] ASoC: intel: sdw_rt711_rt1308_rt715: Use a fixed number of num_links and num__configs mc_probe could be called multiple times and card->num_links will decrease in each time when mc_probe is called. Same as num_configs. Signed-off-by: Bard Liao --- sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c index 919f77287ed06e..6c69a7ea57ec61 100644 --- a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c +++ b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c @@ -446,8 +446,8 @@ static int mc_probe(struct platform_device *pdev) if (sof_rt711_rt1308_rt715_quirk & SOF_SDW_MONO_SPK) { /* Remove rt1308-2 codec from dailink and codec_conf */ - card->num_links--; - card->num_configs--; + card->num_links = ARRAY_SIZE(dailink) - 1 ; + card->num_configs = ARRAY_SIZE(codec_conf) - 1; } /* Register the card */ From b8f1ab7555bc3c30afe99a8700308e0b2c7d2aaa Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 7 Nov 2019 14:40:08 +0800 Subject: [PATCH 100/110] ASoC: rt700: enable wake_capable Signed-off-by: Shuming Fan --- sound/soc/codecs/rt700-sdw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 5388b14be5f050..9e7a0bf9b5c634 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -399,6 +399,9 @@ static int rt700_read_prop(struct sdw_slave *slave) /* set the timeout values */ prop->clk_stop_timeout = 20; + /* wake-up event */ + prop->wake_capable = 1; + return 0; } From 3c89ee2863a02138c9d35af1357f995e5b0e37ba Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 7 Nov 2019 14:40:22 +0800 Subject: [PATCH 101/110] ASoC: rt711: enable wake_capable Signed-off-by: Shuming Fan --- sound/soc/codecs/rt711-sdw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index 7121fe720654fd..c8a6dfceae1e4f 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -402,6 +402,9 @@ static int rt711_read_prop(struct sdw_slave *slave) /* set the timeout values */ prop->clk_stop_timeout = 20; + /* wake-up event */ + prop->wake_capable = 1; + return 0; } From 321b31c05f87a54bb8e649804b7751e53b87e4cc Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 7 Nov 2019 14:40:34 +0800 Subject: [PATCH 102/110] ASoC: rt715: enable wake_capable Signed-off-by: Shuming Fan --- sound/soc/codecs/rt715-sdw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index 8ec9ef09f6c679..aa6b22236290fd 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -431,6 +431,9 @@ static int rt715_read_prop(struct sdw_slave *slave) /* set the timeout values */ prop->clk_stop_timeout = 20; + /* wake-up event */ + prop->wake_capable = 1; + return 0; } From 3bbd330ebea7dcd168b291368eb46362af79bc40 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 7 Nov 2019 14:45:10 +0800 Subject: [PATCH 103/110] ASoC: rt700: re-do io_init in cache_bypass mode when system resume Signed-off-by: Shuming Fan --- sound/soc/codecs/rt700.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 73911494711f14..44a8cafbad5bfa 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -1133,6 +1133,11 @@ int rt700_io_init(struct device *dev, struct sdw_slave *slave) if (rt700->hw_init) return 0; + if (rt700->first_init) { + regcache_cache_only(rt700->regmap, false); + regcache_cache_bypass(rt700->regmap, true); + } + /* * PM runtime is only enabled when a Slave reports as Attached */ @@ -1148,8 +1153,6 @@ int rt700_io_init(struct device *dev, struct sdw_slave *slave) pm_runtime_mark_last_busy(&slave->dev); pm_runtime_enable(&slave->dev); - - rt700->first_init = true; } pm_runtime_get_noresume(&slave->dev); @@ -1198,10 +1201,12 @@ int rt700_io_init(struct device *dev, struct sdw_slave *slave) /* Finish Initial Settings, set power to D3 */ regmap_write(rt700->regmap, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); - INIT_DELAYED_WORK(&rt700->jack_detect_work, + if (!rt700->first_init) { + INIT_DELAYED_WORK(&rt700->jack_detect_work, rt700_jack_detect_handler); - INIT_DELAYED_WORK(&rt700->jack_btn_check_work, + INIT_DELAYED_WORK(&rt700->jack_btn_check_work, rt700_btn_check_handler); + } /* * if set_jack callback occurred early than io_init, @@ -1210,6 +1215,11 @@ int rt700_io_init(struct device *dev, struct sdw_slave *slave) if (rt700->hs_jack) rt700_jack_init(rt700); + if (rt700->first_init) + regcache_cache_bypass(rt700->regmap, false); + else + rt700->first_init = true; + /* Mark Slave initialization complete */ rt700->hw_init = true; From 574eecd492c219c490735acf75a9d783c8bb9e55 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 7 Nov 2019 14:45:23 +0800 Subject: [PATCH 104/110] ASoC: rt711: re-do io_init in cache_bypass mode when system resume Signed-off-by: Shuming Fan --- sound/soc/codecs/rt711.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index 7190642534397d..4090dc09694d0b 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -1174,6 +1174,11 @@ int rt711_io_init(struct device *dev, struct sdw_slave *slave) if (rt711->hw_init) return 0; + if (rt711->first_init) { + regcache_cache_only(rt711->regmap, false); + regcache_cache_bypass(rt711->regmap, true); + } + /* * PM runtime is only enabled when a Slave reports as Attached */ @@ -1189,8 +1194,6 @@ int rt711_io_init(struct device *dev, struct sdw_slave *slave) pm_runtime_mark_last_busy(&slave->dev); pm_runtime_enable(&slave->dev); - - rt711->first_init = true; } pm_runtime_get_noresume(&slave->dev); @@ -1247,13 +1250,17 @@ int rt711_io_init(struct device *dev, struct sdw_slave *slave) /* Finish Initial Settings, set power to D3 */ regmap_write(rt711->regmap, RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); - INIT_DELAYED_WORK(&rt711->jack_detect_work, + if (rt711->first_init) + rt711_calibration(rt711); + else { + INIT_DELAYED_WORK(&rt711->jack_detect_work, rt711_jack_detect_handler); - INIT_DELAYED_WORK(&rt711->jack_btn_check_work, + INIT_DELAYED_WORK(&rt711->jack_btn_check_work, rt711_btn_check_handler); - mutex_init(&rt711->calibrate_mutex); - INIT_WORK(&rt711->calibration_work, rt711_calibration_work); - schedule_work(&rt711->calibration_work); + mutex_init(&rt711->calibrate_mutex); + INIT_WORK(&rt711->calibration_work, rt711_calibration_work); + schedule_work(&rt711->calibration_work); + } /* * if set_jack callback occurred early than io_init, @@ -1262,6 +1269,11 @@ int rt711_io_init(struct device *dev, struct sdw_slave *slave) if (rt711->hs_jack) rt711_jack_init(rt711); + if (rt711->first_init) + regcache_cache_bypass(rt711->regmap, false); + else + rt711->first_init = true; + /* Mark Slave initialization complete */ rt711->hw_init = true; From b28f04c3e4bffdd7c58ef6549c2fc47078dfecfd Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 7 Nov 2019 14:45:41 +0800 Subject: [PATCH 105/110] ASoC: rt1308-sdw: re-do io_init in cache_bypass mode when system resume Signed-off-by: Shuming Fan --- sound/soc/codecs/rt1308-sdw.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index ba6350366b24b4..98bf6417f4c4ca 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -185,6 +185,11 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) if (ret < 0) goto _io_init_err_; + if (rt1308->first_init) { + regcache_cache_only(rt1308->regmap, false); + regcache_cache_bypass(rt1308->regmap, true); + } + /* * PM runtime is only enabled when a Slave reports as Attached */ @@ -200,8 +205,6 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) pm_runtime_mark_last_busy(&slave->dev); pm_runtime_enable(&slave->dev); - - rt1308->first_init = true; } pm_runtime_get_noresume(&slave->dev); @@ -268,6 +271,11 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) regmap_write(rt1308->regmap, 0xc101, 0xd7); regmap_write(rt1308->regmap, 0xc300, 0x09); + if (rt1308->first_init) + regcache_cache_bypass(rt1308->regmap, false); + else + rt1308->first_init = true; + /* Mark Slave initialization complete */ rt1308->hw_init = true; From e3c392fd310de6b56011b67a30640eaa8fdcd386 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 8 Nov 2019 17:13:22 -0600 Subject: [PATCH 106/110] soundwire: store unattach request to avoid races between codec resume and parent suspend In previous patches, we added enumeration and initialization_complete fields to avoid race condition. When streaming restarts, the Master first resumes, then requests all Slaves to renumerate/reinitialized, and then the Slave devices resume. Intel validation exposed a corner case where the Slave device may transition to D3 when streaming stops, but streaming restarts before the Master transitions to D3. In that case, the Slave status was not cleared as UNATTACHED, and the wait_for_completion will time out. The proposed solution is that when the Master clears the Slave(s) status, the reason for the Slave(s) becoming unattached is stored. When the slave resumes, it can check if a Master-initiated re-enumeration and initialization takes place and skip the wait_for_completion() if there is no reason to wait. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 5 ++++- drivers/soundwire/bus.h | 7 ++++++- drivers/soundwire/intel.c | 16 ++++++++++++---- include/linux/soundwire/sdw.h | 4 ++++ 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index ec4796fcbde1ae..f255bad443851b 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1191,7 +1191,7 @@ int sdw_handle_slave_status(struct sdw_bus *bus, } EXPORT_SYMBOL(sdw_handle_slave_status); -void sdw_clear_slave_status(struct sdw_bus *bus) +void sdw_clear_slave_status(struct sdw_bus *bus, int request) { struct sdw_slave *slave; int i; @@ -1211,6 +1211,9 @@ void sdw_clear_slave_status(struct sdw_bus *bus) if (slave->status != SDW_SLAVE_UNATTACHED) sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED); + + /* keep track of request, used in pm_runtime resume */ + slave->unattach_request = request; } } EXPORT_SYMBOL(sdw_clear_slave_status); diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index d2900cb4de18df..7cdd8f8a4d2c0d 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -207,6 +207,11 @@ static inline int sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val) return sdw_write(slave, addr, tmp); } -void sdw_clear_slave_status(struct sdw_bus *bus); +/* possible requests to force Slaves to become unattached */ +#define SDW_UNATTACH_REQUEST_MASTER_RESET BIT(0) +#define SDW_UNATTACH_REQUEST_MASTER_RESUME BIT(1) +#define SDW_UNATTACH_REQUEST_MASTER_PM_RESUME BIT(2) + +void sdw_clear_slave_status(struct sdw_bus *bus, int request); #endif /* __SDW_BUS_H */ diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index a10b36963e214e..64fd0e94bac0ac 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1508,8 +1508,12 @@ static int intel_resume(struct device *dev) return ret; } - /* make sure all Slaves are tagged as UNATTACHED */ - sdw_clear_slave_status(&sdw->cdns.bus); + /* + * make sure all Slaves are tagged as UNATTACHED and provide + * reason for reinitialization + */ + sdw_clear_slave_status(&sdw->cdns.bus, + SDW_UNATTACH_REQUEST_MASTER_RESET); ret = sdw_cdns_enable_interrupt(cdns, true); if (ret < 0) { @@ -1548,8 +1552,12 @@ static int intel_resume_runtime(struct device *dev) return ret; } - /* make sure all Slaves are tagged as UNATTACHED */ - sdw_clear_slave_status(&sdw->cdns.bus); + /* + * make sure all Slaves are tagged as UNATTACHED and provide + * reason for reinitialization + */ + sdw_clear_slave_status(&sdw->cdns.bus, + SDW_UNATTACH_REQUEST_MASTER_RESET); ret = sdw_cdns_enable_interrupt(cdns, true); if (ret < 0) { diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 450f279d38143b..db3d780cdf0f77 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -557,6 +557,9 @@ struct sdw_slave_ops { * Slave device * @initialization_complete: completion utility to control potential races * on startup between device enumeration and settings being restored + * @unattach_request: status field to keep track why the Slave was + * re-initialized. This is useful to deal with potential race conditions + * between the master suspending and the codec resuming. */ struct sdw_slave { struct sdw_slave_id id; @@ -576,6 +579,7 @@ struct sdw_slave { struct completion probe_complete; struct completion enumeration_complete; struct completion initialization_complete; + int unattach_request; }; #define to_sdw_slave_device(d) \ From 9a659043a282fb5ec65d9b66a6caf1ae456179fa Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 8 Nov 2019 17:42:00 -0600 Subject: [PATCH 107/110] ASoC: codecs: rt1308-sdw: check unattach request before wait_for_completion() Only start the wait_for_completion() if the Master device requested the Slave device status to become unattached, e.g with a bus reset. For normal pm_runtime resume just sync the regmap Signed-off-by: Pierre-Louis Bossart --- sound/soc/codecs/rt1308-sdw.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index 98bf6417f4c4ca..c60db67e33eb22 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -698,6 +698,9 @@ static int rt1308_dev_resume(struct device *dev) if (!rt1308->hw_init) return 0; + if (!slave->unattach_request) + goto regmap_sync; + time = wait_for_completion_timeout(&slave->initialization_complete, msecs_to_jiffies(RT1308_PROBE_TIMEOUT)); if (!time) { @@ -705,6 +708,8 @@ static int rt1308_dev_resume(struct device *dev) return -ETIMEDOUT; } +regmap_sync: + slave->unattach_request = 0; regcache_cache_only(rt1308->regmap, false); regcache_sync_region(rt1308->regmap, 0xc000, 0xcfff); From 27cd4ad8c8ddd4eb28d7ff0a5980d2d0ab7ea56d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 8 Nov 2019 17:42:00 -0600 Subject: [PATCH 108/110] ASoC: codecs: rt700-sdw: check unattach request before wait_for_completion() Only start the wait_for_completion() if the Master device requested the Slave device status to become unattached, e.g with a bus reset. For normal pm_runtime resume just sync the regmap Signed-off-by: Pierre-Louis Bossart --- sound/soc/codecs/rt700-sdw.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 9e7a0bf9b5c634..40954f18ddae7c 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -511,6 +511,9 @@ static int rt700_dev_resume(struct device *dev) if (!rt700->hw_init) return 0; + if (!slave->unattach_request) + goto regmap_sync; + time = wait_for_completion_timeout(&slave->initialization_complete, msecs_to_jiffies(RT700_PROBE_TIMEOUT)); if (!time) { @@ -518,6 +521,8 @@ static int rt700_dev_resume(struct device *dev) return -ETIMEDOUT; } +regmap_sync: + slave->unattach_request = 0; regcache_cache_only(rt700->regmap, false); regcache_sync_region(rt700->regmap, 0x3000, 0x8fff); regcache_sync_region(rt700->regmap, 0x752010, 0x75206b); From ebd261d553dc299d090d1e39c4346ddc4149aafd Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 8 Nov 2019 17:42:01 -0600 Subject: [PATCH 109/110] ASoC: codecs: rt711-sdw: check unattach request before wait_for_completion() Only start the wait_for_completion() if the Master device requested the Slave device status to become unattached, e.g with a bus reset. For normal pm_runtime resume just sync the regmap Signed-off-by: Pierre-Louis Bossart --- sound/soc/codecs/rt711-sdw.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index c8a6dfceae1e4f..ee7fa574d3298d 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -511,6 +511,9 @@ static int rt711_dev_resume(struct device *dev) if (!rt711->hw_init) return 0; + if (!slave->unattach_request) + goto regmap_sync; + time = wait_for_completion_timeout(&slave->initialization_complete, msecs_to_jiffies(RT711_PROBE_TIMEOUT)); if (!time) { @@ -518,6 +521,8 @@ static int rt711_dev_resume(struct device *dev) return -ETIMEDOUT; } +regmap_sync: + slave->unattach_request = 0; regcache_cache_only(rt711->regmap, false); regcache_sync_region(rt711->regmap, 0x3000, 0x8fff); regcache_sync_region(rt711->regmap, 0x752010, 0x752091); From e0b99441163a5db2cd23a14b3ecfe557836afa25 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 8 Nov 2019 17:42:01 -0600 Subject: [PATCH 110/110] ASoC: codecs: rt715-sdw: check unattach request before wait_for_completion() Only start the wait_for_completion() if the Master device requested the Slave device status to become unattached, e.g with a bus reset. For normal pm_runtime resume just sync the regmap Signed-off-by: Pierre-Louis Bossart --- sound/soc/codecs/rt715-sdw.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index aa6b22236290fd..096c9f3bcf1dc4 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -510,6 +510,9 @@ static int rt715_dev_resume(struct device *dev) if (!rt715->hw_init) return 0; + if (!slave->unattach_request) + goto regmap_sync; + time = wait_for_completion_timeout(&slave->initialization_complete, msecs_to_jiffies(RT715_PROBE_TIMEOUT)); if (!time) { @@ -517,6 +520,8 @@ static int rt715_dev_resume(struct device *dev) return -ETIMEDOUT; } +regmap_sync: + slave->unattach_request = 0; regcache_cache_only(rt715->regmap, false); regcache_sync_region(rt715->regmap, 0x3000, 0x8fff); regcache_sync_region(rt715->regmap, 0x752039, 0x752039);