From 868299c1a201676681630dc918af409572988ac1 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 9 Oct 2019 11:34:58 -0500 Subject: [PATCH 01/46] soundwire: sdw_slave: add probe_complete structure and new fields When a Slave device becomes synchronized with the bus, it may report its presence in PING frames, as well as optionally asserting an in-band PREQ signal. The bus driver will detect a new Device0, start the enumeration process and assign it a non-zero device number. The SoundWire enumeration provides an arbitration to deal with multiple Slaves reporting ATTACHED at the same time. The bus driver will also invoke the driver .probe() callback associated with this device. The probe() depends on the Linux device core, which handles the match operations and may result in modules being loaded. Once the non-zero device number is programmed, the Slave will report its new status in PING frames and the Master hardware will typically report this status change with an interrupt. At this point, the .update_status() callback of the codec driver will be invoked (usually from an interrupt thread or workqueue scheduled from the interrupt thread). The first race condition which can happen is between the .probe(), which allocates the resources, and .update_status() where initializations are typically handled. The .probe() is only called once during the initial boot, while .update_status() will be called for every bus hardware reset and if the Slave device loses synchronization (an unlikely event but with non-zero probability). The time difference between the end of the enumeration process and a change of status reported by the hardware may be as small as one SoundWire PING frame. The scheduling of the interrupt thread, which invokes .update_status() is not deterministic, but can be small enough to create a race condition. With a 48 kHz frame rate and ideal scheduling cases, the .probe() may be pre-empted within double-digit microseconds. Since there is no guarantee that the .probe() completes by the time .update_status() is invoked as a result of an interrupt, it's not unusual for the .update_status() to rely on data structures that have not been allocated yet, leading to kernel oopses. This patch adds a probe_complete utility, which is used in the sdw_update_slave_status() routine. The codec driver does not need to do anything and can safely assume all resources are allocated in its update_status() callback. 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 28745b9ba27954..cb1db4a7475d5b 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -547,6 +547,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/implementation-defined interrupts */ struct sdw_slave { struct sdw_slave_id id; @@ -561,6 +565,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 950c2c0aa91bdbf04de03d7c258fd8842d88048c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 26 Sep 2019 18:42:47 -0500 Subject: [PATCH 02/46] soundwire: sdw_slave: add enumeration_complete structure When the Master starts the bus (be it during the initial boot or system resume), it usually performs a HardReset to make sure electrical levels are correct, then enables the control channel. While the PM framework guarantees that the Slave devices will only become 'active' once the Master completes the bus initialization, there is still a risk of a race condition: the Slave enumeration is handled in a separate interrupt thread triggered by hardware status changes, so the Slave device may not be ready to accept commands when the Slave driver tries to access the registers and restore settings in its resume or pm_runtime_resume callbacks. In those cases, any read/write commands from/to the Slave device will result in a timeout. This patch adds an enumeration_complete structure. When the bus is goes through a HardReset sequence and restarted, the Slave will be marked as UNATTACHED, which will result in a call to init_completion(). When the Slave reports its presence during PING frames as a non-zero Device, the Master hardware will issue an interrupt and the bus driver will invoke complete(). The order between init_completion()/complete() is predictable since this is a Master-initiated transition. The Slave driver may use wait_for_completion() in its resume callback. When regmap is used, the Slave driver will typically set its regmap in cache-only mode on suspend, then on resume block on wait_for_completion(&enumeration_complete) to guarantee it is safe to start read/write transactions. It may then exit the cache-only mode and use a regmap_sync to restore settings. All these steps are optional, their use completely depends on the Slave device capabilities and how the Slave driver is implemented. 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 cb1db4a7475d5b..3fa8d875b16baa 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -551,6 +551,9 @@ struct sdw_slave_ops { * @probe_complete: completion utility to control potential races * on startup between driver probe/initialization and SoundWire * Slave state changes/implementation-defined interrupts + * @enumeration_complete: completion utility to control potential races + * on startup between device enumeration and read/write access to the + * Slave device */ struct sdw_slave { struct sdw_slave_id id; @@ -567,6 +570,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 d609f23d9b444ba08f3d7e8a97fc16e73b45c906 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 4 Nov 2019 12:44:46 -0600 Subject: [PATCH 03/46] soundwire: sdw_slave: add initialization_complete definition Slave drivers may have different ways of handling their settings, with or without regmap. During the integration of codec drivers, done in partnership between Intel and Realtek, it became desirable to implement a predictable order between low-level initializations performed in .update_status() (invoked by an interrupt thread) and the settings restored in the resume steps (invoked by the PM core). This patch builds on the previous solution to wait for the Slave device to be fully enumerated. The complete() in this case is signaled not before the .update_status() is called, but after .update_status() returns. Without this patch, the settings were not properly restored, leading to timing-dependent 'no sound after resume' or 'no headset detected after resume' bug reports. Depending on how initialization is handled, a Slave device driver may wait for enumeration_complete, or for initialization_complete, both are valid synchronization points. They are initialized at the same time, they only differ on when complete() is invoked. 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 3fa8d875b16baa..ed42cd79eab7ef 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -554,6 +554,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; @@ -571,6 +573,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 0847d0c9840ec662e8535acf812d06f53a1474f1 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 11 Nov 2019 10:48:31 -0600 Subject: [PATCH 04/46] soundwire: sdw_slave: track unattach_request to handle all init sequences The Slave device initialization can be split in 4 different cases: 1. Master-initiated hardware reset, system suspend-resume and pm_runtime based on clock-stop mode1. To avoid timeouts and a bad audio experience, the Slave device resume operations need to wait for the Slave device to be re-enumerated and its settings restored. 2. Exit from clock-stop mode0. In this case, the Slave device is required to remain enumerated and its context preserved while the clock is stopped, so no re-initialization or wait_for_completion() is necessary. 3. Slave-initiated pm_runtime D3 transition. With the parent child relationship, it is possible that a Slave device becomes 'suspended' while its parent is still 'active' with the bus clock still toggling. In this case, during the pm_runtime resume operation, there is no need to wait for any settings to be restored. 4. Slave reset (sync loss or implementation-defined). In that case the bus remains operational and the Slave device will be re-initialized when it becomes ATTACHED again. In previous patches, we suggested the use of wait_for_completion() to deal with the case #1, but case #2 and #3 do not need any wait. To account for those differences, this patch adds an unattach_request field. The field is explicitly set by the Master for the case #1, and if non-zero the Slave device shall wait on resume. In all other cases, the Slave resume operations can proceed without wait. The only request tracked so far is Master HardReset, but the request is declared as a bit mask for future extensions (if needed). The definition for this value is added in bus.h and does not need to be exposed in sdw.h 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 ed42cd79eab7ef..b7c9eca4332a3a 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -556,6 +556,11 @@ 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: mask field to keep track why the Slave re-attached and + * was re-initialized. This is useful to deal with potential race conditions + * between the Master suspending and the codec resuming, and make sure that + * when the Master triggered a reset the Slave is properly enumerated and + * initialized */ struct sdw_slave { struct sdw_slave_id id; @@ -574,6 +579,7 @@ struct sdw_slave { struct completion probe_complete; struct completion enumeration_complete; struct completion initialization_complete; + u32 unattach_request; }; #define dev_to_sdw_dev(_dev) container_of(_dev, struct sdw_slave, dev) From 6045db12c9f01f4523f38023ea22a2333bd90425 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 23 Oct 2019 12:10:43 -0500 Subject: [PATCH 05/46] soundwire: intel: update interfaces between ASoC and SoundWire The current interfaces between ASoC and SoundWire are limited by the platform_device infrastructure to an init() and exit() (mapped to the platform driver.probe and .remove) To help with the platform detection, machine driver selection and management of power dependencies between DSP and SoundWire IP, the ASoC side requires: a) an ACPI scan helper, to report if any devices are exposed in the DSDT tables, and if any links are disabled by the BIOS. b) a probe helper that allocates the resources without actually starting the bus. c) a startup helper which does start the bus when all power dependencies are settled. d) an exit helper to free all resources e) an interrupt_enable/disable helper, typically invoked after the startup helper but also used in suspend routines. This patch moves all required interfaces to 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 | 77 +++++++++++++++++++++++++++-- 3 files changed, 85 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..034eca8df74808 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -16,24 +16,91 @@ 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_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. + * @link_mask: bit-wise mask listing SoundWire links reported by the + * Controller + * @handle: ACPI parent handle + * @links: information for each link (controller-specific and kept + * opaque here) + */ +struct sdw_intel_ctx { + int count; + void __iomem *mmio_base; + u32 link_mask; + acpi_handle handle; + struct sdw_intel_link_res *links; +}; + +/** + * struct sdw_intel_res - Soundwire Intel global resource structure, + * typically populated by the DSP driver + * + * @count: link count * @mmio_base: mmio base of SoundWire registers * @irq: interrupt number * @handle: ACPI parent handle * @parent: parent device * @ops: callback ops - * @arg: callback arg + * @dev: device implementing hwparams and free callbacks + * @link_mask: bit-wise mask listing links selected by the DSP driver + * This mask may be a subset of the one reported by the controller since + * machine-specific quirks are handled in the DSP driver. */ 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); +/* + * 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 577d8056b2d60758cab46e56923efda88dca479d Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Mon, 19 Aug 2019 13:32:09 -0500 Subject: [PATCH 06/46] soundwire: intel: update stream callbacks for hwparams/free stream operations The SoundWire DAIs for Intel platform are created in drivers/soundwire/intel.c, while the communication with the Intel DSP is all controlled in soc/sof/intel When the DAI status changes, a callback is used to bridge the gap between the two subsystems. The naming of the existing 'config_stream' callback does not map well with any of ALSA/ASoC concepts. This patch renames it as 'params_stream' to be more self-explanatory. A new 'free_stream' callback is added in case any resources allocated in the 'params_stream' stage need to be released. In the SOF implementation, this is used in the hw_free case to release the DMA channels over IPC. These two callbacks now rely on structures which expose the link_id and alh_stream_id (required by the firmware IPC), instead of a list of parameters. The 'void *' definitions are changed to use explicit types, as suggested on alsa-devel during earlier reviews. 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 99dc610212113f..0371d3d5501a97 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -529,17 +529,24 @@ intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf); } -static int intel_config_stream(struct sdw_intel *sdw, +static int intel_params_stream(struct sdw_intel *sdw, struct snd_pcm_substream *substream, struct snd_soc_dai *dai, - struct snd_pcm_hw_params *hw_params, int link_id) + struct snd_pcm_hw_params *hw_params, + int link_id, int alh_stream_id) { struct sdw_intel_link_res *res = sdw->res; + struct sdw_intel_stream_params_data params_data; - if (res->ops && res->ops->config_stream && res->arg) - return res->ops->config_stream(res->arg, - substream, dai, hw_params, link_id); + params_data.substream = substream; + params_data.dai = dai; + params_data.hw_params = hw_params; + params_data.link_id = link_id; + params_data.alh_stream_id = alh_stream_id; + if (res->ops && res->ops->params_stream && res->dev) + return res->ops->params_stream(res->dev, + ¶ms_data); return -EIO; } @@ -654,7 +661,8 @@ static int intel_hw_params(struct snd_pcm_substream *substream, /* Inform DSP about PDI stream number */ - ret = intel_config_stream(sdw, substream, dai, params, + ret = intel_params_stream(sdw, substream, dai, params, + sdw->instance, pdi->intel_alh_id); if (ret) goto error; diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index 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 034eca8df74808..3ccb38d48eef15 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 82154a06786ca412855d04bf0517ef487adb2d5c Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 21 Nov 2019 18:06:37 -0600 Subject: [PATCH 07/46] soundwire: intel: update headers for interrupts The existing use of 6 handlers is problematic in MSI mode. Update headers so that all shared interrupts can be handled with a single handler. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- include/linux/soundwire/sdw_intel.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h index 3ccb38d48eef15..47b7678ca2abf8 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -127,4 +127,6 @@ void sdw_intel_exit(struct sdw_intel_ctx *ctx); void sdw_intel_enable_irq(void __iomem *mmio_base, bool enable); +irqreturn_t sdw_intel_thread(int irq, void *dev_id); + #endif From c2c7f7d9d6a4e4f769d963cc3fbf93cf11ebf12c Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 18 Apr 2018 10:35:14 +0530 Subject: [PATCH 08/46] 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 4abf37b5083fb9..0b95f928ab7e1e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1057,6 +1057,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 d1161213aa391bc59827c4e1f375267e320bcbf0 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Fri, 2 Aug 2019 10:26:02 +0800 Subject: [PATCH 09/46] 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 0b95f928ab7e1e..ddb845cf049b73 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1066,6 +1066,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 0bc6be90fc3cb250d4bee6be9b00a18e26e9fc82 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 15 Aug 2019 10:49:56 +0800 Subject: [PATCH 10/46] 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 ddb845cf049b73..8ffef6466b94dd 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1072,6 +1072,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 c89ab588510b809162ec73949e5f104c8670628d Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Wed, 21 Aug 2019 17:40:49 +0800 Subject: [PATCH 11/46] 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 8ffef6466b94dd..c211941ceb2c8e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1081,6 +1081,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 f7a1d76f221b65be25ae63642d19fde95ebc7b67 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 18 Apr 2019 16:20:05 -0500 Subject: [PATCH 12/46] 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 7d90c6103e7e22b3def0aa5379acfb33c6972b37 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Fri, 18 Oct 2019 17:02:29 +0800 Subject: [PATCH 13/46] 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 b3bbd1054855b84204537060b72e690dd2287a77 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 23 Oct 2019 13:43:46 +0800 Subject: [PATCH 14/46] 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 a5254cdfe8c0699e777a19eefdd7b64419bb6c8a Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 23 Oct 2019 13:44:08 +0800 Subject: [PATCH 15/46] 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 36b8360c3fa800ae8c1e342f6cdd2ad965edc743 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 23 Oct 2019 13:44:23 +0800 Subject: [PATCH 16/46] 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 fbb78043339bf83d7652a4f910928ead102240a9 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Thu, 24 Oct 2019 13:29:54 +0800 Subject: [PATCH 17/46] 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 e5566b8fc7b065c05bbfc1a6450dd85c7aa24bd3 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 24 Oct 2019 20:50:50 +0800 Subject: [PATCH 18/46] 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 3ccd04e49aa38f4bbffdc5fceb56e85369549dcd Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Fri, 25 Oct 2019 10:16:19 +0800 Subject: [PATCH 19/46] 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 383a457d01f19d00132f8a93e5e5f73af846ee9b Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 28 Oct 2019 17:26:53 +0800 Subject: [PATCH 20/46] 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 3b823cfd63978032ab8af0f9699d29879877652f Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 28 Oct 2019 17:27:06 +0800 Subject: [PATCH 21/46] 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 6c92896fb9e2df7e20247113801d924e71eedae7 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 28 Oct 2019 17:27:21 +0800 Subject: [PATCH 22/46] 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 9aabdf88eb2b58e6d32c73e65d4b863bcad7ef93 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Tue, 29 Oct 2019 11:28:03 +0800 Subject: [PATCH 23/46] 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 969099df57b5db4e8601a6af77abcb1e6f230917 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 30 Oct 2019 18:34:28 +0800 Subject: [PATCH 24/46] 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 06a3fdca288475e65685e1d1a0402e9e974bb05a Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 4 Nov 2019 14:10:01 +0800 Subject: [PATCH 25/46] 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 7e2aabd184da4cb97c75e618466354b700071ef0 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 4 Nov 2019 12:56:51 -0600 Subject: [PATCH 26/46] 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 33c409e0d079048ecf96727e36eebe7bf0765709 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 4 Nov 2019 12:56:52 -0600 Subject: [PATCH 27/46] 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 20b5da54f8b2fad6a83544e681d7e96b718dd6b9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 4 Nov 2019 12:56:52 -0600 Subject: [PATCH 28/46] 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 18367b25a39b0837829534deaeb950188f42959c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 4 Nov 2019 12:56:53 -0600 Subject: [PATCH 29/46] 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 dafa4896f411a94e6885d7eaec0c9ab6be34c62d Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 7 Nov 2019 14:40:08 +0800 Subject: [PATCH 30/46] 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 d8640f52ba1d42313558f8a6ae97919fe5ecfce1 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 7 Nov 2019 14:40:22 +0800 Subject: [PATCH 31/46] 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 6c9f5140eccaefc26e3ba4839d7ff167dd5dc9bb Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 7 Nov 2019 14:40:34 +0800 Subject: [PATCH 32/46] 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 bdf02cbd649793e9fdfdc9d898835557b79072ca Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 7 Nov 2019 14:45:10 +0800 Subject: [PATCH 33/46] 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 2f5253e7c39d8a83342daa621a5b5049f973b61d Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 7 Nov 2019 14:45:23 +0800 Subject: [PATCH 34/46] 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 727c28a81ec3c0ad00ab2c7e7063a7f769d8ec59 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 7 Nov 2019 14:45:41 +0800 Subject: [PATCH 35/46] 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 06aad311d6e2297d3a2f6368f149004b0c7de31f Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Mon, 11 Nov 2019 10:19:52 +0800 Subject: [PATCH 36/46] ASoC: codec: rt715: Remove unused rt715_index_read function. Signed-off-by: Jack Yu --- sound/soc/codecs/rt715.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index 40e006b0fc6138..fce9c6939eda0c 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -53,22 +53,6 @@ static int rt715_index_write(struct regmap *regmap, unsigned int reg, 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) << 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); - } - - 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) From 1278f0c01e7b398660c5282bf4f2115018268de2 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 8 Nov 2019 17:42:00 -0600 Subject: [PATCH 37/46] 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 e883ae2c78d47bf7d15bacb0084b903e8cfa0420 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 8 Nov 2019 17:42:00 -0600 Subject: [PATCH 38/46] 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 a89062ce57f7aa72c01725869b0a4f3c6be2a479 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 8 Nov 2019 17:42:01 -0600 Subject: [PATCH 39/46] 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 2394d0c652b289a7ce44582179a75d4baa4e9f86 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 8 Nov 2019 17:42:01 -0600 Subject: [PATCH 40/46] 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); From a24326971176c52c4e7051ba4b782b1b73330f1d Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 13 Nov 2019 17:21:42 +0800 Subject: [PATCH 41/46] ASoC: rt711: fix no sound output after waking up from deep s3 The resume function needs to cache sync JD2 settings. Signed-off-by: Shuming Fan --- sound/soc/codecs/rt711-sdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index ee7fa574d3298d..4174d175925c33 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -525,7 +525,7 @@ static int rt711_dev_resume(struct device *dev) slave->unattach_request = 0; regcache_cache_only(rt711->regmap, false); regcache_sync_region(rt711->regmap, 0x3000, 0x8fff); - regcache_sync_region(rt711->regmap, 0x752010, 0x752091); + regcache_sync_region(rt711->regmap, 0x752009, 0x752091); return 0; } From 00ebe88dcea3dfd10a2f83587b6e907d2eed501c Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Fri, 22 Nov 2019 13:51:27 +0800 Subject: [PATCH 42/46] Correct some sdw default registers and add missing registers in rt715_reg_defaults[] Signed-off-by: Jack Yu --- sound/soc/codecs/rt715-sdw.c | 62 +++++++++++++++++++++++++++++------- sound/soc/codecs/rt715-sdw.h | 47 ++++++++++++++++++++++++--- 2 files changed, 92 insertions(+), 17 deletions(-) diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index 096c9f3bcf1dc4..d1fe32a9397df4 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -22,17 +22,18 @@ static bool rt715_readable_register(struct device *dev, unsigned int reg) { switch (reg) { + case 0x00e0 ... 0x00e5: + case 0x00ee ... 0x00ef: + case 0x00f0 ... 0x00f5: + case 0x00fe ... 0x00ff: 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 0x2000 ... 0x2016: + case 0x201a ... 0x2027: case 0x2029 ... 0x202a: case 0x202d ... 0x2034: case 0x2200 ... 0x2204: @@ -40,11 +41,50 @@ static bool rt715_readable_register(struct device *dev, unsigned int reg) 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 0x3122: + case 0x3123: + case 0x3124: + case 0x3125: + case 0x3607: + case 0x3608: + case 0x3609: + case 0x3610: + case 0x3611: + case 0x3627: + case 0x3712: + case 0x3713: + case 0x3718: + case 0x3719: + case 0x371a: + case 0x371b: + case 0x371d: + case 0x3729: + case 0x385e: + case 0x3859: + case 0x4c12: + case 0x4c13: + case 0x4c1d: + case 0x4c29: + case 0x4d12: + case 0x4d13: + case 0x4d1d: + case 0x4d29: + case 0x4e12: + case 0x4e13: + case 0x4e1d: + case 0x4e29: + case 0x4f12: + case 0x4f13: + case 0x4f1d: + case 0x4f29: + case 0x7307: + case 0x7308: + case 0x7309: + case 0x7327: + case 0x8387: + case 0x8388: + case 0x8389: + case 0x83a7: case 0x752039: return true; default: @@ -72,8 +112,6 @@ static bool rt715_volatile_register(struct device *dev, unsigned int reg) case 0x202d ... 0x202f: /* BRA */ case 0x2201 ... 0x2212: /* i2c debug */ case 0x2220 ... 0x2223: /* decoded HD-A */ - case 0x9c00 ... 0x9cff: - case 0xb900 ... 0xb9ff: return true; default: return false; diff --git a/sound/soc/codecs/rt715-sdw.h b/sound/soc/codecs/rt715-sdw.h index 0bc0efada86296..0a74a6497c2089 100644 --- a/sound/soc/codecs/rt715-sdw.h +++ b/sound/soc/codecs/rt715-sdw.h @@ -43,6 +43,7 @@ static const struct reg_default rt715_reg_defaults[] = { { 0x0060, 0x00 }, { 0x0070, 0x00 }, { 0x0080, 0x00 }, + { 0x0088, 0x10 }, { 0x00e0, 0x00 }, { 0x00e1, 0x00 }, { 0x00e2, 0x00 }, @@ -64,7 +65,7 @@ static const struct reg_default rt715_reg_defaults[] = { { 0x0202, 0x20 }, { 0x0203, 0x00 }, { 0x0204, 0x00 }, - { 0x0205, 0x00 }, + { 0x0205, 0x03 }, { 0x0220, 0x00 }, { 0x0221, 0x00 }, { 0x0222, 0x00 }, @@ -88,7 +89,7 @@ static const struct reg_default rt715_reg_defaults[] = { { 0x0402, 0x20 }, { 0x0403, 0x00 }, { 0x0404, 0x00 }, - { 0x0405, 0x00 }, + { 0x0405, 0x0f }, { 0x0420, 0x00 }, { 0x0421, 0x00 }, { 0x0422, 0x00 }, @@ -112,7 +113,7 @@ static const struct reg_default rt715_reg_defaults[] = { { 0x0602, 0x20 }, { 0x0603, 0x00 }, { 0x0604, 0x00 }, - { 0x0605, 0x00 }, + { 0x0605, 0xff }, { 0x0620, 0x00 }, { 0x0621, 0x00 }, { 0x0622, 0x00 }, @@ -152,7 +153,7 @@ static const struct reg_default rt715_reg_defaults[] = { { 0x0f12, 0x00 }, { 0x0f13, 0x00 }, { 0x0f14, 0x00 }, - { 0x0f15, 0xff }, + { 0x0f15, 0x00 }, { 0x0f16, 0x00 }, { 0x0f17, 0x00 }, { 0x0f18, 0x00 }, @@ -194,7 +195,7 @@ static const struct reg_default rt715_reg_defaults[] = { { 0x200c, 0x00 }, { 0x200d, 0x00 }, { 0x200e, 0x00 }, - { 0x200f, 0x00 }, + { 0x200f, 0x10 }, { 0x2010, 0x00 }, { 0x2011, 0x00 }, { 0x2012, 0x00 }, @@ -262,6 +263,42 @@ static const struct reg_default rt715_reg_defaults[] = { { 0x22f1, 0x00 }, { 0x22f2, 0x00 }, { 0x22f3, 0x00 }, + { 0x3122, 0x02 }, + { 0x3123, 0x03 }, + { 0x3124, 0x00 }, + { 0x3125, 0x01 }, + { 0x3607, 0x00 }, + { 0x3608, 0x00 }, + { 0x3609, 0x00 }, + { 0x3610, 0x00 }, + { 0x3611, 0x00 }, + { 0x3627, 0x00 }, + { 0x3712, 0x00 }, + { 0x3713, 0x00 }, + { 0x3718, 0x00 }, + { 0x3719, 0x00 }, + { 0x371a, 0x00 }, + { 0x371b, 0x00 }, + { 0x371d, 0x00 }, + { 0x3729, 0x00 }, + { 0x385e, 0x00 }, + { 0x3859, 0x00 }, + { 0x4c12, 0x411111f0 }, + { 0x4c13, 0x411111f0 }, + { 0x4c1d, 0x411111f0 }, + { 0x4c29, 0x411111f0 }, + { 0x4d12, 0x411111f0 }, + { 0x4d13, 0x411111f0 }, + { 0x4d1d, 0x411111f0 }, + { 0x4d29, 0x411111f0 }, + { 0x4e12, 0x411111f0 }, + { 0x4e13, 0x411111f0 }, + { 0x4e1d, 0x411111f0 }, + { 0x4e29, 0x411111f0 }, + { 0x4f12, 0x411111f0 }, + { 0x4f13, 0x411111f0 }, + { 0x4f1d, 0x411111f0 }, + { 0x4f29, 0x411111f0 }, { 0x7307, 0x97 }, { 0x8387, 0x97 }, { 0x7308, 0x97 }, From 2724b80244b05f93fa3181d77ca003f6669e74b8 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Fri, 22 Nov 2019 13:22:11 +0800 Subject: [PATCH 43/46] ASoC: rt5682: Add the field "is_sdw" of private data The field "is_sdw" is used for distinguishing the driver whether is run in soundwire mode or not. That will run the separated setting in runtime to make sure the driver can be run with the same build between i2s mode and soundwire mode. Signed-off-by: Oder Chiou --- sound/soc/codecs/rt5682.c | 151 ++++++++++++++++++++++---------------- sound/soc/codecs/rt5682.h | 3 + 2 files changed, 90 insertions(+), 64 deletions(-) diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index b1713fffa3eb92..02047800498670 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -56,6 +56,7 @@ struct rt5682_priv { struct delayed_work jack_detect_work; struct delayed_work jd_check_work; struct mutex calibrate_mutex; + bool is_sdw; int sysclk; int sysclk_src; @@ -804,10 +805,11 @@ static const struct snd_kcontrol_new rt5682_if1_45_adc_swap_mux = static const struct snd_kcontrol_new rt5682_if1_67_adc_swap_mux = SOC_DAPM_ENUM("IF1 67 ADC Swap Mux", rt5682_if1_67_adc_enum); -static void rt5682_reset(struct regmap *regmap) +static void rt5682_reset(struct rt5682_priv *rt5682) { - regmap_write(regmap, RT5682_RESET, 0); - regmap_write(regmap, RT5682_I2C_MODE, 1); + regmap_write(rt5682->regmap, RT5682_RESET, 0); + if (!rt5682->is_sdw) + regmap_write(rt5682->regmap, RT5682_I2C_MODE, 1); } /** * rt5682_sel_asrc_clk_src - select ASRC clock source for a set of filters @@ -870,6 +872,8 @@ static int rt5682_button_detect(struct snd_soc_component *component) static void rt5682_enable_push_button_irq(struct snd_soc_component *component, bool enable) { + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + if (enable) { snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, RT5682_SAR_BUTT_DET_MASK, RT5682_SAR_BUTT_DET_EN); @@ -879,8 +883,15 @@ static void rt5682_enable_push_button_irq(struct snd_soc_component *component, snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2, RT5682_4BTN_IL_MASK | RT5682_4BTN_IL_RST_MASK, RT5682_4BTN_IL_EN | RT5682_4BTN_IL_NOR); - snd_soc_component_update_bits(component, RT5682_IRQ_CTRL_3, - RT5682_IL_IRQ_MASK, RT5682_IL_IRQ_EN); + if (rt5682->is_sdw) + snd_soc_component_update_bits(component, + RT5682_IRQ_CTRL_3, + RT5682_IL_IRQ_MASK | RT5682_IL_IRQ_TYPE_MASK, + RT5682_IL_IRQ_EN | RT5682_IL_IRQ_PUL); + else + snd_soc_component_update_bits(component, + RT5682_IRQ_CTRL_3, RT5682_IL_IRQ_MASK, + RT5682_IL_IRQ_EN); } else { snd_soc_component_update_bits(component, RT5682_IRQ_CTRL_3, RT5682_IL_IRQ_MASK, RT5682_IL_IRQ_DIS); @@ -998,62 +1009,69 @@ static int rt5682_set_jack_detect(struct snd_soc_component *component, rt5682->hs_jack = hs_jack; - if (!hs_jack) { - regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, - RT5682_JD1_EN_MASK, RT5682_JD1_DIS); - regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, - RT5682_POW_JDH | RT5682_POW_JDL, 0); - cancel_delayed_work_sync(&rt5682->jack_detect_work); - return 0; - } + if (!rt5682->is_sdw) { + if (!hs_jack) { + regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, + RT5682_JD1_EN_MASK, RT5682_JD1_DIS); + regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, + RT5682_POW_JDH | RT5682_POW_JDL, 0); + cancel_delayed_work_sync(&rt5682->jack_detect_work); + return 0; + } - switch (rt5682->pdata.jd_src) { - case RT5682_JD1: - snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_2, - RT5682_EXT_JD_SRC, RT5682_EXT_JD_SRC_MANUAL); - snd_soc_component_write(component, RT5682_CBJ_CTRL_1, 0xd042); - snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_3, - RT5682_CBJ_IN_BUF_EN, RT5682_CBJ_IN_BUF_EN); - snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, - RT5682_SAR_POW_MASK, RT5682_SAR_POW_EN); - regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, - RT5682_GP1_PIN_MASK, RT5682_GP1_PIN_IRQ); - regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, + switch (rt5682->pdata.jd_src) { + case RT5682_JD1: + snd_soc_component_update_bits(component, + RT5682_CBJ_CTRL_2, RT5682_EXT_JD_SRC, + RT5682_EXT_JD_SRC_MANUAL); + snd_soc_component_write(component, RT5682_CBJ_CTRL_1, + 0xd042); + snd_soc_component_update_bits(component, + RT5682_CBJ_CTRL_3, RT5682_CBJ_IN_BUF_EN, + RT5682_CBJ_IN_BUF_EN); + snd_soc_component_update_bits(component, + RT5682_SAR_IL_CMD_1, RT5682_SAR_POW_MASK, + RT5682_SAR_POW_EN); + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP1_PIN_MASK, RT5682_GP1_PIN_IRQ); + regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, RT5682_POW_IRQ | RT5682_POW_JDH | RT5682_POW_ANA, RT5682_POW_IRQ | RT5682_POW_JDH | RT5682_POW_ANA); - regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_2, - RT5682_PWR_JDH | RT5682_PWR_JDL, - RT5682_PWR_JDH | RT5682_PWR_JDL); - regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, - RT5682_JD1_EN_MASK | RT5682_JD1_POL_MASK, - RT5682_JD1_EN | RT5682_JD1_POL_NOR); - regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_4, - 0x7f7f, (rt5682->pdata.btndet_delay << 8 | - rt5682->pdata.btndet_delay)); - regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_5, - 0x7f7f, (rt5682->pdata.btndet_delay << 8 | - rt5682->pdata.btndet_delay)); - regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_6, - 0x7f7f, (rt5682->pdata.btndet_delay << 8 | - rt5682->pdata.btndet_delay)); - regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_7, - 0x7f7f, (rt5682->pdata.btndet_delay << 8 | - rt5682->pdata.btndet_delay)); - mod_delayed_work(system_power_efficient_wq, - &rt5682->jack_detect_work, msecs_to_jiffies(250)); - break; + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_2, + RT5682_PWR_JDH | RT5682_PWR_JDL, + RT5682_PWR_JDH | RT5682_PWR_JDL); + regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, + RT5682_JD1_EN_MASK | RT5682_JD1_POL_MASK, + RT5682_JD1_EN | RT5682_JD1_POL_NOR); + regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_4, + 0x7f7f, (rt5682->pdata.btndet_delay << 8 | + rt5682->pdata.btndet_delay)); + regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_5, + 0x7f7f, (rt5682->pdata.btndet_delay << 8 | + rt5682->pdata.btndet_delay)); + regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_6, + 0x7f7f, (rt5682->pdata.btndet_delay << 8 | + rt5682->pdata.btndet_delay)); + regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_7, + 0x7f7f, (rt5682->pdata.btndet_delay << 8 | + rt5682->pdata.btndet_delay)); + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, + msecs_to_jiffies(250)); + break; - case RT5682_JD_NULL: - regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, - RT5682_JD1_EN_MASK, RT5682_JD1_DIS); - regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, - RT5682_POW_JDH | RT5682_POW_JDL, 0); - break; + case RT5682_JD_NULL: + regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, + RT5682_JD1_EN_MASK, RT5682_JD1_DIS); + regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, + RT5682_POW_JDH | RT5682_POW_JDL, 0); + break; - default: - dev_warn(component->dev, "Wrong JD source\n"); - break; + default: + dev_warn(component->dev, "Wrong JD source\n"); + break; + } } return 0; @@ -1133,11 +1151,13 @@ static void rt5682_jack_detect_handler(struct work_struct *work) SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3); - if (rt5682->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3)) - schedule_delayed_work(&rt5682->jd_check_work, 0); - else - cancel_delayed_work_sync(&rt5682->jd_check_work); + if (!rt5682->is_sdw) { + if (rt5682->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3)) + schedule_delayed_work(&rt5682->jd_check_work, 0); + else + cancel_delayed_work_sync(&rt5682->jd_check_work); + } mutex_unlock(&rt5682->calibrate_mutex); } @@ -1231,6 +1251,9 @@ static int set_filter_clk(struct snd_soc_dapm_widget *w, static const int div_f[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48}; static const int div_o[] = {1, 2, 4, 6, 8, 12, 16, 24, 32, 48}; + if (rt5682->is_sdw) + return 0; + val = snd_soc_component_read32(component, RT5682_GPIO_CTRL_1) & RT5682_GP4_PIN_MASK; if (w->shift == RT5682_PWR_ADC_S1F_BIT && @@ -2331,7 +2354,7 @@ static void rt5682_remove(struct snd_soc_component *component) { struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); - rt5682_reset(rt5682->regmap); + rt5682_reset(rt5682); } #ifdef CONFIG_PM @@ -2473,7 +2496,7 @@ static void rt5682_calibrate(struct rt5682_priv *rt5682) mutex_lock(&rt5682->calibrate_mutex); - rt5682_reset(rt5682->regmap); + rt5682_reset(rt5682); regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2af); usleep_range(15000, 20000); regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xf2af); @@ -2584,7 +2607,7 @@ static int rt5682_i2c_probe(struct i2c_client *i2c, return -ENODEV; } - rt5682_reset(rt5682->regmap); + rt5682_reset(rt5682); mutex_init(&rt5682->calibrate_mutex); rt5682_calibrate(rt5682); @@ -2674,7 +2697,7 @@ static void rt5682_i2c_shutdown(struct i2c_client *client) { struct rt5682_priv *rt5682 = i2c_get_clientdata(client); - rt5682_reset(rt5682->regmap); + rt5682_reset(rt5682); } #ifdef CONFIG_OF diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h index 18faaa2a49a0ce..61a577dacdaa3d 100644 --- a/sound/soc/codecs/rt5682.h +++ b/sound/soc/codecs/rt5682.h @@ -1096,6 +1096,9 @@ #define RT5682_IL_IRQ_MASK (0x1 << 7) #define RT5682_IL_IRQ_DIS (0x0 << 7) #define RT5682_IL_IRQ_EN (0x1 << 7) +#define RT5682_IL_IRQ_TYPE_MASK (0x1 << 4) +#define RT5682_IL_IRQ_LEV (0x0 << 4) +#define RT5682_IL_IRQ_PUL (0x1 << 4) /* GPIO Control 1 (0x00c0) */ #define RT5682_GP1_PIN_MASK (0x3 << 14) From 7d4c813f3c5da0b0493343cbc7e2c7e9e7d2efb2 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Tue, 26 Nov 2019 16:32:18 +0800 Subject: [PATCH 44/46] ASoC: rt5682: Add the soundwire support This patch adds the soundwire support for ALC5682. Signed-off-by: Oder Chiou --- sound/soc/codecs/Kconfig | 6 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt5682-sdw.c | 335 +++++++++++++++++++++++ sound/soc/codecs/rt5682-sdw.h | 18 ++ sound/soc/codecs/rt5682.c | 497 ++++++++++++++++++++++++++++++++-- sound/soc/codecs/rt5682.h | 45 ++- 6 files changed, 871 insertions(+), 32 deletions(-) create mode 100644 sound/soc/codecs/rt5682-sdw.c create mode 100644 sound/soc/codecs/rt5682-sdw.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index c211941ceb2c8e..4c661db2ff7b54 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1057,6 +1057,12 @@ config SND_SOC_RT5677_SPI config SND_SOC_RT5682 tristate +config SND_SOC_RT5682_SDW + tristate "Realtek RT5682 Codec - SDW" + depends on SOUNDWIRE + select SND_SOC_RT5682 + select REGMAP_SOUNDWIRE + config SND_SOC_RT700 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 823de4b00f1640..90885e4f4d1f90 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -175,6 +175,7 @@ snd-soc-rt5670-objs := rt5670.o snd-soc-rt5677-objs := rt5677.o snd-soc-rt5677-spi-objs := rt5677-spi.o snd-soc-rt5682-objs := rt5682.o +snd-soc-rt5682-sdw-objs := rt5682-sdw.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o @@ -470,6 +471,7 @@ obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o obj-$(CONFIG_SND_SOC_RT5682) += snd-soc-rt5682.o +obj-$(CONFIG_SND_SOC_RT5682_SDW) += snd-soc-rt5682-sdw.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c new file mode 100644 index 00000000000000..1e47fa85a1ff7f --- /dev/null +++ b/sound/soc/codecs/rt5682-sdw.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt5682-sdw.c -- RT5682 ALSA SoC audio component driver + * + * Copyright 2019 Realtek Semiconductor Corp. + * Author: Oder Chiou + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt5682.h" +#include "rt5682-sdw.h" + +static bool rt5682_sdw_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00e0: + case 0x00f0: + case 0x3000: + case 0x3001: + case 0x3004: + case 0x3005: + case 0x3008: + return true; + default: + return false; + } +} + +const struct regmap_config rt5682_sdw_regmap = { + .name = "sdw", + .reg_bits = 32, + .val_bits = 8, + .max_register = RT5682_I2C_MODE, + .readable_reg = rt5682_sdw_readable_register, + .cache_type = REGCACHE_NONE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt5682_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev); + + /* Update the status */ + rt5682->status = status; + + if (status == SDW_SLAVE_UNATTACHED) + rt5682->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt5682->hw_init || rt5682->status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt5682_io_init(&slave->dev, slave); +} + +static int rt5682_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 = 0x4; /* BITMAP: 00000100 */ + prop->sink_ports = 0x2; /* BITMAP: 00000010 */ + + 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; + + /* wake-up event */ + prop->wake_capable = 1; + + return 0; +} + +/* Bus clock frequency */ +#define RT5682_CLK_FREQ_9600000HZ 9600000 +#define RT5682_CLK_FREQ_12000000HZ 12000000 +#define RT5682_CLK_FREQ_6000000HZ 6000000 +#define RT5682_CLK_FREQ_4800000HZ 4800000 +#define RT5682_CLK_FREQ_2400000HZ 2400000 +#define RT5682_CLK_FREQ_12288000HZ 12288000 + +int rt5682_clock_config(struct device *dev) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + unsigned int clk_freq, value; + + clk_freq = (rt5682->params.curr_dr_freq >> 1); + + switch (clk_freq) { + case RT5682_CLK_FREQ_12000000HZ: + value = 0x0; + break; + case RT5682_CLK_FREQ_6000000HZ: + value = 0x1; + break; + case RT5682_CLK_FREQ_9600000HZ: + value = 0x2; + break; + case RT5682_CLK_FREQ_4800000HZ: + value = 0x3; + break; + case RT5682_CLK_FREQ_2400000HZ: + value = 0x4; + break; + case RT5682_CLK_FREQ_12288000HZ: + value = 0x5; + break; + default: + return -EINVAL; + } + + regmap_write(rt5682->sdw_regmap, 0xe0, value); + regmap_write(rt5682->sdw_regmap, 0xf0, value); + + dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq); + + return 0; +} + +static int rt5682_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev); + int ret; + + memcpy(&rt5682->params, params, sizeof(*params)); + + ret = rt5682_clock_config(&slave->dev); + if (ret < 0) + dev_err(&slave->dev, "Invalid clk config"); + + return ret; +} + +static int rt5682_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct rt5682_priv *rt5682 = 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, + &rt5682->jack_detect_work, msecs_to_jiffies(250)); + } + + return 0; +} + +static struct sdw_slave_ops rt5682_slave_ops = { + .read_prop = rt5682_read_prop, + .interrupt_callback = rt5682_interrupt_callback, + .update_status = rt5682_update_status, + .bus_config = rt5682_bus_config, +}; + +static int rt5682_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap; + + /* Assign ops */ + slave->ops = &rt5682_slave_ops; + + /* Regmap Initialization */ + regmap = devm_regmap_init_sdw(slave, &rt5682_sdw_regmap); + if (!regmap) + return -EINVAL; + + rt5682_sdw_init(&slave->dev, regmap, slave); + + return 0; +} + +static int rt5682_sdw_remove(struct sdw_slave *slave) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev); + + if (rt5682 && rt5682->hw_init) + cancel_delayed_work(&rt5682->jack_detect_work); + + return 0; +} + +static const struct sdw_device_id rt5682_id[] = { + SDW_SLAVE_ENTRY(0x025d, 0x5682, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt5682_id); + +static int rt5682_dev_suspend(struct device *dev) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + + if (!rt5682->hw_init) + return 0; + + regcache_cache_only(rt5682->regmap, true); + regcache_mark_dirty(rt5682->regmap); + + return 0; +} + +#define rt5682_PROBE_TIMEOUT 2000 + +static int rt5682_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = to_sdw_slave_device(dev); + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt5682->hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(rt5682_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt5682->regmap, false); + regcache_sync(rt5682->regmap); + + return 0; +} + +static const struct dev_pm_ops rt5682_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt5682_dev_suspend, rt5682_dev_resume) + SET_RUNTIME_PM_OPS(rt5682_dev_suspend, rt5682_dev_resume, NULL) +}; + +static struct sdw_driver rt5682_sdw_driver = { + .driver = { + .name = "rt5682", + .owner = THIS_MODULE, + .pm = &rt5682_pm, + }, + .probe = rt5682_sdw_probe, + .remove = rt5682_sdw_remove, + .ops = &rt5682_slave_ops, + .id_table = rt5682_id, +}; +module_sdw_driver(rt5682_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT5682 driver SDW"); +MODULE_AUTHOR("Oder Chiou "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5682-sdw.h b/sound/soc/codecs/rt5682-sdw.h new file mode 100644 index 00000000000000..688a172ad74570 --- /dev/null +++ b/sound/soc/codecs/rt5682-sdw.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5682-sdw.h -- RT5682 SDW ALSA SoC audio driver + * + * Copyright 2019 Realtek Semiconductor Corp. + * Author: Oder Chiou + */ + +#ifndef __RT5682_SDW_H__ +#define __RT5682_SDW_H__ + +#define RT5682_SDW_ADDR_L 0x3000 +#define RT5682_SDW_ADDR_H 0x3001 +#define RT5682_SDW_DATA_L 0x3004 +#define RT5682_SDW_DATA_H 0x3005 +#define RT5682_SDW_CMD 0x3008 + +#endif /* __RT5682_SDW_H__ */ diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 02047800498670..d85dfbaca5d143 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -11,13 +11,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include @@ -31,8 +31,7 @@ #include "rl6231.h" #include "rt5682.h" - -#define RT5682_NUM_SUPPLIES 3 +#include "rt5682-sdw.h" static const char *rt5682_supply_names[RT5682_NUM_SUPPLIES] = { "AVDD", @@ -47,30 +46,6 @@ static const struct rt5682_platform_data i2s_default_platform_data = { .btndet_delay = 16, }; -struct rt5682_priv { - struct snd_soc_component *component; - struct rt5682_platform_data pdata; - struct regmap *regmap; - struct snd_soc_jack *hs_jack; - struct regulator_bulk_data supplies[RT5682_NUM_SUPPLIES]; - struct delayed_work jack_detect_work; - struct delayed_work jd_check_work; - struct mutex calibrate_mutex; - bool is_sdw; - - int sysclk; - int sysclk_src; - int lrck[RT5682_AIFS]; - int bclk[RT5682_AIFS]; - int master[RT5682_AIFS]; - - int pll_src; - int pll_in; - int pll_out; - - int jack_type; -}; - static const struct reg_sequence patch_list[] = { {RT5682_HP_IMP_SENS_CTRL_19, 0x1000}, {RT5682_DAC_ADC_DIG_VOL1, 0xa020}, @@ -805,6 +780,21 @@ static const struct snd_kcontrol_new rt5682_if1_45_adc_swap_mux = static const struct snd_kcontrol_new rt5682_if1_67_adc_swap_mux = SOC_DAPM_ENUM("IF1 67 ADC Swap Mux", rt5682_if1_67_adc_enum); +static const char * const rt5682_dac_select[] = { + "IF1", "SOUND" +}; +static SOC_ENUM_SINGLE_DECL(rt5682_dacl_enum, + RT5682_AD_DA_MIXER, RT5682_DAC1_L_SEL_SFT, rt5682_dac_select); + +static const struct snd_kcontrol_new rt5682_dac_l_mux = + SOC_DAPM_ENUM("DAC L Mux", rt5682_dacl_enum); + +static SOC_ENUM_SINGLE_DECL(rt5682_dacr_enum, + RT5682_AD_DA_MIXER, RT5682_DAC1_R_SEL_SFT, rt5682_dac_select); + +static const struct snd_kcontrol_new rt5682_dac_r_mux = + SOC_DAPM_ENUM("DAC R Mux", rt5682_dacr_enum); + static void rt5682_reset(struct rt5682_priv *rt5682) { regmap_write(rt5682->regmap, RT5682_RESET, 0); @@ -1708,6 +1698,8 @@ static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = { SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SOUND DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SOUND DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), /* Digital Interface Select */ SND_SOC_DAPM_MUX("IF1 01 ADC Swap Mux", SND_SOC_NOPM, 0, 0, @@ -1724,12 +1716,19 @@ static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = { SND_SOC_DAPM_MUX("ADCDAT Mux", SND_SOC_NOPM, 0, 0, &rt5682_adcdat_pin_ctrl), + SND_SOC_DAPM_MUX("DAC L Mux", SND_SOC_NOPM, 0, 0, + &rt5682_dac_l_mux), + SND_SOC_DAPM_MUX("DAC R Mux", SND_SOC_NOPM, 0, 0, + &rt5682_dac_r_mux), + /* Audio Interface */ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, RT5682_I2S1_SDP, RT5682_SEL_ADCDAT_SFT, 1), SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, RT5682_I2S2_SDP, RT5682_I2S2_PIN_CFG_SFT, 1), SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SDWRX", "SDW Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("SDWTX", "SDW Capture", 0, SND_SOC_NOPM, 0, 0), /* Output Side */ /* DAC mixer before sound effect */ @@ -1882,8 +1881,8 @@ static const struct snd_soc_dapm_route rt5682_dapm_routes[] = { {"IF1_ADC Mux", "Slot 2", "IF1 23 ADC Swap Mux"}, {"IF1_ADC Mux", "Slot 4", "IF1 45 ADC Swap Mux"}, {"IF1_ADC Mux", "Slot 6", "IF1 67 ADC Swap Mux"}, - {"IF1_ADC Mux", NULL, "I2S1"}, {"ADCDAT Mux", "ADCDAT1", "IF1_ADC Mux"}, + {"AIF1TX", NULL, "I2S1"}, {"AIF1TX", NULL, "ADCDAT Mux"}, {"IF2 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, {"IF2 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, @@ -1892,6 +1891,10 @@ static const struct snd_soc_dapm_route rt5682_dapm_routes[] = { {"ADCDAT Mux", "ADCDAT2", "IF2 ADC Swap Mux"}, {"AIF2TX", NULL, "ADCDAT Mux"}, + {"SDWTX", NULL, "PLL2B"}, + {"SDWTX", NULL, "PLL2F"}, + {"SDWTX", NULL, "ADCDAT Mux"}, + {"IF1 DAC1 L", NULL, "AIF1RX"}, {"IF1 DAC1 L", NULL, "I2S1"}, {"IF1 DAC1 L", NULL, "DAC Stereo1 Filter"}, @@ -1899,10 +1902,24 @@ static const struct snd_soc_dapm_route rt5682_dapm_routes[] = { {"IF1 DAC1 R", NULL, "I2S1"}, {"IF1 DAC1 R", NULL, "DAC Stereo1 Filter"}, + {"SOUND DAC L", NULL, "SDWRX"}, + {"SOUND DAC L", NULL, "DAC Stereo1 Filter"}, + {"SOUND DAC L", NULL, "PLL2B"}, + {"SOUND DAC L", NULL, "PLL2F"}, + {"SOUND DAC R", NULL, "SDWRX"}, + {"SOUND DAC R", NULL, "DAC Stereo1 Filter"}, + {"SOUND DAC R", NULL, "PLL2B"}, + {"SOUND DAC R", NULL, "PLL2F"}, + + {"DAC L Mux", "IF1", "IF1 DAC1 L"}, + {"DAC L Mux", "SOUND", "SOUND DAC L"}, + {"DAC R Mux", "IF1", "IF1 DAC1 R"}, + {"DAC R Mux", "SOUND", "SOUND DAC R"}, + {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"}, - {"DAC1 MIXL", "DAC1 Switch", "IF1 DAC1 L"}, + {"DAC1 MIXL", "DAC1 Switch", "DAC L Mux"}, {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"}, - {"DAC1 MIXR", "DAC1 Switch", "IF1 DAC1 R"}, + {"DAC1 MIXR", "DAC1 Switch", "DAC R Mux"}, {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"}, {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"}, @@ -2399,6 +2416,194 @@ static const struct snd_soc_dai_ops rt5682_aif2_dai_ops = { .set_bclk_ratio = rt5682_set_bclk_ratio, }; +#if IS_ENABLED(CONFIG_SND_SOC_RT5682_SDW) +struct sdw_stream_data { + struct sdw_stream_runtime *sdw_stream; +}; + +static int rt5682_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 rt5682_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 rt5682_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 rt5682_priv *rt5682 = 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_p = 0, val_c = 0, osr_p = 0, osr_c = 0; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!stream) + return -ENOMEM; + + if (!rt5682->slave) + return -EINVAL; + + /* SoundWire specific configuration */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + port = 1; + } else { + direction = SDW_DATA_DIR_TX; + port = 2; + } + + 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(rt5682->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)) { + case 48000: + val_p = RT5682_SDW_REF_1_48K; + val_c = RT5682_SDW_REF_2_48K; + break; + case 96000: + val_p = RT5682_SDW_REF_1_96K; + val_c = RT5682_SDW_REF_2_96K; + break; + case 192000: + val_p = RT5682_SDW_REF_1_192K; + val_c = RT5682_SDW_REF_2_192K; + break; + case 32000: + val_p = RT5682_SDW_REF_1_32K; + val_c = RT5682_SDW_REF_2_32K; + break; + case 24000: + val_p = RT5682_SDW_REF_1_24K; + val_c = RT5682_SDW_REF_2_24K; + break; + case 16000: + val_p = RT5682_SDW_REF_1_16K; + val_c = RT5682_SDW_REF_2_16K; + break; + case 12000: + val_p = RT5682_SDW_REF_1_12K; + val_c = RT5682_SDW_REF_2_12K; + break; + case 8000: + val_p = RT5682_SDW_REF_1_8K; + val_c = RT5682_SDW_REF_2_8K; + break; + case 44100: + val_p = RT5682_SDW_REF_1_44K; + val_c = RT5682_SDW_REF_2_44K; + break; + case 88200: + val_p = RT5682_SDW_REF_1_88K; + val_c = RT5682_SDW_REF_2_88K; + break; + case 176400: + val_p = RT5682_SDW_REF_1_176K; + val_c = RT5682_SDW_REF_2_176K; + break; + case 22050: + val_p = RT5682_SDW_REF_1_22K; + val_c = RT5682_SDW_REF_2_22K; + break; + case 11025: + val_p = RT5682_SDW_REF_1_11K; + val_c = RT5682_SDW_REF_2_11K; + break; + default: + return -EINVAL; + } + + if (params_rate(params) <= 48000) { + osr_p = RT5682_DAC_OSR_D_8; + osr_c = RT5682_ADC_OSR_D_8; + } else if (params_rate(params) <= 96000) { + osr_p = RT5682_DAC_OSR_D_4; + osr_c = RT5682_ADC_OSR_D_4; + } else { + osr_p = RT5682_DAC_OSR_D_2; + osr_c = RT5682_ADC_OSR_D_2; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(rt5682->regmap, RT5682_SDW_REF_CLK, + RT5682_SDW_REF_1_MASK, val_p); + regmap_update_bits(rt5682->regmap, RT5682_ADDA_CLK_1, + RT5682_DAC_OSR_MASK, osr_p); + } else { + regmap_update_bits(rt5682->regmap, RT5682_SDW_REF_CLK, + RT5682_SDW_REF_2_MASK, val_c); + regmap_update_bits(rt5682->regmap, RT5682_ADDA_CLK_1, + RT5682_ADC_OSR_MASK, osr_c); + } + + return retval; +} + +static int rt5682_sdw_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct sdw_stream_data *stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt5682->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt5682->slave, stream->sdw_stream); + return 0; +} + +static struct snd_soc_dai_ops rt5682_sdw_ops = { + .hw_params = rt5682_sdw_hw_params, + .hw_free = rt5682_sdw_hw_free, + .set_sdw_stream = rt5682_set_sdw_stream, + .shutdown = rt5682_sdw_shutdown, +}; +#endif + static struct snd_soc_dai_driver rt5682_dai[] = { { .name = "rt5682-aif1", @@ -2431,6 +2636,27 @@ static struct snd_soc_dai_driver rt5682_dai[] = { }, .ops = &rt5682_aif2_dai_ops, }, +#if IS_ENABLED(CONFIG_SND_SOC_RT5682_SDW) + { + .name = "rt5682-sdw", + .id = RT5682_SDW, + .playback = { + .stream_name = "SDW Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .capture = { + .stream_name = "SDW Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .ops = &rt5682_sdw_ops, + }, +#endif }; static const struct snd_soc_component_driver soc_component_dev_rt5682 = { @@ -2541,6 +2767,217 @@ static void rt5682_calibrate(struct rt5682_priv *rt5682) } +#if IS_ENABLED(CONFIG_SND_SOC_RT5682_SDW) +static int rt5682_sdw_read(void *context, unsigned int reg, unsigned int *val) +{ + struct device *dev = context; + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + unsigned int data_l, data_h; + + regmap_write(rt5682->sdw_regmap, RT5682_SDW_CMD, 0); + regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_H, (reg >> 8) & 0xff); + regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_L, (reg & 0xff)); + regmap_read(rt5682->sdw_regmap, RT5682_SDW_DATA_H, &data_h); + regmap_read(rt5682->sdw_regmap, RT5682_SDW_DATA_L, &data_l); + + *val = (data_h << 8) | data_l; + + dev_vdbg(dev, "[%s] %04x => %04x\n", __func__, reg, *val); + + return 0; +} + +static int rt5682_sdw_write(void *context, unsigned int reg, unsigned int val) +{ + struct device *dev = context; + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + + regmap_write(rt5682->sdw_regmap, RT5682_SDW_CMD, 1); + regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_H, (reg >> 8) & 0xff); + regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_L, (reg & 0xff)); + regmap_write(rt5682->sdw_regmap, RT5682_SDW_DATA_H, (val >> 8) & 0xff); + regmap_write(rt5682->sdw_regmap, RT5682_SDW_DATA_L, (val & 0xff)); + + dev_vdbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val); + + return 0; +} + +static const struct regmap_config rt5682_sdw_regmap = { + .reg_bits = 16, + .val_bits = 16, + .max_register = RT5682_I2C_MODE, + .volatile_reg = rt5682_volatile_register, + .readable_reg = rt5682_readable_register, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5682_reg, + .num_reg_defaults = ARRAY_SIZE(rt5682_reg), + .use_single_read = true, + .use_single_write = true, + .reg_read = rt5682_sdw_read, + .reg_write = rt5682_sdw_write, +}; + +int rt5682_sdw_init(struct device *dev, struct regmap *regmap, + struct sdw_slave *slave) +{ + struct rt5682_priv *rt5682; + int ret; + + rt5682 = devm_kzalloc(dev, sizeof(*rt5682), GFP_KERNEL); + if (!rt5682) + return -ENOMEM; + + dev_set_drvdata(dev, rt5682); + rt5682->slave = slave; + rt5682->sdw_regmap = regmap; + rt5682->is_sdw = true; + + rt5682->regmap = devm_regmap_init(dev, NULL, dev, &rt5682_sdw_regmap); + if (IS_ERR(rt5682->regmap)) { + ret = PTR_ERR(rt5682->regmap); + dev_err(dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt5682->hw_init = false; + rt5682->first_init = false; + + mutex_init(&rt5682->calibrate_mutex); + INIT_DELAYED_WORK(&rt5682->jack_detect_work, + rt5682_jack_detect_handler); + + ret = devm_snd_soc_register_component(dev, &soc_component_dev_rt5682, + rt5682_dai, ARRAY_SIZE(rt5682_dai)); + + dev_dbg(&slave->dev, "%s\n", __func__); + + return ret; +} +EXPORT_SYMBOL_GPL(rt5682_sdw_init); + +int rt5682_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + int ret = 0; + unsigned int val; + + if (rt5682->hw_init) + return 0; + + regmap_read(rt5682->regmap, RT5682_DEVICE_ID, &val); + if (val != DEVICE_ID) { + pr_err("Device with ID register %x is not rt5682\n", val); + return -ENODEV; + } + + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + if (!rt5682->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); + } + + pm_runtime_get_noresume(&slave->dev); + + rt5682_reset(rt5682); + + if (rt5682->first_init) + regcache_cache_bypass(rt5682->regmap, true); + + rt5682_calibrate(rt5682); + + if (rt5682->first_init) { + regcache_cache_bypass(rt5682->regmap, false); + regcache_mark_dirty(rt5682->regmap); + regcache_sync(rt5682->regmap); + + /* volatile registers */ + regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_2, + RT5682_EXT_JD_SRC, RT5682_EXT_JD_SRC_MANUAL); + + goto reinit; + } + + ret = regmap_multi_reg_write(rt5682->regmap, patch_list, + ARRAY_SIZE(patch_list)); + if (ret != 0) + dev_warn(dev, "Failed to apply regmap patch: %d\n", ret); + + regmap_write(rt5682->regmap, RT5682_DEPOP_1, 0x0000); + + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1, + RT5682_LDO1_DVO_MASK | RT5682_HP_DRIVER_MASK, + RT5682_LDO1_DVO_12 | RT5682_HP_DRIVER_5X); + regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0380); + regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000); + regmap_update_bits(rt5682->regmap, RT5682_BIAS_CUR_CTRL_8, + RT5682_HPA_CP_BIAS_CTRL_MASK, RT5682_HPA_CP_BIAS_3UA); + regmap_update_bits(rt5682->regmap, RT5682_CHARGE_PUMP_1, + RT5682_CP_CLK_HP_MASK, RT5682_CP_CLK_HP_300KHZ); + + /* Soundwire */ + regmap_write(rt5682->regmap, RT5682_PLL2_INTERNAL, 0xa266); + regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_1, 0x1700); + regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_2, 0x0006); + regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_3, 0x2600); + regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_4, 0x0c8f); + regmap_write(rt5682->regmap, RT5682_PLL_TRACK_2, 0x3000); + regmap_write(rt5682->regmap, RT5682_PLL_TRACK_3, 0x4000); + regmap_update_bits(rt5682->regmap, RT5682_GLB_CLK, + RT5682_SCLK_SRC_MASK | RT5682_PLL2_SRC_MASK, + RT5682_SCLK_SRC_PLL2 | RT5682_PLL2_SRC_SDW); + + regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_2, + RT5682_EXT_JD_SRC, RT5682_EXT_JD_SRC_MANUAL); + regmap_write(rt5682->regmap, RT5682_CBJ_CTRL_1, 0xd042); + regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_3, + RT5682_CBJ_IN_BUF_EN, RT5682_CBJ_IN_BUF_EN); + regmap_update_bits(rt5682->regmap, RT5682_SAR_IL_CMD_1, + RT5682_SAR_POW_MASK, RT5682_SAR_POW_EN); + regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, + RT5682_POW_IRQ | RT5682_POW_JDH | + RT5682_POW_ANA, RT5682_POW_IRQ | + RT5682_POW_JDH | RT5682_POW_ANA); + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_2, + RT5682_PWR_JDH, RT5682_PWR_JDH); + regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, + RT5682_JD1_EN_MASK | RT5682_JD1_IRQ_MASK, + RT5682_JD1_EN | RT5682_JD1_IRQ_PUL); + +reinit: + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, msecs_to_jiffies(250)); + + /* Mark Slave initialization complete */ + rt5682->hw_init = true; + rt5682->first_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 ret; +} +EXPORT_SYMBOL_GPL(rt5682_io_init); +#endif + static int rt5682_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h index 61a577dacdaa3d..a5c8429cebcae0 100644 --- a/sound/soc/codecs/rt5682.h +++ b/sound/soc/codecs/rt5682.h @@ -10,6 +10,9 @@ #define __RT5682_H__ #include +#include +#include +#include #define DEVICE_ID 0x6530 @@ -177,7 +180,7 @@ #define RT5682_TEST_MODE_CTRL_4 0x0148 #define RT5682_TEST_MODE_CTRL_5 0x0149 #define RT5682_PLL1_INTERNAL 0x0150 -#define RT5682_PLL2_INTERNAL 0x0151 +#define RT5682_PLL2_INTERNAL 0x0156 #define RT5682_STO_NG2_CTRL_1 0x0160 #define RT5682_STO_NG2_CTRL_2 0x0161 #define RT5682_STO_NG2_CTRL_3 0x0162 @@ -354,7 +357,6 @@ #define RT5682_R_EQ_POST_VOL 0x03f3 #define RT5682_I2C_MODE 0xffff - /* global definition */ #define RT5682_L_MUTE (0x1 << 15) #define RT5682_L_MUTE_SFT 15 @@ -1091,6 +1093,9 @@ #define RT5682_JD1_POL_MASK (0x1 << 13) #define RT5682_JD1_POL_NOR (0x0 << 13) #define RT5682_JD1_POL_INV (0x1 << 13) +#define RT5682_JD1_IRQ_MASK (0x1 << 10) +#define RT5682_JD1_IRQ_LEV (0x0 << 10) +#define RT5682_JD1_IRQ_PUL (0x1 << 10) /* IRQ Control 3 (0x00b8) */ #define RT5682_IL_IRQ_MASK (0x1 << 7) @@ -1317,6 +1322,7 @@ enum { enum { RT5682_AIF1, RT5682_AIF2, + RT5682_SDW, RT5682_AIFS }; @@ -1332,7 +1338,42 @@ enum { RT5682_CLK_SEL_I2S2_ASRC, }; +#define RT5682_NUM_SUPPLIES 3 + +struct rt5682_priv { + struct snd_soc_component *component; + struct rt5682_platform_data pdata; + struct regmap *regmap; + struct regmap *sdw_regmap; + struct snd_soc_jack *hs_jack; + struct regulator_bulk_data supplies[RT5682_NUM_SUPPLIES]; + struct delayed_work jack_detect_work; + struct delayed_work jd_check_work; + struct mutex calibrate_mutex; + struct sdw_slave *slave; + enum sdw_slave_status status; + struct sdw_bus_params params; + bool hw_init; + bool first_init; + bool is_sdw; + + int sysclk; + int sysclk_src; + int lrck[RT5682_AIFS]; + int bclk[RT5682_AIFS]; + int master[RT5682_AIFS]; + + int pll_src; + int pll_in; + int pll_out; + + int jack_type; +}; + int rt5682_sel_asrc_clk_src(struct snd_soc_component *component, unsigned int filter_mask, unsigned int clk_src); +int rt5682_sdw_init(struct device *dev, struct regmap *regmap, + struct sdw_slave *slave); +int rt5682_io_init(struct device *dev, struct sdw_slave *slave); #endif /* __RT5682_H__ */ From 77b3ef4e113c8848e3ec04843c6eed44e8aa5574 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Mon, 2 Dec 2019 19:03:11 +0800 Subject: [PATCH 45/46] Add missing rt715_reg_defaults values. Signed-off-by: Jack Yu --- sound/soc/codecs/rt715-sdw.c | 24 ++++++++++++++++++++++++ sound/soc/codecs/rt715-sdw.h | 24 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index d1fe32a9397df4..c35591fd281b3e 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -77,14 +77,38 @@ static bool rt715_readable_register(struct device *dev, unsigned int reg) case 0x4f13: case 0x4f1d: case 0x4f29: + case 0x7207: + case 0x7208: + case 0x7209: + case 0x7227: case 0x7307: case 0x7308: case 0x7309: + case 0x7312: + case 0x7313: + case 0x7318: + case 0x7319: + case 0x731a: + case 0x731b: + case 0x731d: case 0x7327: + case 0x7329: + case 0x8287: + case 0x8288: + case 0x8289: + case 0x82a7: case 0x8387: case 0x8388: case 0x8389: + case 0x8392: + case 0x8393: + case 0x8398: + case 0x8399: + case 0x839a: + case 0x839b: + case 0x839d: case 0x83a7: + case 0x83a9: case 0x752039: return true; default: diff --git a/sound/soc/codecs/rt715-sdw.h b/sound/soc/codecs/rt715-sdw.h index 0a74a6497c2089..5d7661e335ae55 100644 --- a/sound/soc/codecs/rt715-sdw.h +++ b/sound/soc/codecs/rt715-sdw.h @@ -299,14 +299,38 @@ static const struct reg_default rt715_reg_defaults[] = { { 0x4f13, 0x411111f0 }, { 0x4f1d, 0x411111f0 }, { 0x4f29, 0x411111f0 }, + { 0x7207, 0x00 }, + { 0x8287, 0x00 }, + { 0x7208, 0x00 }, + { 0x8288, 0x00 }, + { 0x7209, 0x00 }, + { 0x8289, 0x00 }, + { 0x7227, 0x00 }, + { 0x82a7, 0x00 }, { 0x7307, 0x97 }, { 0x8387, 0x97 }, { 0x7308, 0x97 }, { 0x8388, 0x97 }, { 0x7309, 0x97 }, { 0x8389, 0x97 }, + { 0x7312, 0x00 }, + { 0x8392, 0x00 }, + { 0x7313, 0x00 }, + { 0x8393, 0x00 }, + { 0x7318, 0x00 }, + { 0x8398, 0x00 }, + { 0x7319, 0x00 }, + { 0x8399, 0x00 }, + { 0x731a, 0x00 }, + { 0x839a, 0x00 }, + { 0x731b, 0x00 }, + { 0x839b, 0x00 }, + { 0x731d, 0x00 }, + { 0x839d, 0x00 }, { 0x7327, 0x97 }, { 0x83a7, 0x97 }, + { 0x7329, 0x00 }, + { 0x83a9, 0x00 }, { 0x752039, 0xa500 }, }; From ebe45fad8ca44a7ab10c9f388cbfa9e48d12ea74 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Mon, 2 Dec 2019 13:23:43 +0800 Subject: [PATCH 46/46] ASoC: codec: Fix IO timeout when doing jack detection Since btn_check_handler and jack_detect_handler work in work thread, we need to wake up codecs when doing jack detection. Signed-off-by: Rander Wang --- sound/soc/codecs/rt700.c | 28 ++++++++++++++++++++++++++-- sound/soc/codecs/rt711.c | 28 ++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 44a8cafbad5bfa..cfaf2f6ef982d8 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -156,8 +156,9 @@ 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; + struct sdw_slave *slave = rt700->slave; unsigned int jack_status = 0, reg; + int btn_type = 0, ret; if (!rt700->hs_jack) return; @@ -165,6 +166,12 @@ static void rt700_jack_detect_handler(struct work_struct *work) if (!rt700->component->card->instantiated) return; + ret = pm_runtime_get_sync(&slave->dev); + if (ret < 0) { + dev_err(&slave->dev, "Failed to resume device: %d\n", ret); + return ret; + } + reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT; ret = regmap_read(rt700->regmap, reg, &jack_status); if (ret < 0) @@ -209,9 +216,14 @@ static void rt700_jack_detect_handler(struct work_struct *work) &rt700->jack_btn_check_work, msecs_to_jiffies(200)); } + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put(&slave->dev); + return; io_error: + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put(&slave->dev); pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); } @@ -219,8 +231,15 @@ 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; + struct sdw_slave *slave = rt700->slave; unsigned int jack_status = 0, reg; + int btn_type = 0, ret; + + ret = pm_runtime_get_sync(&slave->dev); + if (ret < 0) { + dev_err(&slave->dev, "Failed to resume device: %d\n", ret); + return ret; + } reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT; ret = regmap_read(rt700->regmap, reg, &jack_status); @@ -263,9 +282,14 @@ static void rt700_btn_check_handler(struct work_struct *work) &rt700->jack_btn_check_work, msecs_to_jiffies(200)); } + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put(&slave->dev); + return; io_error: + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put(&slave->dev); pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); } diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index 4090dc09694d0b..4422665a02923d 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -234,8 +234,9 @@ 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; + struct sdw_slave *slave = rt711->slave; unsigned int jack_status = 0, reg; + int btn_type = 0, ret; if (!rt711->hs_jack) return; @@ -243,6 +244,12 @@ static void rt711_jack_detect_handler(struct work_struct *work) if (!rt711->component->card->instantiated) return; + ret = pm_runtime_get_sync(&slave->dev); + if (ret < 0) { + dev_err(&slave->dev, "Failed to resume device: %d\n", ret); + return ret; + } + reg = RT711_VERB_GET_PIN_SENSE | RT711_HP_OUT; ret = regmap_read(rt711->regmap, reg, &jack_status); if (ret < 0) @@ -287,9 +294,14 @@ static void rt711_jack_detect_handler(struct work_struct *work) &rt711->jack_btn_check_work, msecs_to_jiffies(200)); } + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put(&slave->dev); + return; io_error: + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put(&slave->dev); pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); } @@ -297,8 +309,15 @@ 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; + struct sdw_slave *slave = rt711->slave; unsigned int jack_status = 0, reg; + int btn_type = 0, ret; + + ret = pm_runtime_get_sync(&slave->dev); + if (ret < 0) { + dev_err(&slave->dev, "Failed to resume device: %d\n", ret); + return ret; + } reg = RT711_VERB_GET_PIN_SENSE | RT711_HP_OUT; ret = regmap_read(rt711->regmap, reg, &jack_status); @@ -342,9 +361,14 @@ static void rt711_btn_check_handler(struct work_struct *work) &rt711->jack_btn_check_work, msecs_to_jiffies(200)); } + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put(&slave->dev); + return; io_error: + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put(&slave->dev); pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); }