From 442798a06b6907ea3edbdf9fef81a1b681fcf30b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 10 Sep 2019 18:46:08 -0500 Subject: [PATCH 001/170] soundwire: renames to prepare support for master drivers/devices Add clearer references to sdw_slave_driver for internal macros No change for sdw_driver and module_sdw_driver to avoid compatibility issues with existing codec devices No functionality change. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus_type.c | 21 +++++++++++---------- include/linux/soundwire/sdw_type.h | 18 ++++++++++-------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 4a465f55039f4b..370b9475266228 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -34,7 +34,7 @@ sdw_get_device_id(struct sdw_slave *slave, struct sdw_driver *drv) static int sdw_bus_match(struct device *dev, struct device_driver *ddrv) { struct sdw_slave *slave = dev_to_sdw_dev(dev); - struct sdw_driver *drv = drv_to_sdw_driver(ddrv); + struct sdw_driver *drv = drv_to_sdw_slave_driver(ddrv); return !!sdw_get_device_id(slave, drv); } @@ -70,7 +70,7 @@ EXPORT_SYMBOL_GPL(sdw_bus_type); static int sdw_drv_probe(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); - struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); + struct sdw_driver *drv = drv_to_sdw_slave_driver(dev->driver); const struct sdw_device_id *id; int ret; @@ -116,7 +116,7 @@ static int sdw_drv_probe(struct device *dev) static int sdw_drv_remove(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); - struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); + struct sdw_driver *drv = drv_to_sdw_slave_driver(dev->driver); int ret = 0; if (drv->remove) @@ -130,20 +130,21 @@ static int sdw_drv_remove(struct device *dev) static void sdw_drv_shutdown(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); - struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); + struct sdw_driver *drv = drv_to_sdw_slave_driver(dev->driver); if (drv->shutdown) drv->shutdown(slave); } /** - * __sdw_register_driver() - register a SoundWire Slave driver + * __sdw_register_slave_driver() - register a SoundWire Slave driver * @drv: driver to register * @owner: owning module/driver * * Return: zero on success, else a negative error code. */ -int __sdw_register_driver(struct sdw_driver *drv, struct module *owner) +int __sdw_register_slave_driver(struct sdw_driver *drv, + struct module *owner) { drv->driver.bus = &sdw_bus_type; @@ -164,17 +165,17 @@ int __sdw_register_driver(struct sdw_driver *drv, struct module *owner) return driver_register(&drv->driver); } -EXPORT_SYMBOL_GPL(__sdw_register_driver); +EXPORT_SYMBOL_GPL(__sdw_register_slave_driver); /** - * sdw_unregister_driver() - unregisters the SoundWire Slave driver + * sdw_unregister_slave_driver() - unregisters the SoundWire Slave driver * @drv: driver to unregister */ -void sdw_unregister_driver(struct sdw_driver *drv) +void sdw_unregister_slave_driver(struct sdw_driver *drv) { driver_unregister(&drv->driver); } -EXPORT_SYMBOL_GPL(sdw_unregister_driver); +EXPORT_SYMBOL_GPL(sdw_unregister_slave_driver); static int __init sdw_bus_init(void) { diff --git a/include/linux/soundwire/sdw_type.h b/include/linux/soundwire/sdw_type.h index aaa7f4267c149a..abaa2127815254 100644 --- a/include/linux/soundwire/sdw_type.h +++ b/include/linux/soundwire/sdw_type.h @@ -6,13 +6,15 @@ extern struct bus_type sdw_bus_type; -#define drv_to_sdw_driver(_drv) container_of(_drv, struct sdw_driver, driver) +#define drv_to_sdw_slave_driver(_drv) \ + container_of(_drv, struct sdw_driver, driver) -#define sdw_register_driver(drv) \ - __sdw_register_driver(drv, THIS_MODULE) +#define sdw_register_slave_driver(drv) \ + __sdw_register_slave_driver(drv, THIS_MODULE) -int __sdw_register_driver(struct sdw_driver *drv, struct module *owner); -void sdw_unregister_driver(struct sdw_driver *drv); +int __sdw_register_slave_driver(struct sdw_driver *drv, + struct module *owner); +void sdw_unregister_slave_driver(struct sdw_driver *drv); int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size); @@ -24,7 +26,7 @@ int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size); * module init/exit. This eliminates a lot of boilerplate. Each module may only * use this macro once, and calling it replaces module_init() and module_exit() */ -#define module_sdw_driver(__sdw_driver) \ - module_driver(__sdw_driver, sdw_register_driver, \ - sdw_unregister_driver) +#define module_sdw_driver(__sdw_slave_driver) \ + module_driver(__sdw_slave_driver, sdw_register_slave_driver, \ + sdw_unregister_slave_driver) #endif /* __SOUNDWIRE_TYPES_H */ From 91b053c50f313eebb4b9c74ef61a9e28bdf80a71 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 10 Sep 2019 19:58:17 -0500 Subject: [PATCH 002/170] soundwire: rename dev_to_sdw_dev macro Since we want to introduce master devices, rename macro so that we have consistency between slave and master device access. Signed-off-by: Pierre-Louis Bossart --- drivers/base/regmap/regmap-sdw.c | 4 ++-- drivers/soundwire/bus.c | 2 +- drivers/soundwire/bus_type.c | 11 ++++++----- drivers/soundwire/slave.c | 2 +- include/linux/soundwire/sdw.h | 3 ++- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/base/regmap/regmap-sdw.c b/drivers/base/regmap/regmap-sdw.c index 50a66382d87d09..d1fc0c22180a17 100644 --- a/drivers/base/regmap/regmap-sdw.c +++ b/drivers/base/regmap/regmap-sdw.c @@ -10,7 +10,7 @@ static int regmap_sdw_write(void *context, unsigned int reg, unsigned int val) { struct device *dev = context; - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = to_sdw_slave_device(dev); return sdw_write(slave, reg, val); } @@ -18,7 +18,7 @@ static int regmap_sdw_write(void *context, unsigned int reg, unsigned int val) static int regmap_sdw_read(void *context, unsigned int reg, unsigned int *val) { struct device *dev = context; - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = to_sdw_slave_device(dev); int read; read = sdw_read(slave, reg); diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index be5d437058ed19..4b22ee996a6566 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -110,7 +110,7 @@ EXPORT_SYMBOL(sdw_add_bus_master); static int sdw_delete_slave(struct device *dev, void *data) { - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = to_sdw_slave_device(dev); struct sdw_bus *bus = slave->bus; sdw_slave_debugfs_exit(slave); diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 370b9475266228..c0585bcc8a41b8 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -33,7 +33,7 @@ sdw_get_device_id(struct sdw_slave *slave, struct sdw_driver *drv) static int sdw_bus_match(struct device *dev, struct device_driver *ddrv) { - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = to_sdw_slave_device(dev); struct sdw_driver *drv = drv_to_sdw_slave_driver(ddrv); return !!sdw_get_device_id(slave, drv); @@ -49,7 +49,7 @@ int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size) static int sdw_uevent(struct device *dev, struct kobj_uevent_env *env) { - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = to_sdw_slave_device(dev); char modalias[32]; sdw_slave_modalias(slave, modalias, sizeof(modalias)); @@ -69,7 +69,7 @@ EXPORT_SYMBOL_GPL(sdw_bus_type); static int sdw_drv_probe(struct device *dev) { - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = to_sdw_slave_device(dev); struct sdw_driver *drv = drv_to_sdw_slave_driver(dev->driver); const struct sdw_device_id *id; int ret; @@ -115,8 +115,9 @@ static int sdw_drv_probe(struct device *dev) static int sdw_drv_remove(struct device *dev) { - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = to_sdw_slave_device(dev); struct sdw_driver *drv = drv_to_sdw_slave_driver(dev->driver); + int ret = 0; if (drv->remove) @@ -129,7 +130,7 @@ static int sdw_drv_remove(struct device *dev) static void sdw_drv_shutdown(struct device *dev) { - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = to_sdw_slave_device(dev); struct sdw_driver *drv = drv_to_sdw_slave_driver(dev->driver); if (drv->shutdown) diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index 19919975bb6da4..48a513680db665 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -9,7 +9,7 @@ static void sdw_slave_release(struct device *dev) { - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = to_sdw_slave_device(dev); kfree(slave); } diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index b7c9eca4332a3a..5b1180f1e6b558 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -582,7 +582,8 @@ struct sdw_slave { u32 unattach_request; }; -#define dev_to_sdw_dev(_dev) container_of(_dev, struct sdw_slave, dev) +#define to_sdw_slave_device(d) \ + container_of(d, struct sdw_slave, dev) struct sdw_driver { const char *name; From f80f064a5c082eb3e8320f23e6bf9fe1763ca645 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 10 Sep 2019 20:27:41 -0500 Subject: [PATCH 003/170] soundwire: rename drv_to_sdw_slave_driver macro Align with previous renames and shorten macro No functionality change Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus_type.c | 9 ++++----- include/linux/soundwire/sdw_type.h | 3 ++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index c0585bcc8a41b8..2b2830b622faef 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -34,7 +34,7 @@ sdw_get_device_id(struct sdw_slave *slave, struct sdw_driver *drv) static int sdw_bus_match(struct device *dev, struct device_driver *ddrv) { struct sdw_slave *slave = to_sdw_slave_device(dev); - struct sdw_driver *drv = drv_to_sdw_slave_driver(ddrv); + struct sdw_driver *drv = to_sdw_slave_driver(ddrv); return !!sdw_get_device_id(slave, drv); } @@ -70,7 +70,7 @@ EXPORT_SYMBOL_GPL(sdw_bus_type); static int sdw_drv_probe(struct device *dev) { struct sdw_slave *slave = to_sdw_slave_device(dev); - struct sdw_driver *drv = drv_to_sdw_slave_driver(dev->driver); + struct sdw_driver *drv = to_sdw_slave_driver(dev->driver); const struct sdw_device_id *id; int ret; @@ -116,8 +116,7 @@ static int sdw_drv_probe(struct device *dev) static int sdw_drv_remove(struct device *dev) { struct sdw_slave *slave = to_sdw_slave_device(dev); - struct sdw_driver *drv = drv_to_sdw_slave_driver(dev->driver); - + struct sdw_driver *drv = to_sdw_slave_driver(dev->driver); int ret = 0; if (drv->remove) @@ -131,7 +130,7 @@ static int sdw_drv_remove(struct device *dev) static void sdw_drv_shutdown(struct device *dev) { struct sdw_slave *slave = to_sdw_slave_device(dev); - struct sdw_driver *drv = drv_to_sdw_slave_driver(dev->driver); + struct sdw_driver *drv = to_sdw_slave_driver(dev->driver); if (drv->shutdown) drv->shutdown(slave); diff --git a/include/linux/soundwire/sdw_type.h b/include/linux/soundwire/sdw_type.h index abaa2127815254..7d4bc6a979bf6b 100644 --- a/include/linux/soundwire/sdw_type.h +++ b/include/linux/soundwire/sdw_type.h @@ -6,7 +6,7 @@ extern struct bus_type sdw_bus_type; -#define drv_to_sdw_slave_driver(_drv) \ +#define to_sdw_slave_driver(_drv) \ container_of(_drv, struct sdw_driver, driver) #define sdw_register_slave_driver(drv) \ @@ -29,4 +29,5 @@ int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size); #define module_sdw_driver(__sdw_slave_driver) \ module_driver(__sdw_slave_driver, sdw_register_slave_driver, \ sdw_unregister_slave_driver) + #endif /* __SOUNDWIRE_TYPES_H */ From 1056a21f0f6162e44d484a3a6b0cea1a4c280fc2 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 10 Sep 2019 21:53:10 -0500 Subject: [PATCH 004/170] soundwire: bus_type: rename sdw_drv_ to sdw_slave_drv Before we add master driver support, make sure there is no ambiguity and no occurrences of sdw_drv_ functions. No functionality change. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus_type.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 2b2830b622faef..9a0fd3ee1014a7 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -67,7 +67,7 @@ struct bus_type sdw_bus_type = { }; EXPORT_SYMBOL_GPL(sdw_bus_type); -static int sdw_drv_probe(struct device *dev) +static int sdw_slave_drv_probe(struct device *dev) { struct sdw_slave *slave = to_sdw_slave_device(dev); struct sdw_driver *drv = to_sdw_slave_driver(dev->driver); @@ -113,7 +113,7 @@ static int sdw_drv_probe(struct device *dev) return 0; } -static int sdw_drv_remove(struct device *dev) +static int sdw_slave_drv_remove(struct device *dev) { struct sdw_slave *slave = to_sdw_slave_device(dev); struct sdw_driver *drv = to_sdw_slave_driver(dev->driver); @@ -127,7 +127,7 @@ static int sdw_drv_remove(struct device *dev) return ret; } -static void sdw_drv_shutdown(struct device *dev) +static void sdw_slave_drv_shutdown(struct device *dev) { struct sdw_slave *slave = to_sdw_slave_device(dev); struct sdw_driver *drv = to_sdw_slave_driver(dev->driver); @@ -155,13 +155,13 @@ int __sdw_register_slave_driver(struct sdw_driver *drv, } drv->driver.owner = owner; - drv->driver.probe = sdw_drv_probe; + drv->driver.probe = sdw_slave_drv_probe; if (drv->remove) - drv->driver.remove = sdw_drv_remove; + drv->driver.remove = sdw_slave_drv_remove; if (drv->shutdown) - drv->driver.shutdown = sdw_drv_shutdown; + drv->driver.shutdown = sdw_slave_drv_shutdown; return driver_register(&drv->driver); } From cc2eec2b1483f25b0907cb636c418f243bf3fc3e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 11 Sep 2019 14:26:53 -0500 Subject: [PATCH 005/170] soundwire: intel: rename res field as link_res There are too many fields called 'res' so add prefix to make it easier to track what the structures are. Pure rename, no functionality change Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 0371d3d5501a97..64f97bb1a13573 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -103,7 +103,7 @@ enum intel_pdi_type { struct sdw_intel { struct sdw_cdns cdns; int instance; - struct sdw_intel_link_res *res; + struct sdw_intel_link_res *link_res; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; #endif @@ -193,8 +193,8 @@ static ssize_t intel_sprintf(void __iomem *mem, bool l, static int intel_reg_show(struct seq_file *s_file, void *data) { struct sdw_intel *sdw = s_file->private; - void __iomem *s = sdw->res->shim; - void __iomem *a = sdw->res->alh; + void __iomem *s = sdw->link_res->shim; + void __iomem *a = sdw->link_res->alh; char *buf; ssize_t ret; int i, j; @@ -289,7 +289,7 @@ static void intel_debugfs_exit(struct sdw_intel *sdw) {} static int intel_link_power_up(struct sdw_intel *sdw) { unsigned int link_id = sdw->instance; - void __iomem *shim = sdw->res->shim; + void __iomem *shim = sdw->link_res->shim; int spa_mask, cpa_mask; int link_control, ret; @@ -309,7 +309,7 @@ static int intel_link_power_up(struct sdw_intel *sdw) static int intel_shim_init(struct sdw_intel *sdw) { - void __iomem *shim = sdw->res->shim; + void __iomem *shim = sdw->link_res->shim; unsigned int link_id = sdw->instance; int sync_reg, ret; u16 ioctl = 0, act = 0; @@ -370,7 +370,7 @@ static int intel_shim_init(struct sdw_intel *sdw) static void intel_pdi_init(struct sdw_intel *sdw, struct sdw_cdns_stream_config *config) { - void __iomem *shim = sdw->res->shim; + void __iomem *shim = sdw->link_res->shim; unsigned int link_id = sdw->instance; int pcm_cap, pdm_cap; @@ -404,7 +404,7 @@ static void intel_pdi_init(struct sdw_intel *sdw, static int intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm) { - void __iomem *shim = sdw->res->shim; + void __iomem *shim = sdw->link_res->shim; unsigned int link_id = sdw->instance; int count; @@ -476,7 +476,7 @@ static int intel_pdi_ch_update(struct sdw_intel *sdw) static void intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) { - void __iomem *shim = sdw->res->shim; + void __iomem *shim = sdw->link_res->shim; unsigned int link_id = sdw->instance; int pdi_conf = 0; @@ -508,7 +508,7 @@ intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) static void intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) { - void __iomem *alh = sdw->res->alh; + void __iomem *alh = sdw->link_res->alh; unsigned int link_id = sdw->instance; unsigned int conf; @@ -535,7 +535,7 @@ static int intel_params_stream(struct sdw_intel *sdw, struct snd_pcm_hw_params *hw_params, int link_id, int alh_stream_id) { - struct sdw_intel_link_res *res = sdw->res; + struct sdw_intel_link_res *res = sdw->link_res; struct sdw_intel_stream_params_data params_data; params_data.substream = substream; @@ -558,7 +558,7 @@ static int intel_pre_bank_switch(struct sdw_bus *bus) { struct sdw_cdns *cdns = bus_to_cdns(bus); struct sdw_intel *sdw = cdns_to_intel(cdns); - void __iomem *shim = sdw->res->shim; + void __iomem *shim = sdw->link_res->shim; int sync_reg; /* Write to register only for multi-link */ @@ -577,7 +577,7 @@ static int intel_post_bank_switch(struct sdw_bus *bus) { struct sdw_cdns *cdns = bus_to_cdns(bus); struct sdw_intel *sdw = cdns_to_intel(cdns); - void __iomem *shim = sdw->res->shim; + void __iomem *shim = sdw->link_res->shim; int sync_reg, ret; /* Write to register only for multi-link */ @@ -934,9 +934,9 @@ static int intel_probe(struct platform_device *pdev) return -ENOMEM; sdw->instance = pdev->id; - sdw->res = dev_get_platdata(&pdev->dev); + sdw->link_res = dev_get_platdata(&pdev->dev); sdw->cdns.dev = &pdev->dev; - sdw->cdns.registers = sdw->res->registers; + sdw->cdns.registers = sdw->link_res->registers; sdw->cdns.instance = sdw->instance; sdw->cdns.msg_count = 0; sdw->cdns.bus.dev = &pdev->dev; @@ -976,11 +976,12 @@ static int intel_probe(struct platform_device *pdev) intel_pdi_ch_update(sdw); /* Acquire IRQ */ - ret = request_threaded_irq(sdw->res->irq, sdw_cdns_irq, sdw_cdns_thread, + ret = request_threaded_irq(sdw->link_res->irq, + sdw_cdns_irq, sdw_cdns_thread, IRQF_SHARED, KBUILD_MODNAME, &sdw->cdns); if (ret < 0) { dev_err(sdw->cdns.dev, "unable to grab IRQ %d, disabling device\n", - sdw->res->irq); + sdw->link_res->irq); goto err_init; } @@ -1010,7 +1011,7 @@ static int intel_probe(struct platform_device *pdev) err_interrupt: sdw_cdns_enable_interrupt(&sdw->cdns, false); - free_irq(sdw->res->irq, sdw); + free_irq(sdw->link_res->irq, sdw); err_init: sdw_delete_bus_master(&sdw->cdns.bus); return ret; @@ -1025,7 +1026,7 @@ static int intel_remove(struct platform_device *pdev) if (!sdw->cdns.bus.prop.hw_disabled) { intel_debugfs_exit(sdw); sdw_cdns_enable_interrupt(&sdw->cdns, false); - free_irq(sdw->res->irq, sdw); + free_irq(sdw->link_res->irq, sdw); snd_soc_unregister_component(sdw->cdns.dev); } sdw_delete_bus_master(&sdw->cdns.bus); From 19c93d9ee7cafa968cfe933952476c9babf8822d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 10 Sep 2019 19:11:58 -0500 Subject: [PATCH 006/170] soundwire: add support for sdw_slave_type In the existing SoundWire code, the bus does not have any explicit representation for Master Devices - only SoundWire Slaves are exposed. In SoundWire, the Master Device provides the clock, synchronization information and command/control channels. When multiple links are supported, a Controller may expose more than one Master Device; they are typically embedded inside a larger audio cluster (be it in an SOC/chipset or an external audio codec), and we need to describe it using the Linux device and driver model. This will allow for configuration functions to account for external dependencies such as power rails, clock sources or wake-up mechanisms. This transition will also allow for better sysfs support without the reference count issues mentioned in the initial reviews. In this patch, we first convert the existing code to use an explicit sdw_slave_type and add error checks if this type is not set. In follow-up patches we can add support for the sdw_master_type. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus_type.c | 23 ++++++++++++++++++----- drivers/soundwire/slave.c | 7 ++++++- include/linux/soundwire/sdw_type.h | 6 ++++++ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 9a0fd3ee1014a7..9719680a1e4846 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -49,13 +49,26 @@ int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size) static int sdw_uevent(struct device *dev, struct kobj_uevent_env *env) { - struct sdw_slave *slave = to_sdw_slave_device(dev); + struct sdw_slave *slave; char modalias[32]; - sdw_slave_modalias(slave, modalias, sizeof(modalias)); - - if (add_uevent_var(env, "MODALIAS=%s", modalias)) - return -ENOMEM; + if (is_sdw_slave(dev)) { + slave = to_sdw_slave_device(dev); + + sdw_slave_modalias(slave, modalias, sizeof(modalias)); + + if (add_uevent_var(env, "MODALIAS=%s", modalias)) + return -ENOMEM; + } else { + /* + * We only need to handle uevents for the Slave device + * type. This error cannot happen unless the .uevent + * callback is set to use this function for a + * different device type (e.g. Master or Monitor) + */ + dev_err(dev, "uevent for unknown Soundwire type\n"); + return -EINVAL; + } return 0; } diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index 48a513680db665..c87267f12a3b97 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -14,6 +14,11 @@ static void sdw_slave_release(struct device *dev) kfree(slave); } +struct device_type sdw_slave_type = { + .name = "sdw_slave", + .release = sdw_slave_release, +}; + static int sdw_slave_add(struct sdw_bus *bus, struct sdw_slave_id *id, struct fwnode_handle *fwnode) { @@ -41,9 +46,9 @@ static int sdw_slave_add(struct sdw_bus *bus, id->class_id, id->unique_id); } - slave->dev.release = sdw_slave_release; slave->dev.bus = &sdw_bus_type; slave->dev.of_node = of_node_get(to_of_node(fwnode)); + slave->dev.type = &sdw_slave_type; slave->bus = bus; slave->status = SDW_SLAVE_UNATTACHED; slave->dev_num = 0; diff --git a/include/linux/soundwire/sdw_type.h b/include/linux/soundwire/sdw_type.h index 7d4bc6a979bf6b..c681b3426478e7 100644 --- a/include/linux/soundwire/sdw_type.h +++ b/include/linux/soundwire/sdw_type.h @@ -5,6 +5,12 @@ #define __SOUNDWIRE_TYPES_H extern struct bus_type sdw_bus_type; +extern struct device_type sdw_slave_type; + +static inline int is_sdw_slave(const struct device *dev) +{ + return dev->type == &sdw_slave_type; +} #define to_sdw_slave_driver(_drv) \ container_of(_drv, struct sdw_driver, driver) From 7c6b27ded52c04ff9b352c20cf43b5cb09a294c0 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 14 Nov 2019 10:37:04 -0600 Subject: [PATCH 007/170] soundwire: slave: move uevent handling to slave device level Currently the .uevent callback is set at the bus level. This is not necessary, we only really need to deal with uevents for the Slave device, so move the uevent handling at that level. Suggested-by: Vinod Koul Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.h | 2 ++ drivers/soundwire/bus_type.c | 3 +-- drivers/soundwire/slave.c | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index cb482da914da94..be01a5f3d00bae 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -6,6 +6,8 @@ #define DEFAULT_BANK_SWITCH_TIMEOUT 3000 +int sdw_uevent(struct device *dev, struct kobj_uevent_env *env); + #if IS_ENABLED(CONFIG_ACPI) int sdw_acpi_find_slaves(struct sdw_bus *bus); #else diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 9719680a1e4846..605bc7ae57a8ed 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -47,7 +47,7 @@ int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size) slave->id.mfg_id, slave->id.part_id); } -static int sdw_uevent(struct device *dev, struct kobj_uevent_env *env) +int sdw_uevent(struct device *dev, struct kobj_uevent_env *env) { struct sdw_slave *slave; char modalias[32]; @@ -76,7 +76,6 @@ static int sdw_uevent(struct device *dev, struct kobj_uevent_env *env) struct bus_type sdw_bus_type = { .name = "soundwire", .match = sdw_bus_match, - .uevent = sdw_uevent, }; EXPORT_SYMBOL_GPL(sdw_bus_type); diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index c87267f12a3b97..014c3ece1f1768 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -17,6 +17,7 @@ static void sdw_slave_release(struct device *dev) struct device_type sdw_slave_type = { .name = "sdw_slave", .release = sdw_slave_release, + .uevent = sdw_uevent, }; static int sdw_slave_add(struct sdw_bus *bus, From 534ef0188402faf7d01e5308f7f7a1e512e1ed60 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 10 Sep 2019 19:23:22 -0500 Subject: [PATCH 008/170] soundwire: add initial definitions for sdw_master_device Since we want an explicit support for the SoundWire Master device, add the definitions, following the Greybus example of a 'Host Device'. A parent (such as the Intel audio controller) would use sdw_md_add() to create the device, passing a driver as a parameter. The sdw_md_release() would be called when put_device() is invoked by the parent. We use the shortcut 'md' for 'master device' to avoid very long function names. The 'Master Device' driver exposes interfaces for probe/startup/shutdown/remove and a placeholder for autonomous clock stop support (when the bus control is handed over to a lower-power solution, typically in D0ix modes). Module namespace support is added in a separate patch. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/Makefile | 2 +- drivers/soundwire/bus_type.c | 5 ++- drivers/soundwire/master.c | 63 ++++++++++++++++++++++++++++++ include/linux/soundwire/sdw.h | 38 ++++++++++++++++++ include/linux/soundwire/sdw_type.h | 9 +++++ 5 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 drivers/soundwire/master.c diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index 563894e5ecaf50..89b29819dd3ad8 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -4,7 +4,7 @@ # #Bus Objs -soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o stream.o +soundwire-bus-objs := bus_type.o bus.o master.o slave.o mipi_disco.o stream.o obj-$(CONFIG_SOUNDWIRE) += soundwire-bus.o ifdef CONFIG_DEBUG_FS diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 605bc7ae57a8ed..2e4e8c2e629f46 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -66,7 +66,10 @@ int sdw_uevent(struct device *dev, struct kobj_uevent_env *env) * callback is set to use this function for a * different device type (e.g. Master or Monitor) */ - dev_err(dev, "uevent for unknown Soundwire type\n"); + if (is_sdw_master_device(dev)) + dev_err(dev, "uevent for SoundWire Master type\n"); + else + dev_err(dev, "uevent for unknown Soundwire type\n"); return -EINVAL; } diff --git a/drivers/soundwire/master.c b/drivers/soundwire/master.c new file mode 100644 index 00000000000000..b018c561708e99 --- /dev/null +++ b/drivers/soundwire/master.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: (GPL-2.0) +// Copyright(c) 2019 Intel Corporation. + +#include +#include +#include +#include +#include "bus.h" + +static void sdw_md_release(struct device *dev) +{ + struct sdw_master_device *md = to_sdw_master_device(dev); + + kfree(md); +} + +struct device_type sdw_md_type = { + .name = "soundwire_master", + .release = sdw_md_release, +}; + +struct sdw_master_device *sdw_md_add(struct sdw_md_driver *driver, + struct device *parent, + struct fwnode_handle *fwnode, + int link_id) +{ + struct sdw_master_device *md; + int ret; + + if (!driver->probe) { + dev_err(parent, "mandatory probe callback missing\n"); + return ERR_PTR(-EINVAL); + } + + md = kzalloc(sizeof(*md), GFP_KERNEL); + if (!md) + return ERR_PTR(-ENOMEM); + + md->link_id = link_id; + + md->driver = driver; + + md->dev.parent = parent; + md->dev.fwnode = fwnode; + md->dev.bus = &sdw_bus_type; + md->dev.type = &sdw_md_type; + md->dev.dma_mask = md->dev.parent->dma_mask; + dev_set_name(&md->dev, "sdw-master-%d", md->link_id); + + ret = device_register(&md->dev); + if (ret) { + dev_err(parent, "Failed to add master: ret %d\n", ret); + /* + * On err, don't free but drop ref as this will be freed + * when release method is invoked. + */ + put_device(&md->dev); + return ERR_PTR(-ENOMEM); + } + + return md; +} +EXPORT_SYMBOL_GPL(sdw_md_add); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 5b1180f1e6b558..f73c355de5e5cb 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -585,6 +585,16 @@ struct sdw_slave { #define to_sdw_slave_device(d) \ container_of(d, struct sdw_slave, dev) +struct sdw_master_device { + struct device dev; + int link_id; + struct sdw_md_driver *driver; + void *pdata; +}; + +#define to_sdw_master_device(d) \ + container_of(d, struct sdw_master_device, dev) + struct sdw_driver { const char *name; @@ -599,6 +609,29 @@ struct sdw_driver { struct device_driver driver; }; +/** + * struct sdw_md_driver - SoundWire 'Master Device' driver + * + * @probe: initializations and allocation (hardware may not be enabled yet) + * @startup: initialization handled after the hardware is enabled, all + * clock/power dependencies are available + * @shutdown: cleanups before hardware is disabled (optional) + * @free: free all remaining resources + * @autonomous_clock_stop_enable: enable/disable driver control while + * in clock-stop mode, typically in always-on/D0ix modes. When the driver + * yields control, another entity in the system (typically firmware + * running on an always-on microprocessor) is responsible to tracking + * Slave-initiated wakes + */ +struct sdw_md_driver { + int (*probe)(struct sdw_master_device *md, void *link_ctx); + int (*startup)(struct sdw_master_device *md); + int (*shutdown)(struct sdw_master_device *md); + int (*remove)(struct sdw_master_device *md); + int (*autonomous_clock_stop_enable)(struct sdw_master_device *md, + bool state); +}; + #define SDW_SLAVE_ENTRY(_mfg_id, _part_id, _drv_data) \ { .mfg_id = (_mfg_id), .part_id = (_part_id), \ .driver_data = (unsigned long)(_drv_data) } @@ -788,6 +821,11 @@ struct sdw_bus { int sdw_add_bus_master(struct sdw_bus *bus); void sdw_delete_bus_master(struct sdw_bus *bus); +struct sdw_master_device *sdw_md_add(struct sdw_md_driver *driver, + struct device *parent, + struct fwnode_handle *fwnode, + int link_id); + /** * sdw_port_config: Master or Slave Port configuration * diff --git a/include/linux/soundwire/sdw_type.h b/include/linux/soundwire/sdw_type.h index c681b3426478e7..a2a5ea7843a68d 100644 --- a/include/linux/soundwire/sdw_type.h +++ b/include/linux/soundwire/sdw_type.h @@ -6,15 +6,24 @@ extern struct bus_type sdw_bus_type; extern struct device_type sdw_slave_type; +extern struct device_type sdw_md_type; static inline int is_sdw_slave(const struct device *dev) { return dev->type == &sdw_slave_type; } +static inline int is_sdw_master_device(const struct device *dev) +{ + return dev->type == &sdw_md_type; +} + #define to_sdw_slave_driver(_drv) \ container_of(_drv, struct sdw_driver, driver) +#define to_sdw_md_driver(_drv) \ + container_of(_drv, struct sdw_md_driver, driver) + #define sdw_register_slave_driver(drv) \ __sdw_register_slave_driver(drv, THIS_MODULE) From 7c8e259dec6180bcaad9e9ac17f1c3b3f8201a9a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 23 Oct 2019 12:12:51 -0500 Subject: [PATCH 009/170] soundwire: intel: remove platform devices and use 'Master Devices' instead Use sdw_master_device and driver instead of platform devices To quote GregKH: "Don't mess with a platform device unless you really have no other possible choice. And even then, don't do it and try to do something else. Platform devices are really abused, don't perpetuate it " In addition, rather than a plain-vanilla init/exit, this patch provides 3 steps in the initialization (ACPI scan, probe, startup) which makes it easier to verify hardware support for SoundWire, allocate required resources as early as possible, and conversely help make the startup() callback lighter-weight with only hardware register setup. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 91 ++++++----- drivers/soundwire/intel.h | 8 +- drivers/soundwire/intel_init.c | 275 ++++++++++++++++++++++++--------- 3 files changed, 267 insertions(+), 107 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 64f97bb1a13573..36dbcbab0d65d6 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -92,8 +92,6 @@ #define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0) #define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16) -#define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1) - enum intel_pdi_type { INTEL_PDI_IN = 0, INTEL_PDI_OUT = 1, @@ -923,24 +921,23 @@ static int intel_init(struct sdw_intel *sdw) /* * probe and init */ -static int intel_probe(struct platform_device *pdev) +static int intel_master_probe(struct sdw_master_device *md, void *link_ctx) { - struct sdw_cdns_stream_config config; struct sdw_intel *sdw; int ret; - sdw = devm_kzalloc(&pdev->dev, sizeof(*sdw), GFP_KERNEL); + sdw = devm_kzalloc(&md->dev, sizeof(*sdw), GFP_KERNEL); if (!sdw) return -ENOMEM; - sdw->instance = pdev->id; - sdw->link_res = dev_get_platdata(&pdev->dev); - sdw->cdns.dev = &pdev->dev; + sdw->instance = md->link_id; + sdw->link_res = link_ctx; + sdw->cdns.dev = &md->dev; sdw->cdns.registers = sdw->link_res->registers; - sdw->cdns.instance = sdw->instance; + sdw->cdns.instance = md->link_id; sdw->cdns.msg_count = 0; - sdw->cdns.bus.dev = &pdev->dev; - sdw->cdns.bus.link_id = pdev->id; + sdw->cdns.bus.dev = &md->dev; + sdw->cdns.bus.link_id = md->link_id; sdw_cdns_probe(&sdw->cdns); @@ -948,16 +945,50 @@ static int intel_probe(struct platform_device *pdev) sdw_intel_ops.read_prop = intel_prop_read; sdw->cdns.bus.ops = &sdw_intel_ops; - platform_set_drvdata(pdev, sdw); + md->pdata = sdw; + + /* set driver data, accessed by snd_soc_dai_set_drvdata() */ + dev_set_drvdata(&md->dev, &sdw->cdns); ret = sdw_add_bus_master(&sdw->cdns.bus); if (ret) { - dev_err(&pdev->dev, "sdw_add_bus_master fail: %d\n", ret); + dev_err(&md->dev, "sdw_add_bus_master fail: %d\n", ret); return ret; } if (sdw->cdns.bus.prop.hw_disabled) { - dev_info(&pdev->dev, "SoundWire master %d is disabled, ignoring\n", + dev_info(&md->dev, "SoundWire master %d is disabled, ignoring\n", + sdw->cdns.bus.link_id); + return 0; + } + + /* Acquire IRQ */ + ret = request_threaded_irq(sdw->link_res->irq, + sdw_cdns_irq, sdw_cdns_thread, + IRQF_SHARED, KBUILD_MODNAME, &sdw->cdns); + if (ret < 0) { + dev_err(sdw->cdns.dev, "unable to grab IRQ %d, disabling device\n", + sdw->link_res->irq); + goto err_init; + } + + return 0; + +err_init: + sdw_delete_bus_master(&sdw->cdns.bus); + return ret; +} + +static int intel_master_startup(struct sdw_master_device *md) +{ + struct sdw_cdns_stream_config config; + struct sdw_intel *sdw; + int ret; + + sdw = md->pdata; + + if (sdw->cdns.bus.prop.hw_disabled) { + dev_info(&md->dev, "SoundWire master %d is disabled, ignoring\n", sdw->cdns.bus.link_id); return 0; } @@ -975,16 +1006,6 @@ static int intel_probe(struct platform_device *pdev) intel_pdi_ch_update(sdw); - /* Acquire IRQ */ - ret = request_threaded_irq(sdw->link_res->irq, - sdw_cdns_irq, sdw_cdns_thread, - IRQF_SHARED, KBUILD_MODNAME, &sdw->cdns); - if (ret < 0) { - dev_err(sdw->cdns.dev, "unable to grab IRQ %d, disabling device\n", - sdw->link_res->irq); - goto err_init; - } - ret = sdw_cdns_enable_interrupt(&sdw->cdns, true); if (ret < 0) { dev_err(sdw->cdns.dev, "cannot enable interrupts\n"); @@ -1011,17 +1032,17 @@ static int intel_probe(struct platform_device *pdev) err_interrupt: sdw_cdns_enable_interrupt(&sdw->cdns, false); - free_irq(sdw->link_res->irq, sdw); err_init: + free_irq(sdw->link_res->irq, sdw); sdw_delete_bus_master(&sdw->cdns.bus); return ret; } -static int intel_remove(struct platform_device *pdev) +static int intel_master_remove(struct sdw_master_device *md) { struct sdw_intel *sdw; - sdw = platform_get_drvdata(pdev); + sdw = md->pdata; if (!sdw->cdns.bus.prop.hw_disabled) { intel_debugfs_exit(sdw); @@ -1031,19 +1052,17 @@ static int intel_remove(struct platform_device *pdev) } sdw_delete_bus_master(&sdw->cdns.bus); + device_unregister(&md->dev); + return 0; } -static struct platform_driver sdw_intel_drv = { - .probe = intel_probe, - .remove = intel_remove, - .driver = { - .name = "int-sdw", - - }, +struct sdw_md_driver intel_sdw_driver = { + .probe = intel_master_probe, + .startup = intel_master_startup, + .remove = intel_master_remove, }; - -module_platform_driver(sdw_intel_drv); +EXPORT_SYMBOL(intel_sdw_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_ALIAS("platform:int-sdw"); diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index 38b7c125fb1052..cfab2f00214d25 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -7,7 +7,7 @@ /** * struct sdw_intel_link_res - Soundwire Intel link resource structure, * typically populated by the controller driver. - * @pdev: platform_device + * @md: master device * @mmio_base: mmio base of SoundWire registers * @registers: Link IO registers base * @shim: Audio shim pointer @@ -17,7 +17,7 @@ * @dev: device implementing hw_params and free callbacks */ struct sdw_intel_link_res { - struct platform_device *pdev; + struct sdw_master_device *md; void __iomem *mmio_base; /* not strictly needed, useful for debug */ void __iomem *registers; void __iomem *shim; @@ -27,4 +27,8 @@ struct sdw_intel_link_res { struct device *dev; }; +#define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1) + +extern struct sdw_md_driver intel_sdw_driver; + #endif /* __SDW_INTEL_LOCAL_H */ diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 4b769409f6f849..42f7ae034beabf 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include "intel.h" @@ -23,22 +23,47 @@ #define SDW_LINK_BASE 0x30000 #define SDW_LINK_SIZE 0x10000 -static int link_mask; -module_param_named(sdw_link_mask, link_mask, int, 0444); +static int ctrl_link_mask; +module_param_named(sdw_link_mask, ctrl_link_mask, int, 0444); MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)"); -static int sdw_intel_cleanup_pdev(struct sdw_intel_ctx *ctx) +static bool is_link_enabled(struct fwnode_handle *fw_node, int i) +{ + struct fwnode_handle *link; + char name[32]; + u32 quirk_mask = 0; + + /* Find master handle */ + snprintf(name, sizeof(name), + "mipi-sdw-link-%d-subproperties", i); + + link = fwnode_get_named_child_node(fw_node, name); + if (!link) + return false; + + fwnode_property_read_u32(link, + "intel-quirk-mask", + &quirk_mask); + + if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE) + return false; + + return true; +} + +static int sdw_intel_cleanup(struct sdw_intel_ctx *ctx) { struct sdw_intel_link_res *link = ctx->links; + struct sdw_master_device *md; int i; if (!link) return 0; - for (i = 0; i < ctx->count; i++) { - if (link->pdev) - platform_device_unregister(link->pdev); - link++; + for (i = 0; i < ctx->count; i++, link++) { + md = link->md; + if (md) + md->driver->remove(md); } kfree(ctx->links); @@ -47,112 +72,194 @@ static int sdw_intel_cleanup_pdev(struct sdw_intel_ctx *ctx) return 0; } -static struct sdw_intel_ctx -*sdw_intel_add_controller(struct sdw_intel_res *res) +static int +sdw_intel_scan_controller(struct sdw_intel_acpi_info *info) { - struct platform_device_info pdevinfo; - struct platform_device *pdev; - struct sdw_intel_link_res *link; - struct sdw_intel_ctx *ctx; struct acpi_device *adev; int ret, i; u8 count; - u32 caps; - if (acpi_bus_get_device(res->handle, &adev)) - return NULL; + if (acpi_bus_get_device(info->handle, &adev)) + return -EINVAL; /* Found controller, find links supported */ count = 0; ret = fwnode_property_read_u8_array(acpi_fwnode_handle(adev), "mipi-sdw-master-count", &count, 1); - /* Don't fail on error, continue and use hw value */ + /* + * In theory we could check the number of links supported in + * hardware, but in that step we cannot assume SoundWire IP is + * powered. + * + * In addition, if the BIOS doesn't even provide this + * 'master-count' property then all the inits based on link + * masks will fail as well. + * + * We will check the hardware capabilities in the startup() step + */ + if (ret) { dev_err(&adev->dev, "Failed to read mipi-sdw-master-count: %d\n", ret); - count = SDW_MAX_LINKS; + return -EINVAL; } - /* Check SNDWLCAP.LCOUNT */ - caps = ioread32(res->mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP); - caps &= GENMASK(2, 0); - - /* Check HW supported vs property value and use min of two */ - count = min_t(u8, caps, count); - /* Check count is within bounds */ if (count > SDW_MAX_LINKS) { dev_err(&adev->dev, "Link count %d exceeds max %d\n", count, SDW_MAX_LINKS); - return NULL; + return -EINVAL; } else if (!count) { dev_warn(&adev->dev, "No SoundWire links detected\n"); - return NULL; + return -EINVAL; } + dev_dbg(&adev->dev, "ACPI reports %d SDW Link devices\n", count); + + info->count = count; + for (i = 0; i < count; i++) { + if (ctrl_link_mask && !(ctrl_link_mask & BIT(i))) { + dev_dbg(&adev->dev, + "Link %d masked, will not be enabled\n", i); + continue; + } + + if (!is_link_enabled(acpi_fwnode_handle(adev), i)) { + dev_dbg(&adev->dev, + "Link %d not selected in firmware\n", i); + continue; + } + + info->link_mask |= BIT(i); + } + + return 0; +} + +static struct sdw_intel_ctx +*sdw_intel_probe_controller(struct sdw_intel_res *res) +{ + struct sdw_intel_link_res *link; + struct sdw_intel_ctx *ctx; + struct acpi_device *adev; + struct sdw_master_device *md; + u32 link_mask; + int count; + int i; + + if (!res) + return NULL; + + if (acpi_bus_get_device(res->handle, &adev)) + return NULL; + + if (!res->count) + return NULL; + + count = res->count; dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count); ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return NULL; - ctx->count = count; - ctx->links = kcalloc(ctx->count, sizeof(*ctx->links), GFP_KERNEL); + ctx->links = kcalloc(count, sizeof(*ctx->links), GFP_KERNEL); if (!ctx->links) goto link_err; + ctx->count = count; + ctx->mmio_base = res->mmio_base; + ctx->link_mask = res->link_mask; + ctx->handle = res->handle; + link = ctx->links; + link_mask = ctx->link_mask; /* Create SDW Master devices */ - for (i = 0; i < count; i++) { - if (link_mask && !(link_mask & BIT(i))) { - dev_dbg(&adev->dev, - "Link %d masked, will not be enabled\n", i); - link++; + for (i = 0; i < count; i++, link++) { + if (link_mask && !(link_mask & BIT(i))) continue; - } + md = sdw_md_add(&intel_sdw_driver, + res->parent, + acpi_fwnode_handle(adev), + i); + + if (IS_ERR(md)) { + dev_err(&adev->dev, "Could not create link %d\n", i); + goto err; + } + link->md = md; + link->mmio_base = res->mmio_base; link->registers = res->mmio_base + SDW_LINK_BASE - + (SDW_LINK_SIZE * i); + + (SDW_LINK_SIZE * i); link->shim = res->mmio_base + SDW_SHIM_BASE; link->alh = res->mmio_base + SDW_ALH_BASE; - + link->irq = res->irq; link->ops = res->ops; link->dev = res->dev; - memset(&pdevinfo, 0, sizeof(pdevinfo)); - - pdevinfo.parent = res->parent; - pdevinfo.name = "int-sdw"; - pdevinfo.id = i; - pdevinfo.fwnode = acpi_fwnode_handle(adev); - - pdev = platform_device_register_full(&pdevinfo); - if (IS_ERR(pdev)) { - dev_err(&adev->dev, - "platform device creation failed: %ld\n", - PTR_ERR(pdev)); - goto pdev_err; - } - - link->pdev = pdev; - link++; + /* let the SoundWire master driver to its probe */ + md->driver->probe(md, link); } return ctx; -pdev_err: - sdw_intel_cleanup_pdev(ctx); +err: + sdw_intel_cleanup(ctx); link_err: kfree(ctx); return NULL; } +static int +sdw_intel_startup_controller(struct sdw_intel_ctx *ctx) +{ + struct acpi_device *adev; + struct sdw_intel_link_res *link; + struct sdw_master_device *md; + u32 caps; + u32 link_mask; + int i; + + if (acpi_bus_get_device(ctx->handle, &adev)) + return -EINVAL; + + /* Check SNDWLCAP.LCOUNT */ + caps = ioread32(ctx->mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP); + caps &= GENMASK(2, 0); + + /* Check HW supported vs property value */ + if (caps < ctx->count) { + dev_err(&adev->dev, + "BIOS master count is larger than hardware capabilities\n"); + return -EINVAL; + } + + if (!ctx->links) + return -EINVAL; + + link = ctx->links; + link_mask = ctx->link_mask; + + /* Create SDW Master devices */ + for (i = 0; i < ctx->count; i++, link++) { + if (link_mask && !(link_mask & BIT(i))) + continue; + + md = link->md; + + md->driver->startup(md); + } + + return 0; +} + static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, void *cdata, void **return_value) { - struct sdw_intel_res *res = cdata; + struct sdw_intel_acpi_info *info = cdata; struct acpi_device *adev; acpi_status status; u64 adr; @@ -166,7 +273,7 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, return AE_NOT_FOUND; } - res->handle = handle; + info->handle = handle; /* * On some Intel platforms, multiple children of the HDAS @@ -183,36 +290,66 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, } /** - * sdw_intel_init() - SoundWire Intel init routine + * sdw_intel_acpi_scan() - SoundWire Intel init routine * @parent_handle: ACPI parent handle - * @res: resource data + * @info: description of what firmware/DSDT tables expose * - * This scans the namespace and creates SoundWire link controller devices - * based on the info queried. + * This scans the namespace and queries firmware to figure out which + * links to enable. A follow-up use of sdw_intel_probe() and + * sdw_intel_startup() is required for creation of devices and bus + * startup */ -void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res) +int sdw_intel_acpi_scan(acpi_handle *parent_handle, + struct sdw_intel_acpi_info *info) { acpi_status status; status = acpi_walk_namespace(ACPI_TYPE_DEVICE, parent_handle, 1, sdw_intel_acpi_cb, - NULL, res, NULL); + NULL, info, NULL); if (ACPI_FAILURE(status)) - return NULL; + return -ENODEV; - return sdw_intel_add_controller(res); + return sdw_intel_scan_controller(info); } +EXPORT_SYMBOL(sdw_intel_acpi_scan); +/** + * sdw_intel_probe() - SoundWire Intel probe routine + * @parent_handle: ACPI parent handle + * @res: resource data + * + * This creates SoundWire Master and Slave devices below the controller. + * All the information necessary is stored in the context, and the res + * argument pointer can be freed after this step. + */ +struct sdw_intel_ctx +*sdw_intel_probe(struct sdw_intel_res *res) +{ + return sdw_intel_probe_controller(res); +} +EXPORT_SYMBOL(sdw_intel_probe); + +/** + * sdw_intel_startup() - SoundWire Intel startup + * @ctx: SoundWire context allocated in the probe + * + */ +int sdw_intel_startup(struct sdw_intel_ctx *ctx) +{ + return sdw_intel_startup_controller(ctx); +} +EXPORT_SYMBOL(sdw_intel_startup); /** * sdw_intel_exit() - SoundWire Intel exit - * @arg: callback context + * @ctx: SoundWire context allocated in the probe * * Delete the controller instances created and cleanup */ void sdw_intel_exit(struct sdw_intel_ctx *ctx) { - sdw_intel_cleanup_pdev(ctx); + sdw_intel_cleanup(ctx); kfree(ctx); } EXPORT_SYMBOL(sdw_intel_exit); From 09cc602a1e63037c6a15806d35375286038919ff Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 12 Sep 2019 17:35:48 +0800 Subject: [PATCH 010/170] soundwire: register master device driver While we don't have a matching function, setting an device driver is still necessary for ASoC to register DAI components as well as power management. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 6 ++++++ drivers/soundwire/intel_init.c | 10 ++++++++++ drivers/soundwire/master.c | 1 + include/linux/soundwire/sdw.h | 1 + 4 files changed, 18 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 36dbcbab0d65d6..29ebdb07e6228d 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "cadence_master.h" #include "bus.h" #include "intel.h" @@ -1058,6 +1059,11 @@ static int intel_master_remove(struct sdw_master_device *md) } struct sdw_md_driver intel_sdw_driver = { + .driver = { + .name = "intel-sdw", + .owner = THIS_MODULE, + .bus = &sdw_bus_type, + }, .probe = intel_master_probe, .startup = intel_master_startup, .remove = intel_master_remove, diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 42f7ae034beabf..a30d95ee71b745 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -146,6 +146,7 @@ static struct sdw_intel_ctx struct sdw_master_device *md; u32 link_mask; int count; + int err; int i; if (!res) @@ -176,6 +177,12 @@ static struct sdw_intel_ctx link = ctx->links; link_mask = ctx->link_mask; + err = driver_register(&intel_sdw_driver.driver); + if (err) { + dev_err(&adev->dev, "failed to register sdw master driver\n"); + goto register_err; + } + /* Create SDW Master devices */ for (i = 0; i < count; i++, link++) { if (link_mask && !(link_mask & BIT(i))) @@ -209,6 +216,8 @@ static struct sdw_intel_ctx err: sdw_intel_cleanup(ctx); link_err: + driver_unregister(&intel_sdw_driver.driver); +register_err: kfree(ctx); return NULL; } @@ -350,6 +359,7 @@ EXPORT_SYMBOL(sdw_intel_startup); void sdw_intel_exit(struct sdw_intel_ctx *ctx) { sdw_intel_cleanup(ctx); + driver_unregister(&intel_sdw_driver.driver); kfree(ctx); } EXPORT_SYMBOL(sdw_intel_exit); diff --git a/drivers/soundwire/master.c b/drivers/soundwire/master.c index b018c561708e99..1c6bb293468cde 100644 --- a/drivers/soundwire/master.c +++ b/drivers/soundwire/master.c @@ -46,6 +46,7 @@ struct sdw_master_device *sdw_md_add(struct sdw_md_driver *driver, md->dev.type = &sdw_md_type; md->dev.dma_mask = md->dev.parent->dma_mask; dev_set_name(&md->dev, "sdw-master-%d", md->link_id); + md->dev.driver = &driver->driver; ret = device_register(&md->dev); if (ret) { diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index f73c355de5e5cb..58f50257dfa8d1 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -630,6 +630,7 @@ struct sdw_md_driver { int (*remove)(struct sdw_master_device *md); int (*autonomous_clock_stop_enable)(struct sdw_master_device *md, bool state); + struct device_driver driver; }; #define SDW_SLAVE_ENTRY(_mfg_id, _part_id, _drv_data) \ From 1aa5c2be582985be7500af10365fc923e4ce3e37 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Mon, 19 Aug 2019 16:43:06 +0800 Subject: [PATCH 011/170] soundwire: intel: add prepare support in sdw dai driver The existing code does not expose a prepare operation, which is very much needed to deal with underflow and resume operations. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 29ebdb07e6228d..689eb350f15e26 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -698,6 +698,21 @@ static int intel_hw_params(struct snd_pcm_substream *substream, return ret; } +static int intel_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_cdns_dma_data *dma; + + dma = snd_soc_dai_get_dma_data(dai, substream); + if (!dma) { + dev_err(dai->dev, "failed to get dma data in %s", + __func__); + return -EIO; + } + + return sdw_prepare_stream(dma->stream); +} + static int intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -744,6 +759,7 @@ static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, static const struct snd_soc_dai_ops intel_pcm_dai_ops = { .hw_params = intel_hw_params, + .prepare = intel_prepare, .hw_free = intel_hw_free, .shutdown = intel_shutdown, .set_sdw_stream = intel_pcm_set_sdw_stream, @@ -751,6 +767,7 @@ static const struct snd_soc_dai_ops intel_pcm_dai_ops = { static const struct snd_soc_dai_ops intel_pdm_dai_ops = { .hw_params = intel_hw_params, + .prepare = intel_prepare, .hw_free = intel_hw_free, .shutdown = intel_shutdown, .set_sdw_stream = intel_pdm_set_sdw_stream, From d37ea6f4b7f0543007d05951a9c373d55caefe62 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Mon, 19 Aug 2019 16:48:46 +0800 Subject: [PATCH 012/170] soundwire: intel: add trigger support in sdw dai driver The existing code does not expose a trigger callback, which is very much required for streaming. The SoundWire stream is enabled and disabled in trigger function. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 689eb350f15e26..4ff15327fbd758 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -713,6 +713,43 @@ static int intel_prepare(struct snd_pcm_substream *substream, return sdw_prepare_stream(dma->stream); } +static int intel_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct sdw_cdns_dma_data *dma; + int ret; + + dma = snd_soc_dai_get_dma_data(dai, substream); + if (!dma) { + dev_err(dai->dev, "failed to get dma data in %s", __func__); + return -EIO; + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + ret = sdw_enable_stream(dma->stream); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + ret = sdw_disable_stream(dma->stream); + break; + + default: + ret = -EINVAL; + break; + } + + if (ret) + dev_err(dai->dev, + "%s trigger %d failed: %d", + __func__, cmd, ret); + return ret; +} + static int intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -760,6 +797,7 @@ static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, static const struct snd_soc_dai_ops intel_pcm_dai_ops = { .hw_params = intel_hw_params, .prepare = intel_prepare, + .trigger = intel_trigger, .hw_free = intel_hw_free, .shutdown = intel_shutdown, .set_sdw_stream = intel_pcm_set_sdw_stream, @@ -768,6 +806,7 @@ static const struct snd_soc_dai_ops intel_pcm_dai_ops = { static const struct snd_soc_dai_ops intel_pdm_dai_ops = { .hw_params = intel_hw_params, .prepare = intel_prepare, + .trigger = intel_trigger, .hw_free = intel_hw_free, .shutdown = intel_shutdown, .set_sdw_stream = intel_pdm_set_sdw_stream, From 0120269d080f012fc2d5bf07b8838c174d8fa465 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Mon, 19 Aug 2019 16:55:01 +0800 Subject: [PATCH 013/170] soundwire: intel: add sdw_stream_setup helper for .startup callback The sdw stream is allocated and stored in dai to share the sdw runtime information. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 65 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 4ff15327fbd758..b4a9337b64d236 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -616,6 +616,69 @@ static int intel_post_bank_switch(struct sdw_bus *bus) * DAI routines */ +static int sdw_stream_setup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sdw_stream_runtime *sdw_stream = NULL; + char *name; + int i, ret; + + name = kzalloc(32, GFP_KERNEL); + if (!name) + return -ENOMEM; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snprintf(name, 32, "%s-Playback", dai->name); + else + snprintf(name, 32, "%s-Capture", dai->name); + + sdw_stream = sdw_alloc_stream(name); + if (!sdw_stream) { + dev_err(dai->dev, "alloc stream failed for DAI %s", dai->name); + ret = -ENOMEM; + goto error; + } + + /* Set stream pointer on CPU DAI */ + ret = snd_soc_dai_set_sdw_stream(dai, sdw_stream, substream->stream); + if (ret < 0) { + dev_err(dai->dev, "failed to set stream pointer on cpu dai %s", + dai->name); + goto release_stream; + } + + /* Set stream pointer on all CODEC DAIs */ + for (i = 0; i < rtd->num_codecs; i++) { + ret = snd_soc_dai_set_sdw_stream(rtd->codec_dais[i], sdw_stream, + substream->stream); + if (ret < 0) { + dev_err(dai->dev, "failed to set stream pointer on codec dai %s", + rtd->codec_dais[i]->name); + goto release_stream; + } + } + + return 0; + +release_stream: + sdw_release_stream(sdw_stream); +error: + kfree(name); + return ret; +} + +static int intel_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + /* + * TODO: add pm_runtime support here, the startup callback + * will make sure the IP is 'active' + */ + + return sdw_stream_setup(substream, dai); +} + static int intel_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -795,6 +858,7 @@ static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, } static const struct snd_soc_dai_ops intel_pcm_dai_ops = { + .startup = intel_startup, .hw_params = intel_hw_params, .prepare = intel_prepare, .trigger = intel_trigger, @@ -804,6 +868,7 @@ static const struct snd_soc_dai_ops intel_pcm_dai_ops = { }; static const struct snd_soc_dai_ops intel_pdm_dai_ops = { + .startup = intel_startup, .hw_params = intel_hw_params, .prepare = intel_prepare, .trigger = intel_trigger, From ca67db54555d9635110672f288541316117034f7 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 23 Oct 2019 14:05:33 -0500 Subject: [PATCH 014/170] soundwire: intel: free all resources on hw_free() Make sure all calls to the SoundWire stream API are done and involve callback Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 40 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index b4a9337b64d236..0e77df0a776027 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -549,6 +549,25 @@ static int intel_params_stream(struct sdw_intel *sdw, return -EIO; } +static int intel_free_stream(struct sdw_intel *sdw, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, + int link_id) +{ + struct sdw_intel_link_res *res = sdw->link_res; + struct sdw_intel_stream_free_data free_data; + + free_data.substream = substream; + free_data.dai = dai; + free_data.link_id = link_id; + + if (res->ops && res->ops->free_stream && res->dev) + return res->ops->free_stream(res->dev, + &free_data); + + return 0; +} + /* * bank switch routines */ @@ -817,6 +836,7 @@ static int intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_cdns_dma_data *dma; int ret; @@ -824,12 +844,28 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) if (!dma) return -EIO; + ret = sdw_deprepare_stream(dma->stream); + if (ret) { + dev_err(dai->dev, "sdw_deprepare_stream: failed %d", ret); + return ret; + } + ret = sdw_stream_remove_master(&cdns->bus, dma->stream); - if (ret < 0) + if (ret < 0) { dev_err(dai->dev, "remove master from stream %s failed: %d\n", dma->stream->name, ret); + return ret; + } - return ret; + ret = intel_free_stream(sdw, substream, dai, sdw->instance); + if (ret < 0) { + dev_err(dai->dev, "intel_free_stream: failed %d", ret); + return ret; + } + + sdw_release_stream(dma->stream); + + return 0; } static void intel_shutdown(struct snd_pcm_substream *substream, From 11f8c28233d745604d23caea865d728b6dcdffac Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 23 Oct 2019 14:38:00 -0500 Subject: [PATCH 015/170] soundwire: intel_init: add implementation of sdw_intel_enable_irq() This function is required to enable all interrupts across all links. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel_init.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index a30d95ee71b745..391e8763638f39 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -137,6 +137,30 @@ sdw_intel_scan_controller(struct sdw_intel_acpi_info *info) return 0; } +#define HDA_DSP_REG_ADSPIC2 (0x10) +#define HDA_DSP_REG_ADSPIS2 (0x14) +#define HDA_DSP_REG_ADSPIC2_SNDW BIT(5) + +/** + * sdw_intel_enable_irq() - enable/disable Intel SoundWire IRQ + * @mmio_base: The mmio base of the control register + * @enable: true if enable + */ +void sdw_intel_enable_irq(void __iomem *mmio_base, bool enable) +{ + u32 val; + + val = readl(mmio_base + HDA_DSP_REG_ADSPIC2); + + if (enable) + val |= HDA_DSP_REG_ADSPIC2_SNDW; + else + val &= ~HDA_DSP_REG_ADSPIC2_SNDW; + + writel(val, mmio_base + HDA_DSP_REG_ADSPIC2); +} +EXPORT_SYMBOL(sdw_intel_enable_irq); + static struct sdw_intel_ctx *sdw_intel_probe_controller(struct sdw_intel_res *res) { From 3379a2b31b367b1facf188932d41a846cb101c99 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 16 Dec 2019 12:31:18 -0600 Subject: [PATCH 016/170] soundwire: intel_init: use EXPORT_SYMBOL_NS Make sure all symbols in this soundwire-intel-init module are exported with a namespace. The MODULE_IMPORT_NS will be used in Intel/SOF HDaudio modules to be posted in a separate series. Namespaces are only introduced for the Intel parts of the SoundWire code at this time, in future patches we should also add namespaces for Cadence parts and the SoundWire core. Suggested-by: Greg KH Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel_init.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 391e8763638f39..0f83907e1bc7b3 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -159,7 +159,7 @@ void sdw_intel_enable_irq(void __iomem *mmio_base, bool enable) writel(val, mmio_base + HDA_DSP_REG_ADSPIC2); } -EXPORT_SYMBOL(sdw_intel_enable_irq); +EXPORT_SYMBOL_NS(sdw_intel_enable_irq, SOUNDWIRE_INTEL_INIT); static struct sdw_intel_ctx *sdw_intel_probe_controller(struct sdw_intel_res *res) @@ -346,7 +346,7 @@ int sdw_intel_acpi_scan(acpi_handle *parent_handle, return sdw_intel_scan_controller(info); } -EXPORT_SYMBOL(sdw_intel_acpi_scan); +EXPORT_SYMBOL_NS(sdw_intel_acpi_scan, SOUNDWIRE_INTEL_INIT); /** * sdw_intel_probe() - SoundWire Intel probe routine @@ -362,7 +362,7 @@ struct sdw_intel_ctx { return sdw_intel_probe_controller(res); } -EXPORT_SYMBOL(sdw_intel_probe); +EXPORT_SYMBOL_NS(sdw_intel_probe, SOUNDWIRE_INTEL_INIT); /** * sdw_intel_startup() - SoundWire Intel startup @@ -373,7 +373,7 @@ int sdw_intel_startup(struct sdw_intel_ctx *ctx) { return sdw_intel_startup_controller(ctx); } -EXPORT_SYMBOL(sdw_intel_startup); +EXPORT_SYMBOL_NS(sdw_intel_startup, SOUNDWIRE_INTEL_INIT); /** * sdw_intel_exit() - SoundWire Intel exit * @ctx: SoundWire context allocated in the probe @@ -386,7 +386,7 @@ void sdw_intel_exit(struct sdw_intel_ctx *ctx) driver_unregister(&intel_sdw_driver.driver); kfree(ctx); } -EXPORT_SYMBOL(sdw_intel_exit); +EXPORT_SYMBOL_NS(sdw_intel_exit, SOUNDWIRE_INTEL_INIT); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("Intel Soundwire Init Library"); From 8bf05ed791687ba305dd37ed4c98334c7a2b0de6 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 16 Dec 2019 14:00:21 -0600 Subject: [PATCH 017/170] soundwire: intel: use EXPORT_SYMBOL_NS The soundwire-intel module exports an 'intel_sdw_driver' structure, which is declared with a namespace explicitly imported by the soundwire-intel-init module. The use of namespaces might be deemed overkill here, but it did help enforce a proper code partitioning for follow-up patches on clock-stop support. Suggested-by: Greg KH Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 2 +- drivers/soundwire/intel_init.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 0e77df0a776027..c24b5f30789d5d 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1225,7 +1225,7 @@ struct sdw_md_driver intel_sdw_driver = { .startup = intel_master_startup, .remove = intel_master_remove, }; -EXPORT_SYMBOL(intel_sdw_driver); +EXPORT_SYMBOL_NS(intel_sdw_driver, SOUNDWIRE_INTEL); MODULE_LICENSE("Dual BSD/GPL"); MODULE_ALIAS("platform:int-sdw"); diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 0f83907e1bc7b3..0acb92a3c6d196 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -390,3 +390,4 @@ EXPORT_SYMBOL_NS(sdw_intel_exit, SOUNDWIRE_INTEL_INIT); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("Intel Soundwire Init Library"); +MODULE_IMPORT_NS(SOUNDWIRE_INTEL); From dfebef954a47d9f7540b624913141c54c4dba9ce Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 24 Sep 2019 16:16:46 +0800 Subject: [PATCH 018/170] soundwire: intel/cadence: merge Soundwire interrupt handlers/threads The existing code uses one pair of interrupt handler/thread per link but at the hardware level the interrupt is shared. This works fine for legacy PCI interrupts, but leads to timeouts in MSI (Message-Signaled Interrupt) mode, likely due to edges being lost. This patch unifies interrupt handling for all links. The dedicated handler is removed since we use a common one for all shared interrupt sources, and the thread function takes care of dealing with interrupt sources. This partition follows the model used for the SOF IPC on HDaudio platforms, where similar timeout issues were noticed and doing all the interrupt handling/clearing in the thread improved reliability/stability. Validation results with 4 links active in parallel show a night-and-day improvement with no timeouts noticed even during stress tests. Latency and quality of service are not affected by the change - mostly because events on a SoundWire link are throttled by the bus frame rate (typically 8..48kHz). Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.c | 17 +++++++++-------- drivers/soundwire/cadence_master.h | 4 ++++ drivers/soundwire/intel.c | 27 ++++++--------------------- drivers/soundwire/intel.h | 2 ++ drivers/soundwire/intel_init.c | 20 +++++++++++++++++++- 5 files changed, 40 insertions(+), 30 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index fed21e2b22774d..362fb6e49bfe09 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "bus.h" #include "cadence_master.h" @@ -745,7 +746,7 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id) CDNS_MCP_INT_SLAVE_MASK, 0); int_status &= ~CDNS_MCP_INT_SLAVE_MASK; - ret = IRQ_WAKE_THREAD; + schedule_work(&cdns->work); } cdns_writel(cdns, CDNS_MCP_INTSTAT, int_status); @@ -754,13 +755,14 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id) EXPORT_SYMBOL(sdw_cdns_irq); /** - * sdw_cdns_thread() - Cadence irq thread handler - * @irq: irq number - * @dev_id: irq context + * To update slave status in a work since we will need to handle + * other interrupts eg. CDNS_MCP_INT_RX_WL during the update slave + * process. */ -irqreturn_t sdw_cdns_thread(int irq, void *dev_id) +static void cdns_update_slave_status_work(struct work_struct *work) { - struct sdw_cdns *cdns = dev_id; + struct sdw_cdns *cdns = + container_of(work, struct sdw_cdns, work); u32 slave0, slave1; dev_dbg_ratelimited(cdns->dev, "Slave status change\n"); @@ -777,9 +779,7 @@ irqreturn_t sdw_cdns_thread(int irq, void *dev_id) cdns_updatel(cdns, CDNS_MCP_INTMASK, CDNS_MCP_INT_SLAVE_MASK, CDNS_MCP_INT_SLAVE_MASK); - return IRQ_HANDLED; } -EXPORT_SYMBOL(sdw_cdns_thread); /* * init routines @@ -1187,6 +1187,7 @@ int sdw_cdns_probe(struct sdw_cdns *cdns) init_completion(&cdns->tx_complete); cdns->bus.port_ops = &cdns_port_ops; + INIT_WORK(&cdns->work, cdns_update_slave_status_work); return 0; } EXPORT_SYMBOL(sdw_cdns_probe); diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 001457cbe5ad2e..0f108fd31fd943 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -126,6 +126,10 @@ struct sdw_cdns { bool link_up; unsigned int msg_count; + + struct work_struct work; + + struct list_head list; }; #define bus_to_cdns(_bus) container_of(_bus, struct sdw_cdns, bus) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index c24b5f30789d5d..dc12bfdfaaa9e5 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1096,6 +1096,7 @@ static int intel_master_probe(struct sdw_master_device *md, void *link_ctx) sdw->cdns.msg_count = 0; sdw->cdns.bus.dev = &md->dev; sdw->cdns.bus.link_id = md->link_id; + sdw->link_res->cdns = &sdw->cdns; sdw_cdns_probe(&sdw->cdns); @@ -1114,27 +1115,12 @@ static int intel_master_probe(struct sdw_master_device *md, void *link_ctx) return ret; } - if (sdw->cdns.bus.prop.hw_disabled) { - dev_info(&md->dev, "SoundWire master %d is disabled, ignoring\n", + if (sdw->cdns.bus.prop.hw_disabled) + dev_info(&md->dev, + "SoundWire master %d is disabled, will be ignored\n", sdw->cdns.bus.link_id); - return 0; - } - - /* Acquire IRQ */ - ret = request_threaded_irq(sdw->link_res->irq, - sdw_cdns_irq, sdw_cdns_thread, - IRQF_SHARED, KBUILD_MODNAME, &sdw->cdns); - if (ret < 0) { - dev_err(sdw->cdns.dev, "unable to grab IRQ %d, disabling device\n", - sdw->link_res->irq); - goto err_init; - } return 0; - -err_init: - sdw_delete_bus_master(&sdw->cdns.bus); - return ret; } static int intel_master_startup(struct sdw_master_device *md) @@ -1146,7 +1132,8 @@ static int intel_master_startup(struct sdw_master_device *md) sdw = md->pdata; if (sdw->cdns.bus.prop.hw_disabled) { - dev_info(&md->dev, "SoundWire master %d is disabled, ignoring\n", + dev_info(&md->dev, + "SoundWire master %d is disabled, ignoring\n", sdw->cdns.bus.link_id); return 0; } @@ -1191,7 +1178,6 @@ static int intel_master_startup(struct sdw_master_device *md) err_interrupt: sdw_cdns_enable_interrupt(&sdw->cdns, false); err_init: - free_irq(sdw->link_res->irq, sdw); sdw_delete_bus_master(&sdw->cdns.bus); return ret; } @@ -1205,7 +1191,6 @@ static int intel_master_remove(struct sdw_master_device *md) if (!sdw->cdns.bus.prop.hw_disabled) { intel_debugfs_exit(sdw); sdw_cdns_enable_interrupt(&sdw->cdns, false); - free_irq(sdw->link_res->irq, sdw); snd_soc_unregister_component(sdw->cdns.dev); } sdw_delete_bus_master(&sdw->cdns.bus); diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index cfab2f00214d25..2ae4fa7486861d 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -25,6 +25,8 @@ struct sdw_intel_link_res { int irq; const struct sdw_intel_ops *ops; struct device *dev; + struct sdw_cdns *cdns; + struct list_head list; }; #define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1) diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 0acb92a3c6d196..82aeb350c9bc15 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -11,8 +11,10 @@ #include #include #include +#include #include #include +#include "cadence_master.h" #include "intel.h" #define SDW_LINK_TYPE 4 /* from Intel ACPI documentation */ @@ -161,6 +163,19 @@ void sdw_intel_enable_irq(void __iomem *mmio_base, bool enable) } EXPORT_SYMBOL_NS(sdw_intel_enable_irq, SOUNDWIRE_INTEL_INIT); +irqreturn_t sdw_intel_thread(int irq, void *dev_id) +{ + struct sdw_intel_ctx *ctx = dev_id; + struct sdw_intel_link_res *link; + + list_for_each_entry(link, &ctx->link_list, list) + sdw_cdns_irq(irq, link->cdns); + + sdw_intel_enable_irq(ctx->mmio_base, true); + return IRQ_HANDLED; +} +EXPORT_SYMBOL(sdw_intel_thread); + static struct sdw_intel_ctx *sdw_intel_probe_controller(struct sdw_intel_res *res) { @@ -207,6 +222,8 @@ static struct sdw_intel_ctx goto register_err; } + INIT_LIST_HEAD(&ctx->link_list); + /* Create SDW Master devices */ for (i = 0; i < count; i++, link++) { if (link_mask && !(link_mask & BIT(i))) @@ -227,12 +244,13 @@ static struct sdw_intel_ctx + (SDW_LINK_SIZE * i); link->shim = res->mmio_base + SDW_SHIM_BASE; link->alh = res->mmio_base + SDW_ALH_BASE; - link->irq = res->irq; link->ops = res->ops; link->dev = res->dev; /* let the SoundWire master driver to its probe */ md->driver->probe(md, link); + + list_add_tail(&link->list, &ctx->link_list); } return ctx; From 9106c05de1651a40efa141b89647f926c11b9254 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 1 Aug 2019 19:53:15 -0500 Subject: [PATCH 019/170] soundwire: bus: fix race condition with probe_complete signaling The driver probe takes care of basic initialization and is invoked when a Slave becomes attached, after a match between the Slave DevID registers and ACPI/DT entries. The update_status callback is invoked when a Slave state changes, e.g. when it is assigned a non-zero Device Number and it reports with an ATTACHED/ALERT state. The state change detection is usually hardware-based and based on the SoundWire frame rate (e.g. double-digit microseconds) while the probe is a pure software operation, which may involve a kernel module load. In corner cases, it's possible that the state changes before the probe completes. This patch suggests the use of wait_for_completion to avoid races on startup, so that the update_status callback does not rely on invalid pointers/data structures. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 25 ++++++++++++++++++++++--- drivers/soundwire/bus.h | 1 + drivers/soundwire/bus_type.c | 5 +++++ drivers/soundwire/slave.c | 2 ++ 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 4b22ee996a6566..d99acbc614c68b 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -961,10 +961,29 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) static int sdw_update_slave_status(struct sdw_slave *slave, enum sdw_slave_status status) { - if (slave->ops && slave->ops->update_status) - return slave->ops->update_status(slave, status); + unsigned long time; - return 0; + if (!slave->probed) { + /* + * the slave status update is typically handled in an + * interrupt thread, which can race with the driver + * probe, e.g. when a module needs to be loaded. + * + * make sure the probe is complete before updating + * status. + */ + time = wait_for_completion_timeout(&slave->probe_complete, + msecs_to_jiffies(DEFAULT_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Probe not complete, timed out\n"); + return -ETIMEDOUT; + } + } + + if (!slave->ops || !slave->ops->update_status) + return 0; + + return slave->ops->update_status(slave, status); } /** diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index be01a5f3d00bae..93e7bbab0938fd 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -5,6 +5,7 @@ #define __SDW_BUS_H #define DEFAULT_BANK_SWITCH_TIMEOUT 3000 +#define DEFAULT_PROBE_TIMEOUT 2000 int sdw_uevent(struct device *dev, struct kobj_uevent_env *env); diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 2e4e8c2e629f46..5dd13c74556618 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -125,6 +125,11 @@ static int sdw_slave_drv_probe(struct device *dev) slave->bus->clk_stop_timeout = max_t(u32, slave->bus->clk_stop_timeout, slave->prop.clk_stop_timeout); + slave->probed = true; + complete(&slave->probe_complete); + + dev_dbg(dev, "probe complete\n"); + return 0; } diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index 014c3ece1f1768..15ac89ecae0109 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -53,6 +53,8 @@ static int sdw_slave_add(struct sdw_bus *bus, slave->bus = bus; slave->status = SDW_SLAVE_UNATTACHED; slave->dev_num = 0; + init_completion(&slave->probe_complete); + slave->probed = false; mutex_lock(&bus->bus_lock); list_add_tail(&slave->node, &bus->slaves); From 8f6ee9ce8c3b7e09eb0c309dae5fbc393e82e224 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 10 Jun 2019 19:09:14 -0500 Subject: [PATCH 020/170] soundwire: bus: add PM/no-PM versions of read/write functions Add support for pm_runtime with the appropriate error checks for sdw_write/read functions, e.g. when pm_runtime is not supported. Also expose internal functions without pm_runtime support, which are required to perform any sort of suspend/resume operation, as well as any enumeration tasks. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 68 +++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index d99acbc614c68b..9d64bb4a82422f 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -317,6 +317,46 @@ int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave, return 0; } +/* + * Read/Write IO functions. + * no_pm versions can only be called by the bus, e.g. while enumerating or + * handling suspend-resume sequences. + * all clients need to use the pm versions + */ + +static int +sdw_nread_no_pm(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) +{ + struct sdw_msg msg; + int ret; + + ret = sdw_fill_msg(&msg, slave, addr, count, + slave->dev_num, SDW_MSG_FLAG_READ, val); + if (ret < 0) + return ret; + + return sdw_transfer(slave->bus, &msg); +} + +static int +sdw_nwrite_no_pm(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) +{ + struct sdw_msg msg; + int ret; + + ret = sdw_fill_msg(&msg, slave, addr, count, + slave->dev_num, SDW_MSG_FLAG_WRITE, val); + if (ret < 0) + return ret; + + return sdw_transfer(slave->bus, &msg); +} + +static int sdw_write_no_pm(struct sdw_slave *slave, u32 addr, u8 value) +{ + return sdw_nwrite_no_pm(slave, addr, 1, &value); +} + /** * sdw_nread() - Read "n" contiguous SDW Slave registers * @slave: SDW Slave @@ -326,19 +366,17 @@ int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave, */ int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) { - struct sdw_msg msg; int ret; - ret = sdw_fill_msg(&msg, slave, addr, count, - slave->dev_num, SDW_MSG_FLAG_READ, val); - if (ret < 0) - return ret; - ret = pm_runtime_get_sync(slave->bus->dev); - if (ret < 0) + if (ret < 0 && ret != -EACCES) { + pm_runtime_put_noidle(slave->bus->dev); return ret; + } + + ret = sdw_nread_no_pm(slave, addr, count, val); - ret = sdw_transfer(slave->bus, &msg); + pm_runtime_mark_last_busy(slave->bus->dev); pm_runtime_put(slave->bus->dev); return ret; @@ -354,19 +392,17 @@ EXPORT_SYMBOL(sdw_nread); */ int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) { - struct sdw_msg msg; int ret; - ret = sdw_fill_msg(&msg, slave, addr, count, - slave->dev_num, SDW_MSG_FLAG_WRITE, val); - if (ret < 0) - return ret; - ret = pm_runtime_get_sync(slave->bus->dev); - if (ret < 0) + if (ret < 0 && ret != -EACCES) { + pm_runtime_put_noidle(slave->bus->dev); return ret; + } + + ret = sdw_nwrite_no_pm(slave, addr, count, val); - ret = sdw_transfer(slave->bus, &msg); + pm_runtime_mark_last_busy(slave->bus->dev); pm_runtime_put(slave->bus->dev); return ret; From 3aa6ed15de8cc8c91662d42ee559097ab15b7c9e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 26 Sep 2019 16:19:00 -0500 Subject: [PATCH 021/170] soundwire: bus: write Slave Device Number without runtime_pm While handling the Device0, we can safely use sdw_write_no_pm. This move will also helps us track that all other usages of sdw_write() happen when the Slave is already enumerated. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 9d64bb4a82422f..4ddab1e94c9958 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -513,7 +513,7 @@ static int sdw_assign_device_num(struct sdw_slave *slave) slave->dev_num = 0; } - ret = sdw_write(slave, SDW_SCP_DEVNUMBER, dev_num); + ret = sdw_write_no_pm(slave, SDW_SCP_DEVNUMBER, dev_num); if (ret < 0) { dev_err(&slave->dev, "Program device_num %d failed: %d\n", dev_num, ret); From f872469eaa25024bfb2137ced74c568722b3fbe5 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 20 Sep 2019 14:08:57 -0500 Subject: [PATCH 022/170] soundwire: intel: add helpers for link power down and shim wake These routines are required for power management Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index dc12bfdfaaa9e5..ebe75ac568f93e 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -363,6 +363,59 @@ static int intel_shim_init(struct sdw_intel *sdw) return ret; } +static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) +{ + void __iomem *shim = sdw->link_res->shim; + unsigned int link_id = sdw->instance; + u16 wake_en, wake_sts; + + if (wake_enable) { + /* Enable the wakeup */ + intel_writew(shim, SDW_SHIM_WAKEEN, + (SDW_SHIM_WAKEEN_ENABLE << link_id)); + } else { + /* Disable the wake up interrupt */ + wake_en = intel_readw(shim, SDW_SHIM_WAKEEN); + wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id); + intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); + + /* Clear wake status */ + wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); + wake_sts |= (SDW_SHIM_WAKEEN_ENABLE << link_id); + intel_writew(shim, SDW_SHIM_WAKESTS_STATUS, wake_sts); + } +} + +static int intel_link_power_down(struct sdw_intel *sdw) +{ + int link_control, spa_mask, cpa_mask, ret; + unsigned int link_id = sdw->instance; + void __iomem *shim = sdw->link_res->shim; + u16 ioctl; + + /* Glue logic */ + ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); + ioctl |= SDW_SHIM_IOCTL_BKE; + ioctl |= SDW_SHIM_IOCTL_COE; + intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); + + ioctl &= ~(SDW_SHIM_IOCTL_MIF); + intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); + + /* Link power down sequence */ + link_control = intel_readl(shim, SDW_SHIM_LCTL); + spa_mask = ~(SDW_SHIM_LCTL_SPA << link_id); + cpa_mask = (SDW_SHIM_LCTL_CPA << link_id); + link_control &= spa_mask; + + ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); + if (ret < 0) + return ret; + + sdw->cdns.link_up = false; + return 0; +} + /* * PDI routines */ From 9f3aa31176f9d12c378d32b833dbc3bd272fbe12 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 20 Sep 2019 14:21:18 -0500 Subject: [PATCH 023/170] soundwire: intel: Add basic power management support Implement suspend/resume capabilities (not runtime_pm for now) The resume part is essentially a full-blown re-enumeration. When S0ix is supported, we will select clock stop mode when the ACPI target state is S0, and tear down the link for S3. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 75 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index ebe75ac568f93e..62f0aaf769e188 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1253,11 +1253,86 @@ static int intel_master_remove(struct sdw_master_device *md) return 0; } +/* + * PM calls + */ + +#ifdef CONFIG_PM + +static int intel_suspend(struct device *dev) +{ + struct sdw_cdns *cdns = dev_get_drvdata(dev); + struct sdw_intel *sdw = cdns_to_intel(cdns); + int ret; + + if (cdns->bus.prop.hw_disabled) { + dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", + cdns->bus.link_id); + return 0; + } + + ret = sdw_cdns_enable_interrupt(cdns, false); + if (ret < 0) { + dev_err(dev, "cannot disable interrupts on suspend\n"); + return ret; + } + + ret = intel_link_power_down(sdw); + if (ret) { + dev_err(dev, "Link power down failed: %d", ret); + return ret; + } + + intel_shim_wake(sdw, false); + + return 0; +} + +static int intel_resume(struct device *dev) +{ + struct sdw_cdns *cdns = dev_get_drvdata(dev); + struct sdw_intel *sdw = cdns_to_intel(cdns); + int ret; + + if (cdns->bus.prop.hw_disabled) { + dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", + cdns->bus.link_id); + return 0; + } + + ret = intel_init(sdw); + if (ret) { + dev_err(dev, "%s failed: %d", __func__, ret); + return ret; + } + + ret = sdw_cdns_enable_interrupt(cdns, true); + if (ret < 0) { + dev_err(dev, "cannot enable interrupts during resume\n"); + return ret; + } + + ret = sdw_cdns_exit_reset(cdns); + if (ret < 0) { + dev_err(dev, "unable to exit bus reset sequence during resume\n"); + return ret; + } + + return ret; +} + +#endif + +static const struct dev_pm_ops intel_pm = { + SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume) +}; + struct sdw_md_driver intel_sdw_driver = { .driver = { .name = "intel-sdw", .owner = THIS_MODULE, .bus = &sdw_bus_type, + .pm = &intel_pm, }, .probe = intel_master_probe, .startup = intel_master_startup, From b0459b44a278b35522d81e670049ca7e7a1a81d1 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 20 Sep 2019 14:51:39 -0500 Subject: [PATCH 024/170] soundwire: intel: add pm_runtime support Add basic hooks in DAI .startup and .shutdown callbacks. The SoundWire IP should be powered between those two calls. The power dependencies between SoundWire and DSP are handled with the parent/child relationship, before the SoundWire master device becomes active the parent device will become active and power-up the shared rails. For now the strategy is to rely on complete enumeration when the device becomes active, so the code is a copy/paste of the sequence for system suspend/resume. In future patches, the strategy will optionally be to rely on clock stop if the enumeration time is prohibitive or when the devices connected to a link can signal a wake. A module parameter is added to make integration of new Slave devices easier, to e.g. keep the device active or prevent clock-stop. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 108 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 4 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 62f0aaf769e188..aaf7b6d9befdbf 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +23,20 @@ #include "bus.h" #include "intel.h" +/* + * debug/config flags for the Intel SoundWire Master. + * + * Since we may have multiple masters active, we can have up to 8 + * flags reused in each byte, with master0 using the ls-byte, etc. + */ + +#define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME BIT(0) +#define SDW_INTEL_MASTER_DISABLE_CLOCK_STOP BIT(1) + +static int md_flags; +module_param_named(sdw_md_flags, md_flags, int, 0444); +MODULE_PARM_DESC(sdw_md_flags, "SoundWire Intel Master device flags (0x0 all off)"); + /* Intel SHIM Registers Definition */ #define SDW_SHIM_LCAP 0x0 #define SDW_SHIM_LCTL 0x4 @@ -743,10 +758,16 @@ static int sdw_stream_setup(struct snd_pcm_substream *substream, static int intel_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - /* - * TODO: add pm_runtime support here, the startup callback - * will make sure the IP is 'active' - */ + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + int ret; + + ret = pm_runtime_get_sync(cdns->dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(cdns->dev, + "pm_runtime_get_sync failed in %s, ret %d\n", + __func__, ret); + pm_runtime_put_noidle(cdns->dev); + } return sdw_stream_setup(substream, dai); } @@ -925,6 +946,7 @@ static void intel_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct sdw_cdns_dma_data *dma; + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); dma = snd_soc_dai_get_dma_data(dai, substream); if (!dma) @@ -932,6 +954,9 @@ static void intel_shutdown(struct snd_pcm_substream *substream, snd_soc_dai_set_dma_data(dai, substream, NULL); kfree(dma); + + pm_runtime_mark_last_busy(cdns->dev); + pm_runtime_put_autosuspend(cdns->dev); } static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, @@ -1180,6 +1205,7 @@ static int intel_master_startup(struct sdw_master_device *md) { struct sdw_cdns_stream_config config; struct sdw_intel *sdw; + int link_flags; int ret; sdw = md->pdata; @@ -1226,6 +1252,17 @@ static int intel_master_startup(struct sdw_master_device *md) intel_debugfs_init(sdw); + /* Enable runtime PM */ + link_flags = md_flags >> (sdw->cdns.bus.link_id * 8); + if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) { + pm_runtime_set_autosuspend_delay(&md->dev, 3000); + pm_runtime_use_autosuspend(&md->dev); + pm_runtime_mark_last_busy(&md->dev); + + pm_runtime_set_active(&md->dev); + pm_runtime_enable(&md->dev); + } + return 0; err_interrupt: @@ -1288,6 +1325,35 @@ static int intel_suspend(struct device *dev) return 0; } +static int intel_suspend_runtime(struct device *dev) +{ + struct sdw_cdns *cdns = dev_get_drvdata(dev); + struct sdw_intel *sdw = cdns_to_intel(cdns); + int ret; + + if (cdns->bus.prop.hw_disabled) { + dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", + cdns->bus.link_id); + return 0; + } + + ret = sdw_cdns_enable_interrupt(cdns, false); + if (ret < 0) { + dev_err(dev, "cannot disable interrupts on suspend\n"); + return ret; + } + + ret = intel_link_power_down(sdw); + if (ret) { + dev_err(dev, "Link power down failed: %d", ret); + return ret; + } + + intel_shim_wake(sdw, false); + + return 0; +} + static int intel_resume(struct device *dev) { struct sdw_cdns *cdns = dev_get_drvdata(dev); @@ -1321,10 +1387,44 @@ static int intel_resume(struct device *dev) return ret; } +static int intel_resume_runtime(struct device *dev) +{ + struct sdw_cdns *cdns = dev_get_drvdata(dev); + struct sdw_intel *sdw = cdns_to_intel(cdns); + int ret; + + if (cdns->bus.prop.hw_disabled) { + dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", + cdns->bus.link_id); + return 0; + } + + ret = intel_init(sdw); + if (ret) { + dev_err(dev, "%s failed: %d", __func__, ret); + return ret; + } + + ret = sdw_cdns_enable_interrupt(cdns, true); + if (ret < 0) { + dev_err(dev, "cannot enable interrupts during resume\n"); + return ret; + } + + ret = sdw_cdns_exit_reset(cdns); + if (ret < 0) { + dev_err(dev, "unable to exit bus reset sequence during resume\n"); + return ret; + } + + return ret; +} + #endif static const struct dev_pm_ops intel_pm = { SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume) + SET_RUNTIME_PM_OPS(intel_suspend_runtime, intel_resume_runtime, NULL) }; struct sdw_md_driver intel_sdw_driver = { From 787b2fbaa2005603522a8a7e36c77d14393fd3e9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 11 Oct 2019 17:15:41 -0500 Subject: [PATCH 025/170] soundwire: intel: reset pm_runtime status during system resume The system resume does the entire bus re-initialization and brings it to full-power. If the device was pm_runtime suspended, there is no need to run the pm_runtime resume sequence after the system runtime. Follow the documentation from runtime_pm.rst, and conditionally disable, set_active and re-enable the device on system resume. Note that pm_runtime_suspended() is used instead of pm_runtime_status_suspended() so that we can deal with the case where pm_runtime is disabled. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 30 ++++++++++++++++++++++++++++++ include/linux/soundwire/sdw.h | 1 + 2 files changed, 31 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index aaf7b6d9befdbf..96df563bdb2185 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1298,6 +1298,7 @@ static int intel_master_remove(struct sdw_master_device *md) static int intel_suspend(struct device *dev) { + struct sdw_master_device *md = to_sdw_master_device(dev); struct sdw_cdns *cdns = dev_get_drvdata(dev); struct sdw_intel *sdw = cdns_to_intel(cdns); int ret; @@ -1308,6 +1309,20 @@ static int intel_suspend(struct device *dev) return 0; } + if (pm_runtime_suspended(dev)) { + dev_dbg(dev, + "%s: pm_runtime status: suspended\n", + __func__); + + /* + * keep track of the state for the system resume, where + * we will need to reset the pm_runtime status to active + */ + md->pm_runtime_suspended = true; + + return 0; + } + ret = sdw_cdns_enable_interrupt(cdns, false); if (ret < 0) { dev_err(dev, "cannot disable interrupts on suspend\n"); @@ -1356,6 +1371,7 @@ static int intel_suspend_runtime(struct device *dev) static int intel_resume(struct device *dev) { + struct sdw_master_device *md = to_sdw_master_device(dev); struct sdw_cdns *cdns = dev_get_drvdata(dev); struct sdw_intel *sdw = cdns_to_intel(cdns); int ret; @@ -1366,6 +1382,20 @@ static int intel_resume(struct device *dev) return 0; } + if (md->pm_runtime_suspended) { + dev_dbg(dev, + "%s: pm_runtime status was suspended, forcing active\n", + __func__); + + /* follow required sequence from runtime_pm.rst */ + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_enable(dev); + + md->pm_runtime_suspended = false; + } + ret = intel_init(sdw); if (ret) { dev_err(dev, "%s failed: %d", __func__, ret); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 58f50257dfa8d1..1a765b10b8bbaa 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -590,6 +590,7 @@ struct sdw_master_device { int link_id; struct sdw_md_driver *driver; void *pdata; + bool pm_runtime_suspended; }; #define to_sdw_master_device(d) \ From b0c1081a9875d72c8d5bcda160855be09d654020 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 25 Nov 2019 18:22:20 -0600 Subject: [PATCH 026/170] soundwire: intel: fix race condition on system resume Previous patches took care of the case where the master device is pm_runtime 'suspended' when a system suspend occurs. In the case where the master device was not suspended, e.g. if suspend occurred while streaming audio, Intel validation noticed a race condition the enumeration started by the system resume may race with pm_runtime suspend. This can be simply fixed by updating the status before exiting system resume. GitHub issue: https://github.com/thesofproject/linux/issues/1482 Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 96df563bdb2185..8e973a6e38e1d9 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1414,6 +1414,18 @@ static int intel_resume(struct device *dev) return ret; } + /* + * after system resume, the pm_runtime suspend() may kick in + * during the enumeration, before any children device force the + * master device to remain active. Using pm_runtime_get() + * routines is not really possible, since it'd prevent the + * master from suspending. + * A reasonable compromise is to update the pm_runtime + * counters and delay the pm_runtime suspend by several + * seconds, by which all enumeration should be complete. + */ + pm_runtime_mark_last_busy(cdns->dev); + return ret; } From 86dea11e06134e432fdff25056ca44c791494da3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 26 Sep 2019 18:26:11 -0500 Subject: [PATCH 027/170] soundwire: bus: add helper to reset Slave status to UNATTACHED When resuming, we need to re-enumerate and restart from UNATTACHED. This will help implement a more robust state machine avoiding race conditions on resume. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 24 ++++++++++++++++++++++++ drivers/soundwire/bus.h | 2 ++ 2 files changed, 26 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 4ddab1e94c9958..455962555ef8b8 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1108,3 +1108,27 @@ int sdw_handle_slave_status(struct sdw_bus *bus, return ret; } EXPORT_SYMBOL(sdw_handle_slave_status); + +void sdw_clear_slave_status(struct sdw_bus *bus) +{ + struct sdw_slave *slave; + int i; + + /* Check all non-zero devices */ + for (i = 1; i <= SDW_MAX_DEVICES; i++) { + mutex_lock(&bus->bus_lock); + if (test_bit(i, bus->assigned) == false) { + mutex_unlock(&bus->bus_lock); + continue; + } + mutex_unlock(&bus->bus_lock); + + slave = sdw_get_slave(bus, i); + if (!slave) + continue; + + if (slave->status != SDW_SLAVE_UNATTACHED) + sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED); + } +} +EXPORT_SYMBOL(sdw_clear_slave_status); diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 93e7bbab0938fd..4e6fb5d2f5ccd9 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -167,4 +167,6 @@ sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val) return sdw_write(slave, addr, tmp); } +void sdw_clear_slave_status(struct sdw_bus *bus); + #endif /* __SDW_BUS_H */ From 930654c6cf7ed9c739690b1f52799d4dfe4812b2 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 26 Sep 2019 18:32:52 -0500 Subject: [PATCH 028/170] soundwire: intel: call helper to reset Slave states on resume This helps make sure they are all UNATTACHED and reset the state machines. At the moment we perform a bus reset both for resume and pm_resume, this will be modified when clock-stop mode is supported Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 8e973a6e38e1d9..bb02dd023199f7 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1402,6 +1402,9 @@ static int intel_resume(struct device *dev) return ret; } + /* make sure all Slaves are tagged as UNATTACHED */ + sdw_clear_slave_status(&sdw->cdns.bus); + ret = sdw_cdns_enable_interrupt(cdns, true); if (ret < 0) { dev_err(dev, "cannot enable interrupts during resume\n"); @@ -1447,6 +1450,9 @@ static int intel_resume_runtime(struct device *dev) return ret; } + /* make sure all Slaves are tagged as UNATTACHED */ + sdw_clear_slave_status(&sdw->cdns.bus); + ret = sdw_cdns_enable_interrupt(cdns, true); if (ret < 0) { dev_err(dev, "cannot enable interrupts during resume\n"); From fe865567b9c6b02e8eb98ccf99027edc96aa93e7 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 26 Sep 2019 18:37:00 -0500 Subject: [PATCH 029/170] soundwire: bus: check first if Slaves become UNATTACHED Before checking for the presence of Device0, we first need to clean-up the internal state of Slaves that are no longer attached. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 455962555ef8b8..b76c0a10ef7a27 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1034,6 +1034,24 @@ int sdw_handle_slave_status(struct sdw_bus *bus, struct sdw_slave *slave; int i, ret = 0; + /* first check if any Slaves fell off the bus */ + for (i = 1; i <= SDW_MAX_DEVICES; i++) { + mutex_lock(&bus->bus_lock); + if (test_bit(i, bus->assigned) == false) { + mutex_unlock(&bus->bus_lock); + continue; + } + mutex_unlock(&bus->bus_lock); + + slave = sdw_get_slave(bus, i); + if (!slave) + continue; + + if (status[i] == SDW_SLAVE_UNATTACHED && + slave->status != SDW_SLAVE_UNATTACHED) + sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED); + } + if (status[0] == SDW_SLAVE_ATTACHED) { dev_dbg(bus->dev, "Slave attached, programming device number\n"); ret = sdw_program_device_num(bus); From 2a673af83a0d08c7f7a62da4d722f9f564fccb5a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 26 Sep 2019 18:47:35 -0500 Subject: [PATCH 030/170] soundwire: bus: fix race condition with enumeration_complete signaling This patch adds the signaling needed for Slave drivers to wait until the enumeration completes so that race conditions when issuing read/write commands are avoided. The calls for wait_for_completion() will be added in codec drivers in follow-up patches. The order between init_completion() and complete() is deterministic, the Slave is marked as UNATTACHED either during a Master-initiated HardReset, or when the hardware detects the Slave no longer reports as ATTACHED. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 20 ++++++++++++++++++++ drivers/soundwire/slave.c | 1 + 2 files changed, 21 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index b76c0a10ef7a27..61bbeeb3e8eb6d 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -637,6 +637,26 @@ static void sdw_modify_slave_status(struct sdw_slave *slave, enum sdw_slave_status status) { mutex_lock(&slave->bus->bus_lock); + + dev_vdbg(&slave->dev, + "%s: changing status slave %d status %d new status %d\n", + __func__, slave->dev_num, slave->status, status); + + if (status == SDW_SLAVE_UNATTACHED) { + dev_dbg(&slave->dev, + "%s: initializing completion for Slave %d\n", + __func__, slave->dev_num); + + init_completion(&slave->enumeration_complete); + + } else if ((status == SDW_SLAVE_ATTACHED) && + (slave->status == SDW_SLAVE_UNATTACHED)) { + dev_dbg(&slave->dev, + "%s: signaling completion for Slave %d\n", + __func__, slave->dev_num); + + complete(&slave->enumeration_complete); + } slave->status = status; mutex_unlock(&slave->bus->bus_lock); } diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index 15ac89ecae0109..76fdfbd8b50d5d 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -52,6 +52,7 @@ static int sdw_slave_add(struct sdw_bus *bus, slave->dev.type = &sdw_slave_type; slave->bus = bus; slave->status = SDW_SLAVE_UNATTACHED; + init_completion(&slave->enumeration_complete); slave->dev_num = 0; init_completion(&slave->probe_complete); slave->probed = false; From 89c2a10d2ff4554121544524a3978f6164cedcb3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 4 Nov 2019 12:47:01 -0600 Subject: [PATCH 031/170] soundwire: bus: fix race condition with initialization_complete signaling Waiting for the enumeration to be complete may not be enough for a Slave driver, there is a possible race condition between resume operations and initializations handled in an interrupt thread, which can results in settings not being fully restored after system or pm_runtime resume. This patch builds on the changes added for enumeration_complete, init_completion() is called when the Slave device becomes UNATTACHED, as done with enumeration_complete. The difference with the enumeration_complete case is that complete() is signaled after the Slave device is fully initialized after the .update_status() callback is called. A Slave device driver can decide to wait on either of the two complete() cases, depending on its initialization code and requirements. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 8 ++++++++ drivers/soundwire/slave.c | 1 + 2 files changed, 9 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 61bbeeb3e8eb6d..09c19bc0af59a3 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -648,6 +648,7 @@ static void sdw_modify_slave_status(struct sdw_slave *slave, __func__, slave->dev_num); init_completion(&slave->enumeration_complete); + init_completion(&slave->initialization_complete); } else if ((status == SDW_SLAVE_ATTACHED) && (slave->status == SDW_SLAVE_UNATTACHED)) { @@ -1052,6 +1053,7 @@ int sdw_handle_slave_status(struct sdw_bus *bus, { enum sdw_slave_status prev_status; struct sdw_slave *slave; + bool attached_initializing; int i, ret = 0; /* first check if any Slaves fell off the bus */ @@ -1097,6 +1099,8 @@ int sdw_handle_slave_status(struct sdw_bus *bus, if (!slave) continue; + attached_initializing = false; + switch (status[i]) { case SDW_SLAVE_UNATTACHED: if (slave->status == SDW_SLAVE_UNATTACHED) @@ -1123,6 +1127,8 @@ int sdw_handle_slave_status(struct sdw_bus *bus, if (prev_status == SDW_SLAVE_ALERT) break; + attached_initializing = true; + ret = sdw_initialize_slave(slave); if (ret) dev_err(bus->dev, @@ -1141,6 +1147,8 @@ int sdw_handle_slave_status(struct sdw_bus *bus, if (ret) dev_err(slave->bus->dev, "Update Slave status failed:%d\n", ret); + if (attached_initializing) + complete(&slave->initialization_complete); } return ret; diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index 76fdfbd8b50d5d..d2a952d9bd4755 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -53,6 +53,7 @@ static int sdw_slave_add(struct sdw_bus *bus, slave->bus = bus; slave->status = SDW_SLAVE_UNATTACHED; init_completion(&slave->enumeration_complete); + init_completion(&slave->initialization_complete); slave->dev_num = 0; init_completion(&slave->probe_complete); slave->probed = false; From fcc8349d92f7adfa8fda8771c495452b808c7141 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 8 Nov 2019 17:13:22 -0600 Subject: [PATCH 032/170] soundwire: bus: fix race condition by tracking UNATTACHED transition In previous patches, we added enumeration_ and initialization_complete fields to avoid race conditions. When streaming restarts, the Master first resumes, then requests all Slaves to renumerate/reinitialized, and then the Slave devices resume. Intel validation exposed a corner case where the Slave device may transition to D3 when streaming stops, but streaming restarts before the Master transitions to D3. In that case, the Slave status was not cleared as UNATTACHED, and the wait_for_completion will time out. The proposed solution is that when the Master clears the Slave(s) status, the reason for the Slave(s) becoming unattached is memorized. When the slave resumes, it can check if a Master-initiated re-enumeration and initialization takes place and skip the wait_for_completion() if there is no reason to wait. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 5 ++++- drivers/soundwire/bus.h | 8 +++++++- drivers/soundwire/intel.c | 16 ++++++++++++---- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 09c19bc0af59a3..be4e0946433ff6 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1155,7 +1155,7 @@ int sdw_handle_slave_status(struct sdw_bus *bus, } EXPORT_SYMBOL(sdw_handle_slave_status); -void sdw_clear_slave_status(struct sdw_bus *bus) +void sdw_clear_slave_status(struct sdw_bus *bus, u32 request) { struct sdw_slave *slave; int i; @@ -1175,6 +1175,9 @@ void sdw_clear_slave_status(struct sdw_bus *bus) if (slave->status != SDW_SLAVE_UNATTACHED) sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED); + + /* keep track of request, used in pm_runtime resume */ + slave->unattach_request = request; } } EXPORT_SYMBOL(sdw_clear_slave_status); diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 4e6fb5d2f5ccd9..ee362472003a93 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -167,6 +167,12 @@ sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val) return sdw_write(slave, addr, tmp); } -void sdw_clear_slave_status(struct sdw_bus *bus); +/* + * At the moment we only track Master-initiated hw_reset. + * Additional fields can be added as needed + */ +#define SDW_UNATTACH_REQUEST_MASTER_RESET BIT(0) + +void sdw_clear_slave_status(struct sdw_bus *bus, u32 request); #endif /* __SDW_BUS_H */ diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index bb02dd023199f7..4d4d41af9b5935 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1402,8 +1402,12 @@ static int intel_resume(struct device *dev) return ret; } - /* make sure all Slaves are tagged as UNATTACHED */ - sdw_clear_slave_status(&sdw->cdns.bus); + /* + * make sure all Slaves are tagged as UNATTACHED and provide + * reason for reinitialization + */ + sdw_clear_slave_status(&sdw->cdns.bus, + SDW_UNATTACH_REQUEST_MASTER_RESET); ret = sdw_cdns_enable_interrupt(cdns, true); if (ret < 0) { @@ -1450,8 +1454,12 @@ static int intel_resume_runtime(struct device *dev) return ret; } - /* make sure all Slaves are tagged as UNATTACHED */ - sdw_clear_slave_status(&sdw->cdns.bus); + /* + * make sure all Slaves are tagged as UNATTACHED and provide + * reason for reinitialization + */ + sdw_clear_slave_status(&sdw->cdns.bus, + SDW_UNATTACH_REQUEST_MASTER_RESET); ret = sdw_cdns_enable_interrupt(cdns, true); if (ret < 0) { From 83ff8558e3b99da01927e31391a2c93da8948221 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Oct 2019 21:05:13 -0500 Subject: [PATCH 033/170] soundwire: intel: disable pm_runtime when removing a master Prevent race conditions between remove and resume by disabling pm_runtime. Note that this only takes care of pm_runtime at the Master level, the same precautions are needed when removing a Slave device. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 4d4d41af9b5935..ae120102001d7b 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1276,6 +1276,8 @@ static int intel_master_remove(struct sdw_master_device *md) { struct sdw_intel *sdw; + pm_runtime_disable(&md->dev); + sdw = md->pdata; if (!sdw->cdns.bus.prop.hw_disabled) { From 70cd30550577ac1a80733d507c65f4bd76d07fa7 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Oct 2019 21:02:08 -0500 Subject: [PATCH 034/170] soundwire: bus: disable pm_runtime in sdw_slave_delete Before removing the slave device, disable pm_runtime to prevent any race condition with the resume being executed after the bus and slave devices are removed. Since this pm_runtime_disable() is handled in common routines, implementations of Slave drivers do not need to call it in their .remove() routine. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index be4e0946433ff6..4f2128b6746a01 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -113,6 +113,8 @@ static int sdw_delete_slave(struct device *dev, void *data) struct sdw_slave *slave = to_sdw_slave_device(dev); struct sdw_bus *bus = slave->bus; + pm_runtime_disable(dev); + sdw_slave_debugfs_exit(slave); mutex_lock(&bus->bus_lock); From 01f4ebf34b14a7232c1bf6d70dea3725b93a8758 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 14 Nov 2019 11:14:55 -0600 Subject: [PATCH 035/170] soundwire: stream: remove redundant pr_err traces Only keep pr_err to flag critical configuration errors that will typically only happen during system integration. For errors on prepare/deprepare/enable/disable, the caller can do a much better job with more information on the DAI and device that caused the issue. Suggested-by: Cezary Rojewski Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/stream.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index e69f94a8c3a865..178ae92b8cc1cc 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1554,8 +1554,6 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); ret = _sdw_prepare_stream(stream); - if (ret < 0) - pr_err("Prepare for stream:%s failed: %d\n", stream->name, ret); sdw_release_bus_lock(stream); return ret; @@ -1622,8 +1620,6 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); ret = _sdw_enable_stream(stream); - if (ret < 0) - pr_err("Enable for stream:%s failed: %d\n", stream->name, ret); sdw_release_bus_lock(stream); return ret; @@ -1698,8 +1694,6 @@ int sdw_disable_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); ret = _sdw_disable_stream(stream); - if (ret < 0) - pr_err("Disable for stream:%s failed: %d\n", stream->name, ret); sdw_release_bus_lock(stream); return ret; @@ -1756,8 +1750,6 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); ret = _sdw_deprepare_stream(stream); - if (ret < 0) - pr_err("De-prepare for stream:%d failed: %d\n", ret, ret); sdw_release_bus_lock(stream); return ret; From 688b8311e784748b489ec6f48ded5d6820d6e8d0 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Sat, 12 Oct 2019 14:38:06 -0500 Subject: [PATCH 036/170] soundwire: stream: update state machine and add state checks The state machine and notes don't accurately explain or allow transitions from STREAM_DEPREPARED and STREAM_DISABLED. Add more explanations and allow for more transitions as a result of a trigger_stop(), trigger_suspend() and prepare(), depending on the ALSA/ASoC layer behavior defined by the INFO_RESUME and INFO_PAUSE flags. Also add basic checks to help debug inconsistent states and illegal state machine transitions. Signed-off-by: Pierre-Louis Bossart --- Documentation/driver-api/soundwire/stream.rst | 63 +++++++++++++------ drivers/soundwire/stream.c | 37 +++++++++++ 2 files changed, 82 insertions(+), 18 deletions(-) diff --git a/Documentation/driver-api/soundwire/stream.rst b/Documentation/driver-api/soundwire/stream.rst index 5351bd2f34a870..9b7418ff8d59aa 100644 --- a/Documentation/driver-api/soundwire/stream.rst +++ b/Documentation/driver-api/soundwire/stream.rst @@ -156,22 +156,27 @@ Below shows the SoundWire stream states and state transition diagram. :: +-----------+ +------------+ +----------+ +----------+ | ALLOCATED +---->| CONFIGURED +---->| PREPARED +---->| ENABLED | | STATE | | STATE | | STATE | | STATE | - +-----------+ +------------+ +----------+ +----+-----+ - ^ - | - | - v - +----------+ +------------+ +----+-----+ + +-----------+ +------------+ +---+--+---+ +----+-----+ + ^ ^ ^ + | | | + __| |___________ | + | | | + v | v + +----------+ +-----+------+ +-+--+-----+ | RELEASED |<----------+ DEPREPARED |<-------+ DISABLED | | STATE | | STATE | | STATE | +----------+ +------------+ +----------+ -NOTE: State transition between prepare and deprepare is supported in Spec -but not in the software (subsystem) +NOTE: State transitions between ``SDW_STREAM_ENABLED`` and +``SDW_STREAM_DISABLED`` are only relevant when then INFO_PAUSE flag is +supported at the ALSA/ASoC level. Likewise the transition between +``SDW_DISABLED_STATE`` and ``SDW_PREPARED_STATE`` depends on the +INFO_RESUME flag. -NOTE2: Stream state transition checks need to be handled by caller -framework, for example ALSA/ASoC. No checks for stream transition exist in -SoundWire subsystem. +NOTE2: The framework implements basic state transition checks, but +does not e.g. check if a transition from DISABLED to ENABLED is valid +on a specific platform. Such tests need to be added at the ALSA/ASoC +level. Stream State Operations ----------------------- @@ -246,6 +251,9 @@ SDW_STREAM_PREPARED Prepare state of stream. Operations performed before entering in this state: + (0) Steps 1 and 2 are omitted in the case of a resume operation, + where the bus bandwidth is known. + (1) Bus parameters such as bandwidth, frame shape, clock frequency, are computed based on current stream as well as already active stream(s) on Bus. Re-computation is required to accommodate current @@ -270,13 +278,15 @@ Prepare state of stream. Operations performed before entering in this state: After all above operations are successful, stream state is set to ``SDW_STREAM_PREPARED``. -Bus implements below API for PREPARE state which needs to be called once per -stream. From ASoC DPCM framework, this stream state is linked to -.prepare() operation. +Bus implements below API for PREPARE state which needs to be called +once per stream. From ASoC DPCM framework, this stream state is linked +to .prepare() operation. Since the .trigger() operations may not +follow the .prepare(), a direct transitions from +``SDW_STREAM_PREPARED`` to ``SDW_STREAM_DEPREPARED`` is allowed. .. code-block:: c - int sdw_prepare_stream(struct sdw_stream_runtime * stream); + int sdw_prepare_stream(struct sdw_stream_runtime * stream, bool resume); SDW_STREAM_ENABLED @@ -332,6 +342,14 @@ Bus implements below API for DISABLED state which needs to be called once per stream. From ASoC DPCM framework, this stream state is linked to .trigger() stop operation. +When the INFO_PAUSE flag is supported, a direct transition to +``SDW_STREAM_ENABLED`` is allowed. + +For resume operations where ASoC will use the .prepare() callback, the +stream can transition from ``SDW_STREAM_DISABLED`` to +``SDW_STREAM_PREPARED``, with all required settings restored but +without updating the bandwidth and bit allocation. + .. code-block:: c int sdw_disable_stream(struct sdw_stream_runtime * stream); @@ -353,9 +371,18 @@ state: After all above operations are successful, stream state is set to ``SDW_STREAM_DEPREPARED``. -Bus implements below API for DEPREPARED state which needs to be called once -per stream. From ASoC DPCM framework, this stream state is linked to -.trigger() stop operation. +Bus implements below API for DEPREPARED state which needs to be called +once per stream. ALSA/ASoC do not have a concept of 'deprepare', and +the mapping from this stream state to ALSA/ASoC operation may be +implementation specific. + +When the INFO_PAUSE flag is supported, the stream state is linked to +the .hw_free() operation - the stream is not deprepared on a +TRIGGER_STOP. + +Other implementations may transition to the ``SDW_STREAM_DEPREPARED`` +state on TRIGGER_STOP, should they require a transition through the +``SDW_STREAM_PREPARED`` state. .. code-block:: c diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 178ae92b8cc1cc..6aa0b5d370c039 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1553,8 +1553,18 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); + if (stream->state != SDW_STREAM_CONFIGURED && + stream->state != SDW_STREAM_DEPREPARED && + stream->state != SDW_STREAM_DISABLED) { + pr_err("%s: %s: inconsistent state state %d\n", + __func__, stream->name, stream->state); + ret = -EINVAL; + goto state_err; + } + ret = _sdw_prepare_stream(stream); +state_err: sdw_release_bus_lock(stream); return ret; } @@ -1619,8 +1629,17 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); + if (stream->state != SDW_STREAM_PREPARED && + stream->state != SDW_STREAM_DISABLED) { + pr_err("%s: %s: inconsistent state state %d\n", + __func__, stream->name, stream->state); + ret = -EINVAL; + goto state_err; + } + ret = _sdw_enable_stream(stream); +state_err: sdw_release_bus_lock(stream); return ret; } @@ -1693,8 +1712,16 @@ int sdw_disable_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); + if (stream->state != SDW_STREAM_ENABLED) { + pr_err("%s: %s: inconsistent state state %d\n", + __func__, stream->name, stream->state); + ret = -EINVAL; + goto state_err; + } + ret = _sdw_disable_stream(stream); +state_err: sdw_release_bus_lock(stream); return ret; } @@ -1749,8 +1776,18 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream) } sdw_acquire_bus_lock(stream); + + if (stream->state != SDW_STREAM_PREPARED && + stream->state != SDW_STREAM_DISABLED) { + pr_err("%s: %s: inconsistent state state %d\n", + __func__, stream->name, stream->state); + ret = -EINVAL; + goto state_err; + } + ret = _sdw_deprepare_stream(stream); +state_err: sdw_release_bus_lock(stream); return ret; } From bcf004f1b07286bc24ba15dab5ef6c3dcff146fd Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 20 Sep 2019 14:48:26 +0800 Subject: [PATCH 037/170] soundwire: stream: only prepare stream when it is configured. We don't need to prepare the stream again if the stream is already prepared. sdw_prepare_stream() could be called multiple times without calling sdw_deprepare_stream(). We call sdw_prepare_stream() in the prepare dai ops and sdw_deprepare_stream() in the hw_free dai ops. If an xrun happens, sdw_prepare_stream() will be called but sdw_deprepare_stream() will not, which results in an imbalance and an invalid total bandwidth. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/stream.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 6aa0b5d370c039..bd0bddf738302a 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1544,7 +1544,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) */ int sdw_prepare_stream(struct sdw_stream_runtime *stream) { - int ret = 0; + int ret; if (!stream) { pr_err("SoundWire: Handle not found for stream\n"); @@ -1553,6 +1553,11 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream) sdw_acquire_bus_lock(stream); + if (stream->state == SDW_STREAM_PREPARED) { + ret = 0; + goto state_err; + } + if (stream->state != SDW_STREAM_CONFIGURED && stream->state != SDW_STREAM_DEPREPARED && stream->state != SDW_STREAM_DISABLED) { From 917dfb95e233ad73bbd06d9b10e7ec1f4bad1ffe Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 15 Oct 2019 09:43:47 -0500 Subject: [PATCH 038/170] soundwire: stream: do not update parameters during DISABLED-PREPARED transition After a system suspend, the ALSA/ASoC core will invoke the .prepare() callback and a TRIGGER_START when INFO_RESUME is not supported. Likewise, when an underflow occurs, the .prepare callback will be invoked. In both cases, the stream can be in DISABLED mode, and will transition into the PREPARED mode. We however don't want the bus bandwidth to be recomputed. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/stream.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index bd0bddf738302a..c28ce7f0d7424f 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1460,7 +1460,8 @@ static void sdw_release_bus_lock(struct sdw_stream_runtime *stream) } } -static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) +static int _sdw_prepare_stream(struct sdw_stream_runtime *stream, + bool update_params) { struct sdw_master_runtime *m_rt; struct sdw_bus *bus = NULL; @@ -1480,6 +1481,9 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) return -EINVAL; } + if (!update_params) + goto program_params; + /* Increment cumulative bus bandwidth */ /* TODO: Update this during Device-Device support */ bus->params.bandwidth += m_rt->stream->params.rate * @@ -1495,6 +1499,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) } } +program_params: /* Program params */ ret = sdw_program_params(bus); if (ret < 0) { @@ -1544,6 +1549,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) */ int sdw_prepare_stream(struct sdw_stream_runtime *stream) { + bool update_params = true; int ret; if (!stream) { @@ -1567,7 +1573,16 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream) goto state_err; } - ret = _sdw_prepare_stream(stream); + /* + * when the stream is DISABLED, this means sdw_prepare_stream() + * is called as a result of an underflow or a resume operation. + * In this case, the bus parameters shall not be recomputed, but + * still need to be re-applied + */ + if (stream->state == SDW_STREAM_DISABLED) + update_params = false; + + ret = _sdw_prepare_stream(stream, update_params); state_err: sdw_release_bus_lock(stream); From f39c514d538a6c1a76207abc5795dfbdfa3809bf Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 14 Oct 2019 17:35:52 +0800 Subject: [PATCH 039/170] soundwire: intel: reinitialize IP+DSP in .prepare(), but only when resuming The .prepare() callback is invoked for normal streaming, underflows or during the system resume transition. In the latter case, the context for the ALH PDIs is lost, and the DSP is not initialized properly either, but the bus parameters don't need to be recomputed. Conversely, when doing a regular .prepare() during an underflow, the ALH/SHIM registers shall not be changed as the hardware cannot be reprogrammed after the DMA started (hardware spec requirement). This patch adds storage of PDI and hw_params in the DAI dma context, and the difference between the types of .prepare() usages is handled via a simple boolean, updated when suspending, and tested for in the .prepare() case. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.h | 4 ++ drivers/soundwire/intel.c | 69 +++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 0f108fd31fd943..cd4feef0b9001b 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -81,6 +81,8 @@ struct sdw_cdns_stream_config { * @bus: Bus handle * @stream_type: Stream type * @link_id: Master link id + * @hw_params: hw_params to be applied in .prepare step + * @suspended: status set when suspended, to be used in .prepare */ struct sdw_cdns_dma_data { char *name; @@ -89,6 +91,8 @@ struct sdw_cdns_dma_data { struct sdw_bus *bus; enum sdw_stream_type stream_type; int link_id; + struct snd_pcm_hw_params *hw_params; + bool suspended; }; /** diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index ae120102001d7b..be450e14d94756 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -814,6 +814,10 @@ static int intel_hw_params(struct snd_pcm_substream *substream, intel_pdi_alh_configure(sdw, pdi); sdw_cdns_config_stream(cdns, ch, dir, pdi); + /* store pdi and hw_params, may be needed in prepare step */ + dma->suspended = false; + dma->pdi = pdi; + dma->hw_params = params; /* Inform DSP about PDI stream number */ ret = intel_params_stream(sdw, substream, dai, params, @@ -857,7 +861,11 @@ static int intel_hw_params(struct snd_pcm_substream *substream, static int intel_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_cdns_dma_data *dma; + int ch, dir; + int ret; dma = snd_soc_dai_get_dma_data(dai, substream); if (!dma) { @@ -866,7 +874,42 @@ static int intel_prepare(struct snd_pcm_substream *substream, return -EIO; } - return sdw_prepare_stream(dma->stream); + if (dma->suspended) { + + dma->suspended = false; + + /* + * .prepare() is called after system resume, where we + * need to reinitialize the SHIM/ALH/Cadence IP. + * .prepare() is also called to deal with underflows, + * but in those cases we cannot touch ALH/SHIM + * registers + */ + + /* configure stream */ + ch = params_channels(dma->hw_params); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + dir = SDW_DATA_DIR_RX; + else + dir = SDW_DATA_DIR_TX; + + intel_pdi_shim_configure(sdw, dma->pdi); + intel_pdi_alh_configure(sdw, dma->pdi); + sdw_cdns_config_stream(cdns, ch, dir, dma->pdi); + + /* Inform DSP about PDI stream number */ + ret = intel_params_stream(sdw, substream, dai, + dma->hw_params, + sdw->instance, + dma->pdi->intel_alh_id); + if (ret) + goto err; + } + + ret = sdw_prepare_stream(dma->stream); + +err: + return ret; } static int intel_trigger(struct snd_pcm_substream *substream, int cmd, @@ -937,6 +980,8 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) return ret; } + dma->hw_params = NULL; + dma->pdi = NULL; sdw_release_stream(dma->stream); return 0; @@ -959,6 +1004,26 @@ static void intel_shutdown(struct snd_pcm_substream *substream, pm_runtime_put_autosuspend(cdns->dev); } +static int intel_dai_suspend(struct snd_soc_dai *dai) +{ + struct sdw_cdns_dma_data *dma; + + /* + * we don't have a .suspend dai_ops, and we don't have access + * to the substream, so let's mark both capture and playback + * DMA contexts as suspended + */ + dma = dai->playback_dma_data; + if (dma) + dma->suspended = true; + + dma = dai->capture_dma_data; + if (dma) + dma->suspended = true; + + return 0; +} + static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction) { @@ -1030,6 +1095,8 @@ static int intel_create_dai(struct sdw_cdns *cdns, dais[i].ops = &intel_pcm_dai_ops; else dais[i].ops = &intel_pdm_dai_ops; + + dais[i].suspend = intel_dai_suspend; } return 0; From f60d3e11e83e185072046cd39d000c1ab8526b66 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 24 Oct 2019 13:43:14 -0500 Subject: [PATCH 040/170] soundwire: intel: pm_runtime idle scheduling Add quirk and pm_runtime idle scheduling to let the Master suspend if no Slaves become attached. This can happen when a link is not marked as disabled and has devices exposed in the DSDT, if the power is controlled by sideband means or the link includes a pluggable connector. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index be450e14d94756..da29fe6e375272 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -30,8 +30,9 @@ * flags reused in each byte, with master0 using the ls-byte, etc. */ -#define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME BIT(0) -#define SDW_INTEL_MASTER_DISABLE_CLOCK_STOP BIT(1) +#define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME BIT(0) +#define SDW_INTEL_MASTER_DISABLE_CLOCK_STOP BIT(1) +#define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE BIT(2) static int md_flags; module_param_named(sdw_md_flags, md_flags, int, 0444); @@ -1330,6 +1331,22 @@ static int intel_master_startup(struct sdw_master_device *md) pm_runtime_enable(&md->dev); } + /* + * Slave devices have an pm_runtime of 'Unsupported' until + * they report as ATTACHED. If they don't, e.g. because there + * are no Slave devices populated or if the power-on is + * delayed or dependent on a power switch, the Master will + * remain active and prevent its parent from suspending. + * + * Conditionally force the pm_runtime core to re-evaluate the + * Master status in the absence of any Slave activity. A quirk + * is provided to e.g. deal with Slaves that may be powered on + * with a delay. A more complete solution would require the + * definition of Master properties. + */ + if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE)) + pm_runtime_idle(&md->dev); + return 0; err_interrupt: @@ -1443,6 +1460,7 @@ static int intel_resume(struct device *dev) struct sdw_master_device *md = to_sdw_master_device(dev); struct sdw_cdns *cdns = dev_get_drvdata(dev); struct sdw_intel *sdw = cdns_to_intel(cdns); + int link_flags; int ret; if (cdns->bus.prop.hw_disabled) { @@ -1463,6 +1481,10 @@ static int intel_resume(struct device *dev) pm_runtime_enable(dev); md->pm_runtime_suspended = false; + + link_flags = md_flags >> (sdw->cdns.bus.link_id * 8); + if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE)) + pm_runtime_idle(&md->dev); } ret = intel_init(sdw); From a57921cf4907af72a4f9735cbebda78958721c13 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Sat, 12 Oct 2019 12:32:57 -0500 Subject: [PATCH 041/170] [HACK] add traces to debug aplay suspend/resume issue Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao --- drivers/soundwire/intel.c | 52 +++++++++++++++++++++++++++++++++++--- drivers/soundwire/stream.c | 11 ++++++++ sound/soc/Makefile | 2 ++ sound/soc/sof/pcm.c | 7 +++++ sound/soc/sof/pm.c | 11 ++++++++ 5 files changed, 80 insertions(+), 3 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index da29fe6e375272..6e7e1be77dcd75 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -762,6 +762,8 @@ static int intel_startup(struct snd_pcm_substream *substream, struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); int ret; + dev_err(dai->dev, "%s: %s: start\n", __func__, dai->name); + ret = pm_runtime_get_sync(cdns->dev); if (ret < 0 && ret != -EACCES) { dev_err_ratelimited(cdns->dev, @@ -770,7 +772,11 @@ static int intel_startup(struct snd_pcm_substream *substream, pm_runtime_put_noidle(cdns->dev); } - return sdw_stream_setup(substream, dai); + ret = sdw_stream_setup(substream, dai); + + dev_err(dai->dev, "%s: %s: done\n", __func__, dai->name); + + return ret; } static int intel_hw_params(struct snd_pcm_substream *substream, @@ -787,6 +793,8 @@ static int intel_hw_params(struct snd_pcm_substream *substream, int ret; bool pcm = true; + dev_err(dai->dev, "%s: %s: start\n", __func__, dai->name); + dma = snd_soc_dai_get_dma_data(dai, substream); if (!dma) return -EIO; @@ -856,6 +864,8 @@ static int intel_hw_params(struct snd_pcm_substream *substream, kfree(pconfig); error: + + dev_err(dai->dev, "%s: %s: done\n", __func__, dai->name); return ret; } @@ -868,6 +878,8 @@ static int intel_prepare(struct snd_pcm_substream *substream, int ch, dir; int ret; + dev_err(dai->dev, "%s: %s: start\n", __func__, dai->name); + dma = snd_soc_dai_get_dma_data(dai, substream); if (!dma) { dev_err(dai->dev, "failed to get dma data in %s", @@ -909,6 +921,7 @@ static int intel_prepare(struct snd_pcm_substream *substream, ret = sdw_prepare_stream(dma->stream); + dev_err(dai->dev, "%s: %s: done\n", __func__, dai->name); err: return ret; } @@ -919,6 +932,8 @@ static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct sdw_cdns_dma_data *dma; int ret; + dev_err(dai->dev, "%s: %s: start\n", __func__, dai->name); + dma = snd_soc_dai_get_dma_data(dai, substream); if (!dma) { dev_err(dai->dev, "failed to get dma data in %s", __func__); @@ -926,14 +941,18 @@ static int intel_trigger(struct snd_pcm_substream *substream, int cmd, } switch (cmd) { + case SNDRV_PCM_TRIGGER_RESUME: + dev_err(dai->dev, "%s: %s: resume\n", __func__, dai->name); + /* fallthrough */ case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - case SNDRV_PCM_TRIGGER_RESUME: ret = sdw_enable_stream(dma->stream); break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: + dev_err(dai->dev, "%s: %s: suspend\n", __func__, dai->name); + /* fallthrough */ + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP: ret = sdw_disable_stream(dma->stream); break; @@ -947,6 +966,9 @@ static int intel_trigger(struct snd_pcm_substream *substream, int cmd, dev_err(dai->dev, "%s trigger %d failed: %d", __func__, cmd, ret); + + dev_err(dai->dev, "%s: %s: done\n", __func__, dai->name); + return ret; } @@ -958,6 +980,8 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) struct sdw_cdns_dma_data *dma; int ret; + dev_err(dai->dev, "%s: %s: start\n", __func__, dai->name); + dma = snd_soc_dai_get_dma_data(dai, substream); if (!dma) return -EIO; @@ -985,6 +1009,8 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) dma->pdi = NULL; sdw_release_stream(dma->stream); + dev_err(dai->dev, "%s: %s: done\n", __func__, dai->name); + return 0; } @@ -994,6 +1020,8 @@ static void intel_shutdown(struct snd_pcm_substream *substream, struct sdw_cdns_dma_data *dma; struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + dev_err(dai->dev, "%s: %s: start\n", __func__, dai->name); + dma = snd_soc_dai_get_dma_data(dai, substream); if (!dma) return; @@ -1003,6 +1031,8 @@ static void intel_shutdown(struct snd_pcm_substream *substream, pm_runtime_mark_last_busy(cdns->dev); pm_runtime_put_autosuspend(cdns->dev); + + dev_err(dai->dev, "%s: %s: done\n", __func__, dai->name); } static int intel_dai_suspend(struct snd_soc_dai *dai) @@ -1395,6 +1425,8 @@ static int intel_suspend(struct device *dev) return 0; } + dev_err(dev, "%s start\n", __func__); + if (pm_runtime_suspended(dev)) { dev_dbg(dev, "%s: pm_runtime status: suspended\n", @@ -1423,6 +1455,8 @@ static int intel_suspend(struct device *dev) intel_shim_wake(sdw, false); + dev_err(dev, "%s done\n", __func__); + return 0; } @@ -1438,6 +1472,8 @@ static int intel_suspend_runtime(struct device *dev) return 0; } + dev_err(dev, "%s start\n", __func__); + ret = sdw_cdns_enable_interrupt(cdns, false); if (ret < 0) { dev_err(dev, "cannot disable interrupts on suspend\n"); @@ -1452,6 +1488,8 @@ static int intel_suspend_runtime(struct device *dev) intel_shim_wake(sdw, false); + dev_err(dev, "%s done\n", __func__); + return 0; } @@ -1469,6 +1507,8 @@ static int intel_resume(struct device *dev) return 0; } + dev_err(dev, "%s start\n", __func__); + if (md->pm_runtime_suspended) { dev_dbg(dev, "%s: pm_runtime status was suspended, forcing active\n", @@ -1524,6 +1564,8 @@ static int intel_resume(struct device *dev) */ pm_runtime_mark_last_busy(cdns->dev); + dev_err(dev, "%s done\n", __func__); + return ret; } @@ -1539,6 +1581,8 @@ static int intel_resume_runtime(struct device *dev) return 0; } + dev_err(dev, "%s start\n", __func__); + ret = intel_init(sdw); if (ret) { dev_err(dev, "%s failed: %d", __func__, ret); @@ -1564,6 +1608,8 @@ static int intel_resume_runtime(struct device *dev) return ret; } + dev_err(dev, "%s done\n", __func__); + return ret; } diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index c28ce7f0d7424f..3e8ff16fed052b 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1586,6 +1586,9 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream) state_err: sdw_release_bus_lock(stream); + + pr_err("%s: %s: done\n", __func__, stream->name); + return ret; } EXPORT_SYMBOL(sdw_prepare_stream); @@ -1661,6 +1664,8 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream) state_err: sdw_release_bus_lock(stream); + + pr_err("%s: %s: done\n", __func__, stream->name); return ret; } EXPORT_SYMBOL(sdw_enable_stream); @@ -1743,6 +1748,9 @@ int sdw_disable_stream(struct sdw_stream_runtime *stream) state_err: sdw_release_bus_lock(stream); + + pr_err("%s: %s: done\n", __func__, stream->name); + return ret; } EXPORT_SYMBOL(sdw_disable_stream); @@ -1809,6 +1817,9 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream) state_err: sdw_release_bus_lock(stream); + + pr_err("%s: %s: done\n", __func__, stream->name); + return ret; } EXPORT_SYMBOL(sdw_deprepare_stream); diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 861a21b7948443..429add4afe3827 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,3 +1,5 @@ +ccflags-y += -DDEBUG + # SPDX-License-Identifier: GPL-2.0 snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-utils.o soc-dai.o soc-component.o snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 9bb6388742e1a3..d6f72352141db7 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -341,6 +341,9 @@ static int sof_pcm_trigger(struct snd_soc_component *component, return 0; } + dev_dbg(sdev->dev, "pcm: trigger resume stream %d dir %d cmd %d\n", + spcm->pcm.pcm_id, substream->stream, cmd); + /* set up hw_params */ ret = sof_pcm_prepare(component, substream); if (ret < 0) { @@ -374,6 +377,10 @@ static int sof_pcm_trigger(struct snd_soc_component *component, spcm->stream[substream->stream].suspend_ignored = true; return 0; } + + dev_dbg(sdev->dev, "pcm: trigger suspend stream %d dir %d cmd %d\n", + spcm->pcm.pcm_id, substream->stream, cmd); + /* fallthrough */ case SNDRV_PCM_TRIGGER_STOP: stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 84290bbeebddb0..76c301a5ecf374 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -52,6 +52,8 @@ static int sof_resume(struct device *dev, bool runtime_resume) struct snd_sof_dev *sdev = dev_get_drvdata(dev); int ret; + dev_err(dev, "%s\n", __func__); + /* do nothing if dsp resume callbacks are not set */ if (!sof_ops(sdev)->resume || !sof_ops(sdev)->runtime_resume) return 0; @@ -131,6 +133,8 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) struct snd_sof_dev *sdev = dev_get_drvdata(dev); int ret; + dev_err(dev, "%s\n", __func__); + /* do nothing if dsp suspend callback is not set */ if (!sof_ops(sdev)->suspend) return 0; @@ -199,6 +203,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) int snd_sof_runtime_suspend(struct device *dev) { + dev_err(dev, "%s\n", __func__); return sof_suspend(dev, true); } EXPORT_SYMBOL(snd_sof_runtime_suspend); @@ -207,12 +212,14 @@ int snd_sof_runtime_idle(struct device *dev) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); + dev_err(dev, "%s\n", __func__); return snd_sof_dsp_runtime_idle(sdev); } EXPORT_SYMBOL(snd_sof_runtime_idle); int snd_sof_runtime_resume(struct device *dev) { + dev_err(dev, "%s\n", __func__); return sof_resume(dev, true); } EXPORT_SYMBOL(snd_sof_runtime_resume); @@ -274,6 +281,8 @@ int snd_sof_resume(struct device *dev) struct snd_sof_dev *sdev = dev_get_drvdata(dev); int ret; + dev_err(dev, "%s\n", __func__); + if (snd_sof_dsp_d0i3_on_suspend(sdev)) { /* resume from D0I3 */ dev_dbg(sdev->dev, "DSP will exit from D0i3...\n"); @@ -303,6 +312,8 @@ int snd_sof_suspend(struct device *dev) struct snd_sof_dev *sdev = dev_get_drvdata(dev); int ret; + dev_err(dev, "%s\n", __func__); + if (snd_sof_dsp_d0i3_on_suspend(sdev)) { /* suspend to D0i3 */ dev_dbg(sdev->dev, "DSP is trying to enter D0i3...\n"); From 329520dc5edb9e2c13e0622ce0cfc2da8359bca8 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 21 Nov 2019 17:01:15 -0600 Subject: [PATCH 042/170] pm: add more traces Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/pm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 76c301a5ecf374..aa0b3ad957127f 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -17,6 +17,8 @@ static int sof_send_pm_ctx_ipc(struct snd_sof_dev *sdev, int cmd) struct sof_ipc_pm_ctx pm_ctx; struct sof_ipc_reply reply; + dev_err(sdev->dev, "%s\n", __func__); + memset(&pm_ctx, 0, sizeof(pm_ctx)); /* configure ctx save ipc message */ From 617cbf4da3e686424d81c7f91aa8bd9052ca1b47 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Mon, 2 Dec 2019 13:05:46 +0800 Subject: [PATCH 043/170] soundwire: cadence_master: remove config update for interrupt setting Config only needs to be updated when setting MCP_Config, MCP_Control and MCP_CmdCtrl to make these register setting effective. When updating config in master, master will communicate with slave to update status. Communication will be failed when masters and slaves are in clock stop state, and this unnecessary config update makes interrupt setting failed. Tested on Comet Lake with soundwire enabled Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 362fb6e49bfe09..25002ff6766bfb 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -812,7 +812,7 @@ int sdw_cdns_exit_reset(struct sdw_cdns *cdns) EXPORT_SYMBOL(sdw_cdns_exit_reset); /** - * sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config + * sdw_cdns_enable_interrupt() - Enable SDW interrupts * @cdns: Cadence instance */ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state) @@ -853,8 +853,7 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state) cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, slave_intmask1); cdns_writel(cdns, CDNS_MCP_INTMASK, mask); - /* commit changes */ - return cdns_update_config(cdns); + return 0; } EXPORT_SYMBOL(sdw_cdns_enable_interrupt); From 24b6f3b76b14007eff7d1ddcf40f9d43786e1403 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 2 Dec 2019 16:05:13 -0600 Subject: [PATCH 044/170] soundwire: intel: add mutex to prevent concurrent access to SHIM registers Some of the SHIM registers exposed fields that are link specific, and in addition some of the power-related registers (SPA/CPA) take time to be updated. Uncontrolled access leads to timeouts or errors. Add a mutex at the controller level, shared by all links, so that all accesses to such registers are serialized, and follow a pattern of read-modify-write. GitHub issue: https://github.com/thesofproject/linux/issues/1555 Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 37 ++++++++++++++++++++++++++++------ drivers/soundwire/intel.h | 2 ++ drivers/soundwire/intel_init.c | 3 +++ 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 6e7e1be77dcd75..711526dfd018fe 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -308,6 +308,8 @@ static int intel_link_power_up(struct sdw_intel *sdw) int spa_mask, cpa_mask; int link_control, ret; + mutex_lock(sdw->link_res->shim_lock); + /* Link power up sequence */ link_control = intel_readl(shim, SDW_SHIM_LCTL); spa_mask = (SDW_SHIM_LCTL_SPA << link_id); @@ -315,6 +317,8 @@ static int intel_link_power_up(struct sdw_intel *sdw) link_control |= spa_mask; ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); + mutex_unlock(sdw->link_res->shim_lock); + if (ret < 0) return ret; @@ -329,6 +333,8 @@ static int intel_shim_init(struct sdw_intel *sdw) int sync_reg, ret; u16 ioctl = 0, act = 0; + mutex_lock(sdw->link_res->shim_lock); + /* Initialize Shim */ ioctl |= SDW_SHIM_IOCTL_BKE; intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); @@ -373,6 +379,8 @@ static int intel_shim_init(struct sdw_intel *sdw) sync_reg |= SDW_SHIM_SYNC_SYNCCPU; ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg, SDW_SHIM_SYNC_SYNCCPU); + mutex_unlock(sdw->link_res->shim_lock); + if (ret < 0) dev_err(sdw->cdns.dev, "Failed to set sync period: %d\n", ret); @@ -385,13 +393,15 @@ static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) unsigned int link_id = sdw->instance; u16 wake_en, wake_sts; + mutex_lock(sdw->link_res->shim_lock); + wake_en = intel_readw(shim, SDW_SHIM_WAKEEN); + if (wake_enable) { /* Enable the wakeup */ - intel_writew(shim, SDW_SHIM_WAKEEN, - (SDW_SHIM_WAKEEN_ENABLE << link_id)); + wake_en |= (SDW_SHIM_WAKEEN_ENABLE << link_id); + intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); } else { /* Disable the wake up interrupt */ - wake_en = intel_readw(shim, SDW_SHIM_WAKEEN); wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id); intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); @@ -400,6 +410,7 @@ static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) wake_sts |= (SDW_SHIM_WAKEEN_ENABLE << link_id); intel_writew(shim, SDW_SHIM_WAKESTS_STATUS, wake_sts); } + mutex_unlock(sdw->link_res->shim_lock); } static int intel_link_power_down(struct sdw_intel *sdw) @@ -409,6 +420,8 @@ static int intel_link_power_down(struct sdw_intel *sdw) void __iomem *shim = sdw->link_res->shim; u16 ioctl; + mutex_lock(sdw->link_res->shim_lock); + /* Glue logic */ ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); ioctl |= SDW_SHIM_IOCTL_BKE; @@ -425,6 +438,8 @@ static int intel_link_power_down(struct sdw_intel *sdw) link_control &= spa_mask; ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); + mutex_unlock(sdw->link_res->shim_lock); + if (ret < 0) return ret; @@ -652,11 +667,15 @@ static int intel_pre_bank_switch(struct sdw_bus *bus) if (!bus->multi_link) return 0; + mutex_lock(sdw->link_res->shim_lock); + /* Read SYNC register */ sync_reg = intel_readl(shim, SDW_SHIM_SYNC); sync_reg |= SDW_SHIM_SYNC_CMDSYNC << sdw->instance; intel_writel(shim, SDW_SHIM_SYNC, sync_reg); + mutex_unlock(sdw->link_res->shim_lock); + return 0; } @@ -671,6 +690,8 @@ static int intel_post_bank_switch(struct sdw_bus *bus) if (!bus->multi_link) return 0; + mutex_lock(sdw->link_res->shim_lock); + /* Read SYNC register */ sync_reg = intel_readl(shim, SDW_SHIM_SYNC); @@ -682,9 +703,10 @@ static int intel_post_bank_switch(struct sdw_bus *bus) * * So, set the SYNCGO bit only if CMDSYNC bit is set for any Master. */ - if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK)) - return 0; - + if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK)) { + ret = 0; + goto unlock; + } /* * Set SyncGO bit to synchronously trigger a bank switch for * all the masters. A write to SYNCGO bit clears CMDSYNC bit for all @@ -694,6 +716,9 @@ static int intel_post_bank_switch(struct sdw_bus *bus) ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg, SDW_SHIM_SYNC_SYNCGO); +unlock: + mutex_unlock(sdw->link_res->shim_lock); + if (ret < 0) dev_err(sdw->cdns.dev, "Post bank switch failed: %d\n", ret); diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index 2ae4fa7486861d..1d8740b452c6e0 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -15,6 +15,7 @@ * @irq: Interrupt line * @ops: Shim callback ops * @dev: device implementing hw_params and free callbacks + * @shim_lock: mutex to handle access to shared SHIM registers */ struct sdw_intel_link_res { struct sdw_master_device *md; @@ -27,6 +28,7 @@ struct sdw_intel_link_res { struct device *dev; struct sdw_cdns *cdns; struct list_head list; + struct mutex *shim_lock; /* protect shared registers */ }; #define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1) diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 82aeb350c9bc15..018e4e23dd0509 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -212,6 +212,7 @@ static struct sdw_intel_ctx ctx->mmio_base = res->mmio_base; ctx->link_mask = res->link_mask; ctx->handle = res->handle; + mutex_init(&ctx->shim_lock); link = ctx->links; link_mask = ctx->link_mask; @@ -299,6 +300,8 @@ sdw_intel_startup_controller(struct sdw_intel_ctx *ctx) if (link_mask && !(link_mask & BIT(i))) continue; + link->shim_lock = &ctx->shim_lock; + md = link->md; md->driver->startup(md); From 81ff8b0b44bdacde899ff14e738073bf4b3ed730 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 3 Dec 2019 17:02:10 -0600 Subject: [PATCH 045/170] soundwire: intel: add CLK_STOP_TEARDOWN for pm_runtime suspend Now that we have options, add support for TEARDOWN mode (same functionality as existing code) All other modes will be added in follow-up patches. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 82 ++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 32 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 711526dfd018fe..9795ba3caab180 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1489,6 +1489,7 @@ static int intel_suspend_runtime(struct device *dev) { struct sdw_cdns *cdns = dev_get_drvdata(dev); struct sdw_intel *sdw = cdns_to_intel(cdns); + u32 clock_stop_quirks; int ret; if (cdns->bus.prop.hw_disabled) { @@ -1499,23 +1500,31 @@ static int intel_suspend_runtime(struct device *dev) dev_err(dev, "%s start\n", __func__); - ret = sdw_cdns_enable_interrupt(cdns, false); - if (ret < 0) { - dev_err(dev, "cannot disable interrupts on suspend\n"); - return ret; - } + clock_stop_quirks = sdw->link_res->clock_stop_quirks; - ret = intel_link_power_down(sdw); - if (ret) { - dev_err(dev, "Link power down failed: %d", ret); - return ret; - } + if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) { + ret = sdw_cdns_enable_interrupt(cdns, false); + if (ret < 0) { + dev_err(dev, "cannot disable interrupts on suspend\n"); + return ret; + } - intel_shim_wake(sdw, false); + ret = intel_link_power_down(sdw); + if (ret) { + dev_err(dev, "Link power down failed: %d", ret); + return ret; + } + + intel_shim_wake(sdw, false); + } else { + dev_err(dev, "%s clock_stop_quirks %x unsupported\n", + __func__, clock_stop_quirks); + ret = -EINVAL; + } dev_err(dev, "%s done\n", __func__); - return 0; + return ret; } static int intel_resume(struct device *dev) @@ -1598,6 +1607,7 @@ static int intel_resume_runtime(struct device *dev) { struct sdw_cdns *cdns = dev_get_drvdata(dev); struct sdw_intel *sdw = cdns_to_intel(cdns); + u32 clock_stop_quirks; int ret; if (cdns->bus.prop.hw_disabled) { @@ -1608,29 +1618,37 @@ static int intel_resume_runtime(struct device *dev) dev_err(dev, "%s start\n", __func__); - ret = intel_init(sdw); - if (ret) { - dev_err(dev, "%s failed: %d", __func__, ret); - return ret; - } + clock_stop_quirks = sdw->link_res->clock_stop_quirks; - /* - * make sure all Slaves are tagged as UNATTACHED and provide - * reason for reinitialization - */ - sdw_clear_slave_status(&sdw->cdns.bus, - SDW_UNATTACH_REQUEST_MASTER_RESET); + if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) { + ret = intel_init(sdw); + if (ret) { + dev_err(dev, "%s failed: %d", __func__, ret); + return ret; + } - ret = sdw_cdns_enable_interrupt(cdns, true); - if (ret < 0) { - dev_err(dev, "cannot enable interrupts during resume\n"); - return ret; - } + /* + * make sure all Slaves are tagged as UNATTACHED and + * provide reason for reinitialization + */ + sdw_clear_slave_status(&sdw->cdns.bus, + SDW_UNATTACH_REQUEST_MASTER_RESET); - ret = sdw_cdns_exit_reset(cdns); - if (ret < 0) { - dev_err(dev, "unable to exit bus reset sequence during resume\n"); - return ret; + ret = sdw_cdns_enable_interrupt(cdns, true); + if (ret < 0) { + dev_err(dev, "cannot enable interrupts during resume\n"); + return ret; + } + + ret = sdw_cdns_exit_reset(cdns); + if (ret < 0) { + dev_err(dev, "unable to exit bus reset sequence during resume\n"); + return ret; + } + } else { + dev_err(dev, "%s clock_stop_quirks %x unsupported\n", + __func__, clock_stop_quirks); + ret = -EINVAL; } dev_err(dev, "%s done\n", __func__); From c38822b00f6704e05abd16bbc9c8690d9a3e85f4 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Fri, 8 Nov 2019 16:30:56 +0800 Subject: [PATCH 046/170] soundwire: bus: add clock stop helpers SoundWire supports two clock stop modes. Add support to handle the clock stop modes and add pm_runtime calls in the bus. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 335 ++++++++++++++++++++++++++++++++++ include/linux/soundwire/sdw.h | 24 +++ 2 files changed, 359 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 4f2128b6746a01..16f4458f302ac1 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -2,6 +2,7 @@ // Copyright(c) 2015-17 Intel Corporation. #include +#include #include #include #include @@ -359,6 +360,52 @@ static int sdw_write_no_pm(struct sdw_slave *slave, u32 addr, u8 value) return sdw_nwrite_no_pm(slave, addr, 1, &value); } +static int +sdw_bread_no_pm(struct sdw_bus *bus, u16 dev_num, u32 addr) +{ + struct sdw_msg msg; + u8 buf; + int ret; + + ret = sdw_fill_msg(&msg, NULL, addr, 1, dev_num, + SDW_MSG_FLAG_READ, &buf); + if (ret) + return ret; + + ret = sdw_transfer(bus, &msg); + if (ret < 0) + return ret; + else + return buf; +} + +static int +sdw_bwrite_no_pm(struct sdw_bus *bus, u16 dev_num, u32 addr, u8 value) +{ + struct sdw_msg msg; + int ret; + + ret = sdw_fill_msg(&msg, NULL, addr, 1, dev_num, + SDW_MSG_FLAG_WRITE, &value); + if (ret) + return ret; + + return sdw_transfer(bus, &msg); +} + +static int +sdw_read_no_pm(struct sdw_slave *slave, u32 addr) +{ + u8 buf; + int ret; + + ret = sdw_nread_no_pm(slave, addr, 1, &buf); + if (ret < 0) + return ret; + else + return buf; +} + /** * sdw_nread() - Read "n" contiguous SDW Slave registers * @slave: SDW Slave @@ -664,6 +711,294 @@ static void sdw_modify_slave_status(struct sdw_slave *slave, mutex_unlock(&slave->bus->bus_lock); } +static enum sdw_clk_stop_mode sdw_get_clk_stop_mode(struct sdw_slave *slave) +{ + enum sdw_clk_stop_mode mode; + + /* + * Query for clock stop mode if Slave implements + * ops->get_clk_stop_mode, else read from property. + */ + if (slave->ops && slave->ops->get_clk_stop_mode) { + mode = slave->ops->get_clk_stop_mode(slave); + } else { + if (slave->prop.clk_stop_mode1) + mode = SDW_CLK_STOP_MODE1; + else + mode = SDW_CLK_STOP_MODE0; + } + + return mode; +} + +static int sdw_slave_clk_stop_callback(struct sdw_slave *slave, + enum sdw_clk_stop_mode mode, + enum sdw_clk_stop_type type) +{ + int ret; + + if (slave->ops && slave->ops->clk_stop) { + ret = slave->ops->clk_stop(slave, mode, type); + if (ret < 0) { + dev_err(&slave->dev, + "Clk Stop type =%d failed: %d\n", type, ret); + return ret; + } + } + + return 0; +} + +static int sdw_slave_clk_stop_prepare(struct sdw_slave *slave, + enum sdw_clk_stop_mode mode, + bool prepare) +{ + bool wake_en; + u32 val = 0; + int ret; + + wake_en = slave->prop.wake_capable; + + if (prepare) { + val = SDW_SCP_SYSTEMCTRL_CLK_STP_PREP; + + if (mode == SDW_CLK_STOP_MODE1) + val |= SDW_SCP_SYSTEMCTRL_CLK_STP_MODE1; + + if (wake_en) + val |= SDW_SCP_SYSTEMCTRL_WAKE_UP_EN; + } else { + val = sdw_read_no_pm(slave, SDW_SCP_SYSTEMCTRL); + + val &= ~(SDW_SCP_SYSTEMCTRL_CLK_STP_PREP); + } + + ret = sdw_write_no_pm(slave, SDW_SCP_SYSTEMCTRL, val); + + if (ret != 0) + dev_err(&slave->dev, + "Clock Stop prepare failed for slave: %d", ret); + + return ret; +} + +static int sdw_bus_wait_for_clk_prep_deprep(struct sdw_bus *bus, u16 dev_num) +{ + int retry = bus->clk_stop_timeout; + int val; + + do { + val = sdw_bread_no_pm(bus, dev_num, SDW_SCP_STAT) & + SDW_SCP_STAT_CLK_STP_NF; + if (!val) + break; + + usleep_range(1000, 1500); + retry--; + } while (retry); + + if (retry && !val) { + dev_info(bus->dev, "clock stop prep/de-prep done slave:%d", + dev_num); + return 0; + } + + dev_err(bus->dev, "clock stop prep/de-prep failed slave:%d", + dev_num); + + return -ETIMEDOUT; +} + +/** + * sdw_bus_prep_clk_stop: prepare Slave(s) for clock stop + * + * @bus: SDW bus instance + * + * Query Slave for clock stop mode and prepare for that mode. + */ +int sdw_bus_prep_clk_stop(struct sdw_bus *bus) +{ + enum sdw_clk_stop_mode slave_mode; + bool simple_clk_stop = true; + struct sdw_slave *slave; + bool is_slave = false; + int ret = 0; + + /* + * In order to save on transition time, prepare + * each Slave and then wait for all Slave(s) to be + * prepared for clock stop. + */ + list_for_each_entry(slave, &bus->slaves, node) { + if (!slave->dev_num) + continue; + + /* Identify if Slave(s) are available on Bus */ + is_slave = true; + + if (slave->status != SDW_SLAVE_ATTACHED && + slave->status != SDW_SLAVE_ALERT) + continue; + + slave_mode = sdw_get_clk_stop_mode(slave); + slave->curr_clk_stop_mode = slave_mode; + + ret = sdw_slave_clk_stop_callback(slave, slave_mode, + SDW_CLK_PRE_PREPARE); + if (ret < 0) { + dev_err(&slave->dev, + "pre-prepare failed:%d", ret); + return ret; + } + + ret = sdw_slave_clk_stop_prepare(slave, + slave_mode, true); + if (ret < 0) { + dev_err(&slave->dev, + "pre-prepare failed:%d", ret); + return ret; + } + + if (slave_mode == SDW_CLK_STOP_MODE1) + simple_clk_stop = false; + } + + if (is_slave && !simple_clk_stop) { + ret = sdw_bus_wait_for_clk_prep_deprep(bus, + SDW_BROADCAST_DEV_NUM); + if (ret < 0) + return ret; + } + + /* Inform slaves that prep is done */ + list_for_each_entry(slave, &bus->slaves, node) { + if (!slave->dev_num) + continue; + + if (slave->status != SDW_SLAVE_ATTACHED && + slave->status != SDW_SLAVE_ALERT) + continue; + + slave_mode = slave->curr_clk_stop_mode; + + if (slave_mode == SDW_CLK_STOP_MODE1) { + ret = sdw_slave_clk_stop_callback(slave, + slave_mode, + SDW_CLK_POST_PREPARE); + + if (ret < 0) { + dev_err(&slave->dev, + "post-prepare failed:%d", ret); + } + } + } + + return ret; +} +EXPORT_SYMBOL(sdw_bus_prep_clk_stop); + +/** + * sdw_bus_clk_stop: stop bus clock + * + * @bus: SDW bus instance + * + * After preparing the Slaves for clock stop, stop the clock by broadcasting + * write to SCP_CTRL register. + */ +int sdw_bus_clk_stop(struct sdw_bus *bus) +{ + int ret; + + /* + * broadcast clock stop now, attached Slaves will ACK this, + * unattached will ignore + */ + ret = sdw_bwrite_no_pm(bus, SDW_BROADCAST_DEV_NUM, + SDW_SCP_CTRL, SDW_SCP_CTRL_CLK_STP_NOW); + if (ret < 0) { + dev_err(bus->dev, + "ClockStopNow Broadcast message failed %d", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(sdw_bus_clk_stop); + +/** + * sdw_bus_exit_clk_stop: Exit clock stop mode + * + * @bus: SDW bus instance + * + * This De-prepares the Slaves by exiting Clock Stop Mode 0. For the Slaves + * exiting Clock Stop Mode 1, they will be de-prepared after they enumerate + * back. + */ +int sdw_bus_exit_clk_stop(struct sdw_bus *bus) +{ + enum sdw_clk_stop_mode mode; + bool simple_clk_stop = true; + struct sdw_slave *slave; + bool is_slave = false; + int ret; + + /* + * In order to save on transition time, de-prepare + * each Slave and then wait for all Slave(s) to be + * de-prepared after clock resume. + */ + list_for_each_entry(slave, &bus->slaves, node) { + if (!slave->dev_num) + continue; + + /* Identify if Slave(s) are available on Bus */ + is_slave = true; + + if (slave->status != SDW_SLAVE_ATTACHED && + slave->status != SDW_SLAVE_ALERT) + continue; + + mode = slave->curr_clk_stop_mode; + + if (mode == SDW_CLK_STOP_MODE1) { + simple_clk_stop = false; + continue; + } + + ret = sdw_slave_clk_stop_callback(slave, mode, + SDW_CLK_PRE_DEPREPARE); + if (ret < 0) + dev_warn(&slave->dev, + "clk stop deprep failed:%d", ret); + + ret = sdw_slave_clk_stop_prepare(slave, mode, + false); + + if (ret < 0) + dev_warn(&slave->dev, + "clk stop deprep failed:%d", ret); + } + + if (is_slave && !simple_clk_stop) + sdw_bus_wait_for_clk_prep_deprep(bus, SDW_BROADCAST_DEV_NUM); + + list_for_each_entry(slave, &bus->slaves, node) { + if (!slave->dev_num) + continue; + + if (slave->status != SDW_SLAVE_ATTACHED && + slave->status != SDW_SLAVE_ALERT) + continue; + + mode = slave->curr_clk_stop_mode; + sdw_slave_clk_stop_callback(slave, mode, + SDW_CLK_POST_DEPREPARE); + } + + return 0; +} +EXPORT_SYMBOL(sdw_bus_exit_clk_stop); + int sdw_configure_dpn_intr(struct sdw_slave *slave, int port, bool enable, int mask) { diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 1a765b10b8bbaa..1d3a1eca66d110 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -79,6 +79,21 @@ enum sdw_slave_status { SDW_SLAVE_RESERVED = 3, }; +/** + * enum sdw_clk_stop_type: clock stop operations + * + * @SDW_CLK_PRE_PREPARE: pre clock stop prepare + * @SDW_CLK_POST_PREPARE: post clock stop prepare + * @SDW_CLK_PRE_DEPREPARE: pre clock stop de-prepare + * @SDW_CLK_POST_DEPREPARE: post clock stop de-prepare + */ +enum sdw_clk_stop_type { + SDW_CLK_PRE_PREPARE = 0, + SDW_CLK_POST_PREPARE, + SDW_CLK_PRE_DEPREPARE, + SDW_CLK_POST_DEPREPARE, +}; + /** * enum sdw_command_response - Command response as defined by SDW spec * @SDW_CMD_OK: cmd was successful @@ -533,6 +548,11 @@ struct sdw_slave_ops { int (*port_prep)(struct sdw_slave *slave, struct sdw_prepare_ch *prepare_ch, enum sdw_port_prep_ops pre_ops); + int (*get_clk_stop_mode)(struct sdw_slave *slave); + int (*clk_stop)(struct sdw_slave *slave, + enum sdw_clk_stop_mode mode, + enum sdw_clk_stop_type type); + }; /** @@ -574,6 +594,7 @@ struct sdw_slave { #endif struct list_head node; struct completion *port_ready; + enum sdw_clk_stop_mode curr_clk_stop_mode; u16 dev_num; bool probed; struct completion probe_complete; @@ -931,6 +952,9 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream); int sdw_enable_stream(struct sdw_stream_runtime *stream); int sdw_disable_stream(struct sdw_stream_runtime *stream); int sdw_deprepare_stream(struct sdw_stream_runtime *stream); +int sdw_bus_prep_clk_stop(struct sdw_bus *bus); +int sdw_bus_clk_stop(struct sdw_bus *bus); +int sdw_bus_exit_clk_stop(struct sdw_bus *bus); /* messaging and data APIs */ From b10cfa1ac9263a7988e947f0b737499483880bb3 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Thu, 5 Dec 2019 11:57:09 -0600 Subject: [PATCH 047/170] soundwire: cadence_master: simplifiy cdns_init() There is no need for the clock_stop_exit argument with the latest implementation Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.c | 12 +----------- drivers/soundwire/cadence_master.h | 2 +- drivers/soundwire/intel.c | 2 +- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 25002ff6766bfb..caf5b7edb83714 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -978,22 +978,12 @@ static u32 cdns_set_initial_frame_shape(int n_rows, int n_cols) * sdw_cdns_init() - Cadence initialization * @cdns: Cadence instance */ -int sdw_cdns_init(struct sdw_cdns *cdns, bool clock_stop_exit) +int sdw_cdns_init(struct sdw_cdns *cdns) { struct sdw_bus *bus = &cdns->bus; struct sdw_master_prop *prop = &bus->prop; u32 val; int divider; - int ret; - - if (clock_stop_exit) { - ret = cdns_clear_bit(cdns, CDNS_MCP_CONTROL, - CDNS_MCP_CONTROL_CLK_STOP_CLR); - if (ret < 0) { - dev_err(cdns->dev, "Couldn't exit from clock stop\n"); - return ret; - } - } /* Set clock divider */ divider = (prop->mclk_freq / prop->max_clk_freq) - 1; diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index cd4feef0b9001b..a7c0cbbbd09657 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -146,7 +146,7 @@ extern struct sdw_master_ops sdw_cdns_master_ops; irqreturn_t sdw_cdns_irq(int irq, void *dev_id); irqreturn_t sdw_cdns_thread(int irq, void *dev_id); -int sdw_cdns_init(struct sdw_cdns *cdns, bool clock_stop_exit); +int sdw_cdns_init(struct sdw_cdns *cdns); int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config); int sdw_cdns_exit_reset(struct sdw_cdns *cdns); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 9795ba3caab180..f44ebbc99848dc 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1274,7 +1274,7 @@ static int intel_init(struct sdw_intel *sdw) intel_link_power_up(sdw); intel_shim_init(sdw); - return sdw_cdns_init(&sdw->cdns, false); + return sdw_cdns_init(&sdw->cdns); } /* From 20d367410bf255a92c16d1f28bb86c61edda4f0e Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Thu, 5 Dec 2019 12:11:52 -0600 Subject: [PATCH 048/170] soundwire: cadence_master: add clock_stop/restart routines Add support for clock stop and restart, with two configuration parameters: 1) when entering the ClockStop mode, Slave-initiated wakes can be prevented. 2) When exiting the ClockStop mode, the caller can request a Bus Reset (either if all Slaves were configured in ClockStopMode1 or the Master IP lost context and enumeration is required) Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.c | 158 ++++++++++++++++++++++++++++- drivers/soundwire/cadence_master.h | 3 + 2 files changed, 160 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index caf5b7edb83714..3f5cb76f535e23 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -223,12 +223,30 @@ static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value) return 0; timeout--; - udelay(50); + usleep_range(50, 100); } while (timeout != 0); return -EAGAIN; } +static int cdns_set_wait(struct sdw_cdns *cdns, int offset, u32 mask, u32 value) +{ + int timeout = 10; + u32 reg_read; + + /* Wait for bit to be set */ + do { + reg_read = readl(cdns->registers + offset); + if ((reg_read & mask) == value) + return 0; + + timeout--; + usleep_range(50, 100); + } while (timeout != 0); + + return -ETIMEDOUT; +} + /* * all changes to the MCP_CONFIG, MCP_CONTROL, MCP_CMDCTRL and MCP_PHYCTRL * need to be confirmed with a write to MCP_CONFIG_UPDATE @@ -1167,6 +1185,144 @@ static const struct sdw_master_port_ops cdns_port_ops = { .dpn_port_enable_ch = cdns_port_enable, }; +/** + * sdw_cdns_clock_stop: Cadence clock stop configuration routine + * + * @cdns: Cadence instance + * @block_wake: prevent wakes if required by the platform + */ +int sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake) +{ + bool slave_attached = false; + struct sdw_slave *slave; + u32 status; + int ret; + + /* Check suspend status */ + status = cdns_readl(cdns, CDNS_MCP_STAT); + if (status & CDNS_MCP_STAT_CLK_STOP) { + dev_dbg(cdns->dev, "Clock is already stopped\n"); + return 1; + } + + /* + * For specific platforms, it is required to be able to put + * master into a state in which it ignores wake-up trials + * in clock stop state + */ + if (block_wake) + cdns_updatel(cdns, CDNS_MCP_CONTROL, + CDNS_MCP_CONTROL_BLOCK_WAKEUP, + CDNS_MCP_CONTROL_BLOCK_WAKEUP); + + list_for_each_entry(slave, &cdns->bus.slaves, node) { + if (slave->status == SDW_SLAVE_ATTACHED || + slave->status == SDW_SLAVE_ALERT) { + slave_attached = true; + break; + } + } + + /* + * This CMD_ACCEPT should be used when there are no devices + * attached on the link when entering clock stop mode. If this is + * not set and there is a broadcast write then the command ignored + * will be treated as a failure + */ + if (!slave_attached) + cdns_updatel(cdns, CDNS_MCP_CONTROL, + CDNS_MCP_CONTROL_CMD_ACCEPT, 1); + else + cdns_updatel(cdns, CDNS_MCP_CONTROL, + CDNS_MCP_CONTROL_CMD_ACCEPT, 0); + + /* commit changes */ + cdns_update_config(cdns); + + /* Prepare slaves for clock stop */ + ret = sdw_bus_prep_clk_stop(&cdns->bus); + if (ret < 0) { + dev_err(cdns->dev, "prepare clock stop failed %d", ret); + return ret; + } + + /* Enter clock stop */ + ret = sdw_bus_clk_stop(&cdns->bus); + if (ret < 0) { + dev_err(cdns->dev, "bus clock stop failed %d", ret); + return ret; + } + + ret = cdns_set_wait(cdns, CDNS_MCP_STAT, + CDNS_MCP_STAT_CLK_STOP, + CDNS_MCP_STAT_CLK_STOP); + if (ret < 0) + dev_err(cdns->dev, "Clock stop failed %d\n", ret); + + return ret; +} +EXPORT_SYMBOL(sdw_cdns_clock_stop); + +/** + * sdw_cdns_clock_restart: Cadence PM clock restart configuration routine + * + * @cdns: Cadence instance + * @bus_reset: context may be lost while in low power modes and the bus + * may require a Severe Reset and re-enumeration after a wake. + */ +int sdw_cdns_clock_restart(struct sdw_cdns *cdns, bool bus_reset) +{ + int ret; + + ret = cdns_clear_bit(cdns, CDNS_MCP_CONTROL, + CDNS_MCP_CONTROL_CLK_STOP_CLR); + if (ret < 0) { + dev_err(cdns->dev, "Couldn't exit from clock stop\n"); + return ret; + } + + ret = cdns_set_wait(cdns, CDNS_MCP_STAT, CDNS_MCP_STAT_CLK_STOP, 0); + if (ret < 0) { + dev_err(cdns->dev, "clock stop exit failed %d\n", ret); + return ret; + } + + cdns_updatel(cdns, CDNS_MCP_CONTROL, + CDNS_MCP_CONTROL_BLOCK_WAKEUP, 0); + + /* + * clear CMD_ACCEPT so that the command ignored + * will be treated as a failure during a broadcast write + */ + cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT, 0); + + if (bus_reset) { + /* program maximum length reset to be safe */ + cdns_updatel(cdns, CDNS_MCP_CONTROL, + CDNS_MCP_CONTROL_RST_DELAY, + CDNS_MCP_CONTROL_RST_DELAY); + + /* use hardware generated reset */ + cdns_updatel(cdns, CDNS_MCP_CONTROL, + CDNS_MCP_CONTROL_HW_RST, + CDNS_MCP_CONTROL_HW_RST); + } + + /* enable bus operations with clock and data */ + cdns_updatel(cdns, CDNS_MCP_CONFIG, + CDNS_MCP_CONFIG_OP, + CDNS_MCP_CONFIG_OP_NORMAL); + + cdns_update_config(cdns); + + ret = sdw_bus_exit_clk_stop(&cdns->bus); + if (ret < 0) + dev_err(cdns->dev, "bus failed to exit clock stop %d\n", ret); + + return ret; +} +EXPORT_SYMBOL(sdw_cdns_clock_restart); + /** * sdw_cdns_probe() - Cadence probe routine * @cdns: Cadence instance diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index a7c0cbbbd09657..8dff77f5d91fa5 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -152,6 +152,9 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, int sdw_cdns_exit_reset(struct sdw_cdns *cdns); int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state); +int sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake); +int sdw_cdns_clock_restart(struct sdw_cdns *cdns, bool bus_reset); + #ifdef CONFIG_DEBUG_FS void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root); #endif From 648accda659b17600214a3a194db11724fccc6d7 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Thu, 5 Dec 2019 13:46:53 +0800 Subject: [PATCH 049/170] soundwire: intel: add CLK_STOP_BUS_RESET support Move existing pm_runtime suspend under the CLK_STOP_TEARDOWN case. In this mode the Master IP will lose all context but in-band wakes are supported. On pm_runtime resume a complete re-enumeration will be performed after a bus reset. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 45 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index f44ebbc99848dc..ea8694c4c2afa4 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1516,6 +1516,26 @@ static int intel_suspend_runtime(struct device *dev) } intel_shim_wake(sdw, false); + } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) { + ret = sdw_cdns_clock_stop(cdns, true); + if (ret < 0) { + dev_err(dev, "cannot enable clock stop on suspend\n"); + return ret; + } + + ret = sdw_cdns_enable_interrupt(cdns, false); + if (ret < 0) { + dev_err(dev, "cannot disable interrupts on suspend\n"); + return ret; + } + + ret = intel_link_power_down(sdw); + if (ret) { + dev_err(dev, "Link power down failed: %d", ret); + return ret; + } + + intel_shim_wake(sdw, true); } else { dev_err(dev, "%s clock_stop_quirks %x unsupported\n", __func__, clock_stop_quirks); @@ -1645,6 +1665,31 @@ static int intel_resume_runtime(struct device *dev) dev_err(dev, "unable to exit bus reset sequence during resume\n"); return ret; } + } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) { + ret = intel_init(sdw); + if (ret) { + dev_err(dev, "%s failed: %d", __func__, ret); + return ret; + } + + /* + * make sure all Slaves are tagged as UNATTACHED and + * provide reason for reinitialization + */ + sdw_clear_slave_status(&sdw->cdns.bus, + SDW_UNATTACH_REQUEST_MASTER_RESET); + + ret = sdw_cdns_enable_interrupt(cdns, true); + if (ret < 0) { + dev_err(dev, "cannot enable interrupts during resume\n"); + return ret; + } + + ret = sdw_cdns_clock_restart(cdns, true); + if (ret < 0) { + dev_err(dev, "unable to restart clock during resume\n"); + return ret; + } } else { dev_err(dev, "%s clock_stop_quirks %x unsupported\n", __func__, clock_stop_quirks); From 426a441ae695b0027d09c41211b28329c73bffef Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 5 Dec 2019 13:51:49 -0600 Subject: [PATCH 050/170] soundwire: intel: add CLK_STOP_NOT_ALLOWED support In case the clock needs to keep running, we need to prevent the Master from entering pm_runtime suspend. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index ea8694c4c2afa4..4fb2e9775e331a 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1329,6 +1329,7 @@ static int intel_master_startup(struct sdw_master_device *md) struct sdw_cdns_stream_config config; struct sdw_intel *sdw; int link_flags; + u32 clock_stop_quirks; int ret; sdw = md->pdata; @@ -1386,6 +1387,20 @@ static int intel_master_startup(struct sdw_master_device *md) pm_runtime_enable(&md->dev); } + clock_stop_quirks = sdw->link_res->clock_stop_quirks; + if (clock_stop_quirks & SDW_INTEL_CLK_STOP_NOT_ALLOWED) { + /* + * To keep the clock running we need to prevent + * pm_runtime suspend from happening by increasing the + * reference count. + * This quirk is specified by the parent PCI device in + * case of specific latency requirements. It will have + * no effect if pm_runtime is disabled by the user via + * a module parameter for testing purposes. + */ + pm_runtime_get_noresume(&md->dev); + } + /* * Slave devices have an pm_runtime of 'Unsupported' until * they report as ATTACHED. If they don't, e.g. because there @@ -1419,6 +1434,12 @@ static int intel_master_remove(struct sdw_master_device *md) sdw = md->pdata; + /* + * Since pm_runtime is already disabled, we don't decrease + * the refcount when the clock_stop_quirk is + * SDW_INTEL_CLK_STOP_NOT_ALLOWED + */ + if (!sdw->cdns.bus.prop.hw_disabled) { intel_debugfs_exit(sdw); sdw_cdns_enable_interrupt(&sdw->cdns, false); From 1e48fde4fed3132f663de8a4fc908d4d2891fc2d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 16 Dec 2019 12:58:55 -0600 Subject: [PATCH 051/170] soundwire: intel: add interface to process wake events Add driver interface to process external wake events processed by the PCI subsystem. Signed-off-by: Pierre-Louis Bossart --- include/linux/soundwire/sdw.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 1d3a1eca66d110..347d2cda76d022 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -652,6 +652,7 @@ struct sdw_md_driver { int (*remove)(struct sdw_master_device *md); int (*autonomous_clock_stop_enable)(struct sdw_master_device *md, bool state); + void (*process_wake_event)(struct sdw_master_device *md); struct device_driver driver; }; From 76492d7ef3c211755759a28deae1a9655e01e4b3 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Fri, 6 Dec 2019 15:00:12 -0600 Subject: [PATCH 052/170] soundwire: intel: add wake interrupt support When system is suspended in clock stop mode on intel platforms, both master and slave are in clock stop mode and soundwire bus is taken over by a glue hardware. The bus message for jack event is processed by this glue hardware, which will trigger an interrupt to resume audio pci device. Then audio pci driver will resume soundwire master and slave, transfer bus ownership to master, finally slave will report jack event to master and codec driver is triggered to check jack status. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 44 ++++++++++++++++++++++++++++++++++ drivers/soundwire/intel_init.c | 17 +++++++++++++ 2 files changed, 61 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 4fb2e9775e331a..ee62459903e04a 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1452,6 +1452,49 @@ static int intel_master_remove(struct sdw_master_device *md) return 0; } +static void intel_master_process_wakeen_event(struct sdw_master_device *md) +{ + struct sdw_intel *sdw; + struct sdw_slave *slave; + struct sdw_bus *bus; + void __iomem *shim; + u16 wake_sts; + + sdw = md->pdata; + + if (sdw->cdns.bus.prop.hw_disabled) { + dev_info(&md->dev, + "SoundWire master %d is disabled, ignoring\n", + sdw->cdns.bus.link_id); + return; + } + + shim = sdw->link_res->shim; + wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); + + if (!(wake_sts & BIT(sdw->instance))) + return; + + /* disable WAKEEN interrupt ASAP to prevent interrupt flood */ + intel_shim_wake(sdw, false); + + bus = &sdw->cdns.bus; + + /* + * wake up master and slave so that slave can notify master + * the wakeen event and let codec driver check codec status + */ + list_for_each_entry(slave, &bus->slaves, node) { + if (slave->prop.wake_capable) { + if (slave->status != SDW_SLAVE_ATTACHED && + slave->status != SDW_SLAVE_ALERT) + continue; + + pm_request_resume(&slave->dev); + } + } +} + /* * PM calls */ @@ -1738,6 +1781,7 @@ struct sdw_md_driver intel_sdw_driver = { }, .probe = intel_master_probe, .startup = intel_master_startup, + .process_wake_event = intel_master_process_wakeen_event, .remove = intel_master_remove, }; EXPORT_SYMBOL_NS(intel_sdw_driver, SOUNDWIRE_INTEL); diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 018e4e23dd0509..12c9df488daece 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -409,6 +409,23 @@ void sdw_intel_exit(struct sdw_intel_ctx *ctx) } EXPORT_SYMBOL_NS(sdw_intel_exit, SOUNDWIRE_INTEL_INIT); +void sdw_intel_process_wakeen_event(struct sdw_intel_ctx *ctx) +{ + struct sdw_intel_link_res *link; + struct sdw_master_device *md; + + if (!ctx->links) + return; + + list_for_each_entry(link, &ctx->link_list, list) { + + md = link->md; + + md->driver->process_wake_event(md); + } +} +EXPORT_SYMBOL_NS(sdw_intel_process_wakeen_event, SOUNDWIRE_INTEL_INIT); + MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("Intel Soundwire Init Library"); MODULE_IMPORT_NS(SOUNDWIRE_INTEL); From 4aeb16f7cdc9c9407a31d95ffd3c910bb477b1ae Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 5 Dec 2019 14:15:27 -0600 Subject: [PATCH 053/170] soundwire: intel_init: handle power rail dependencies for clock stop mode When none of the clock stop quirks is specified, the Master IP will assume the context is preserved and will not reset the Bus and restart enumeration. Due to power rail dependencies, the HDaudio controller needs to remain powered and prevented from executing its pm_runtime suspend routine. This choice of course has a power impact, and this mode should only be selected when latency requirements are critical or the parent device can enter D0ix modes. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel_init.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 12c9df488daece..534c6cfbf4afdd 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include "cadence_master.h" @@ -57,12 +58,21 @@ static int sdw_intel_cleanup(struct sdw_intel_ctx *ctx) { struct sdw_intel_link_res *link = ctx->links; struct sdw_master_device *md; + u32 link_mask; int i; if (!link) return 0; + link_mask = ctx->link_mask; + for (i = 0; i < ctx->count; i++, link++) { + if (link_mask && !(link_mask & BIT(i))) + continue; + + if (!link->clock_stop_quirks) + pm_runtime_put_noidle(link->dev); + md = link->md; if (md) md->driver->remove(md); @@ -305,6 +315,16 @@ sdw_intel_startup_controller(struct sdw_intel_ctx *ctx) md = link->md; md->driver->startup(md); + + if (!link->clock_stop_quirks) { + /* + * we need to prevent the parent PCI device + * from entering pm_runtime suspend, so that + * power rails to the SoundWire IP are not + * turned off. + */ + pm_runtime_get_noresume(link->dev); + } } return 0; From 368838ceec3fba55086980271d7f73ef5cff11f3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 5 Dec 2019 14:24:47 -0600 Subject: [PATCH 054/170] soundwire: intel: support clock_stop mode without quirks In this mode, on restart the bus restarts immediately, the Slaves remain synchronized and all context is kept intact. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index ee62459903e04a..42d4cd818da834 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1580,7 +1580,8 @@ static int intel_suspend_runtime(struct device *dev) } intel_shim_wake(sdw, false); - } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) { + } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET || + !clock_stop_quirks) { ret = sdw_cdns_clock_stop(cdns, true); if (ret < 0) { dev_err(dev, "cannot enable clock stop on suspend\n"); @@ -1754,6 +1755,24 @@ static int intel_resume_runtime(struct device *dev) dev_err(dev, "unable to restart clock during resume\n"); return ret; } + } else if (!clock_stop_quirks) { + ret = intel_init(sdw); + if (ret) { + dev_err(dev, "%s failed: %d", __func__, ret); + return ret; + } + + ret = sdw_cdns_enable_interrupt(cdns, true); + if (ret < 0) { + dev_err(dev, "cannot enable interrupts during resume\n"); + return ret; + } + + ret = sdw_cdns_clock_restart(cdns, false); + if (ret < 0) { + dev_err(dev, "unable to resume master during resume\n"); + return ret; + } } else { dev_err(dev, "%s clock_stop_quirks %x unsupported\n", __func__, clock_stop_quirks); From de9bf0c944822a9114d54207c874b407faf0a50d Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Fri, 6 Dec 2019 15:49:57 +0800 Subject: [PATCH 055/170] soundwire: cadence_master: add interface to check clock status If master is in clock stop state, driver can't modify registers in master except the registers for clock stop setting. Signed-off-by: Rander Wang --- drivers/soundwire/cadence_master.c | 19 +++++++++++++++++++ drivers/soundwire/cadence_master.h | 1 + 2 files changed, 20 insertions(+) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 3f5cb76f535e23..89e9148f510ca0 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -1185,6 +1185,25 @@ static const struct sdw_master_port_ops cdns_port_ops = { .dpn_port_enable_ch = cdns_port_enable, }; +/** + * sdw_cdns_is_clock_stop: Check clock status + * + * @cdns: Cadence instance + */ +bool sdw_cdns_is_clock_stop(struct sdw_cdns *cdns) +{ + u32 status; + + status = cdns_readl(cdns, CDNS_MCP_STAT) & CDNS_MCP_STAT_CLK_STOP; + if (status) { + dev_dbg(cdns->dev, "Clock is stopped\n"); + return true; + } + + return false; +} +EXPORT_SYMBOL(sdw_cdns_is_clock_stop); + /** * sdw_cdns_clock_stop: Cadence clock stop configuration routine * diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 8dff77f5d91fa5..516ed26c40e3ae 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -152,6 +152,7 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, int sdw_cdns_exit_reset(struct sdw_cdns *cdns); int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state); +bool sdw_cdns_is_clock_stop(struct sdw_cdns *cdns); int sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake); int sdw_cdns_clock_restart(struct sdw_cdns *cdns, bool bus_reset); From 98ace3a5aaba979900c91446dbf317597f4bf90d Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Fri, 6 Dec 2019 15:56:09 +0800 Subject: [PATCH 056/170] soundwire: intel: Fix a io timeout issue if SDW_INTEL_CLK_STOP_BUS_RESET is set If two masters are working and one of them become suspended and activated again, this master will be failed to resumed in SDW_INTEL_CLK_STOP_BUS_RESET mode. The reason is: on intel platform, there are four masters in the same power domain. If one master is active, all other masters are always powered on. Each master will be set to clock stop when it is inactive. And in resume function each master will be initialized because it asummes it is powered off. But if the master is not powered off, it should be in clock stop state, and the normal initialization will be failed in this state. Now check clock status and don't initialize master if it is in clock stop mode. Signed-off-by: Rander Wang --- drivers/soundwire/intel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 42d4cd818da834..22bca43fc47a65 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1274,6 +1274,9 @@ static int intel_init(struct sdw_intel *sdw) intel_link_power_up(sdw); intel_shim_init(sdw); + if (sdw_cdns_is_clock_stop(&sdw->cdns)) + return 0; + return sdw_cdns_init(&sdw->cdns); } From d5c62084515445770b5734d68bfc66b9b01a760f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 9 Dec 2019 13:56:03 -0600 Subject: [PATCH 057/170] soundwire: bus: treat CMD_IGNORED as success on ClockStop If there are no Slaves in ATTACHED or ALERT mode, the CMD_IGNORED/-ENODATA error code is perfectly legit. Filter this case to report errors and let the caller deal with the CMD_IGNORED case. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 16f4458f302ac1..6864241f0a94ed 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -916,8 +916,12 @@ int sdw_bus_clk_stop(struct sdw_bus *bus) ret = sdw_bwrite_no_pm(bus, SDW_BROADCAST_DEV_NUM, SDW_SCP_CTRL, SDW_SCP_CTRL_CLK_STP_NOW); if (ret < 0) { - dev_err(bus->dev, - "ClockStopNow Broadcast message failed %d", ret); + if (ret == -ENODATA) + dev_dbg(bus->dev, + "ClockStopNow Broadcast msg ignored %d", ret); + else + dev_err(bus->dev, + "ClockStopNow Broadcast msg failed %d", ret); return ret; } From a55b75de52750e3ef32d398642ebaa471f8ae10e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 9 Dec 2019 14:39:59 -0600 Subject: [PATCH 058/170] soundwire: cadence_master: fix usage of CMD_ACCEPT cdns_updatel() applies its parameter assuming it's already shifted by the correct amount. Using '1' instead of BIT(1) is incorrect, fix. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 89e9148f510ca0..075c49bb4a670a 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -1250,7 +1250,8 @@ int sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake) */ if (!slave_attached) cdns_updatel(cdns, CDNS_MCP_CONTROL, - CDNS_MCP_CONTROL_CMD_ACCEPT, 1); + CDNS_MCP_CONTROL_CMD_ACCEPT, + CDNS_MCP_CONTROL_CMD_ACCEPT); else cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT, 0); From 2a4f47474b732aaeaf8bae18979831ca684f5096 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 9 Dec 2019 16:41:24 -0600 Subject: [PATCH 059/170] soundwire: cadence_master: log more useful information during timeouts Add the type of command, device number, register offset and length to reverse engineer what caused the issue. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 075c49bb4a670a..161ae60693e596 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -463,7 +463,8 @@ _cdns_xfer_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int cmd, time = wait_for_completion_timeout(&cdns->tx_complete, msecs_to_jiffies(CDNS_TX_TIMEOUT)); if (!time) { - dev_err(cdns->dev, "IO transfer timed out\n"); + dev_err(cdns->dev, "IO transfer timed out, cmd %d device %d addr %x len %d\n", + cmd, msg->dev_num, msg->addr, msg->len); msg->len = 0; return SDW_CMD_TIMEOUT; } From 68854344bbaf859b87f49a1a8220d3f13b2d6bdb Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 3 Dec 2019 16:46:48 -0600 Subject: [PATCH 060/170] soundwire: intel_init: add support for clock_stop quirks Just pass the information provided by the parent (SOF driver) Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.h | 2 ++ drivers/soundwire/intel_init.c | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index 1d8740b452c6e0..0e6b8a5fd7c2a3 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -16,6 +16,7 @@ * @ops: Shim callback ops * @dev: device implementing hw_params and free callbacks * @shim_lock: mutex to handle access to shared SHIM registers + * @clock_stop_quirks: mask defining requested behavior on pm_suspend */ struct sdw_intel_link_res { struct sdw_master_device *md; @@ -29,6 +30,7 @@ struct sdw_intel_link_res { struct sdw_cdns *cdns; struct list_head list; struct mutex *shim_lock; /* protect shared registers */ + u32 clock_stop_quirks; }; #define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1) diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 534c6cfbf4afdd..fe5944ddb16121 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -257,6 +257,7 @@ static struct sdw_intel_ctx link->alh = res->mmio_base + SDW_ALH_BASE; link->ops = res->ops; link->dev = res->dev; + link->clock_stop_quirks = res->clock_stop_quirks; /* let the SoundWire master driver to its probe */ md->driver->probe(md, link); From 6da09afb20db29fed130f00701dab33b1d8400ba Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 9 Dec 2019 13:50:03 -0600 Subject: [PATCH 061/170] soundwire: cadence_master: enter clock stop if there are no Slaves present If there aren't any Slaves present (ATTACHED or ALERT status), we can safely infore CMD_IGNORED/-ENODATA error codes. The Cadence IP is configured to handle such cases as success. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 161ae60693e596..bb5c95cfecc5ab 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -1213,7 +1213,7 @@ EXPORT_SYMBOL(sdw_cdns_is_clock_stop); */ int sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake) { - bool slave_attached = false; + bool slave_present = false; struct sdw_slave *slave; u32 status; int ret; @@ -1238,7 +1238,7 @@ int sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake) list_for_each_entry(slave, &cdns->bus.slaves, node) { if (slave->status == SDW_SLAVE_ATTACHED || slave->status == SDW_SLAVE_ALERT) { - slave_attached = true; + slave_present = true; break; } } @@ -1249,7 +1249,7 @@ int sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake) * not set and there is a broadcast write then the command ignored * will be treated as a failure */ - if (!slave_attached) + if (!slave_present) cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT, CDNS_MCP_CONTROL_CMD_ACCEPT); @@ -1267,9 +1267,12 @@ int sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake) return ret; } - /* Enter clock stop */ + /* + * Enter clock stop mode and only report errors if there are + * Slave devices present (ALERT or ATTACHED) + */ ret = sdw_bus_clk_stop(&cdns->bus); - if (ret < 0) { + if (ret < 0 && slave_present && ret != -ENODATA) { dev_err(cdns->dev, "bus clock stop failed %d", ret); return ret; } From e95d996b7e4c2b90aa9c69f49ae327be9f72c027 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 13 Dec 2019 08:50:40 +0800 Subject: [PATCH 062/170] Soundwire: bus: return in the while loop rather than break and return So we don't need extra if condition outside the while loop. Signed-off-by: Bard Liao --- drivers/soundwire/bus.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 6864241f0a94ed..27c0b0eb0512dc 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -790,19 +790,16 @@ static int sdw_bus_wait_for_clk_prep_deprep(struct sdw_bus *bus, u16 dev_num) do { val = sdw_bread_no_pm(bus, dev_num, SDW_SCP_STAT) & SDW_SCP_STAT_CLK_STP_NF; - if (!val) - break; + if (!val) { + dev_info(bus->dev, "clock stop prep/de-prep done slave:%d", + dev_num); + return 0; + } usleep_range(1000, 1500); retry--; } while (retry); - if (retry && !val) { - dev_info(bus->dev, "clock stop prep/de-prep done slave:%d", - dev_num); - return 0; - } - dev_err(bus->dev, "clock stop prep/de-prep failed slave:%d", dev_num); From 6fd5fadce551322f0a45d7572b5c7552aa25b978 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 13 Dec 2019 08:58:24 +0800 Subject: [PATCH 063/170] Soundwire: set is_slave only if the slave is attached We don't need to do anything for the slave if it is unattached. Signed-off-by: Bard Liao --- drivers/soundwire/bus.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 27c0b0eb0512dc..c11dd307813a1e 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -830,13 +830,13 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus) if (!slave->dev_num) continue; - /* Identify if Slave(s) are available on Bus */ - is_slave = true; - if (slave->status != SDW_SLAVE_ATTACHED && slave->status != SDW_SLAVE_ALERT) continue; + /* Identify if Slave(s) are available on Bus */ + is_slave = true; + slave_mode = sdw_get_clk_stop_mode(slave); slave->curr_clk_stop_mode = slave_mode; @@ -952,13 +952,13 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus) if (!slave->dev_num) continue; - /* Identify if Slave(s) are available on Bus */ - is_slave = true; - if (slave->status != SDW_SLAVE_ATTACHED && slave->status != SDW_SLAVE_ALERT) continue; + /* Identify if Slave(s) are available on Bus */ + is_slave = true; + mode = slave->curr_clk_stop_mode; if (mode == SDW_CLK_STOP_MODE1) { From cea8141e0637e1a4e05b1a48fa45b756463731fa Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 13 Dec 2019 09:26:21 +0800 Subject: [PATCH 064/170] soundwire: test is_slave before list_for_each_entry(slave, &bus->slaves, node) We don't need to test each slave's status if we already know there is no slave attached. Signed-off-by: Bard Liao --- drivers/soundwire/bus.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index c11dd307813a1e..ba88d88dda8bcd 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -867,6 +867,10 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus) return ret; } + /* Don't need to inform slaves if there is no slave attached */ + if (!is_slave) + return ret; + /* Inform slaves that prep is done */ list_for_each_entry(slave, &bus->slaves, node) { if (!slave->dev_num) @@ -983,6 +987,13 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus) if (is_slave && !simple_clk_stop) sdw_bus_wait_for_clk_prep_deprep(bus, SDW_BROADCAST_DEV_NUM); + /* + * Don't need to call slave callback function if there is no slave + * attached + */ + if (!is_slave) + return 0; + list_for_each_entry(slave, &bus->slaves, node) { if (!slave->dev_num) continue; From a47fafe462027b3490c8119635bdf9b649f7c162 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Wed, 11 Dec 2019 16:58:20 +0800 Subject: [PATCH 065/170] soundwire: intel: refine runtime pm for SDW_INTEL_CLK_STOP_BUS_RESET When all the links are suspended, the HDaudio controller may suspend and the power rails to the SoundWire IP may be disabled, requiring a complete re-initialization/enumeration on resume. However, if one or more Masters remained active, the HDaudio controller will remain active and the power rails will remain enabled. As a result, during the link resume step we can check if the context was preserved by verifying if the clock was stopped, and avoid doing a complete bus reset and re-enumeration. Signed-off-by: Rander Wang --- drivers/soundwire/intel.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 22bca43fc47a65..c1699af2f6f169 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1696,6 +1696,8 @@ static int intel_resume_runtime(struct device *dev) struct sdw_cdns *cdns = dev_get_drvdata(dev); struct sdw_intel *sdw = cdns_to_intel(cdns); u32 clock_stop_quirks; + bool clock_stop0; + int status; int ret; if (cdns->bus.prop.hw_disabled) { @@ -1740,12 +1742,23 @@ static int intel_resume_runtime(struct device *dev) return ret; } + /* + * An exception condition occurs for the CLK_STOP_BUS_RESET + * case if one or more masters remain active. In this condition, + * all the masters are powered on for they are in the same power + * domain. Master can preserve its context for clock stop0, so + * there is no need to clear slave status and reset bus. + */ + clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns); + /* * make sure all Slaves are tagged as UNATTACHED and * provide reason for reinitialization */ - sdw_clear_slave_status(&sdw->cdns.bus, - SDW_UNATTACH_REQUEST_MASTER_RESET); + if (!clock_stop0) { + status = SDW_UNATTACH_REQUEST_MASTER_RESET; + sdw_clear_slave_status(&sdw->cdns.bus, status); + } ret = sdw_cdns_enable_interrupt(cdns, true); if (ret < 0) { @@ -1753,7 +1766,7 @@ static int intel_resume_runtime(struct device *dev) return ret; } - ret = sdw_cdns_clock_restart(cdns, true); + ret = sdw_cdns_clock_restart(cdns, !clock_stop0); if (ret < 0) { dev_err(dev, "unable to restart clock during resume\n"); return ret; From a2b72e7b25f0d84ad0c62964c68825bbcb09f3b6 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Wed, 25 Dec 2019 13:28:40 +0800 Subject: [PATCH 066/170] soundwire: stream: fix support for multiple Slaves on the same link The existing code will unconditionally return after dealing with the first Slave on a link. This return should only happen when there is an error case. Tested on Comet Lake platform. Signed-off-by: Rander Wang --- drivers/soundwire/stream.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 3e8ff16fed052b..7d90620ffbbfa9 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -587,10 +587,11 @@ static int sdw_notify_config(struct sdw_master_runtime *m_rt) if (slave->ops->bus_config) { ret = slave->ops->bus_config(slave, &bus->params); - if (ret < 0) + if (ret < 0) { dev_err(bus->dev, "Notify Slave: %d failed\n", slave->dev_num); - return ret; + return ret; + } } } From ebbb5ef2cd90a05fbcec85eb53b982b5596075fb Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 11 Sep 2019 10:27:59 -0500 Subject: [PATCH 067/170] ASoC: SOF: Intel: add SoundWire configuration interface Now that the SoundWire core supports the multi-step initialization, call the relevant APIs. The actual hardware enablement can be done in two places, ideally we'd want to startup the SoundWire IP as soon as possible (while still taking power rail dependencies into account) However when suspend/resume is implemented, the DSP device will be resumed first, and only when the DSP firmware is downloaded/booted would the SoundWire child devices be resumed, so there are only marginal benefits in starting the IP earlier for the first probe. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-loader.c | 13 ++++ sound/soc/sof/intel/hda.c | 129 +++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 44 +++++++++++ 3 files changed, 186 insertions(+) diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 1782f50926391c..257c47129f165f 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -396,6 +396,19 @@ int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev) /* post fw run operations */ int hda_dsp_post_fw_run(struct snd_sof_dev *sdev) { + int ret; + + if (sdev->first_boot) { + ret = hda_sdw_startup(sdev); + if (ret < 0) { + dev_err(sdev->dev, + "error: could not startup SoundWire links\n"); + return ret; + } + } + + hda_sdw_int_enable(sdev, true); + /* re-enable clock gating and power gating */ return hda_dsp_ctrl_clock_power_gating(sdev, true); } diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index d08462f481de38..9bc7fdef161a8f 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -18,7 +18,9 @@ #include #include +#include #include +#include #include #include #include @@ -34,6 +36,98 @@ #define EXCEPT_MAX_HDR_SIZE 0x400 +#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) + +void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable) +{ + sdw_intel_enable_irq(sdev->bar[HDA_DSP_BAR], enable); +} + +static int hda_sdw_acpi_scan(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + acpi_handle handle; + int ret; + + handle = ACPI_HANDLE(sdev->dev); + + /* save ACPI info for the probe step */ + hdev = sdev->pdata->hw_pdata; + + ret = sdw_intel_acpi_scan(handle, &hdev->info); + if (ret < 0) { + dev_err(sdev->dev, "%s failed\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int hda_sdw_probe(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + struct sdw_intel_res res; + acpi_handle handle; + void *sdw; + + handle = ACPI_HANDLE(sdev->dev); + + hdev = sdev->pdata->hw_pdata; + + memset(&res, 0, sizeof(res)); + + res.mmio_base = sdev->bar[HDA_DSP_BAR]; + res.irq = sdev->ipc_irq; + res.handle = hdev->info.handle; + res.parent = sdev->dev; + + /* + * ops and arg fields are not populated for now, + * they will be needed when the DAI callbacks are + * provided + */ + + /* we could filter links here if needed, e.g for quirks */ + res.count = hdev->info.count; + res.link_mask = hdev->info.link_mask; + + sdw = sdw_intel_probe(&res); + if (!sdw) { + dev_err(sdev->dev, "error: SoundWire probe failed\n"); + return -EINVAL; + } + + /* save context */ + hdev->sdw = sdw; + + return 0; +} + +int hda_sdw_startup(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + + hdev = sdev->pdata->hw_pdata; + + return sdw_intel_startup(hdev->sdw); +} + +static int hda_sdw_exit(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + + hdev = sdev->pdata->hw_pdata; + + hda_sdw_int_enable(sdev, false); + + if (hdev->sdw) + sdw_intel_exit(hdev->sdw); + hdev->sdw = NULL; + + return 0; +} +#endif + /* * Debug */ @@ -342,9 +436,21 @@ static const char *fixup_tplg_name(struct snd_sof_dev *sdev, static int hda_init_caps(struct snd_sof_dev *sdev) { struct hdac_bus *bus = sof_to_bus(sdev); + struct snd_sof_pdata *pdata = sdev->pdata; #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) struct hdac_ext_link *hlink; + struct snd_soc_acpi_mach_params *mach_params; + struct snd_soc_acpi_mach *hda_mach; + struct snd_soc_acpi_mach *mach; + const char *tplg_filename; + const char *idisp_str; + const char *dmic_str; + int dmic_num; + int codec_num = 0; + int i; #endif + struct sof_intel_hda_dev *hdev = pdata->hw_pdata; + u32 link_mask; int ret = 0; device_disable_async_suspend(bus->dev); @@ -373,6 +479,27 @@ static int hda_init_caps(struct snd_sof_dev *sdev) return ret; } + /* scan SoundWire capabilities exposed by DSDT */ + ret = hda_sdw_acpi_scan(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: SoundWire ACPI scan error\n"); + return ret; + } + + link_mask = hdev->info.link_mask; + if (!link_mask) { + /* + * probe/allocated SoundWire resources. + * The hardware configuration takes place in hda_sdw_startup + * after power rails are enabled. + */ + ret = hda_sdw_probe(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: SoundWire probe error\n"); + return ret; + } + } + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) if (bus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(bus); @@ -626,6 +753,8 @@ int hda_dsp_remove(struct snd_sof_dev *sdev) snd_hdac_ext_bus_device_remove(bus); #endif + hda_sdw_exit(sdev); + if (!IS_ERR_OR_NULL(hda->dmic_dev)) platform_device_unregister(hda->dmic_dev); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 47408ec0de4046..ea8308c5e9bef6 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -11,6 +11,8 @@ #ifndef __SOF_INTEL_HDA_H #define __SOF_INTEL_HDA_H +#include +#include #include #include #include "shim.h" @@ -414,6 +416,12 @@ struct sof_intel_hda_dev { /* DMIC device */ struct platform_device *dmic_dev; + + /* ACPI information stored between scan and probe steps */ + struct sdw_intel_acpi_info info; + + /* sdw context allocated by SoundWire driver */ + struct sdw_intel_ctx *sdw; }; static inline struct hdac_bus *sof_to_bus(struct snd_sof_dev *s) @@ -607,6 +615,42 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag); int hda_dsp_trace_release(struct snd_sof_dev *sdev); int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd); +/* + * SoundWire support + */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) + +int hda_sdw_startup(struct snd_sof_dev *sdev); +void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable); + +#else + +static inline int hda_sdw_acpi_scan(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline int hda_sdw_probe(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline int hda_sdw_startup(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline int hda_sdw_exit(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable) +{ +} + +#endif + /* common dai driver */ extern struct snd_soc_dai_driver skl_dai[]; From f9c8bfcceb72fbb720d1446b0ab3f961cd755089 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 16 Aug 2019 17:53:00 -0500 Subject: [PATCH 068/170] ASoC: SOF: IPC: dai-intel: move ALH declarations in header file ALH was inserted in the wrong place during integration, add after DMIC to mirror the file used by SOF firmware. No functional change, just text move in the same file to better track changes, if any. Signed-off-by: Pierre-Louis Bossart --- include/sound/sof/dai-intel.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/sound/sof/dai-intel.h b/include/sound/sof/dai-intel.h index 5f1ef5565be69f..04e48227f542b5 100644 --- a/include/sound/sof/dai-intel.h +++ b/include/sound/sof/dai-intel.h @@ -87,6 +87,15 @@ struct sof_ipc_dai_hda_params { uint32_t link_dma_ch; } __packed; +/* ALH Configuration Request - SOF_IPC_DAI_ALH_CONFIG */ +struct sof_ipc_dai_alh_params { + struct sof_ipc_hdr hdr; + uint32_t stream_id; + + /* reserved for future use */ + uint32_t reserved[15]; +} __packed; + /* DMIC Configuration Request - SOF_IPC_DAI_DMIC_CONFIG */ /* This struct is defined per 2ch PDM controller available in the platform. @@ -179,13 +188,4 @@ struct sof_ipc_dai_dmic_params { struct sof_ipc_dai_dmic_pdm_ctrl pdm[0]; } __packed; -/* ALH Configuration Request - SOF_IPC_DAI_ALH_CONFIG */ -struct sof_ipc_dai_alh_params { - struct sof_ipc_hdr hdr; - uint32_t stream_id; - - /* reserved for future use */ - uint32_t reserved[15]; -} __packed; - #endif From 1dea855455389b5e86dbf3ae29a5186cf2705db2 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jun 2019 02:45:55 -0500 Subject: [PATCH 069/170] ASoC: SOF: Intel: hda: add SoundWire stream config/free callbacks These callbacks are invoked when a matching hw_params/hw_free() DAI operation takes place, and will result in IPC operations with the SOF firmware. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda.c | 71 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 9bc7fdef161a8f..c47ff49f723bb4 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -24,6 +24,7 @@ #include #include #include +#include "../sof-audio.h" #include "../ops.h" #include "hda.h" @@ -38,6 +39,74 @@ #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) +static int sdw_params_stream(struct device *dev, + struct sdw_intel_stream_params_data *params_data) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct snd_soc_dai *d = params_data->dai; + struct sof_ipc_dai_config config; + struct sof_ipc_reply reply; + int link_id = params_data->link_id; + int alh_stream_id = params_data->alh_stream_id; + int ret; + u32 size = sizeof(config); + + memset(&config, 0, size); + config.hdr.size = size; + config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; + config.type = SOF_DAI_INTEL_ALH; + config.dai_index = (link_id << 8) | (d->id); + config.alh.stream_id = alh_stream_id; + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + config.hdr.cmd, &config, size, &reply, + sizeof(reply)); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to set DAI hw_params for link %d dai->id %d ALH %d\n", + link_id, d->id, alh_stream_id); + } + + return ret; +} + +static int sdw_free_stream(struct device *dev, + struct sdw_intel_stream_free_data *free_data) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct snd_soc_dai *d = free_data->dai; + struct sof_ipc_dai_config config; + struct sof_ipc_reply reply; + int link_id = free_data->link_id; + int ret; + u32 size = sizeof(config); + + memset(&config, 0, size); + config.hdr.size = size; + config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; + config.type = SOF_DAI_INTEL_ALH; + config.dai_index = (link_id << 8) | d->id; + config.alh.stream_id = 0xFFFF; /* invalid value on purpose */ + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + config.hdr.cmd, &config, size, &reply, + sizeof(reply)); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to free stream for link %d dai->id %d\n", + link_id, d->id); + } + + return ret; +} + +static const struct sdw_intel_ops sdw_callback = { + .params_stream = sdw_params_stream, + .free_stream = sdw_free_stream, +}; + void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable) { sdw_intel_enable_irq(sdev->bar[HDA_DSP_BAR], enable); @@ -80,6 +149,8 @@ static int hda_sdw_probe(struct snd_sof_dev *sdev) res.irq = sdev->ipc_irq; res.handle = hdev->info.handle; res.parent = sdev->dev; + res.ops = &sdw_callback; + res.dev = sdev->dev; /* * ops and arg fields are not populated for now, From 20a05e4dfc5eb6d791fe7334db3027cf0076d15c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 12 Sep 2019 11:13:05 -0500 Subject: [PATCH 070/170] ASoC: SOF: Intel: hda: initial SoundWire machine driver autodetect For now we have a limited number of machine driver configurations, and we can detect them based on the link configuration returned after checking hardware and firmware (BIOS) configurations. It's likely that in the future we will need to check for _ADR match as well, which can easily be done by extending the acpi_info structure. There is a chance that in extreme cases where the BIOS contains too much information we would need to detect which Slave devices actually report as 'attached'. This would be more accurate than static table-based solutions, but it also introduces timing dependencies since we don't know when those devices might become attached, so will only be only be looked at if we see limitations with static methods and the usual quirks based e.g. on DMI information. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda.c | 93 ++++++++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 21 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index c47ff49f723bb4..93b71b38faed0b 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -180,6 +180,9 @@ int hda_sdw_startup(struct snd_sof_dev *sdev) hdev = sdev->pdata->hw_pdata; + if (!hdev->sdw) + return 0; + return sdw_intel_startup(hdev->sdw); } @@ -510,15 +513,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev) struct snd_sof_pdata *pdata = sdev->pdata; #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) struct hdac_ext_link *hlink; - struct snd_soc_acpi_mach_params *mach_params; - struct snd_soc_acpi_mach *hda_mach; - struct snd_soc_acpi_mach *mach; - const char *tplg_filename; - const char *idisp_str; - const char *dmic_str; - int dmic_num; - int codec_num = 0; - int i; #endif struct sof_intel_hda_dev *hdev = pdata->hw_pdata; u32 link_mask; @@ -553,24 +547,31 @@ static int hda_init_caps(struct snd_sof_dev *sdev) /* scan SoundWire capabilities exposed by DSDT */ ret = hda_sdw_acpi_scan(sdev); if (ret < 0) { - dev_err(sdev->dev, "error: SoundWire ACPI scan error\n"); - return ret; + dev_dbg(sdev->dev, "skipping SoundWire, ACPI scan error\n"); + goto skip_soundwire; } link_mask = hdev->info.link_mask; if (!link_mask) { - /* - * probe/allocated SoundWire resources. - * The hardware configuration takes place in hda_sdw_startup - * after power rails are enabled. - */ - ret = hda_sdw_probe(sdev); - if (ret < 0) { - dev_err(sdev->dev, "error: SoundWire probe error\n"); - return ret; - } + dev_dbg(sdev->dev, "skipping SoundWire, no links enabled\n"); + goto skip_soundwire; + } + + /* + * probe/allocate SoundWire resources. + * The hardware configuration takes place in hda_sdw_startup + * after power rails are enabled. + * It's entirely possible to have a mix of I2S/DMIC/SoundWire + * devices, so we allocate the resources in all cases. + */ + ret = hda_sdw_probe(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: SoundWire probe error\n"); + return ret; } +skip_soundwire: + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) if (bus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(bus); @@ -963,6 +964,51 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev) } #endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) +static int hda_sdw_machine_select(struct snd_sof_dev *sdev) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + struct snd_soc_acpi_mach *mach; + struct snd_sof_pdata *pdata = sdev->pdata; + struct sof_intel_hda_dev *hdev = pdata->hw_pdata; + u32 link_mask; + + link_mask = hdev->info.link_mask; + + /* + * Select SoundWire machine driver if needed using the + * alternate tables. This case deals with SoundWire-only + * machines, for mixed cases with I2C/I2S the detection relies + * on the HID list. + */ + if (link_mask && !pdata->machine) { + mach = pdata->desc->alt_machines; + while (mach && mach->link_mask && mach->link_mask != link_mask) + mach++; + if (mach && mach->link_mask) { + dev_dbg(bus->dev, + "SoundWire machine driver %s topology %s\n", + mach->drv_name, + mach->sof_tplg_filename); + pdata->machine = mach; + mach->mach_params.platform = dev_name(sdev->dev); + pdata->fw_filename = mach->sof_fw_filename; + pdata->tplg_filename = mach->sof_tplg_filename; + } else { + dev_info(sdev->dev, + "No SoundWire machine driver found\n"); + } + } + + return 0; +} +#else +static int hda_sdw_machine_select(struct snd_sof_dev *sdev) +{ + return 0; +} +#endif + void hda_set_mach_params(const struct snd_soc_acpi_mach *mach, struct device *dev) { @@ -984,6 +1030,11 @@ void hda_machine_select(struct snd_sof_dev *sdev) sof_pdata->machine = mach; } + /* + * If I2S fails, try SoundWire + */ + hda_sdw_machine_select(sdev); + /* * Choose HDA generic machine driver if mach is NULL. * Otherwise, set certain mach params. From a85abbdf11fa344e5c3ea47aada5b523ffc8d215 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 24 Sep 2019 10:36:44 -0500 Subject: [PATCH 071/170] ASoC: SOF: Intel: hda: disable SoundWire interrupts on suspend Doing this avoid conflicts and errors reported on the bus. The interrupts are only re-enabled on resume after the firmware is downloaded, so the behavior is not fully symmetric Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-dsp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 4a4d318f97ffa5..58388bccf27f30 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -390,6 +390,8 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) #endif int ret; + hda_sdw_int_enable(sdev, false); + /* disable IPC interrupts */ hda_dsp_ipc_int_disable(sdev); From d2177aaebc72bf7ce4c7bba9e083fead3b0ed9f3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 4 Sep 2019 09:42:38 -0500 Subject: [PATCH 072/170] ASoC: SOF: Intel: add build support for SoundWire Select SoundWire capabilities on newer Intel platforms, starting with CannonLake/CoffeeLake/CometLake. As done for HDaudio, the SoundWire link is an opt-in capability. We explicitly test for ACPI to avoid warnings on unmet dependencies on the SoundWire side. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/Kconfig | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index bc62d54c2d55bf..8c2da6a2c9df45 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -168,6 +168,7 @@ config SND_SOC_SOF_CANNONLAKE_SUPPORT config SND_SOC_SOF_CANNONLAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -183,6 +184,7 @@ config SND_SOC_SOF_COFFEELAKE_SUPPORT config SND_SOC_SOF_COFFEELAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -198,6 +200,7 @@ config SND_SOC_SOF_ICELAKE_SUPPORT config SND_SOC_SOF_ICELAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -205,6 +208,7 @@ config SND_SOC_SOF_ICELAKE config SND_SOC_SOF_COMETLAKE_LP tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -220,6 +224,7 @@ config SND_SOC_SOF_COMETLAKE_LP_SUPPORT config SND_SOC_SOF_COMETLAKE_H tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -258,6 +263,7 @@ config SND_SOC_SOF_TIGERLAKE_SUPPORT config SND_SOC_SOF_TIGERLAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -273,6 +279,7 @@ config SND_SOC_SOF_ELKHARTLAKE_SUPPORT config SND_SOC_SOF_ELKHARTLAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -360,6 +367,29 @@ config SND_SOC_SOF_HDA This option is not user-selectable but automagically handled by 'select' statements at a higher level +config SND_SOC_SOF_INTEL_SOUNDWIRE_LINK + bool "SOF support for SoundWire" + depends on SOUNDWIRE && ACPI + help + This adds support for SoundWire with Sound Open Firmware + for Intel(R) platforms. + Say Y if you want to enable SoundWire links with SOF. + If unsure select "N". + +config SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE + tristate + select SND_SOC_SOF_INTEL_SOUNDWIRE if SND_SOC_SOF_INTEL_SOUNDWIRE_LINK + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level + +config SND_SOC_SOF_INTEL_SOUNDWIRE + tristate + select SOUNDWIRE_INTEL + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level + endif ## SND_SOC_SOF_INTEL_PCI endif ## SND_SOC_SOF_INTEL_TOPLEVEL From 4233154c85064a4c13d33d010eab108da2c55e16 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 21 Oct 2019 11:27:21 -0500 Subject: [PATCH 073/170] ASoC: SOF: Intel: hda-ctrl: add reset cycle before parsing capabilities Without this cycle, HDaudio capability parsing fails on some devices. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-ctrl.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index 871b71a15a6331..ff9e8f3782065c 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -64,15 +64,32 @@ int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev) struct hdac_bus *bus = sof_to_bus(sdev); u32 cap, offset, feature; int count = 0; + int ret; + + /* + * On some devices, one reset cycle is necessary before reading + * capabilities + */ + ret = hda_dsp_ctrl_link_reset(sdev, true); + if (ret < 0) + return ret; + ret = hda_dsp_ctrl_link_reset(sdev, false); + if (ret < 0) + return ret; offset = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_LLCH); do { - cap = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, offset); - dev_dbg(sdev->dev, "checking for capabilities at offset 0x%x\n", offset & SOF_HDA_CAP_NEXT_MASK); + cap = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, offset); + + if (cap == -1) { + dev_dbg(bus->dev, "Invalid capability reg read\n"); + break; + } + feature = (cap & SOF_HDA_CAP_ID_MASK) >> SOF_HDA_CAP_ID_OFF; switch (feature) { @@ -105,8 +122,8 @@ int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev) bus->mlcap = bus->remap_addr + offset; break; default: - dev_vdbg(sdev->dev, "found capability %d at 0x%x\n", - feature, offset); + dev_dbg(sdev->dev, "found capability %d at 0x%x\n", + feature, offset); break; } From ffd865ebac56dbb5b36288a77d0b45310d92446e Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 21 Nov 2019 18:11:52 -0600 Subject: [PATCH 074/170] ASoC: SOF: Intel: hda: merge IPC, stream and SoundWire interrupt handlers We have a single irq handler for SOF interrupts. We can further merge SoundWire ones to completely remove MSI interrupts handling issues leading to timeouts. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda.c | 36 ++++++++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 11 +++++++++++ 2 files changed, 47 insertions(+) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 93b71b38faed0b..3ec8416fc6317f 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -200,6 +200,38 @@ static int hda_sdw_exit(struct snd_sof_dev *sdev) return 0; } + +static bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + bool ret = false; + u32 irq_status; + + hdev = sdev->pdata->hw_pdata; + + if (!hdev->sdw) + return ret; + + /* store status */ + irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS2); + + /* invalid message ? */ + if (irq_status == 0xffffffff) + goto out; + + /* SDW message ? */ + if (irq_status & HDA_DSP_REG_ADSPIS2_SNDW) + ret = true; + +out: + return ret; +} + +static irqreturn_t hda_dsp_sdw_thread(int irq, void *context) +{ + return sdw_intel_thread(irq, context); +} + #endif /* @@ -627,6 +659,7 @@ static irqreturn_t hda_dsp_interrupt_handler(int irq, void *context) static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context) { struct snd_sof_dev *sdev = context; + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; /* deal with streams and controller first */ if (hda_dsp_check_stream_irq(sdev)) @@ -635,6 +668,9 @@ static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context) if (hda_dsp_check_ipc_irq(sdev)) sof_ops(sdev)->irq_thread(irq, sdev); + if (hda_dsp_check_sdw_irq(sdev)) + hda_dsp_sdw_thread(irq, hdev->sdw); + /* enable GIE interrupt */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index ea8308c5e9bef6..724103626fe37a 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -232,6 +232,8 @@ #define HDA_DSP_REG_ADSPIC2 (HDA_DSP_GEN_BASE + 0x10) #define HDA_DSP_REG_ADSPIS2 (HDA_DSP_GEN_BASE + 0x14) +#define HDA_DSP_REG_ADSPIS2_SNDW BIT(5) + /* Intel HD Audio Inter-Processor Communication Registers */ #define HDA_DSP_IPC_BASE 0x40 #define HDA_DSP_REG_HIPCT (HDA_DSP_IPC_BASE + 0x00) @@ -649,6 +651,15 @@ static inline void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable) { } +static inline bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev) +{ + return false; +} + +static inline irqreturn_t hda_dsp_sdw_thread(int irq, void *context) +{ + return IRQ_HANDLED; +} #endif /* common dai driver */ From a5bc2b84c6fd4698809c6c02466a45bcc038a189 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 3 Dec 2019 17:14:28 -0600 Subject: [PATCH 075/170] ASoC: SOF: Intel: hda: specify behavior for clock stop For now we only support a complete teardown. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 3ec8416fc6317f..452d73be1299bb 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -151,6 +151,7 @@ static int hda_sdw_probe(struct snd_sof_dev *sdev) res.parent = sdev->dev; res.ops = &sdw_callback; res.dev = sdev->dev; + res.clock_stop_quirks = SDW_INTEL_CLK_STOP_TEARDOWN; /* * ops and arg fields are not populated for now, From 61568d54d9695e06e0c8d90237d9258d44295604 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Thu, 5 Dec 2019 14:48:39 +0800 Subject: [PATCH 076/170] ASoC: SOF: Intel: hda: add WAKEEN interrupt support for SoundWire When a SoundWire link is in clock stop state, a Slave device may wake up the Master for some events such as jack detection. The WAKEEN interrupt will be triggered and processed by the audio pci device. If audio device is in D3, the interrupt will be routed to PME, or aggregated at cAVS level as interrupt when audio device is in D0. This patch only supports D3 case, where the audio pci device will be resumed by a PME event and the WAKEEN interrupt will be processed after audio pci device is powered up and ROM is initialized successfully. The WAKEEN handling is only enabled after the first boot due to dependencies on a shim_lock mutex being initialized. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-loader.c | 18 ++++++++++++++++++ sound/soc/sof/intel/hda.c | 11 +++++++++++ sound/soc/sof/intel/hda.h | 5 +++++ 3 files changed, 34 insertions(+) diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 257c47129f165f..d7e1990610ec7a 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -343,6 +343,24 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) goto cleanup; } + /* + * When a SoundWire link is in clock stop state, a Slave + * device may trigger in-band wakes for events such as jack + * insertion or acoustic event detection. This event will lead + * to a WAKEEN interrupt, handled by the PCI device and routed + * to PME if the PCI device is in D3. The resume function in + * audio PCI driver will be invoked by ACPI for PME event and + * initialize the device and process WAKEEN interrupt. + * + * The WAKEEN interrupt should be processed ASAP to prevent an + * interrupt flood, otherwise other interrupts, such IPC, + * cannot work normally. The WAKEEN is handled after the ROM + * is initialized successfully, which ensures power rails are + * enabled before accessing the SoundWire SHIM registers + */ + if (!sdev->first_boot) + hda_sdw_process_wakeen(sdev); + /* * at this point DSP ROM has been initialized and * should be ready for code loading and firmware boot diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 452d73be1299bb..70c43f190d5003 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -233,6 +233,17 @@ static irqreturn_t hda_dsp_sdw_thread(int irq, void *context) return sdw_intel_thread(irq, context); } +void hda_sdw_process_wakeen(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + + hdev = sdev->pdata->hw_pdata; + if (!hdev->sdw) + return; + + sdw_intel_process_wakeen_event(hdev->sdw); +} + #endif /* diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 724103626fe37a..339d9690cf1047 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -624,6 +624,7 @@ int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd); int hda_sdw_startup(struct snd_sof_dev *sdev); void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable); +void hda_sdw_process_wakeen(struct snd_sof_dev *sdev); #else @@ -660,6 +661,10 @@ static inline irqreturn_t hda_dsp_sdw_thread(int irq, void *context) { return IRQ_HANDLED; } + +static void hda_sdw_process_wakeen(struct snd_sof_dev *sdev) +{ +} #endif /* common dai driver */ From 7e4e845110cfadf691d20018530c547414a30cca Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 5 Dec 2019 13:30:22 -0600 Subject: [PATCH 077/170] ASoC: SOF: Intel: hda: add parameter to control SoundWire clock stop quirks Add module parameter so that the different modes can be quickly tested. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 70c43f190d5003..037c27e73301bb 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -39,6 +39,16 @@ #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) +/* + * The default for SoundWire clock stop quirks is to power gate the IP + * and do a Bus Reset, this will need to be modified when the DSP + * needs to remain in D0i3 so that the Master does not lose context + * and enumeration is not required on clock restart + */ +static int sdw_clock_stop_quirks = SDW_INTEL_CLK_STOP_BUS_RESET; +module_param(sdw_clock_stop_quirks, int, 0444); +MODULE_PARM_DESC(sdw_clock_stop_quirks, "SOF SoundWire clock stop quirks"); + static int sdw_params_stream(struct device *dev, struct sdw_intel_stream_params_data *params_data) { @@ -151,7 +161,7 @@ static int hda_sdw_probe(struct snd_sof_dev *sdev) res.parent = sdev->dev; res.ops = &sdw_callback; res.dev = sdev->dev; - res.clock_stop_quirks = SDW_INTEL_CLK_STOP_TEARDOWN; + res.clock_stop_quirks = sdw_clock_stop_quirks; /* * ops and arg fields are not populated for now, From 4582142e4db5fb05469950fdedc4ac5612b7cc10 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Fri, 6 Dec 2019 17:24:41 +0800 Subject: [PATCH 078/170] Asoc: SOF: Intel: hda: check SoundWire wakeen interrupt in irq thread If pci device is in D0, wakeen interrupt will be aggregated at cAVS level as interrupt. This commit check the wakeen status and process it in irq thread Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda.c | 16 ++++++++++++++++ sound/soc/sof/intel/hda.h | 6 ++++++ 2 files changed, 22 insertions(+) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 037c27e73301bb..c3722368f9081a 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -243,6 +243,19 @@ static irqreturn_t hda_dsp_sdw_thread(int irq, void *context) return sdw_intel_thread(irq, context); } +static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + + hdev = sdev->pdata->hw_pdata; + if (hdev->sdw && + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_SNDW_WAKE_STS)) + return true; + + return false; +} + void hda_sdw_process_wakeen(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hdev; @@ -693,6 +706,9 @@ static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context) if (hda_dsp_check_sdw_irq(sdev)) hda_dsp_sdw_thread(irq, hdev->sdw); + if (hda_sdw_check_wakeen_irq(sdev)) + hda_sdw_process_wakeen(sdev); + /* enable GIE interrupt */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 339d9690cf1047..75da16e1aa7bad 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -233,6 +233,7 @@ #define HDA_DSP_REG_ADSPIS2 (HDA_DSP_GEN_BASE + 0x14) #define HDA_DSP_REG_ADSPIS2_SNDW BIT(5) +#define HDA_DSP_REG_SNDW_WAKE_STS 0x2C192 /* Intel HD Audio Inter-Processor Communication Registers */ #define HDA_DSP_IPC_BASE 0x40 @@ -662,6 +663,11 @@ static inline irqreturn_t hda_dsp_sdw_thread(int irq, void *context) return IRQ_HANDLED; } +static inline bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev) +{ + return false; +} + static void hda_sdw_process_wakeen(struct snd_sof_dev *sdev) { } From 435783881f279de3aacc792bc5bff82c05d1c173 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 16 Dec 2019 13:50:53 -0600 Subject: [PATCH 079/170] ASoC: SOF: Intel: hda: import SOUNDWIRE_INIT namespace Make sure the SoundWire driver can be loaded Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index c3722368f9081a..f469d3bdbca183 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1123,3 +1123,4 @@ MODULE_LICENSE("Dual BSD/GPL"); MODULE_IMPORT_NS(SND_SOC_SOF_HDA_AUDIO_CODEC); MODULE_IMPORT_NS(SND_SOC_SOF_HDA_AUDIO_CODEC_I915); MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); +MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT); From 97ad00f207698c6f10132e4f329e3ba6179ea5a2 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 18 Apr 2018 10:35:14 +0530 Subject: [PATCH 080/170] 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 14668204900790..a0cb17a06efa6e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1058,6 +1058,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 0290fb3898359e..985717ed133c6c 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -279,6 +279,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 @@ -569,6 +571,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 50867f53d82df243434ec897b502710cf2a659e8 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Fri, 2 Aug 2019 10:26:02 +0800 Subject: [PATCH 081/170] 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 a0cb17a06efa6e..aea61b6ede1b07 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1067,6 +1067,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 985717ed133c6c..653c7d9e94e638 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -280,6 +280,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 @@ -572,6 +573,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 5baeac808f5cb1dbf0df896c2f5d1ed197913e58 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 15 Aug 2019 10:49:56 +0800 Subject: [PATCH 082/170] 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 aea61b6ede1b07..c2dd6eb7c7f3ab 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1073,6 +1073,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 653c7d9e94e638..b64a387a14d641 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -281,6 +281,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 @@ -574,6 +575,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 39bca0765102a42736e44b2ac1f10d98b23f8270 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Wed, 21 Aug 2019 17:40:49 +0800 Subject: [PATCH 083/170] 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 c2dd6eb7c7f3ab..543f5efe89a6fd 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1082,6 +1082,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 b64a387a14d641..a70ed120ae6f02 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -282,6 +282,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 @@ -576,6 +577,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 1b23cf3f11b4e6eb82304fa084149c91f8ea16c1 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 18 Apr 2019 16:20:05 -0500 Subject: [PATCH 084/170] 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 a70ed120ae6f02..2cdb67dbff198e 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 705af96d05bbe53b019b275ca1afa4d1f36689fe Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Fri, 18 Oct 2019 17:02:29 +0800 Subject: [PATCH 085/170] 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 836ffea24db40236f8cbbb2860794774862a5aea Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 23 Oct 2019 13:43:46 +0800 Subject: [PATCH 086/170] 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 a37c44a33d70bffb0878be5f3bbe92b5092336f1 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 23 Oct 2019 13:44:08 +0800 Subject: [PATCH 087/170] 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 af97914f5d123bb7ae640e960618bcfa31d0d441 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 23 Oct 2019 13:44:23 +0800 Subject: [PATCH 088/170] 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 62b9eda7618e037b8694ebd2e458b75324817558 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Thu, 24 Oct 2019 13:29:54 +0800 Subject: [PATCH 089/170] 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 d42f2e0da750bc08bfefa3410eabed9be74eb289 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 24 Oct 2019 20:50:50 +0800 Subject: [PATCH 090/170] 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 bfca7ec77e6184cf671b477a2772d71bab62627c Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Fri, 25 Oct 2019 10:16:19 +0800 Subject: [PATCH 091/170] 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 62ebe8d73439a4c6ffd67cc6c53cb28ade782e51 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 28 Oct 2019 17:26:53 +0800 Subject: [PATCH 092/170] 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 a615a1272046ff814b208b8ae55e9fb29edcc67c Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 28 Oct 2019 17:27:06 +0800 Subject: [PATCH 093/170] 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 878f58658488edab16286dd582258752dbb3a950 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 28 Oct 2019 17:27:21 +0800 Subject: [PATCH 094/170] 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 c0a534a0ade6923000cc30a6968eaa63798e411c Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Tue, 29 Oct 2019 11:28:03 +0800 Subject: [PATCH 095/170] 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 93383d28c254dcc526a74cb82d9cfb9f3fb5c8ff Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 30 Oct 2019 18:34:28 +0800 Subject: [PATCH 096/170] 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 5375d7eba305b323c54225b4184bd8d10f67bf65 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 4 Nov 2019 14:10:01 +0800 Subject: [PATCH 097/170] 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 1c6289d0e212b2093990e2e706ded59a3e42ad9d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 4 Nov 2019 12:56:51 -0600 Subject: [PATCH 098/170] 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 c009d3c26ded1c00062890ce44e5ba9a948d9bd3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 4 Nov 2019 12:56:52 -0600 Subject: [PATCH 099/170] 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 9ec56d8d6754eb5d845ff449e2763f886095d13e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 4 Nov 2019 12:56:52 -0600 Subject: [PATCH 100/170] 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 d0e8e04b69d7c36e38c91968a0442680969d656a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 4 Nov 2019 12:56:53 -0600 Subject: [PATCH 101/170] 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 a72cbaad49a6d25fdc4f4344da8ffcf6f05d2381 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 7 Nov 2019 14:40:08 +0800 Subject: [PATCH 102/170] 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 f17134be9e10e88ed5bd7276c560ed510fa8c16a Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 7 Nov 2019 14:40:22 +0800 Subject: [PATCH 103/170] 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 04eda04d00a5c3e083d4684bc20067fbf46799ff Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 7 Nov 2019 14:40:34 +0800 Subject: [PATCH 104/170] 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 ce38a9dd917ccb5761c24e7ba47ac2b00e1fc5a1 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 7 Nov 2019 14:45:10 +0800 Subject: [PATCH 105/170] 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 cbe4765d2d9f628ece53b0dac36c382d924b67a2 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 7 Nov 2019 14:45:23 +0800 Subject: [PATCH 106/170] 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 63e64a088c033ccbe6920eadc8cea8f5c5e6153e Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 7 Nov 2019 14:45:41 +0800 Subject: [PATCH 107/170] 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 35e0a556a5d5e143458e5a27aacb69401222a509 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Mon, 11 Nov 2019 10:19:52 +0800 Subject: [PATCH 108/170] 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 45ac5733095cecee4178f21873c20d7898e20ef2 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 8 Nov 2019 17:42:00 -0600 Subject: [PATCH 109/170] 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 eaef7d5eebfd8062d328abe20f6aa0f83829912e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 8 Nov 2019 17:42:00 -0600 Subject: [PATCH 110/170] 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 3c75fd74c8808b17c3853c108d45b2208f584bcd Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 8 Nov 2019 17:42:01 -0600 Subject: [PATCH 111/170] 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 c24b2be7e5275cd1108eaeac2f69db930e273ce5 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 8 Nov 2019 17:42:01 -0600 Subject: [PATCH 112/170] 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 54b4d87a13b47232c833bcdfb0c4df4627e8d3dc Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 13 Nov 2019 17:21:42 +0800 Subject: [PATCH 113/170] 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 3864747d49d0a4be42c53760fa143e96df5bf583 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Fri, 22 Nov 2019 13:51:27 +0800 Subject: [PATCH 114/170] 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 490cd83fafc4bd9cc487f3b96aaafe0f573bb141 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Fri, 22 Nov 2019 13:22:11 +0800 Subject: [PATCH 115/170] 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 | 154 ++++++++++++++++++++++---------------- sound/soc/codecs/rt5682.h | 3 + 2 files changed, 92 insertions(+), 65 deletions(-) diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index ae6f6121bc1b0f..6a65d1efee41dc 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; @@ -805,10 +806,13 @@ 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_CTRL, 0x000f); + regmap_write(rt5682->regmap, RT5682_I2C_MODE, 1); + } } /** * rt5682_sel_asrc_clk_src - select ASRC clock source for a set of filters @@ -871,6 +875,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); @@ -880,8 +886,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); @@ -999,62 +1012,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; @@ -1134,11 +1154,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); } @@ -1232,6 +1254,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 && @@ -2332,7 +2357,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 @@ -2474,8 +2499,7 @@ static void rt5682_calibrate(struct rt5682_priv *rt5682) mutex_lock(&rt5682->calibrate_mutex); - rt5682_reset(rt5682->regmap); - regmap_write(rt5682->regmap, RT5682_I2C_CTRL, 0x000f); + 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); @@ -2586,7 +2610,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); @@ -2676,7 +2700,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 3c1e4d7176505f3f703778cae91354082ce79039 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Tue, 26 Nov 2019 16:32:18 +0800 Subject: [PATCH 116/170] 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 543f5efe89a6fd..89421d817007cb 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1058,6 +1058,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 2cdb67dbff198e..663a5431706084 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 @@ -471,6 +472,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 6a65d1efee41dc..8c477426956189 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}, @@ -806,6 +781,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); @@ -1711,6 +1701,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, @@ -1727,12 +1719,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 */ @@ -1885,8 +1884,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"}, @@ -1895,6 +1894,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"}, @@ -1902,10 +1905,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"}, @@ -2402,6 +2419,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", @@ -2434,6 +2639,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 = { @@ -2544,6 +2770,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 612d0ad89aaf904af152f49fb8e1f30030c373d2 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Mon, 2 Dec 2019 19:03:11 +0800 Subject: [PATCH 117/170] 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 c9beb58ed4917e8ee19b97c12f2d8a8a7b6a5d69 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 4 Dec 2019 16:56:10 +0800 Subject: [PATCH 118/170] ASoC: rt5682: Fix the merging conflict This patch fixes the patch "ASoC: rt5682: fix i2c arbitration lost issue" that is conflicted during the merging. Signed-off-by: Oder Chiou --- sound/soc/codecs/rt5682.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 8c477426956189..975984006a1b5e 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -800,7 +800,6 @@ static void rt5682_reset(struct rt5682_priv *rt5682) { regmap_write(rt5682->regmap, RT5682_RESET, 0); if (!rt5682->is_sdw) { - regmap_write(rt5682->regmap, RT5682_I2C_CTRL, 0x000f); regmap_write(rt5682->regmap, RT5682_I2C_MODE, 1); } } @@ -2726,6 +2725,7 @@ static void rt5682_calibrate(struct rt5682_priv *rt5682) mutex_lock(&rt5682->calibrate_mutex); rt5682_reset(rt5682); + regmap_write(rt5682->regmap, RT5682_I2C_CTRL, 0x000f); regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2af); usleep_range(15000, 20000); regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xf2af); From 6d7e6f282694b5964f45e5a072cf53c46bc72961 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 5 Dec 2019 17:20:40 +0800 Subject: [PATCH 119/170] ASoC: rt700: mark cache_dirty if redo the io_init funciton If the resume process redo the io_init function, I want to make sure the cache value will be synced again. And the first_init variable changes name to first_hw_init. Signed-off-by: Shuming Fan --- sound/soc/codecs/rt700.c | 15 ++++++++------- sound/soc/codecs/rt700.h | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 44a8cafbad5bfa..fbb3f5b3b29b0c 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -1114,7 +1114,7 @@ int rt700_init(struct device *dev, struct regmap *sdw_regmap, * HW init will be performed when device reports present */ rt700->hw_init = false; - rt700->first_init = false; + rt700->first_hw_init = false; ret = devm_snd_soc_register_component(dev, &soc_codec_dev_rt700, @@ -1133,7 +1133,7 @@ int rt700_io_init(struct device *dev, struct sdw_slave *slave) if (rt700->hw_init) return 0; - if (rt700->first_init) { + if (rt700->first_hw_init) { regcache_cache_only(rt700->regmap, false); regcache_cache_bypass(rt700->regmap, true); } @@ -1141,7 +1141,7 @@ int rt700_io_init(struct device *dev, struct sdw_slave *slave) /* * PM runtime is only enabled when a Slave reports as Attached */ - if (!rt700->first_init) { + if (!rt700->first_hw_init) { /* set autosuspend parameters */ pm_runtime_set_autosuspend_delay(&slave->dev, 3000); pm_runtime_use_autosuspend(&slave->dev); @@ -1201,7 +1201,7 @@ 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); - if (!rt700->first_init) { + if (!rt700->first_hw_init) { INIT_DELAYED_WORK(&rt700->jack_detect_work, rt700_jack_detect_handler); INIT_DELAYED_WORK(&rt700->jack_btn_check_work, @@ -1215,10 +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) + if (rt700->first_hw_init) { regcache_cache_bypass(rt700->regmap, false); - else - rt700->first_init = true; + regcache_mark_dirty(rt700->regmap); + } else + rt700->first_hw_init = true; /* Mark Slave initialization complete */ rt700->hw_init = true; diff --git a/sound/soc/codecs/rt700.h b/sound/soc/codecs/rt700.h index 11f046e6255e29..8e6e233ce3aad9 100644 --- a/sound/soc/codecs/rt700.h +++ b/sound/soc/codecs/rt700.h @@ -20,7 +20,7 @@ struct rt700_priv { enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; - bool first_init; + bool first_hw_init; struct snd_soc_jack *hs_jack; struct delayed_work jack_detect_work; struct delayed_work jack_btn_check_work; From 7c905455ec502200d97a6ec533c622c65d4b7a7c Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 5 Dec 2019 17:21:15 +0800 Subject: [PATCH 120/170] ASoC: rt711: mark cache_dirty if redo the io_init funciton If the resume process redo the io_init function, I want to make sure the cache value will be synced again. And the first_init variable changes name to first_hw_init. Signed-off-by: Shuming Fan --- sound/soc/codecs/rt711.c | 15 ++++++++------- sound/soc/codecs/rt711.h | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index 4090dc09694d0b..688fe05b0ee961 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -1152,7 +1152,7 @@ int rt711_init(struct device *dev, struct regmap *sdw_regmap, * HW init will be performed when device reports present */ rt711->hw_init = false; - rt711->first_init = false; + rt711->first_hw_init = false; /* JD source uses JD2 in default */ rt711->jd_src = RT711_JD2; @@ -1174,7 +1174,7 @@ int rt711_io_init(struct device *dev, struct sdw_slave *slave) if (rt711->hw_init) return 0; - if (rt711->first_init) { + if (rt711->first_hw_init) { regcache_cache_only(rt711->regmap, false); regcache_cache_bypass(rt711->regmap, true); } @@ -1182,7 +1182,7 @@ int rt711_io_init(struct device *dev, struct sdw_slave *slave) /* * PM runtime is only enabled when a Slave reports as Attached */ - if (!rt711->first_init) { + if (!rt711->first_hw_init) { /* set autosuspend parameters */ pm_runtime_set_autosuspend_delay(&slave->dev, 3000); pm_runtime_use_autosuspend(&slave->dev); @@ -1250,7 +1250,7 @@ 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); - if (rt711->first_init) + if (rt711->first_hw_init) rt711_calibration(rt711); else { INIT_DELAYED_WORK(&rt711->jack_detect_work, @@ -1269,10 +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) + if (rt711->first_hw_init) { regcache_cache_bypass(rt711->regmap, false); - else - rt711->first_init = true; + regcache_mark_dirty(rt711->regmap); + } else + rt711->first_hw_init = true; /* Mark Slave initialization complete */ rt711->hw_init = true; diff --git a/sound/soc/codecs/rt711.h b/sound/soc/codecs/rt711.h index ac7509bfa32d8b..c4b0572a5f9d44 100644 --- a/sound/soc/codecs/rt711.h +++ b/sound/soc/codecs/rt711.h @@ -20,7 +20,7 @@ struct rt711_priv { enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; - bool first_init; + bool first_hw_init; struct snd_soc_jack *hs_jack; struct delayed_work jack_detect_work; struct delayed_work jack_btn_check_work; From 5f71283f7e1d023fa7b2d20c98221d8786bedfde Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 5 Dec 2019 17:21:44 +0800 Subject: [PATCH 121/170] ASoC: rt715: mark cache_dirty if redo the io_init funciton If the resume process redo the io_init function, I want to make sure the cache value will be synced again. And the first_init variable changes name to first_hw_init. Signed-off-by: Shuming Fan --- sound/soc/codecs/rt715.c | 11 +++++++---- sound/soc/codecs/rt715.h | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index fce9c6939eda0c..5c6f05b8d8ab3c 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -779,7 +779,7 @@ int rt715_init(struct device *dev, struct regmap *sdw_regmap, * HW init will be performed when device reports present */ rt715->hw_init = false; - rt715->first_init = false; + rt715->first_hw_init = false; ret = devm_snd_soc_register_component(dev, &soc_codec_dev_rt715, @@ -799,7 +799,7 @@ int rt715_io_init(struct device *dev, struct sdw_slave *slave) /* * PM runtime is only enabled when a Slave reports as Attached */ - if (!rt715->first_init) { + if (!rt715->first_hw_init) { /* set autosuspend parameters */ pm_runtime_set_autosuspend_delay(&slave->dev, 3000); pm_runtime_use_autosuspend(&slave->dev); @@ -811,8 +811,6 @@ int rt715_io_init(struct device *dev, struct sdw_slave *slave) pm_runtime_mark_last_busy(&slave->dev); pm_runtime_enable(&slave->dev); - - rt715->first_init = true; } pm_runtime_get_noresume(&slave->dev); @@ -855,6 +853,11 @@ int rt715_io_init(struct device *dev, struct sdw_slave *slave) /* Finish Initial Settings, set power to D3 */ regmap_write(rt715->regmap, RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + if (rt715->first_hw_init) + regcache_mark_dirty(rt715->regmap); + else + rt715->first_hw_init = true; + /* Mark Slave initialization complete */ rt715->hw_init = true; diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h index 52d24cfb581d1b..df0f24f9bc0c77 100644 --- a/sound/soc/codecs/rt715.h +++ b/sound/soc/codecs/rt715.h @@ -21,7 +21,7 @@ struct rt715_priv { enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; - bool first_init; + bool first_hw_init; }; struct sdw_stream_data { From fcf1cf883c0981d483ded74c87997bcc45b91659 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 5 Dec 2019 17:22:07 +0800 Subject: [PATCH 122/170] ASoC: rt1308-sdw: mark cache_dirty if redo the io_init funciton If the resume process redo the io_init function, I want to make sure the cache value will be synced again. And the first_init variable changes name to first_hw_init. Signed-off-by: Shuming Fan --- sound/soc/codecs/rt1308-sdw.c | 13 +++++++------ sound/soc/codecs/rt1308-sdw.h | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index c60db67e33eb22..8df824af6e6e74 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -185,7 +185,7 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) if (ret < 0) goto _io_init_err_; - if (rt1308->first_init) { + if (rt1308->first_hw_init) { regcache_cache_only(rt1308->regmap, false); regcache_cache_bypass(rt1308->regmap, true); } @@ -193,7 +193,7 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) /* * PM runtime is only enabled when a Slave reports as Attached */ - if (!rt1308->first_init) { + if (!rt1308->first_hw_init) { /* set autosuspend parameters */ pm_runtime_set_autosuspend_delay(&slave->dev, 3000); pm_runtime_use_autosuspend(&slave->dev); @@ -271,10 +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) + if (rt1308->first_hw_init) { regcache_cache_bypass(rt1308->regmap, false); - else - rt1308->first_init = true; + regcache_mark_dirty(rt1308->regmap); + } else + rt1308->first_hw_init = true; /* Mark Slave initialization complete */ rt1308->hw_init = true; @@ -639,7 +640,7 @@ static int rt1308_sdw_init(struct device *dev, struct regmap *regmap, * HW init will be performed when device reports present */ rt1308->hw_init = false; - rt1308->first_init = false; + rt1308->first_hw_init = false; ret = devm_snd_soc_register_component(dev, &soc_component_sdw_rt1308, diff --git a/sound/soc/codecs/rt1308-sdw.h b/sound/soc/codecs/rt1308-sdw.h index e83440be32f240..c9341e70d6cf56 100644 --- a/sound/soc/codecs/rt1308-sdw.h +++ b/sound/soc/codecs/rt1308-sdw.h @@ -159,7 +159,7 @@ struct rt1308_sdw_priv { enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; - bool first_init; + bool first_hw_init; }; struct sdw_stream_data { From 60f3a61e23e5fb1ec2c25c866807646db8c8eb82 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 9 Dec 2019 14:45:27 +0800 Subject: [PATCH 123/170] ASoC: rt700: Add pin sense check during jack type detection To avoid IO error, the codec driver needs to check the pin sense during jack type detection. Due to the jack type detection will take some times in the loop, in case the jack is removed during this time, then the driver goes into suspend mode. The IO error will occur in the rest of process. Signed-off-by: Shuming Fan --- sound/soc/codecs/rt700.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index fbb3f5b3b29b0c..257686c936cfff 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -115,6 +115,7 @@ static int rt700_headset_detect(struct rt700_priv *rt700) { unsigned int buf, loop = 0; int ret; + unsigned int jack_status = 0, reg; ret = rt700_index_read(rt700->regmap, RT700_COMBO_JACK_AUTO_CTL2, &buf); @@ -130,6 +131,11 @@ static int rt700_headset_detect(struct rt700_priv *rt700) RT700_COMBO_JACK_AUTO_CTL2, &buf); if (ret < 0) goto io_error; + + reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT; + ret = regmap_read(rt700->regmap, reg, &jack_status); + if ((jack_status & (1 << 31)) == 0) + goto remove_error; } if (loop >= 500) @@ -150,6 +156,9 @@ static int rt700_headset_detect(struct rt700_priv *rt700) io_error: pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); return ret; +remove_error: + pr_err_ratelimited("Jack removal in %s\n", __func__); + return -ENODEV; } static void rt700_jack_detect_handler(struct work_struct *work) From 5cf171e18ad76e2677b7913cc7a34d64d555cd22 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 9 Dec 2019 14:45:46 +0800 Subject: [PATCH 124/170] ASoC: rt711: Add pin sense check during jack type detection To avoid IO error, the codec driver needs to check the pin sense during jack type detection. Due to the jack type detection will take some times in the loop, in case the jack is removed during this time, then the driver goes into suspend mode. The IO error will occur in the rest of process. Signed-off-by: Shuming Fan --- sound/soc/codecs/rt711.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index 688fe05b0ee961..0d05f331c18315 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -193,6 +193,7 @@ static int rt711_headset_detect(struct rt711_priv *rt711) { unsigned int buf, loop = 0; int ret; + unsigned int jack_status = 0, reg; ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG, RT711_COMBO_JACK_AUTO_CTL2, &buf); @@ -208,6 +209,13 @@ static int rt711_headset_detect(struct rt711_priv *rt711) RT711_COMBO_JACK_AUTO_CTL2, &buf); if (ret < 0) goto io_error; + + reg = RT711_VERB_GET_PIN_SENSE | RT711_HP_OUT; + ret = regmap_read(rt711->regmap, reg, &jack_status); + if (ret < 0) + goto io_error; + if ((jack_status & (1 << 31)) == 0) + goto remove_error; } if (loop >= 500) @@ -228,6 +236,9 @@ static int rt711_headset_detect(struct rt711_priv *rt711) io_error: pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); return ret; +remove_error: + pr_err_ratelimited("Jack removal in %s\n", __func__); + return -ENODEV; } static void rt711_jack_detect_handler(struct work_struct *work) From 69efc295eda1750764792512af3678e29ed492a3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 6 Dec 2019 12:54:01 -0600 Subject: [PATCH 125/170] ASoC: codecs: rt711-sdw: add traces for suspend-resume These traces show the suspend can happen before jack detection is complete [ 184.045885] rt711 sdw:0:25d:711:0: [rt711_sdw_read] 7520 85a0 9c20 aca0 => 00000404 [ 184.056884] rt711 sdw:0:25d:711:0: [rt711_sdw_read] 7520 85a0 9c20 aca0 => 00000404 [ 184.067823] rt711 sdw:0:25d:711:0: [rt711_sdw_read] 7520 85a0 9c20 aca0 => 00000404 [ 184.078824] rt711 sdw:0:25d:711:0: [rt711_sdw_read] 7520 85a0 9c20 aca0 => 00000404 [ 184.089928] rt711 sdw:0:25d:711:0: [rt711_sdw_read] 7520 85a0 9c20 aca0 => 00000404 [ 184.100949] rt711 sdw:0:25d:711:0: [rt711_sdw_read] 7520 85a0 9c20 aca0 => 00000404 [ 184.111990] rt711 sdw:0:25d:711:0: [rt711_sdw_read] 7520 85a0 9c20 aca0 => 00000404 [ 184.121175] rt711 sdw:0:25d:711:0: rt711_dev_suspend start [ 184.121183] rt711 sdw:0:25d:711:0: rt711_dev_suspend end [ 184.121205] Failed to get private value: 752046 => 0000 ret=-16 [ 184.121214] IO error in rt711_headset_detect, ret -16 [ 184.121221] IO error in rt711_jack_detect_handler, ret -16 Signed-off-by: Pierre-Louis Bossart --- sound/soc/codecs/rt711-sdw.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index 4174d175925c33..435da5b121dde4 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -492,11 +492,15 @@ static int rt711_dev_suspend(struct device *dev) { struct rt711_priv *rt711 = dev_get_drvdata(dev); + dev_dbg(dev, "%s start\n", __func__); + if (!rt711->hw_init) return 0; regcache_cache_only(rt711->regmap, true); + dev_dbg(dev, "%s end\n", __func__); + return 0; } @@ -508,6 +512,8 @@ static int rt711_dev_resume(struct device *dev) struct rt711_priv *rt711 = dev_get_drvdata(dev); unsigned long time; + dev_dbg(dev, "%s start\n", __func__); + if (!rt711->hw_init) return 0; @@ -527,6 +533,8 @@ static int rt711_dev_resume(struct device *dev) regcache_sync_region(rt711->regmap, 0x3000, 0x8fff); regcache_sync_region(rt711->regmap, 0x752009, 0x752091); + dev_dbg(dev, "%s end\n", __func__); + return 0; } From 105e892fe0b272348a2b8c300858ead86ff83245 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Sat, 24 Aug 2019 15:52:40 -0500 Subject: [PATCH 126/170] ASoC: Intel: common: soc-acpi: declare new tables for SoundWire We cannot really lump SoundWire-based configurations into the same tables since the mechanisms to identify boards is based on link configurations and _ADR instead of _HID for I2S, so define new tables Signed-off-by: Pierre-Louis Bossart --- include/sound/soc-acpi-intel-match.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h index 20c0bee3b959db..ab6f75a86611dc 100644 --- a/include/sound/soc-acpi-intel-match.h +++ b/include/sound/soc-acpi-intel-match.h @@ -31,6 +31,12 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_ehl_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_sdw_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_sdw_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_sdw_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_sdw_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[]; + /* * generic table used for HDA codec-based platforms, possibly with * additional ACPI-enumerated codecs From 1e8f6e55e000889fd1bd2d47645dd1cea442838e Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 20 Jun 2019 17:47:01 +0800 Subject: [PATCH 127/170] ASoC: Intel: common: add match tables for ICL w/ SoundWire The two configurations are with the Realtek 3-in-1 board requiring all 4 links to be enabled, or basic configuration with the on-board RT700 using link0. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- .../soc/intel/common/soc-acpi-intel-icl-match.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-icl-match.c b/sound/soc/intel/common/soc-acpi-intel-icl-match.c index 38977669b57653..63b6197a605606 100644 --- a/sound/soc/intel/common/soc-acpi-intel-icl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-icl-match.c @@ -33,5 +33,22 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[] = { }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_icl_machines); +struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_sdw_machines[] = { + { + .link_mask = 0xF, /* 4 active links required */ + .drv_name = "sdw_rt711_rt1308_rt715", + .sof_fw_filename = "sof-icl.ri", + .sof_tplg_filename = "sof-icl-rt711-rt1308-rt715.tplg", + }, + { + .link_mask = 0x1, /* rt700 connected on link0 */ + .drv_name = "sdw_rt700", + .sof_fw_filename = "sof-icl.ri", + .sof_tplg_filename = "sof-icl-rt700.tplg", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_icl_sdw_machines); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel Common ACPI Match module"); From 12f3d88ef1d7d1813146f905cfe504f96ae735be Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Mon, 22 Jul 2019 17:20:53 +0800 Subject: [PATCH 128/170] ASoC: Intel: common: add match tables for CNL/CFL/CML w/ SoundWire The two configurations are with the Realtek 3-in-1 board requiring all 4 links to be enabled, or basic configuration with the on-board RT700 using link1. For now we only have definitions for CML, CNL and CFL are just placeholders. Signed-off-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- .../intel/common/soc-acpi-intel-cfl-match.c | 5 ++++ .../intel/common/soc-acpi-intel-cml-match.c | 23 +++++++++++++++++++ .../intel/common/soc-acpi-intel-cnl-match.c | 5 ++++ 3 files changed, 33 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-cfl-match.c b/sound/soc/intel/common/soc-acpi-intel-cfl-match.c index d6fd2026d0b863..ff9d6938b9f668 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cfl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cfl-match.c @@ -14,5 +14,10 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_machines[] = { }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cfl_machines); +struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_sdw_machines[] = { + {} +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cfl_sdw_machines); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-cml-match.c b/sound/soc/intel/common/soc-acpi-intel-cml-match.c index fb9ba881970604..70d53cafbc4474 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cml-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cml-match.c @@ -59,5 +59,28 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[] = { }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cml_machines); +struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_sdw_machines[] = { + { + .link_mask = 0xF, /* 4 active links required */ + .drv_name = "sdw_rt711_rt1308_rt715", + .sof_fw_filename = "sof-cml.ri", + .sof_tplg_filename = "sof-cml-rt711-rt1308-rt715.tplg", + }, + { + .link_mask = 0xB, /* 3 active links required */ + .drv_name = "sdw_rt711_rt1308_rt715", + .sof_fw_filename = "sof-cml.ri", + .sof_tplg_filename = "sof-cml-rt711-rt1308-mono-rt715.tplg", + }, + { + .link_mask = 0x2, /* RT700 connected on Link1 */ + .drv_name = "sdw_rt700", + .sof_fw_filename = "sof-cml.ri", + .sof_tplg_filename = "sof-cml-rt700.tplg", + }, + {} +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cml_sdw_machines); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c index 27588841c8b005..828980d5630d46 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -27,5 +27,10 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines); +struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_sdw_machines[] = { + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_sdw_machines); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel Common ACPI Match module"); From f10e759656e9954928fa2559dacb2c0c44ce9c5e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 4 Sep 2019 10:01:58 -0500 Subject: [PATCH 129/170] ASoC: Intel: common: add match tables for TGL w/ SoundWire RT711 is in SoundWire mode on link0 and RT1308 on SSP2. Signed-off-by: Pierre-Louis Bossart --- .../soc/intel/common/soc-acpi-intel-tgl-match.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c index b4687a5d1962e6..3d8daacfb334d4 100644 --- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c @@ -17,9 +17,10 @@ static struct snd_soc_acpi_codecs tgl_codecs = { struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = { { .id = "10EC1308", - .drv_name = "tgl_rt1308", + .drv_name = "rt711_rt1308", + .link_mask = 0x1, /* RT711 on SoundWire link0 */ .sof_fw_filename = "sof-tgl.ri", - .sof_tplg_filename = "sof-tgl-rt1308.tplg", + .sof_tplg_filename = "sof-tgl-rt711-rt1308.tplg", }, { .id = "10EC5682", @@ -33,5 +34,16 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = { }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_machines); +struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[] = { + { + .link_mask = 0x1, /* this will only enable rt711 for now */ + .drv_name = "sdw_rt711_rt1308_rt715", + .sof_fw_filename = "sof-tgl.ri", + .sof_tplg_filename = "sof-tgl-rt711-rt1308-rt715.tplg", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_sdw_machines); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel Common ACPI Match module"); From 1df597a95e3764526c56f1682c1821d676c774c3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 4 Sep 2019 10:05:06 -0500 Subject: [PATCH 130/170] ASoC: SOF: Intel: reference SoundWire machine lists Use static tables to automatically select the relevant configurations. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/sof-pci-dev.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index 9d09131a6a56cd..1ee58e45063187 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -118,6 +118,7 @@ static const struct sof_dev_desc tng_desc = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE) static const struct sof_dev_desc cnl_desc = { .machines = snd_soc_acpi_intel_cnl_machines, + .alt_machines = snd_soc_acpi_intel_cnl_sdw_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -135,6 +136,7 @@ static const struct sof_dev_desc cnl_desc = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE) static const struct sof_dev_desc cfl_desc = { .machines = snd_soc_acpi_intel_cfl_machines, + .alt_machines = snd_soc_acpi_intel_cfl_sdw_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -154,6 +156,7 @@ static const struct sof_dev_desc cfl_desc = { static const struct sof_dev_desc cml_desc = { .machines = snd_soc_acpi_intel_cml_machines, + .alt_machines = snd_soc_acpi_intel_cml_sdw_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -171,6 +174,7 @@ static const struct sof_dev_desc cml_desc = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE) static const struct sof_dev_desc icl_desc = { .machines = snd_soc_acpi_intel_icl_machines, + .alt_machines = snd_soc_acpi_intel_icl_sdw_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -188,6 +192,7 @@ static const struct sof_dev_desc icl_desc = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE) static const struct sof_dev_desc tgl_desc = { .machines = snd_soc_acpi_intel_tgl_machines, + .alt_machines = snd_soc_acpi_intel_tgl_sdw_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, From 497bfc99420c71c842d974e380ae266789c24006 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 10 Apr 2019 20:58:52 -0500 Subject: [PATCH 131/170] ASoC: Intel: boards: add sdw_rt700 machine driver This machine driver supports CML and ICL RVP boards in SoundWire mode only. These boards need to be reworked to support the SoundWire link and the BIOS advanced config menu also needs to select SoundWire for links 0 (ICL) or 1 (CML). Unlike a lot of machine drivers, we use different DAI links for capture and playback since SoundWire PDIs can do capture OR playback, not both simultaneously. Signed-off-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 16 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/sdw_rt700.c | 319 +++++++++++++++++++++++++++++ 3 files changed, 337 insertions(+) create mode 100644 sound/soc/intel/boards/sdw_rt700.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 1e9701d9a56088..6a635f4eba56d1 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -551,4 +551,20 @@ config SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH endif ## SND_SOC_SOF_JASPERLAKE +if SND_SOC_SOF_INTEL_SOUNDWIRE + +config SND_SOC_INTEL_SOUNDWIRE_RT700_MACH + tristate "SoundWire with RT700 codec" + depends on SOUNDWIRE && ACPI + select SND_SOC_RT700_SDW + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI if SND_SOC_SOF_HDA_LINK + help + Add support for Intel SoundWire-based platforms connected to RT700 + on link 0 or 1 + If unsure select "N" + +endif + + endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 7b97f9527befb9..f8a59d67351311 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -33,6 +33,7 @@ snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o snd-soc-tgl-rt1308-objs := tgl_rt1308.o hda_dsp_common.o snd-soc-sof_da7219_max98373-objs := sof_da7219_max98373.o hda_dsp_common.o +snd-soc-sdw-rt700-objs := sdw_rt700.o hda_dsp_common.o obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o @@ -68,3 +69,4 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ss obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o obj-$(CONFIG_SND_SOC_INTEL_TGL_RT1308_MACH) += snd-soc-tgl-rt1308.o obj-$(CONFIG_SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH) += snd-soc-sof_da7219_max98373.o +obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT700_MACH) += snd-soc-sdw-rt700.o diff --git a/sound/soc/intel/boards/sdw_rt700.c b/sound/soc/intel/boards/sdw_rt700.c new file mode 100644 index 00000000000000..fd46f0b18b149e --- /dev/null +++ b/sound/soc/intel/boards/sdw_rt700.c @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2016-19 Intel Corporation + +/* + * sdw_rt700 - ASOC Machine driver for Intel SoundWire platforms + * connected to ALC700 device + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/hdac_hdmi.h" +#include "hda_dsp_common.h" + +struct mc_private { + struct list_head hdmi_pcm_list; + bool common_hdmi_codec_drv; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) +static struct snd_soc_jack hdmi[3]; + +struct hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +static int hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + /* dai_link id is 1:1 mapped to the PCM device */ + pcm->device = rtd->dai_link->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +#define NAME_SIZE 32 +static int card_late_probe(struct snd_soc_card *card) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(card); + struct hdmi_pcm *pcm; + struct snd_soc_component *component = NULL; + int err, i = 0; + char jack_name[NAME_SIZE]; + + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct hdmi_pcm, + head); + component = pcm->codec_dai->component; + + if (ctx->common_hdmi_codec_drv) + return hda_dsp_hdmi_build_controls(card, component); + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &hdmi[i], + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &hdmi[i]); + if (err < 0) + return err; + + i++; + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} +#else +static int card_late_probe(struct snd_soc_card *card) +{ + return 0; +} +#endif + +static const struct snd_soc_dapm_widget widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +static const struct snd_soc_dapm_route map[] = { + /*Headphones*/ + { "Headphones", NULL, "HP" }, + { "Speaker", NULL, "SPK" }, + { "MIC2", NULL, "AMIC" }, +}; + +static const struct snd_kcontrol_new controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), + SOC_DAPM_PIN_SWITCH("AMIC"), + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +SND_SOC_DAILINK_DEF(sdw0_pin2, + DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin2"))); +SND_SOC_DAILINK_DEF(sdw0_pin3, + DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin3"))); +SND_SOC_DAILINK_DEF(sdw0_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("sdw:0:25d:700:0", "rt700-aif1"))); + +SND_SOC_DAILINK_DEF(sdw1_pin2, + DAILINK_COMP_ARRAY(COMP_CPU("SDW1 Pin2"))); +SND_SOC_DAILINK_DEF(sdw1_pin3, + DAILINK_COMP_ARRAY(COMP_CPU("SDW1 Pin3"))); +SND_SOC_DAILINK_DEF(sdw1_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("sdw:1:25d:700:0", "rt700-aif1"))); + +SND_SOC_DAILINK_DEF(dmic_pin, + DAILINK_COMP_ARRAY(COMP_CPU("DMIC01 Pin"))); + +SND_SOC_DAILINK_DEF(dmic16k_pin, + DAILINK_COMP_ARRAY(COMP_CPU("DMIC16k Pin"))); + +SND_SOC_DAILINK_DEF(dmic_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi"))); + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) +SND_SOC_DAILINK_DEF(idisp1_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp1 Pin"))); +SND_SOC_DAILINK_DEF(idisp1_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi1"))); + +SND_SOC_DAILINK_DEF(idisp2_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp2 Pin"))); +SND_SOC_DAILINK_DEF(idisp2_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi2"))); + +SND_SOC_DAILINK_DEF(idisp3_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp3 Pin"))); +SND_SOC_DAILINK_DEF(idisp3_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi3"))); +#endif + +SND_SOC_DAILINK_DEF(platform, + DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:1f.3"))); + +struct snd_soc_dai_link dailink[] = { + { + .name = "SDW0-Playback", + .id = 0, + .no_pcm = 1, + .dpcm_playback = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw0_pin2, sdw0_codec, platform), + }, + { + .name = "SDW0-Capture", + .id = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw0_pin3, sdw0_codec, platform), + }, + { + .name = "dmic01", + .id = 2, + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(dmic_pin, dmic_codec, platform), + }, + { + .name = "dmic16k", + .id = 3, + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(dmic16k_pin, dmic_codec, platform), + }, +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) + { + .name = "iDisp1", + .id = 4, + .init = hdmi_init, + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(idisp1_pin, idisp1_codec, platform), + }, + { + .name = "iDisp2", + .id = 5, + .init = hdmi_init, + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(idisp2_pin, idisp2_codec, platform), + }, + { + .name = "iDisp3", + .id = 6, + .init = hdmi_init, + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(idisp3_pin, idisp3_codec, platform), + }, +#endif +}; + +/* SoC card */ +static struct snd_soc_card card_sdw_rt700 = { + .name = "sdw-rt700", + .dai_link = dailink, + .num_links = ARRAY_SIZE(dailink), + .controls = controls, + .num_controls = ARRAY_SIZE(controls), + .dapm_widgets = widgets, + .num_dapm_widgets = ARRAY_SIZE(widgets), + .dapm_routes = map, + .num_dapm_routes = ARRAY_SIZE(map), + .late_probe = card_late_probe, +}; + +static int mc_probe(struct platform_device *pdev) +{ + struct mc_private *ctx; + struct snd_soc_acpi_mach *mach; + const char *platform_name; + struct snd_soc_card *card = &card_sdw_rt700; + const char *board; + int ret; + + dev_dbg(&pdev->dev, "Entry %s\n", __func__); + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); +#endif + + board = dmi_get_system_info(DMI_BOARD_NAME); + if (strstr(board, "CometLake U DDR4 HR")) { + dailink[0].name = "SDW1-Playback"; + dailink[0].codecs = sdw1_codec; + dailink[0].num_codecs = ARRAY_SIZE(sdw1_codec); + dailink[0].cpus = sdw1_pin2; + dailink[0].num_cpus = ARRAY_SIZE(sdw1_pin2); + + dailink[1].name = "SDW1-Capture"; + dailink[1].codecs = sdw1_codec; + dailink[1].num_codecs = ARRAY_SIZE(sdw1_codec); + dailink[1].cpus = sdw1_pin3; + dailink[1].num_cpus = ARRAY_SIZE(sdw1_pin3); + } + + card->dev = &pdev->dev; + + /* override platform name, if required */ + mach = (&pdev->dev)->platform_data; + platform_name = mach->mach_params.platform; + + ret = snd_soc_fixup_dai_links_platform_name(card, platform_name); + if (ret) + return ret; + + ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv; + + snd_soc_card_set_drvdata(card, ctx); + + /* Register the card */ + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { + dev_err(card->dev, "snd_soc_register_card failed %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, card); + + return ret; +} + +static struct platform_driver sdw_rt700_driver = { + .driver = { + .name = "sdw_rt700", + .pm = &snd_soc_pm_ops, + }, + .probe = mc_probe, +}; + +module_platform_driver(sdw_rt700_driver); + +MODULE_DESCRIPTION("ASoC SoundWire RT700 Machine driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_AUTHOR("Pierre-Louis Bossart "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sdw_rt700"); From 8b339b3c12ff8a17e4e48a2638c7c4e7ac83b8c9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 26 Aug 2019 15:07:50 -0500 Subject: [PATCH 132/170] ASoC: Intel: boards: add sdw_rt711_rt1308_rt715 3-in-1 config support Add configuration with 4 SoundWire links connected to RT711, RT1308 (2x) and RT715, using the '3-in-1' test card. This topology will also be used on actual form-factors. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 12 + sound/soc/intel/boards/Makefile | 2 + .../soc/intel/boards/sdw_rt711_rt1308_rt715.c | 461 ++++++++++++++++++ 3 files changed, 475 insertions(+) create mode 100644 sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 6a635f4eba56d1..514368de7a7f54 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -564,6 +564,18 @@ config SND_SOC_INTEL_SOUNDWIRE_RT700_MACH on link 0 or 1 If unsure select "N" +config SND_SOC_INTEL_SOUNDWIRE_RT711_RT1308_RT715_MACH + tristate "SoundWire with RT711, RT1308 and RT715" + depends on SOUNDWIRE && ACPI + select SND_SOC_RT711_SDW + select SND_SOC_RT1308_SDW + select SND_SOC_RT715_SDW + select SND_SOC_HDAC_HDMI if SND_SOC_SOF_HDA_LINK + help + Add support for Intel SoundWire-based platforms connected to RT711, + RT1308 and RT715 + If unsure select "N". + endif diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index f8a59d67351311..6f734adbeec654 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -34,6 +34,7 @@ snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o snd-soc-tgl-rt1308-objs := tgl_rt1308.o hda_dsp_common.o snd-soc-sof_da7219_max98373-objs := sof_da7219_max98373.o hda_dsp_common.o snd-soc-sdw-rt700-objs := sdw_rt700.o hda_dsp_common.o +snd-soc-sdw-rt711-rt1308-rt715-objs := sdw_rt711_rt1308_rt715.o hda_dsp_common.o obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o @@ -70,3 +71,4 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o obj-$(CONFIG_SND_SOC_INTEL_TGL_RT1308_MACH) += snd-soc-tgl-rt1308.o obj-$(CONFIG_SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH) += snd-soc-sof_da7219_max98373.o obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT700_MACH) += snd-soc-sdw-rt700.o +obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT711_RT1308_RT715_MACH) += snd-soc-sdw-rt711-rt1308-rt715.o diff --git a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c new file mode 100644 index 00000000000000..b594e0a9effc46 --- /dev/null +++ b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c @@ -0,0 +1,461 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Intel Corporation + +/* + * sdw_rt711_rt1308_rt715 - ASOC Machine driver for Intel SoundWire platforms + * connected to 3 Realtek devices + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/hdac_hdmi.h" +#include "hda_dsp_common.h" + +/* comment out this define for mono configurations */ +#define ENABLE_RT1308_SDW2 + +#define MAX_NO_PROPS 2 + +extern struct bus_type sdw_bus_type; + +enum { + SOF_RT711_JD_SRC_JD1 = 1, + SOF_RT711_JD_SRC_JD2 = 2, +}; + +#define SOF_RT711_JDSRC(quirk) ((quirk) & GENMASK(1, 0)) + +static unsigned long sof_rt711_rt1308_rt715_quirk = SOF_RT711_JD_SRC_JD1; + +struct mc_private { + struct list_head hdmi_pcm_list; + bool common_hdmi_codec_drv; + struct snd_soc_jack sdw_headset; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) +static struct snd_soc_jack hdmi[3]; + +struct hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +static int hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + /* dai_link id is 1:1 mapped to the PCM device */ + pcm->device = rtd->dai_link->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +#define NAME_SIZE 32 +static int card_late_probe(struct snd_soc_card *card) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(card); + struct hdmi_pcm *pcm; + struct snd_soc_component *component = NULL; + int err, i = 0; + char jack_name[NAME_SIZE]; + + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct hdmi_pcm, + head); + component = pcm->codec_dai->component; + + if (ctx->common_hdmi_codec_drv) + return hda_dsp_hdmi_build_controls(card, component); + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &hdmi[i], + NULL, 0); + + if (err) + return err; + + err = snd_jack_add_new_kctl(hdmi[i].jack, + jack_name, SND_JACK_AVOUT); + if (err) + dev_warn(component->dev, "failed creating Jack kctl\n"); + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &hdmi[i]); + if (err < 0) + return err; + + i++; + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} +#else +static int card_late_probe(struct snd_soc_card *card) +{ + return 0; +} +#endif + +static struct snd_soc_jack_pin sdw_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int headset_init(struct snd_soc_pcm_runtime *rtd) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_jack *jack; + int ret; + + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &ctx->sdw_headset, + sdw_jack_pins, + ARRAY_SIZE(sdw_jack_pins)); + if (ret) { + dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n", + ret); + return ret; + } + + jack = &ctx->sdw_headset; + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); + + ret = snd_soc_component_set_jack(component, jack, NULL); + + if (ret) + dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n", + ret); + + return ret; +} + +static int sof_rt711_rt1308_rt715_quirk_cb(const struct dmi_system_id *id) +{ + sof_rt711_rt1308_rt715_quirk = (unsigned long)id->driver_data; + return 1; +} + +static const struct dmi_system_id sof_sdw_rt711_rt1308_rt715_quirk_table[] = { + { + .callback = sof_rt711_rt1308_rt715_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + }, + .driver_data = (void *)(SOF_RT711_JD_SRC_JD2), + }, + {} +}; + +/* + * Note this MUST be called before snd_soc_register_card(), so that the props + * are in place before the codec component driver's probe function parses them. + */ +static int sof_rt711_add_codec_device_props(const char *sdw_dev_name) +{ + struct property_entry props[MAX_NO_PROPS] = {}; + struct device *sdw_dev; + int ret, cnt = 0; + + sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_dev_name); + if (!sdw_dev) + return -EPROBE_DEFER; + + if (SOF_RT711_JDSRC(sof_rt711_rt1308_rt715_quirk)) { + props[cnt++] = PROPERTY_ENTRY_U32( + "realtek,jd-src", + SOF_RT711_JDSRC(sof_rt711_rt1308_rt715_quirk)); + } + + ret = device_add_properties(sdw_dev, props); + put_device(sdw_dev); + + return ret; +} + +static const struct snd_soc_dapm_widget widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +static const struct snd_soc_dapm_route map[] = { + /* Headphones */ + { "Headphone", NULL, "rt711 HP" }, + { "rt711 MIC2", NULL, "Headset Mic" }, + /* Speakers */ + { "Speaker", NULL, "rt1308-1 SPOL" }, + { "Speaker", NULL, "rt1308-1 SPOR" }, +#ifdef ENABLE_RT1308_SDW2 + { "Speaker", NULL, "rt1308-2 SPOL" }, + { "Speaker", NULL, "rt1308-2 SPOR" }, +#endif +}; + +static const struct snd_kcontrol_new controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +SND_SOC_DAILINK_DEF(sdw0_pin2, + DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin2"))); +SND_SOC_DAILINK_DEF(sdw0_pin3, + DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin3"))); +SND_SOC_DAILINK_DEF(sdw0_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("sdw:0:25d:711:0", "rt711-aif1"))); + +SND_SOC_DAILINK_DEF(sdw1_pin2, + DAILINK_COMP_ARRAY(COMP_CPU("SDW1 Pin2"))); +SND_SOC_DAILINK_DEF(sdw1_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("sdw:1:25d:1308:0", "rt1308-aif"))); + +#ifdef ENABLE_RT1308_SDW2 +SND_SOC_DAILINK_DEF(sdw2_pin2, + DAILINK_COMP_ARRAY(COMP_CPU("SDW2 Pin2"))); +SND_SOC_DAILINK_DEF(sdw2_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("sdw:2:25d:1308:0", "rt1308-aif"))); +#endif + +SND_SOC_DAILINK_DEF(sdw3_pin2, + DAILINK_COMP_ARRAY(COMP_CPU("SDW3 Pin2"))); +SND_SOC_DAILINK_DEF(sdw3_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("sdw:3:25d:715:0", "rt715-aif2"))); + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) +SND_SOC_DAILINK_DEF(idisp1_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp1 Pin"))); +SND_SOC_DAILINK_DEF(idisp1_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi1"))); + +SND_SOC_DAILINK_DEF(idisp2_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp2 Pin"))); +SND_SOC_DAILINK_DEF(idisp2_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi2"))); + +SND_SOC_DAILINK_DEF(idisp3_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp3 Pin"))); +SND_SOC_DAILINK_DEF(idisp3_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi3"))); +#endif + +SND_SOC_DAILINK_DEF(platform, + DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:1f.3"))); + +static struct snd_soc_codec_conf codec_conf[] = { + { + .dev_name = "sdw:0:25d:711:0", + .name_prefix = "rt711", + }, + { + .dev_name = "sdw:1:25d:1308:0", + .name_prefix = "rt1308-1", + }, +#ifdef ENABLE_RT1308_SDW2 + { + .dev_name = "sdw:2:25d:1308:0", + .name_prefix = "rt1308-2", + }, +#endif + { + .dev_name = "sdw:3:25d:715:0", + .name_prefix = "rt715", + }, + +}; + +struct snd_soc_dai_link dailink[] = { + { + .name = "SDW0-Playback", + .id = 0, + .init = headset_init, + .no_pcm = 1, + .dpcm_playback = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw0_pin2, sdw0_codec, platform), + }, + { + .name = "SDW0-Capture", + .id = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw0_pin3, sdw0_codec, platform), + }, + { + .name = "SDW1-Playback", + .id = 2, + .no_pcm = 1, + .dpcm_playback = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw1_pin2, sdw1_codec, platform), + }, +#ifdef ENABLE_RT1308_SDW2 + { + .name = "SDW2-Playback", + .id = 3, + .no_pcm = 1, + .dpcm_playback = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw2_pin2, sdw2_codec, platform), + }, +#endif + { + .name = "SDW3-Capture", + .id = 4, + .no_pcm = 1, + .dpcm_capture = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw3_pin2, sdw3_codec, platform), + }, + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) + { + .name = "iDisp1", + .id = 5, + .init = hdmi_init, + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(idisp1_pin, idisp1_codec, platform), + }, + { + .name = "iDisp2", + .id = 6, + .init = hdmi_init, + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(idisp2_pin, idisp2_codec, platform), + }, + { + .name = "iDisp3", + .id = 7, + .init = hdmi_init, + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(idisp3_pin, idisp3_codec, platform), + }, +#endif +}; + +/* SoC card */ +static struct snd_soc_card card_rt700_rt1308_rt715 = { + .name = "sdw-rt711-1308-715", + .dai_link = dailink, + .num_links = ARRAY_SIZE(dailink), + .controls = controls, + .num_controls = ARRAY_SIZE(controls), + .dapm_widgets = widgets, + .num_dapm_widgets = ARRAY_SIZE(widgets), + .dapm_routes = map, + .num_dapm_routes = ARRAY_SIZE(map), + .late_probe = card_late_probe, + .codec_conf = codec_conf, + .num_configs = ARRAY_SIZE(codec_conf), +}; + +static int mc_probe(struct platform_device *pdev) +{ + struct mc_private *ctx; + struct snd_soc_acpi_mach *mach; + const char *platform_name; + struct snd_soc_card *card = &card_rt700_rt1308_rt715; + int ret; + + dev_dbg(&pdev->dev, "Entry %s\n", __func__); + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + dmi_check_system(sof_sdw_rt711_rt1308_rt715_quirk_table); + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); +#endif + + card->dev = &pdev->dev; + + /* override platform name, if required */ + mach = (&pdev->dev)->platform_data; + platform_name = mach->mach_params.platform; + + ret = snd_soc_fixup_dai_links_platform_name(card, platform_name); + if (ret) + return ret; + + ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv; + + snd_soc_card_set_drvdata(card, ctx); + + sof_rt711_add_codec_device_props("sdw:0:25d:711:0"); + /* Register the card */ + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { + dev_err(card->dev, "snd_soc_register_card failed %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, card); + + return ret; +} + +static struct platform_driver sdw_rt711_rt1308_rt715_driver = { + .driver = { + .name = "sdw_rt711_rt1308_rt715", + .pm = &snd_soc_pm_ops, + }, + .probe = mc_probe, +}; + +module_platform_driver(sdw_rt711_rt1308_rt715_driver); + +MODULE_DESCRIPTION("ASoC SoundWire RT711/1308/715 Machine driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_AUTHOR("Pierre-Louis Bossart "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sdw_rt711_rt1308_rt715"); From d07be8dc2aea4ffd37958d16c07995c4ae0187d8 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 17 Sep 2019 18:29:27 -0500 Subject: [PATCH 133/170] ASoC: Intel: boards: sdw_rt711: add machine driver Simpler subset of rt711_rt1308_rt715. The RT711 is assumed to be located on Link0. Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 9 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/sdw_rt711.c | 276 +++++++++++++++++++++++++++++ 3 files changed, 287 insertions(+) create mode 100644 sound/soc/intel/boards/sdw_rt711.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 514368de7a7f54..cfaf741d090eb3 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -564,6 +564,15 @@ config SND_SOC_INTEL_SOUNDWIRE_RT700_MACH on link 0 or 1 If unsure select "N" +config SND_SOC_INTEL_SOUNDWIRE_RT711_MACH + tristate "SoundWire with RT711 codec" + depends on SOUNDWIRE && ACPI + select SND_SOC_RT711_SDW + help + Add support for Intel SoundWire-based platforms connected to RT711 + on link 0 or 1 + If unsure select "N" + config SND_SOC_INTEL_SOUNDWIRE_RT711_RT1308_RT715_MACH tristate "SoundWire with RT711, RT1308 and RT715" depends on SOUNDWIRE && ACPI diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 6f734adbeec654..1abcfdf1417463 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -34,6 +34,7 @@ snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o snd-soc-tgl-rt1308-objs := tgl_rt1308.o hda_dsp_common.o snd-soc-sof_da7219_max98373-objs := sof_da7219_max98373.o hda_dsp_common.o snd-soc-sdw-rt700-objs := sdw_rt700.o hda_dsp_common.o +snd-soc-sdw-rt711-objs := sdw_rt711.o hda_dsp_common.o snd-soc-sdw-rt711-rt1308-rt715-objs := sdw_rt711_rt1308_rt715.o hda_dsp_common.o obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o @@ -71,4 +72,5 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o obj-$(CONFIG_SND_SOC_INTEL_TGL_RT1308_MACH) += snd-soc-tgl-rt1308.o obj-$(CONFIG_SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH) += snd-soc-sof_da7219_max98373.o obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT700_MACH) += snd-soc-sdw-rt700.o +obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT711_MACH) += snd-soc-sdw-rt711.o obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT711_RT1308_RT715_MACH) += snd-soc-sdw-rt711-rt1308-rt715.o diff --git a/sound/soc/intel/boards/sdw_rt711.c b/sound/soc/intel/boards/sdw_rt711.c new file mode 100644 index 00000000000000..09da46b150992f --- /dev/null +++ b/sound/soc/intel/boards/sdw_rt711.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Intel Corporation + +/* + * sdw_rt711 - ASOC Machine driver for Intel SoundWire platforms + * connected to ALC711 device + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/hdac_hdmi.h" +#include "hda_dsp_common.h" + +struct mc_private { + struct list_head hdmi_pcm_list; + bool common_hdmi_codec_drv; + struct snd_soc_jack sdw_headset; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) +static struct snd_soc_jack hdmi[3]; + +struct hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +static int hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + /* dai_link id is 1:1 mapped to the PCM device */ + pcm->device = rtd->dai_link->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +#define NAME_SIZE 32 +static int card_late_probe(struct snd_soc_card *card) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(card); + struct hdmi_pcm *pcm; + struct snd_soc_component *component = NULL; + int err, i = 0; + char jack_name[NAME_SIZE]; + + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct hdmi_pcm, + head); + component = pcm->codec_dai->component; + + if (ctx->common_hdmi_codec_drv) + return hda_dsp_hdmi_build_controls(card, component); + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &hdmi[i], + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &hdmi[i]); + if (err < 0) + return err; + + i++; + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} +#else +static int card_late_probe(struct snd_soc_card *card) +{ + return 0; +} +#endif + +static struct snd_soc_jack_pin sdw_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int headset_init(struct snd_soc_pcm_runtime *rtd) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_jack *jack; + int ret; + + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &ctx->sdw_headset, + sdw_jack_pins, + ARRAY_SIZE(sdw_jack_pins)); + if (ret) { + dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n", + ret); + return ret; + } + + jack = &ctx->sdw_headset; + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); + + ret = snd_soc_component_set_jack(component, jack, NULL); + + if (ret) + dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n", + ret); + + return ret; +} + +static const struct snd_soc_dapm_widget widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +static const struct snd_soc_dapm_route map[] = { + /*Headphones*/ + { "Headphone", NULL, "HP" }, + { "MIC2", NULL, "Headset Mic" }, +}; + +static const struct snd_kcontrol_new controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +SND_SOC_DAILINK_DEF(sdw0_pin2, + DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin2"))); +SND_SOC_DAILINK_DEF(sdw0_pin3, + DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin3"))); +SND_SOC_DAILINK_DEF(sdw0_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("sdw:0:25d:711:0", "rt711-aif1"))); + +SND_SOC_DAILINK_DEF(platform, + DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:1f.3"))); + +struct snd_soc_dai_link dailink[] = { + { + .name = "SDW0-Playback", + .id = 0, + .init = headset_init, + .no_pcm = 1, + .dpcm_playback = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw0_pin2, sdw0_codec, platform), + }, + { + .name = "SDW0-Capture", + .id = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw0_pin3, sdw0_codec, platform), + }, +}; + +/* SoC card */ +static struct snd_soc_card card_sdw_rt711 = { + .name = "sdw-rt711", + .dai_link = dailink, + .num_links = ARRAY_SIZE(dailink), + .controls = controls, + .num_controls = ARRAY_SIZE(controls), + .dapm_widgets = widgets, + .num_dapm_widgets = ARRAY_SIZE(widgets), + .dapm_routes = map, + .num_dapm_routes = ARRAY_SIZE(map), + .late_probe = card_late_probe, +}; + +static int mc_probe(struct platform_device *pdev) +{ + struct mc_private *ctx; + struct snd_soc_acpi_mach *mach; + const char *platform_name; + struct snd_soc_card *card = &card_sdw_rt711; + int ret; + + dev_dbg(&pdev->dev, "Entry %s\n", __func__); + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); +#endif + + card->dev = &pdev->dev; + + /* override platform name, if required */ + mach = (&pdev->dev)->platform_data; + platform_name = mach->mach_params.platform; + + ret = snd_soc_fixup_dai_links_platform_name(card, platform_name); + if (ret) + return ret; + + ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv; + + snd_soc_card_set_drvdata(card, ctx); + + /* Register the card */ + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { + dev_err(card->dev, "snd_soc_register_card failed %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, card); + + return ret; +} + +static struct platform_driver sdw_rt711_driver = { + .driver = { + .name = "sdw_rt711", + .pm = &snd_soc_pm_ops, + }, + .probe = mc_probe, +}; + +module_platform_driver(sdw_rt711_driver); + +MODULE_DESCRIPTION("ASoC SoundWire RT711 Machine driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_AUTHOR("Pierre-Louis Bossart "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sdw_rt711"); From ab0e339602f5fc4b50dea477811c4513ed9cf722 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 25 Sep 2019 17:29:19 -0500 Subject: [PATCH 134/170] ASoC: Intel: boards: SoundWire rt711 + I2S RT1308 configuration This is the baseline for TGL RVP Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 11 +- sound/soc/intel/boards/Makefile | 4 +- .../{tgl_rt1308.c => sdw_rt711_i2s_rt1308.c} | 225 ++++++++++++------ 3 files changed, 163 insertions(+), 77 deletions(-) rename sound/soc/intel/boards/{tgl_rt1308.c => sdw_rt711_i2s_rt1308.c} (54%) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index cfaf741d090eb3..78f6305f0e3b04 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -515,23 +515,24 @@ config SND_SOC_INTEL_SOF_CML_RT1011_RT5682_MACH endif ## SND_SOC_SOF_COMETLAKE_LP && SND_SOC_SOF_HDA_LINK -if SND_SOC_SOF_TIGERLAKE +if SND_SOC_SOF_TIGERLAKE && SND_SOC_SOF_INTEL_SOUNDWIRE -config SND_SOC_INTEL_TGL_RT1308_MACH - tristate "TGL with RT1308 in I2S Mode" +config SND_SOC_INTEL_TGL_RT711_RT1308_MACH + tristate "TGL with RT711 in SDW mode and RT1308 in I2S Mode" depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST + select SND_SOC_RT711_SDW select SND_SOC_RT1308 select SND_SOC_DMIC select SND_HDA_CODEC_HDMI if SND_SOC_SOF_HDA_AUDIO_CODEC select SND_SOC_HDAC_HDMI if SND_SOC_SOF_HDA_LINK help This adds support for ASoC machine driver for Tigerlake platforms - with RT1308 I2S audio codec. + with SoundWire RT711 and RT1308 I2S audio codec. Say Y if you have such a device. If unsure select "N". -endif ## SND_SOC_SOF_TIGERLAKE +endif ## SND_SOC_SOF_TIGERLAKE && SND_SOC_SOF_INTEL_SOUNDWIRE if SND_SOC_SOF_JASPERLAKE diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 1abcfdf1417463..ae6890349e035a 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -31,8 +31,8 @@ snd-soc-skl_rt286-objs := skl_rt286.o snd-soc-skl_hda_dsp-objs := skl_hda_dsp_generic.o skl_hda_dsp_common.o hda_dsp_common.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o -snd-soc-tgl-rt1308-objs := tgl_rt1308.o hda_dsp_common.o snd-soc-sof_da7219_max98373-objs := sof_da7219_max98373.o hda_dsp_common.o +snd-soc-tgl-rt711-rt1308-objs := sdw_rt711_i2s_rt1308.o hda_dsp_common.o snd-soc-sdw-rt700-objs := sdw_rt700.o hda_dsp_common.o snd-soc-sdw-rt711-objs := sdw_rt711.o hda_dsp_common.o snd-soc-sdw-rt711-rt1308-rt715-objs := sdw_rt711_rt1308_rt715.o hda_dsp_common.o @@ -69,8 +69,8 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o -obj-$(CONFIG_SND_SOC_INTEL_TGL_RT1308_MACH) += snd-soc-tgl-rt1308.o obj-$(CONFIG_SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH) += snd-soc-sof_da7219_max98373.o +obj-$(CONFIG_SND_SOC_INTEL_TGL_RT711_RT1308_MACH) += snd-soc-tgl-rt711-rt1308.o obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT700_MACH) += snd-soc-sdw-rt700.o obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT711_MACH) += snd-soc-sdw-rt711.o obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_RT711_RT1308_RT715_MACH) += snd-soc-sdw-rt711-rt1308-rt715.o diff --git a/sound/soc/intel/boards/tgl_rt1308.c b/sound/soc/intel/boards/sdw_rt711_i2s_rt1308.c similarity index 54% rename from sound/soc/intel/boards/tgl_rt1308.c rename to sound/soc/intel/boards/sdw_rt711_i2s_rt1308.c index 6f8b5b2be66d40..6aa161b84da50e 100644 --- a/sound/soc/intel/boards/tgl_rt1308.c +++ b/sound/soc/intel/boards/sdw_rt711_i2s_rt1308.c @@ -3,45 +3,50 @@ // Copyright(c) 2019 Intel Corporation. All rights reserved. /* - * tgl_rt1308.c - ASoc Machine driver for Intel platforms - * with RT1308 codec. + * tgl_rt711_rt1308.c - ASoc Machine driver for Intel platforms + * with RT711 codec over SoundWire and RT1308 amplifier over I2S */ #include +#include #include +#include +#include +#include +#include +#include +#include #include -#include - -#include +#include #include #include #include #include #include - #include "../../codecs/rt1308.h" #include "../../codecs/hdac_hdmi.h" #include "hda_dsp_common.h" -struct tgl_card_private { +struct mc_private { struct list_head hdmi_pcm_list; bool common_hdmi_codec_drv; + struct snd_soc_jack sdw_headset; }; #if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) -static struct snd_soc_jack tgl_hdmi[4]; +static struct snd_soc_jack hdmi[4]; -struct tgl_hdmi_pcm { +struct hdmi_pcm { struct list_head head; struct snd_soc_dai *codec_dai; int device; }; -static int tgl_hdmi_init(struct snd_soc_pcm_runtime *rtd) +static int hdmi_init(struct snd_soc_pcm_runtime *rtd) { - struct tgl_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *dai = rtd->codec_dai; - struct tgl_hdmi_pcm *pcm; + struct hdmi_pcm *pcm; pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); if (!pcm) @@ -57,15 +62,15 @@ static int tgl_hdmi_init(struct snd_soc_pcm_runtime *rtd) } #define NAME_SIZE 32 -static int tgl_card_late_probe(struct snd_soc_card *card) +static int card_late_probe(struct snd_soc_card *card) { - struct tgl_card_private *ctx = snd_soc_card_get_drvdata(card); - struct tgl_hdmi_pcm *pcm; + struct mc_private *ctx = snd_soc_card_get_drvdata(card); + struct hdmi_pcm *pcm; struct snd_soc_component *component = NULL; int err, i = 0; char jack_name[NAME_SIZE]; - pcm = list_first_entry(&ctx->hdmi_pcm_list, struct tgl_hdmi_pcm, + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct hdmi_pcm, head); component = pcm->codec_dai->component; @@ -77,14 +82,14 @@ static int tgl_card_late_probe(struct snd_soc_card *card) snprintf(jack_name, sizeof(jack_name), "HDMI/DP, pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, - SND_JACK_AVOUT, &tgl_hdmi[i], + SND_JACK_AVOUT, &hdmi[i], NULL, 0); if (err) return err; err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, - &tgl_hdmi[i]); + &hdmi[i]); if (err < 0) return err; @@ -97,14 +102,61 @@ static int tgl_card_late_probe(struct snd_soc_card *card) return hdac_hdmi_jack_port_init(component, &card->dapm); } #else -static int tgl_card_late_probe(struct snd_soc_card *card) +static int card_late_probe(struct snd_soc_card *card) { return 0; } #endif -static int tgl_rt1308_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static struct snd_soc_jack_pin sdw_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int headset_init(struct snd_soc_pcm_runtime *rtd) +{ + struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_jack *jack; + int ret; + + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &ctx->sdw_headset, + sdw_jack_pins, + ARRAY_SIZE(sdw_jack_pins)); + if (ret) { + dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n", + ret); + return ret; + } + + jack = &ctx->sdw_headset; + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); + + ret = snd_soc_component_set_jack(component, jack, NULL); + + if (ret) + dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n", + ret); + + return ret; +} + +static int rt1308_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; @@ -136,27 +188,39 @@ static int tgl_rt1308_hw_params(struct snd_pcm_substream *substream, } /* machine stream operations */ -static struct snd_soc_ops tgl_rt1308_ops = { - .hw_params = tgl_rt1308_hw_params, +static struct snd_soc_ops rt1308_ops = { + .hw_params = rt1308_hw_params, }; -static const struct snd_soc_dapm_widget tgl_rt1308_dapm_widgets[] = { +static const struct snd_soc_dapm_widget widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_SPK("Speakers", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), }; -static const struct snd_kcontrol_new tgl_rt1308_controls[] = { - SOC_DAPM_PIN_SWITCH("Speakers"), -}; +static const struct snd_soc_dapm_route map[] = { + { "Headphone", NULL, "HP" }, + { "MIC2", NULL, "Headset Mic" }, -static const struct snd_soc_dapm_route tgl_rt1308_dapm_routes[] = { { "Speakers", NULL, "SPOL" }, { "Speakers", NULL, "SPOR" }, + { "DMic", NULL, "SoC DMIC"}, +}; - /* digital mics */ - {"DMic", NULL, "SoC DMIC"}, +static const struct snd_kcontrol_new controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Speakers"), }; +SND_SOC_DAILINK_DEF(sdw0_pin2, + DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin2"))); +SND_SOC_DAILINK_DEF(sdw0_pin3, + DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin3"))); +SND_SOC_DAILINK_DEF(sdw0_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("sdw:0:25d:711:0", "rt711-aif1"))); + SND_SOC_DAILINK_DEF(ssp2_pin, DAILINK_COMP_ARRAY(COMP_CPU("SSP2 Pin"))); @@ -195,19 +259,36 @@ SND_SOC_DAILINK_DEF(idisp4_codec, DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi4"))); #endif -static struct snd_soc_dai_link tgl_rt1308_dailink[] = { +struct snd_soc_dai_link dailink[] = { + { + .name = "SDW0-Playback", + .id = 0, + .init = headset_init, + .no_pcm = 1, + .dpcm_playback = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw0_pin2, sdw0_codec, platform), + }, + { + .name = "SDW0-Capture", + .id = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw0_pin3, sdw0_codec, platform), + }, { .name = "SSP2-Codec", - .id = 0, + .id = 2, .no_pcm = 1, - .ops = &tgl_rt1308_ops, + .ops = &rt1308_ops, .dpcm_playback = 1, .nonatomic = true, SND_SOC_DAILINK_REG(ssp2_pin, ssp2_codec, platform), }, { .name = "dmic01", - .id = 1, + .id = 3, .ignore_suspend = 1, .dpcm_capture = 1, .no_pcm = 1, @@ -215,7 +296,7 @@ static struct snd_soc_dai_link tgl_rt1308_dailink[] = { }, { .name = "dmic16k", - .id = 2, + .id = 4, .ignore_suspend = 1, .dpcm_capture = 1, .no_pcm = 1, @@ -224,32 +305,32 @@ static struct snd_soc_dai_link tgl_rt1308_dailink[] = { #if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) { .name = "iDisp1", - .id = 3, - .init = tgl_hdmi_init, + .id = 5, + .init = hdmi_init, .dpcm_playback = 1, .no_pcm = 1, SND_SOC_DAILINK_REG(idisp1_pin, idisp1_codec, platform), }, { .name = "iDisp2", - .id = 4, - .init = tgl_hdmi_init, + .id = 6, + .init = hdmi_init, .dpcm_playback = 1, .no_pcm = 1, SND_SOC_DAILINK_REG(idisp2_pin, idisp2_codec, platform), }, { .name = "iDisp3", - .id = 5, - .init = tgl_hdmi_init, + .id = 7, + .init = hdmi_init, .dpcm_playback = 1, .no_pcm = 1, SND_SOC_DAILINK_REG(idisp3_pin, idisp3_codec, platform), }, { .name = "iDisp4", - .id = 6, - .init = tgl_hdmi_init, + .id = 8, + .init = hdmi_init, .dpcm_playback = 1, .no_pcm = 1, SND_SOC_DAILINK_REG(idisp4_pin, idisp4_codec, platform), @@ -257,41 +338,45 @@ static struct snd_soc_dai_link tgl_rt1308_dailink[] = { #endif }; -/* audio machine driver */ -static struct snd_soc_card tgl_rt1308_card = { - .name = "tgl_rt1308", - .owner = THIS_MODULE, - .dai_link = tgl_rt1308_dailink, - .num_links = ARRAY_SIZE(tgl_rt1308_dailink), - .controls = tgl_rt1308_controls, - .num_controls = ARRAY_SIZE(tgl_rt1308_controls), - .dapm_widgets = tgl_rt1308_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(tgl_rt1308_dapm_widgets), - .dapm_routes = tgl_rt1308_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(tgl_rt1308_dapm_routes), - .late_probe = tgl_card_late_probe, +/* SoC card */ +static struct snd_soc_card card_rt711_rt1308 = { + .name = "rt711-rt1308", + .dai_link = dailink, + .num_links = ARRAY_SIZE(dailink), + .controls = controls, + .num_controls = ARRAY_SIZE(controls), + .dapm_widgets = widgets, + .num_dapm_widgets = ARRAY_SIZE(widgets), + .dapm_routes = map, + .num_dapm_routes = ARRAY_SIZE(map), + .late_probe = card_late_probe, }; -static int tgl_rt1308_probe(struct platform_device *pdev) +static int mc_probe(struct platform_device *pdev) { + struct mc_private *ctx; struct snd_soc_acpi_mach *mach; - struct tgl_card_private *ctx; - struct snd_soc_card *card = &tgl_rt1308_card; + const char *platform_name; + struct snd_soc_card *card = &card_rt711_rt1308; int ret; - card->dev = &pdev->dev; + dev_dbg(&pdev->dev, "Entry %s\n", __func__); ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; - if (IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI)) - INIT_LIST_HEAD(&ctx->hdmi_pcm_list); +#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); +#endif + + card->dev = &pdev->dev; + /* override platform name, if required */ mach = (&pdev->dev)->platform_data; + platform_name = mach->mach_params.platform; - ret = snd_soc_fixup_dai_links_platform_name(card, - mach->mach_params.platform); + ret = snd_soc_fixup_dai_links_platform_name(card, platform_name); if (ret) return ret; @@ -302,17 +387,17 @@ static int tgl_rt1308_probe(struct platform_device *pdev) return devm_snd_soc_register_card(&pdev->dev, card); } -static struct platform_driver tgl_rt1308_driver = { +static struct platform_driver rt711_rt1308_driver = { .driver = { - .name = "tgl_rt1308", + .name = "rt711_rt1308", .pm = &snd_soc_pm_ops, }, - .probe = tgl_rt1308_probe, + .probe = mc_probe, }; -module_platform_driver(tgl_rt1308_driver); +module_platform_driver(rt711_rt1308_driver); MODULE_AUTHOR("Xiuli Pan"); -MODULE_DESCRIPTION("ASoC Intel(R) Tiger Lake + RT1308 Machine driver"); +MODULE_DESCRIPTION("ASoC SoundWire RT711 + RT1308 Machine driver"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:tgl_rt1308"); +MODULE_ALIAS("platform:rt711_rt1308"); From c3b747da742bb1086d6944dd1cc8aa875949f112 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 24 Jun 2019 11:52:23 -0500 Subject: [PATCH 135/170] NOT FOR UPSTREAM: Add debug for boards Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index ae6890349e035a..4c5fbf647e0c19 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -1,3 +1,5 @@ +ccflags-y += -DDEBUG + # SPDX-License-Identifier: GPL-2.0 snd-soc-sst-haswell-objs := haswell.o snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o From 51b7ef5a12a9ba2f69a1cf5b37bc46f4366c2df5 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 5 Nov 2019 14:50:11 +0800 Subject: [PATCH 136/170] ASoC: intel: sdw_rt711_rt1308_rt715: remove ENABLE_RT1308_SDW2 flag We can use quick to decide if we need 2nd rt1308 or not. Signed-off-by: Bard Liao --- .../soc/intel/boards/sdw_rt711_rt1308_rt715.c | 62 ++++++++++++------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c index b594e0a9effc46..919f77287ed06e 100644 --- a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c +++ b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c @@ -26,7 +26,6 @@ #include "hda_dsp_common.h" /* comment out this define for mono configurations */ -#define ENABLE_RT1308_SDW2 #define MAX_NO_PROPS 2 @@ -38,6 +37,7 @@ enum { }; #define SOF_RT711_JDSRC(quirk) ((quirk) & GENMASK(1, 0)) +#define SOF_SDW_MONO_SPK BIT(2) static unsigned long sof_rt711_rt1308_rt715_quirk = SOF_RT711_JD_SRC_JD1; @@ -186,7 +186,8 @@ static const struct dmi_system_id sof_sdw_rt711_rt1308_rt715_quirk_table[] = { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), }, - .driver_data = (void *)(SOF_RT711_JD_SRC_JD2), + .driver_data = (void *)(SOF_RT711_JD_SRC_JD2 | + SOF_SDW_MONO_SPK), }, {} }; @@ -230,10 +231,11 @@ static const struct snd_soc_dapm_route map[] = { /* Speakers */ { "Speaker", NULL, "rt1308-1 SPOL" }, { "Speaker", NULL, "rt1308-1 SPOR" }, -#ifdef ENABLE_RT1308_SDW2 +}; + +static const struct snd_soc_dapm_route second_speaker_map[] = { { "Speaker", NULL, "rt1308-2 SPOL" }, { "Speaker", NULL, "rt1308-2 SPOR" }, -#endif }; static const struct snd_kcontrol_new controls[] = { @@ -242,6 +244,20 @@ static const struct snd_kcontrol_new controls[] = { SOC_DAPM_PIN_SWITCH("Speaker"), }; +static int second_spk_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_dapm_add_routes(&card->dapm, second_speaker_map, + ARRAY_SIZE(second_speaker_map)); + + if (ret) + dev_err(rtd->dev, "second Speaker map addition failed: %d\n", + ret); + return ret; +} + SND_SOC_DAILINK_DEF(sdw0_pin2, DAILINK_COMP_ARRAY(COMP_CPU("SDW0 Pin2"))); SND_SOC_DAILINK_DEF(sdw0_pin3, @@ -254,12 +270,10 @@ SND_SOC_DAILINK_DEF(sdw1_pin2, SND_SOC_DAILINK_DEF(sdw1_codec, DAILINK_COMP_ARRAY(COMP_CODEC("sdw:1:25d:1308:0", "rt1308-aif"))); -#ifdef ENABLE_RT1308_SDW2 SND_SOC_DAILINK_DEF(sdw2_pin2, DAILINK_COMP_ARRAY(COMP_CPU("SDW2 Pin2"))); SND_SOC_DAILINK_DEF(sdw2_codec, DAILINK_COMP_ARRAY(COMP_CODEC("sdw:2:25d:1308:0", "rt1308-aif"))); -#endif SND_SOC_DAILINK_DEF(sdw3_pin2, DAILINK_COMP_ARRAY(COMP_CPU("SDW3 Pin2"))); @@ -295,16 +309,14 @@ static struct snd_soc_codec_conf codec_conf[] = { .dev_name = "sdw:1:25d:1308:0", .name_prefix = "rt1308-1", }, -#ifdef ENABLE_RT1308_SDW2 - { - .dev_name = "sdw:2:25d:1308:0", - .name_prefix = "rt1308-2", - }, -#endif { .dev_name = "sdw:3:25d:715:0", .name_prefix = "rt715", }, + { + .dev_name = "sdw:2:25d:1308:0", + .name_prefix = "rt1308-2", + }, }; @@ -334,16 +346,6 @@ struct snd_soc_dai_link dailink[] = { .nonatomic = true, SND_SOC_DAILINK_REG(sdw1_pin2, sdw1_codec, platform), }, -#ifdef ENABLE_RT1308_SDW2 - { - .name = "SDW2-Playback", - .id = 3, - .no_pcm = 1, - .dpcm_playback = 1, - .nonatomic = true, - SND_SOC_DAILINK_REG(sdw2_pin2, sdw2_codec, platform), - }, -#endif { .name = "SDW3-Capture", .id = 4, @@ -379,6 +381,15 @@ struct snd_soc_dai_link dailink[] = { SND_SOC_DAILINK_REG(idisp3_pin, idisp3_codec, platform), }, #endif + { + .name = "SDW2-Playback", + .id = 3, + .init = second_spk_init, + .no_pcm = 1, + .dpcm_playback = 1, + .nonatomic = true, + SND_SOC_DAILINK_REG(sdw2_pin2, sdw2_codec, platform), + }, }; /* SoC card */ @@ -432,6 +443,13 @@ static int mc_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, ctx); sof_rt711_add_codec_device_props("sdw:0:25d:711:0"); + + if (sof_rt711_rt1308_rt715_quirk & SOF_SDW_MONO_SPK) { + /* Remove rt1308-2 codec from dailink and codec_conf */ + card->num_links--; + card->num_configs--; + } + /* Register the card */ ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { From fdb4618d20649b84a6a4dc65c467d6deabbdfad5 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 7 Nov 2019 16:34:27 +0800 Subject: [PATCH 137/170] ASoC: intel: sdw_rt711_rt1308_rt715: Use a fixed number of num_links and num__configs mc_probe could be called multiple times and card->num_links will decrease in each time when mc_probe is called. Same as num_configs. Signed-off-by: Bard Liao --- sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c index 919f77287ed06e..6c69a7ea57ec61 100644 --- a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c +++ b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c @@ -446,8 +446,8 @@ static int mc_probe(struct platform_device *pdev) if (sof_rt711_rt1308_rt715_quirk & SOF_SDW_MONO_SPK) { /* Remove rt1308-2 codec from dailink and codec_conf */ - card->num_links--; - card->num_configs--; + card->num_links = ARRAY_SIZE(dailink) - 1 ; + card->num_configs = ARRAY_SIZE(codec_conf) - 1; } /* Register the card */ From dd7a15aebbaa824ef663a7ad144735e6b60c22db Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Tue, 12 Nov 2019 13:16:39 +0800 Subject: [PATCH 138/170] ASoC: intel:sdw_rt711_i2s_rt1308: add quirk for rt711 Set rt711 device property on machine driver according to DMI. On TGL, JD1 is used for headphone. Signed-off-by: Rander Wang Signed-off-by: Bard Liao --- sound/soc/intel/boards/sdw_rt711_i2s_rt1308.c | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/sound/soc/intel/boards/sdw_rt711_i2s_rt1308.c b/sound/soc/intel/boards/sdw_rt711_i2s_rt1308.c index 6aa161b84da50e..31bd4003f6dfa7 100644 --- a/sound/soc/intel/boards/sdw_rt711_i2s_rt1308.c +++ b/sound/soc/intel/boards/sdw_rt711_i2s_rt1308.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include #include @@ -27,6 +29,17 @@ #include "../../codecs/hdac_hdmi.h" #include "hda_dsp_common.h" +#define MAX_NO_PROPS 2 + +enum { + SOF_RT711_JD_SRC_JD1 = 1, + SOF_RT711_JD_SRC_JD2 = 2, +}; + +#define SOF_RT711_JDSRC(quirk) ((quirk) & GENMASK(1, 0)) + +static unsigned long sof_rt711_rt1308_quirk = SOF_RT711_JD_SRC_JD1; + struct mc_private { struct list_head hdmi_pcm_list; bool common_hdmi_codec_drv; @@ -192,6 +205,50 @@ static struct snd_soc_ops rt1308_ops = { .hw_params = rt1308_hw_params, }; +static int sof_rt711_rt1308_quirk_cb(const struct dmi_system_id *id) +{ + sof_rt711_rt1308_quirk = (unsigned long)id->driver_data; + return 1; +} + +static const struct dmi_system_id sof_sdw_rt711_rt1308_quirk_table[] = { + { + .callback = sof_rt711_rt1308_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "IntelCorporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Tiger Lake Client"), + }, + .driver_data = (void *)(SOF_RT711_JD_SRC_JD1), + }, + {} +}; + +/* + * Note this MUST be called before snd_soc_register_card(), so that the props + * are in place before the codec component driver's probe function parses them. + */ +static int sof_rt711_add_codec_device_props(const char *sdw_dev_name) +{ + struct property_entry props[MAX_NO_PROPS] = {}; + struct device *sdw_dev; + int ret, cnt = 0; + unsigned int quirk; + + sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_dev_name); + if (!sdw_dev) + return -EPROBE_DEFER; + + if (SOF_RT711_JDSRC(sof_rt711_rt1308_quirk)) { + quirk = SOF_RT711_JDSRC(sof_rt711_rt1308_quirk); + props[cnt++] = PROPERTY_ENTRY_U32("realtek,jd-src", quirk); + } + + ret = device_add_properties(sdw_dev, props); + put_device(sdw_dev); + + return ret; +} + static const struct snd_soc_dapm_widget widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), @@ -366,6 +423,8 @@ static int mc_probe(struct platform_device *pdev) if (!ctx) return -ENOMEM; + dmi_check_system(sof_sdw_rt711_rt1308_quirk_table); + #if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) INIT_LIST_HEAD(&ctx->hdmi_pcm_list); #endif @@ -384,6 +443,8 @@ static int mc_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, ctx); + sof_rt711_add_codec_device_props("sdw:0:25d:711:0"); + return devm_snd_soc_register_card(&pdev->dev, card); } From 358d4f028fe5a6cbeb1e01761632a3c283a4d144 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Tue, 12 Nov 2019 13:17:10 +0800 Subject: [PATCH 139/170] ASoC: intel: refine sdw_rt711_rt1308_rt715 Remove redundant definition. Signed-off-by: Rander Wang --- sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c index 6c69a7ea57ec61..c89515bd695420 100644 --- a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c +++ b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include #include @@ -29,8 +31,6 @@ #define MAX_NO_PROPS 2 -extern struct bus_type sdw_bus_type; - enum { SOF_RT711_JD_SRC_JD1 = 1, SOF_RT711_JD_SRC_JD2 = 2, From bda8d936aa750e0991ad3977c3c43205e96c9d82 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 21 Nov 2019 17:29:19 +0800 Subject: [PATCH 140/170] ASoC: Intel: sdw_rt711_rt1308_rt715: get more specific DMI info Not all Dell machine work with mono rt1308. Signed-off-by: Bard Liao --- sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c index c89515bd695420..24e17528f143dd 100644 --- a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c +++ b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c @@ -185,6 +185,7 @@ static const struct dmi_system_id sof_sdw_rt711_rt1308_rt715_quirk_table[] = { .callback = sof_rt711_rt1308_rt715_quirk_cb, .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"), }, .driver_data = (void *)(SOF_RT711_JD_SRC_JD2 | SOF_SDW_MONO_SPK), From 25a386e9098c1767dc9528223cc51beef1613c14 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 29 Nov 2019 14:52:51 +0800 Subject: [PATCH 141/170] ASoC: Intel: sdw_rt711_rt1308_rt715: add Dell XPS to DMI table To set proper configurations. Signed-off-by: Bard Liao --- sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c index 24e17528f143dd..af0bd2b8f23fb8 100644 --- a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c +++ b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c @@ -190,6 +190,14 @@ static const struct dmi_system_id sof_sdw_rt711_rt1308_rt715_quirk_table[] = { .driver_data = (void *)(SOF_RT711_JD_SRC_JD2 | SOF_SDW_MONO_SPK), }, + { + .callback = sof_rt711_rt1308_rt715_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_MATCH(DMI_PRODUCT_NAME, "XPS"), + }, + .driver_data = (void *)(SOF_RT711_JD_SRC_JD2), + }, {} }; From dbcbaad76a786eb7a0cf2612799dea44ecea7aea Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 5 Nov 2019 10:44:49 +0800 Subject: [PATCH 142/170] ALSA: HDA: intel-dsp-config: add DMI info for Dell laptop The laptop doesn't use DMIC, but use SOF. Signed-off-by: Bard Liao --- sound/hda/intel-dsp-config.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c index be1df80ed01337..6e6e8662534d8a 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/intel-dsp-config.c @@ -204,6 +204,12 @@ static const struct config_entry config_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Google"), } }, + { + .ident = "Dell laptop", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + } + }, {} } }, From b40f838cfba8758f3d7f862ee08516bba50581d9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 10 Jun 2019 19:31:58 -0500 Subject: [PATCH 143/170] soundwire: cadence_master: handle multiple status reports per Slave When a Slave reports multiple status in the sticky bits, find the latest configuration from the mirror of the PING frame status and update the status directly. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.c | 35 +++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index bb5c95cfecc5ab..1f81681d7517fa 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -692,13 +692,36 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns, /* first check if Slave reported multiple status */ if (set_status > 1) { + u32 val; + dev_warn_ratelimited(cdns->dev, - "Slave reported multiple Status: %d\n", - mask); - /* - * TODO: we need to reread the status here by - * issuing a PING cmd - */ + "Slave %d reported multiple Status: %d\n", + i, mask); + + /* check latest status extracted from PING commands */ + val = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT); + val >>= (i * 2); + + switch (val & 0x3) { + case 0: + status[i] = SDW_SLAVE_UNATTACHED; + break; + case 1: + status[i] = SDW_SLAVE_ATTACHED; + break; + case 2: + status[i] = SDW_SLAVE_ALERT; + break; + case 3: + default: + status[i] = SDW_SLAVE_RESERVED; + break; + } + + dev_warn_ratelimited(cdns->dev, + "Slave %d status updated to %d\n", + i, status[i]); + } } From ce9bcf2f5297e29a672765316c7a75bb1bac4f4d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 24 Apr 2018 15:28:28 +0530 Subject: [PATCH 144/170] soundwire: Add generic bandwidth allocation algorithm This algorithm computes bus parameters like clock frequency, frame shape and port transport parameters based on active stream(s) running on the bus. Developers can also implement their own .compute_params() callback for specific resource management algorithm, and set if before calling sdw_add_bus_master() Credits: this patch is based on an earlier internal contribution by Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah. All hard-coded values were removed from the initial contribution to use BIOS information instead. FIXME: remove checkpatch report WARNING: Reusing the krealloc arg is almost always a bug + group->rates = krealloc(group->rates, Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/Kconfig | 4 + drivers/soundwire/Makefile | 3 + drivers/soundwire/bus.c | 6 + drivers/soundwire/bus.h | 46 +- .../soundwire/generic_bandwidth_allocation.c | 407 ++++++++++++++++++ drivers/soundwire/intel.c | 3 + drivers/soundwire/stream.c | 12 + include/linux/soundwire/sdw.h | 3 + 8 files changed, 482 insertions(+), 2 deletions(-) create mode 100644 drivers/soundwire/generic_bandwidth_allocation.c diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig index c725d0a8b288b6..eb73c91b7dba87 100644 --- a/drivers/soundwire/Kconfig +++ b/drivers/soundwire/Kconfig @@ -24,6 +24,7 @@ config SOUNDWIRE_CADENCE config SOUNDWIRE_INTEL tristate "Intel SoundWire Master driver" select SOUNDWIRE_CADENCE + select SOUNDWIRE_GENERIC_ALLOCATION depends on ACPI && SND_SOC help SoundWire Intel Master driver. @@ -31,4 +32,7 @@ config SOUNDWIRE_INTEL enable this config option to get the SoundWire support for that device. +config SOUNDWIRE_GENERIC_ALLOCATION + tristate + endif diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index 89b29819dd3ad8..eb37d69cbdca96 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -7,6 +7,9 @@ soundwire-bus-objs := bus_type.o bus.o master.o slave.o mipi_disco.o stream.o obj-$(CONFIG_SOUNDWIRE) += soundwire-bus.o +soundwire-generic-allocation-objs := generic_bandwidth_allocation.o +obj-$(CONFIG_SOUNDWIRE_GENERIC_ALLOCATION) += soundwire-generic-allocation.o + ifdef CONFIG_DEBUG_FS soundwire-bus-objs += debugfs.o endif diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index ba88d88dda8bcd..011bd37c5ca171 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -31,6 +31,12 @@ int sdw_add_bus_master(struct sdw_bus *bus) return -EINVAL; } + if (!bus->compute_params) { + dev_err(bus->dev, + "Bandwidth allocation not configured, compute_parems no set\n"); + return -EINVAL; + } + mutex_init(&bus->msg_lock); mutex_init(&bus->bus_lock); INIT_LIST_HEAD(&bus->slaves); diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index ee362472003a93..39a433b2cfb0c6 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -68,6 +68,7 @@ struct sdw_msg { }; #define SDW_DOUBLE_RATE_FACTOR 2 +#define SDW_STRM_RATE_GROUPING 1 extern int sdw_rows[SDW_FRAME_ROWS]; extern int sdw_cols[SDW_FRAME_COLS]; @@ -153,9 +154,50 @@ int sdw_transfer_defer(struct sdw_bus *bus, struct sdw_msg *msg, int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave, u32 addr, size_t count, u16 dev_num, u8 flags, u8 *buf); +/* Retrieve and return channel count from channel mask */ +static inline int sdw_ch_mask_to_ch(int ch_mask) +{ + int c = 0; + + for (c = 0; ch_mask; ch_mask >>= 1) + c += ch_mask & 1; + + return c; +} + +/* Fill transport parameter data structure */ +static inline void sdw_fill_xport_params(struct sdw_transport_params *params, + int port_num, bool grp_ctrl_valid, + int grp_ctrl, int sample_int, + int off1, int off2, + int hstart, int hstop, + int pack_mode, int lane_ctrl) +{ + params->port_num = port_num; + params->blk_grp_ctrl_valid = grp_ctrl_valid; + params->blk_grp_ctrl = grp_ctrl; + params->sample_interval = sample_int; + params->offset1 = off1; + params->offset2 = off2; + params->hstart = hstart; + params->hstop = hstop; + params->blk_pkg_mode = pack_mode; + params->lane_ctrl = lane_ctrl; +} + +/* Fill port parameter data structure */ +static inline void sdw_fill_port_params(struct sdw_port_params *params, + int port_num, int bps, + int flow_mode, int data_mode) +{ + params->num = port_num; + params->bps = bps; + params->flow_mode = flow_mode; + params->data_mode = data_mode; +} + /* Read-Modify-Write Slave register */ -static inline int -sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val) +static inline int sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val) { int tmp; diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c new file mode 100644 index 00000000000000..dc7ffa987963ac --- /dev/null +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -0,0 +1,407 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2015-2019 Intel Corporation. + +/* + * Bandwidth management algorithm based on 2^n gears + * + */ + +#include +#include +#include +#include +#include +#include "bus.h" + +#define SDW_STRM_RATE_GROUPING 1 + +struct sdw_group_params { + unsigned int rate; + int full_bw; + int payload_bw; + int hwidth; +}; + +struct sdw_group { + unsigned int count; + unsigned int max_size; + unsigned int *rates; +}; + +struct sdw_transport_data { + int hstart; + int hstop; + int block_offset; + int sub_block_offset; +}; + +static void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt, + struct sdw_transport_data *t_data) +{ + struct sdw_slave_runtime *s_rt = NULL; + struct sdw_port_runtime *p_rt; + int port_bo, sample_int; + unsigned int rate, bps, ch = 0; + + port_bo = t_data->block_offset; + + list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { + rate = m_rt->stream->params.rate; + bps = m_rt->stream->params.bps; + sample_int = (m_rt->bus->params.curr_dr_freq / rate); + + list_for_each_entry(p_rt, &s_rt->port_list, port_node) { + ch = sdw_ch_mask_to_ch(p_rt->ch_mask); + + sdw_fill_xport_params(&p_rt->transport_params, + p_rt->num, true, + SDW_BLK_GRP_CNT_1, + sample_int, port_bo, port_bo >> 8, + t_data->hstart, + t_data->hstop, + (SDW_BLK_GRP_CNT_1 * ch), 0x0); + + sdw_fill_port_params(&p_rt->port_params, + p_rt->num, bps, + SDW_PORT_FLOW_MODE_ISOCH, + SDW_PORT_DATA_MODE_NORMAL); + + port_bo += bps * ch; + } + } +} + +static void sdw_compute_master_ports(struct sdw_master_runtime *m_rt, + struct sdw_group_params *params, + int port_bo, int hstop) +{ + struct sdw_transport_data t_data = {0}; + struct sdw_port_runtime *p_rt; + struct sdw_bus *bus = m_rt->bus; + int sample_int, hstart = 0; + unsigned int rate, bps, ch, no_ch; + + rate = m_rt->stream->params.rate; + bps = m_rt->stream->params.bps; + ch = m_rt->ch_count; + sample_int = (bus->params.curr_dr_freq / rate); + + if (rate != params->rate) + return; + + t_data.hstop = hstop; + hstart = hstop - params->hwidth + 1; + t_data.hstart = hstart; + + list_for_each_entry(p_rt, &m_rt->port_list, port_node) { + no_ch = sdw_ch_mask_to_ch(p_rt->ch_mask); + + sdw_fill_xport_params(&p_rt->transport_params, p_rt->num, + true, SDW_BLK_GRP_CNT_1, sample_int, + port_bo, port_bo >> 8, hstart, hstop, + (SDW_BLK_GRP_CNT_1 * no_ch), 0x0); + + sdw_fill_port_params(&p_rt->port_params, + p_rt->num, bps, + SDW_PORT_FLOW_MODE_ISOCH, + SDW_PORT_DATA_MODE_NORMAL); + + /* Check for first entry */ + if (!(p_rt == list_first_entry(&m_rt->port_list, + struct sdw_port_runtime, + port_node))) { + port_bo += bps * ch; + continue; + } + + t_data.hstart = hstart; + t_data.hstop = hstop; + t_data.block_offset = port_bo; + t_data.sub_block_offset = 0; + port_bo += bps * ch; + } + + sdw_compute_slave_ports(m_rt, &t_data); +} + +static void _sdw_compute_port_params(struct sdw_bus *bus, + struct sdw_group_params *params, int count) +{ + struct sdw_master_runtime *m_rt = NULL; + int hstop = bus->params.col - 1; + int block_offset, port_bo, i; + + /* Run loop for all groups to compute transport parameters */ + for (i = 0; i < count; i++) { + port_bo = 1; + block_offset = 1; + + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + sdw_compute_master_ports(m_rt, ¶ms[i], + port_bo, hstop); + + block_offset += m_rt->ch_count * + m_rt->stream->params.bps; + port_bo = block_offset; + } + + hstop = hstop - params[i].hwidth; + } +} + +static int sdw_compute_group_params(struct sdw_bus *bus, + struct sdw_group_params *params, + int *rates, int count) +{ + struct sdw_master_runtime *m_rt = NULL; + int sel_col = bus->params.col; + unsigned int rate, bps, ch; + int i, column_needed = 0; + + /* Calculate bandwidth per group */ + for (i = 0; i < count; i++) { + params[i].rate = rates[i]; + params[i].full_bw = bus->params.curr_dr_freq / params[i].rate; + } + + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + rate = m_rt->stream->params.rate; + bps = m_rt->stream->params.bps; + ch = m_rt->ch_count; + + for (i = 0; i < count; i++) { + if (rate == params[i].rate) + params[i].payload_bw += bps * ch; + } + } + + for (i = 0; i < count; i++) { + params[i].hwidth = (sel_col * + params[i].payload_bw + params[i].full_bw - 1) / + params[i].full_bw; + + column_needed += params[i].hwidth; + } + + if (column_needed > sel_col - 1) + return -EINVAL; + + return 0; +} + +static int sdw_add_element_group_count(struct sdw_group *group, + unsigned int rate) +{ + int num = group->count; + int i; + + for (i = 0; i <= num; i++) { + if (rate == group->rates[i]) + break; + + if (i != num) + continue; + + if (group->count >= group->max_size) { + group->max_size += 1; + group->rates = krealloc(group->rates, + (sizeof(int) * group->max_size), + GFP_KERNEL); + if (!group->rates) + return -ENOMEM; + } + + group->rates[group->count++] = rate; + } + + return 0; +} + +static int sdw_get_group_count(struct sdw_bus *bus, + struct sdw_group *group) +{ + struct sdw_master_runtime *m_rt; + unsigned int rate; + int ret = 0; + + group->count = 0; + group->max_size = SDW_STRM_RATE_GROUPING; + group->rates = kcalloc(group->max_size, sizeof(int), GFP_KERNEL); + if (!group->rates) + return -ENOMEM; + + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + rate = m_rt->stream->params.rate; + if (m_rt == list_first_entry(&bus->m_rt_list, + struct sdw_master_runtime, + bus_node)) { + group->rates[group->count++] = rate; + + } else { + ret = sdw_add_element_group_count(group, rate); + if (ret < 0) + return ret; + } + } + + return ret; +} + +/** + * sdw_compute_port_params: Compute transport and port parameters + * + * @bus: SDW Bus instance + */ +static int sdw_compute_port_params(struct sdw_bus *bus) +{ + struct sdw_group_params *params = NULL; + struct sdw_group group; + int ret; + + ret = sdw_get_group_count(bus, &group); + if (ret < 0) + goto out; + + if (group.count == 0) + goto out; + + params = kcalloc(group.count, sizeof(*params), GFP_KERNEL); + if (!params) { + ret = -ENOMEM; + goto out; + } + + /* Compute transport parameters for grouped streams */ + ret = sdw_compute_group_params(bus, params, + &group.rates[0], group.count); + if (ret < 0) + goto out; + + _sdw_compute_port_params(bus, params, group.count); + +out: + kfree(params); + kfree(group.rates); + + return ret; +} + +static int sdw_select_row_col(struct sdw_bus *bus, int clk_freq) +{ + struct sdw_master_prop *prop = &bus->prop; + int frame_int, frame_freq; + int r, c; + + for (c = 0; c < SDW_FRAME_COLS; c++) { + for (r = 0; r < SDW_FRAME_ROWS; r++) { + if (sdw_rows[r] != prop->default_row || + sdw_cols[c] != prop->default_col) + continue; + + frame_int = sdw_rows[r] * sdw_cols[c]; + frame_freq = clk_freq / frame_int; + + if ((clk_freq - (frame_freq * SDW_FRAME_CTRL_BITS)) < + bus->params.bandwidth) + continue; + + bus->params.row = sdw_rows[r]; + bus->params.col = sdw_cols[c]; + return 0; + } + } + + return -EINVAL; +} + +/** + * sdw_compute_bus_params: Compute bus parameters + * + * @bus: SDW Bus instance + */ +static int sdw_compute_bus_params(struct sdw_bus *bus) +{ + unsigned int max_dr_freq, curr_dr_freq = 0; + struct sdw_master_prop *mstr_prop = NULL; + int i, clk_values, ret; + bool is_gear = false; + u32 *clk_buf; + + mstr_prop = &bus->prop; + if (!mstr_prop) + return -EINVAL; + + if (mstr_prop->num_clk_gears) { + clk_values = mstr_prop->num_clk_gears; + clk_buf = mstr_prop->clk_gears; + is_gear = true; + } else if (mstr_prop->num_clk_freq) { + clk_values = mstr_prop->num_clk_freq; + clk_buf = mstr_prop->clk_freq; + } else { + clk_values = 1; + clk_buf = NULL; + } + + max_dr_freq = mstr_prop->max_clk_freq * SDW_DOUBLE_RATE_FACTOR; + + for (i = 0; i < clk_values; i++) { + if (!clk_buf) + curr_dr_freq = max_dr_freq; + else + curr_dr_freq = (is_gear) ? + (max_dr_freq >> clk_buf[i]) : + clk_buf[i] * SDW_DOUBLE_RATE_FACTOR; + + if (curr_dr_freq <= bus->params.bandwidth) + continue; + + break; + + /* + * TODO: Check all the Slave(s) port(s) audio modes and find + * whether given clock rate is supported with glitchless + * transition. + */ + } + + if (i == clk_values) + return -EINVAL; + + ret = sdw_select_row_col(bus, curr_dr_freq); + if (ret < 0) + return -EINVAL; + + bus->params.curr_dr_freq = curr_dr_freq; + return 0; +} + +/** + * sdw_compute_params: Compute bus, transport and port parameters + * + * @bus: SDW Bus instance + */ +int sdw_compute_params(struct sdw_bus *bus) +{ + int ret; + + /* Computes clock frequency, frame shape and frame frequency */ + ret = sdw_compute_bus_params(bus); + if (ret < 0) { + dev_err(bus->dev, "Compute bus params failed: %d", ret); + return ret; + } + + /* Compute transport and port params */ + ret = sdw_compute_port_params(bus); + if (ret < 0) { + dev_err(bus->dev, "Compute transport params failed: %d", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(sdw_compute_params); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("SoundWire Generic Bandwidth Allocation"); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index c1699af2f6f169..1a9e070f50fa24 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1313,6 +1313,9 @@ static int intel_master_probe(struct sdw_master_device *md, void *link_ctx) /* set driver data, accessed by snd_soc_dai_set_drvdata() */ dev_set_drvdata(&md->dev, &sdw->cdns); + /* use generic bandwidth allocation algorithm */ + sdw->cdns.bus.compute_params = sdw_compute_params; + ret = sdw_add_bus_master(&sdw->cdns.bus); if (ret) { dev_err(&md->dev, "sdw_add_bus_master fail: %d\n", ret); diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 7d90620ffbbfa9..509286e3bdb5c1 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -26,6 +26,8 @@ int sdw_rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147, 192, 200, 240, 256, 72, 144, 90, 180}; int sdw_cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16}; +EXPORT_SYMBOL(sdw_rows); +EXPORT_SYMBOL(sdw_cols); int sdw_find_col_index(int col) { @@ -1776,6 +1778,16 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) bus->params.bandwidth -= m_rt->stream->params.rate * m_rt->ch_count * m_rt->stream->params.bps; + /* Compute params */ + if (bus->compute_params) { + ret = bus->compute_params(bus); + if (ret < 0) { + dev_err(bus->dev, "Compute params failed: %d", + ret); + return ret; + } + } + /* Program params */ ret = sdw_program_params(bus); if (ret < 0) { diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 347d2cda76d022..33ed48df83a8bb 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -935,6 +935,9 @@ struct sdw_stream_runtime { struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name); void sdw_release_stream(struct sdw_stream_runtime *stream); + +int sdw_compute_params(struct sdw_bus *bus); + int sdw_stream_add_master(struct sdw_bus *bus, struct sdw_stream_config *stream_config, struct sdw_port_config *port_config, From 1d3e3ba849ab64f0110d77cafa7eeb7569c984c0 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 2 Aug 2019 10:14:46 +0800 Subject: [PATCH 145/170] soundwire: dynamic_allocation: set grp_ctrl_valid false Some Realtek codecs don't support grp_ctrl_valid. Set it to false to prevent DPN_BlockCtrl2 reg write failed. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/generic_bandwidth_allocation.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index dc7ffa987963ac..6124c6f8561f32 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -54,7 +54,7 @@ static void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt, ch = sdw_ch_mask_to_ch(p_rt->ch_mask); sdw_fill_xport_params(&p_rt->transport_params, - p_rt->num, true, + p_rt->num, false, SDW_BLK_GRP_CNT_1, sample_int, port_bo, port_bo >> 8, t_data->hstart, @@ -97,7 +97,7 @@ static void sdw_compute_master_ports(struct sdw_master_runtime *m_rt, no_ch = sdw_ch_mask_to_ch(p_rt->ch_mask); sdw_fill_xport_params(&p_rt->transport_params, p_rt->num, - true, SDW_BLK_GRP_CNT_1, sample_int, + false, SDW_BLK_GRP_CNT_1, sample_int, port_bo, port_bo >> 8, hstart, hstop, (SDW_BLK_GRP_CNT_1 * no_ch), 0x0); From 872ac57a20501189695eb72f5a1dbcfc3e9b2455 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 30 Jul 2019 16:14:18 -0500 Subject: [PATCH 146/170] soundwire: bus: fix device number leak on errors If the programming of the dev_number fails due to an IO error, a new device_number will be assigned, resulting in a leak. Make sure we only assign a device_number once per Slave device. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 39 ++++++++++++++++++++++------------- include/linux/soundwire/sdw.h | 4 +++- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 011bd37c5ca171..c0467978a0e872 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -547,26 +547,37 @@ static int sdw_get_device_num(struct sdw_slave *slave) static int sdw_assign_device_num(struct sdw_slave *slave) { int ret, dev_num; + bool new_device = false; + + dev_dbg(slave->bus->dev, "in %s\n", __func__); /* check first if device number is assigned, if so reuse that */ if (!slave->dev_num) { - mutex_lock(&slave->bus->bus_lock); - dev_num = sdw_get_device_num(slave); - mutex_unlock(&slave->bus->bus_lock); - if (dev_num < 0) { - dev_err(slave->bus->dev, "Get dev_num failed: %d\n", - dev_num); - return dev_num; + if (!slave->dev_num_sticky) { + mutex_lock(&slave->bus->bus_lock); + dev_num = sdw_get_device_num(slave); + mutex_unlock(&slave->bus->bus_lock); + if (dev_num < 0) { + dev_err(slave->bus->dev, "Get dev_num failed: %d\n", + dev_num); + return dev_num; + } + slave->dev_num = dev_num; + slave->dev_num_sticky = dev_num; + new_device = true; + } else { + slave->dev_num = slave->dev_num_sticky; } - } else { + } + + if (!new_device) dev_info(slave->bus->dev, - "Slave already registered dev_num:%d\n", + "Slave already registered, reusing dev_num:%d\n", slave->dev_num); - /* Clear the slave->dev_num to transfer message on device 0 */ - dev_num = slave->dev_num; - slave->dev_num = 0; - } + /* Clear the slave->dev_num to transfer message on device 0 */ + dev_num = slave->dev_num; + slave->dev_num = 0; ret = sdw_write_no_pm(slave, SDW_SCP_DEVNUMBER, dev_num); if (ret < 0) { @@ -576,7 +587,7 @@ static int sdw_assign_device_num(struct sdw_slave *slave) } /* After xfer of msg, restore dev_num */ - slave->dev_num = dev_num; + slave->dev_num = slave->dev_num_sticky; return 0; } diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 33ed48df83a8bb..c4d6f96b39292b 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -566,7 +566,8 @@ struct sdw_slave_ops { * @debugfs: Slave debugfs * @node: node for bus list * @port_ready: Port ready completion flag for each Slave port - * @dev_num: Device Number assigned by Bus + * @dev_num: Current Device Number, values can be 0 or dev_num_sticky + * @dev_num_sticky: one-time static Device Number assigned by Bus * @probed: boolean tracking driver state * @probe_complete: completion utility to control potential races * on startup between driver probe/initialization and SoundWire @@ -596,6 +597,7 @@ struct sdw_slave { struct completion *port_ready; enum sdw_clk_stop_mode curr_clk_stop_mode; u16 dev_num; + u16 dev_num_sticky; bool probed; struct completion probe_complete; struct completion enumeration_complete; From 9907107c9d7bba06ce7100dc17f669a9960d72a8 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 19 Aug 2019 12:13:03 -0500 Subject: [PATCH 147/170] soundwire: intel: modify DMAT field for ALH The current value for this field is too low and results in some samples getting dropped and playback being faster than normal. Increase the value to a safer value. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 1a9e070f50fa24..ea36f75b2361ec 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -105,7 +105,7 @@ MODULE_PARM_DESC(sdw_md_flags, "SoundWire Intel Master device flags (0x0 all off #define SDW_ALH_STRMZCFG(x) (0x000 + (0x4 * (x))) #define SDW_ALH_NUM_STREAMS 64 -#define SDW_ALH_STRMZCFG_DMAT_VAL 0x3 +#define SDW_ALH_STRMZCFG_DMAT_VAL 0xf #define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0) #define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16) From db5019a0f13eb05267a0fe323b56450b5e675aef Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 19 Aug 2019 12:18:05 -0500 Subject: [PATCH 148/170] soundwire: intel: fix factor of two in MCLK handling Somehow Intel folks were confused, the property is 2x what the mclk frequency actually is, as checked with the actual bus frequency. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index ea36f75b2361ec..fedac935b4555b 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1237,6 +1237,7 @@ static int sdw_master_read_intel_prop(struct sdw_bus *bus) "intel-sdw-ip-clock", &prop->mclk_freq); + prop->mclk_freq /= 2; fwnode_property_read_u32(link, "intel-quirk-mask", &quirk_mask); From 3c08149f4bc2017f7cb1af07c19833116a969660 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 26 Jun 2019 12:29:42 -0500 Subject: [PATCH 149/170] soundwire: cadence_master: log register write info useful for debug, but can be verbose so only enable if strictly needed. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 1f81681d7517fa..b0ba589f2d8785 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -196,6 +196,7 @@ static inline u32 cdns_readl(struct sdw_cdns *cdns, int offset) static inline void cdns_writel(struct sdw_cdns *cdns, int offset, u32 value) { + dev_vdbg(cdns->dev, "%s %x %x\n", __func__, offset, value); writel(value, cdns->registers + offset); } From 48513096dde797d4fb588bc797b327f61ed3be64 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 10 Jun 2019 19:21:39 -0500 Subject: [PATCH 150/170] add more traces to bus code Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index c0467978a0e872..c637fe742febdc 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -549,6 +549,9 @@ static int sdw_assign_device_num(struct sdw_slave *slave) int ret, dev_num; bool new_device = false; + dev_dbg(slave->bus->dev, "in %s\n", __func__); + + dev_dbg(slave->bus->dev, "in %s\n", __func__); /* check first if device number is assigned, if so reuse that */ @@ -579,6 +582,7 @@ static int sdw_assign_device_num(struct sdw_slave *slave) dev_num = slave->dev_num; slave->dev_num = 0; + dev_dbg(slave->bus->dev, "in %s, writing dev_num %d\n", __func__, dev_num); ret = sdw_write_no_pm(slave, SDW_SCP_DEVNUMBER, dev_num); if (ret < 0) { dev_err(&slave->dev, "Program device_num %d failed: %d\n", @@ -696,6 +700,8 @@ static int sdw_program_device_num(struct sdw_bus *bus) } while (ret == 0 && count < (SDW_MAX_DEVICES * 2)); + dev_err(bus->dev, "End of %s ret %d\n", __func__, ret); + return ret; } @@ -1484,14 +1490,17 @@ int sdw_handle_slave_status(struct sdw_bus *bus, break; case SDW_SLAVE_ATTACHED: - if (slave->status == SDW_SLAVE_ATTACHED) + if (slave->status == SDW_SLAVE_ATTACHED) { + dev_err(bus->dev, "Slave %d status attached, was already attached, ignoring\n", i); break; - + } prev_status = slave->status; sdw_modify_slave_status(slave, SDW_SLAVE_ATTACHED); - if (prev_status == SDW_SLAVE_ALERT) + if (prev_status == SDW_SLAVE_ALERT) { + dev_err(bus->dev, "Slave %d status attached, was alert, ignoring\n", i); break; + } attached_initializing = true; @@ -1509,6 +1518,7 @@ int sdw_handle_slave_status(struct sdw_bus *bus, break; } + dev_err(bus->dev, "Updating Slave %d status\n", i); ret = sdw_update_slave_status(slave, status[i]); if (ret) dev_err(slave->bus->dev, From 0859b7512c4a1daa220139cce1ad530799b997f1 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 1 Aug 2019 18:31:34 -0500 Subject: [PATCH 151/170] add traces for bus Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index c637fe742febdc..29836d34a357d1 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1428,6 +1428,8 @@ int sdw_handle_slave_status(struct sdw_bus *bus, bool attached_initializing; int i, ret = 0; + dev_dbg(bus->dev, "%s: start\n", __func__); + /* first check if any Slaves fell off the bus */ for (i = 1; i <= SDW_MAX_DEVICES; i++) { mutex_lock(&bus->bus_lock); @@ -1523,10 +1525,16 @@ int sdw_handle_slave_status(struct sdw_bus *bus, if (ret) dev_err(slave->bus->dev, "Update Slave status failed:%d\n", ret); + if (attached_initializing) complete(&slave->initialization_complete); + + dev_err(bus->dev, "%s: Updating Slave %d status done\n", + __func__, i); } + dev_dbg(bus->dev, "%s: end\n", __func__); + return ret; } EXPORT_SYMBOL(sdw_handle_slave_status); From 9a3d44d94fd122b0e1bd92109ced350c9d1eec51 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 11 Sep 2019 20:18:19 -0500 Subject: [PATCH 152/170] [HACK] soundwire: add dynamic DEBUG in makefile Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index eb37d69cbdca96..76a5c52b12b412 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -2,6 +2,7 @@ # # Makefile for soundwire core # +ccflags-y += -DDEBUG #Bus Objs soundwire-bus-objs := bus_type.o bus.o master.o slave.o mipi_disco.o stream.o From 738283fcf632fcc6b6c81fff6abb5baed6dbcf79 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 20 Nov 2019 10:23:24 +0800 Subject: [PATCH 153/170] ALSA: HDA: intel-dsp-config: add DMI info for Dell laptop Another Dell laptop uses SOF with CML platform Signed-off-by: Bard Liao --- sound/hda/intel-dsp-config.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c index 6e6e8662534d8a..72c64bf42dec3e 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/intel-dsp-config.c @@ -220,6 +220,19 @@ static const struct config_entry config_table[] = { #endif /* Cometlake-H */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_H) + { + .flags = FLAG_SOF, + .device = 0x06c8, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Dell laptop", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell"), + } + }, + {} + } + }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, .device = 0x06c8, From 08bb1196e3cd47e70a47fbeb90d3e07770fa9a78 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 2 Dec 2019 16:41:11 +0800 Subject: [PATCH 154/170] Soundwire: generic_bandwidth_allocation: don't free params if it is null We will free params when we goto out in sdw_compute_port_params(). But we can't free params if it is not allocated successfully. Signed-off-by: Bard Liao --- drivers/soundwire/generic_bandwidth_allocation.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index 6124c6f8561f32..7a747a302caf5d 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -239,8 +239,10 @@ static int sdw_get_group_count(struct sdw_bus *bus, } else { ret = sdw_add_element_group_count(group, rate); - if (ret < 0) + if (ret < 0) { + kfree(group->rates); return ret; + } } } @@ -260,7 +262,7 @@ static int sdw_compute_port_params(struct sdw_bus *bus) ret = sdw_get_group_count(bus, &group); if (ret < 0) - goto out; + return ret; if (group.count == 0) goto out; @@ -275,12 +277,13 @@ static int sdw_compute_port_params(struct sdw_bus *bus) ret = sdw_compute_group_params(bus, params, &group.rates[0], group.count); if (ret < 0) - goto out; + goto free_params; _sdw_compute_port_params(bus, params, group.count); -out: +free_params: kfree(params); +out: kfree(group.rates); return ret; From b856c0c533ae5aee36a0cab61344cf1589d62c90 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 6 Dec 2019 10:37:38 -0600 Subject: [PATCH 155/170] soundwire: cadence_master: remove useless variable incrementation Fix cppcheck warning: drivers/soundwire/cadence_master.c:992:9: style: Variable 'offset' is assigned a value that is never used. [unreadVariable] offset += stream->num_out; ^ Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index b0ba589f2d8785..cb797668c7eb81 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -990,8 +990,6 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, ret = cdns_allocate_pdi(cdns, &stream->out, stream->num_out, offset); - offset += stream->num_out; - if (ret) return ret; From c37cb0f1b0ce4c0f1c35d9341bb48a5db4eb5ab7 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Wed, 11 Dec 2019 10:18:53 +0800 Subject: [PATCH 156/170] soundwire: cadence_master: fix a io timeout issue in S3 test After system resumes from S3, io timeout occurs when setting one unused master on Comet Lake platform. In this case, the master is reset to default state, and FIFOLEVEL is reset to default value, but msg_count used for tracing FIFOLEVEL is still with old value, so FIFOLEVEL will not be set if a new msg FIFO usage is equal to the old msg_count. This patch updates msg_count to default value of FIFOLEVEL when resetting master. Tested on Comet Lake platform. Signed-off-by: Rander Wang --- drivers/soundwire/cadence_master.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index cb797668c7eb81..d62b5bbe316c58 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -1046,6 +1046,9 @@ int sdw_cdns_init(struct sdw_cdns *cdns) cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, CDNS_DEFAULT_SSP_INTERVAL); cdns_writel(cdns, CDNS_MCP_SSP_CTRL1, CDNS_DEFAULT_SSP_INTERVAL); + /* reset msg_count to default value of FIFOLEVEL */ + cdns->msg_count = cdns_readl(cdns, CDNS_MCP_FIFOLEVEL); + /* flush command FIFOs */ cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_RST, CDNS_MCP_CONTROL_CMD_RST); From a123d0fb46d45b0c787b31f7c0452a30b05cf617 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Wed, 4 Dec 2019 14:32:09 +0800 Subject: [PATCH 157/170] soundwire: bus: fix io error when processing alert event There are two types of io error when processing alert event. The first one is: master receives a alert event for jack event and invokes implement_def function in slave to check jack status. At this time codec is just suspended, so io registers can't be accessed. Another one is: when waken up from clock stop state and bus needs a complete re-enumeration for synchronization issue , slave register can't be accessed. This patch wakes up codec and wait for initialization complete when processing slave alert event, so that io registers can be accessed. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 29836d34a357d1..3e28aaa36e7f99 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1251,12 +1251,19 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) sdw_modify_slave_status(slave, SDW_SLAVE_ALERT); + ret = pm_runtime_get_sync(&slave->dev); + if (ret < 0 && ret != -EACCES) { + dev_err(&slave->dev, "Failed to resume device: %d\n", ret); + pm_runtime_put_noidle(slave->bus->dev); + return ret; + } + /* Read Instat 1, Instat 2 and Instat 3 registers */ ret = sdw_read(slave, SDW_SCP_INT1); if (ret < 0) { dev_err(slave->bus->dev, "SDW_SCP_INT1 read failed:%d\n", ret); - return ret; + goto io_err; } buf = ret; @@ -1264,7 +1271,7 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) if (ret < 0) { dev_err(slave->bus->dev, "SDW_SCP_INT2/3 read failed:%d\n", ret); - return ret; + goto io_err; } do { @@ -1344,7 +1351,7 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) if (ret < 0) { dev_err(slave->bus->dev, "SDW_SCP_INT1 write failed:%d\n", ret); - return ret; + goto io_err; } /* @@ -1355,7 +1362,7 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) if (ret < 0) { dev_err(slave->bus->dev, "SDW_SCP_INT1 read failed:%d\n", ret); - return ret; + goto io_err; } _buf = ret; @@ -1363,7 +1370,7 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) if (ret < 0) { dev_err(slave->bus->dev, "SDW_SCP_INT2/3 read failed:%d\n", ret); - return ret; + goto io_err; } /* Make sure no interrupts are pending */ @@ -1384,6 +1391,10 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) if (count == SDW_READ_INTR_CLEAR_RETRY) dev_warn(slave->bus->dev, "Reached MAX_RETRY on alert read\n"); +io_err: + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + return ret; } From cb5212f1817f03cec0a446f6c1afcd612d65acac Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 5 Dec 2019 15:57:25 -0600 Subject: [PATCH 158/170] soundwire: cadence_master: debug error on Olympic device in clock_stop Somehow the master-2 fails to stop the clock [ 24.007288] intel-sdw sdw-master-2: intel_suspend_runtime start [ 24.007297] intel-sdw sdw-master-2: sdw_cdns_clock_stop: start [ 24.007303] intel-sdw sdw-master-2: sdw_cdns_clock_stop: slave attached 0 [ 24.007498] intel-sdw sdw-master-2: Msg ignored for Slave 15 [ 24.007501] intel-sdw sdw-master-2: ClockStopNow Broadcast message failed -61 [ 24.007505] intel-sdw sdw-master-2: bus clock stop failed -61 [ 24.007508] intel-sdw sdw-master-2: cannot enable clock stop on suspend there are not slaves attached, so the command ignored should be accepted what's not clear is what the CMD_ACCEPT (1) is already the default? Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index d62b5bbe316c58..39ae15675755ac 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -1243,6 +1243,8 @@ int sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake) u32 status; int ret; + dev_dbg(cdns->dev, "%s: start\n", __func__); + /* Check suspend status */ status = cdns_readl(cdns, CDNS_MCP_STAT); if (status & CDNS_MCP_STAT_CLK_STOP) { @@ -1268,6 +1270,8 @@ int sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake) } } + dev_dbg(cdns->dev, "%s: slave attached %d\n", __func__, slave_present); + /* * This CMD_ACCEPT should be used when there are no devices * attached on the link when entering clock stop mode. If this is @@ -1308,6 +1312,8 @@ int sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake) if (ret < 0) dev_err(cdns->dev, "Clock stop failed %d\n", ret); + dev_dbg(cdns->dev, "%s: end\n", __func__); + return ret; } EXPORT_SYMBOL(sdw_cdns_clock_stop); From f9f9a9d732970fa2240af822d0d2e4aefca925b0 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 5 Dec 2019 15:07:33 -0600 Subject: [PATCH 159/170] soundwire: bus: add traces for slave alerts Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 3e28aaa36e7f99..99bbe4979de03a 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1249,6 +1249,8 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) bool slave_notify = false; u8 buf, buf2[2], _buf, _buf2[2]; + dev_dbg(slave->bus->dev, "%s: start\n", __func__); + sdw_modify_slave_status(slave, SDW_SLAVE_ALERT); ret = pm_runtime_get_sync(&slave->dev); @@ -1395,6 +1397,8 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); + dev_dbg(slave->bus->dev, "%s: end\n", __func__); + return ret; } From b8d0df24df88e23ae6527e97a3164825da940308 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 6 Dec 2019 12:29:43 -0600 Subject: [PATCH 160/170] soundwire: cadence_master: add traces for Slave state change Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 39ae15675755ac..145834f428b959 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -677,16 +677,19 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns, } if (mask & CDNS_MCP_SLAVE_INTSTAT_ATTACHED) { + dev_dbg(cdns->dev, "Slave %d ATTACHED\n", i); status[i] = SDW_SLAVE_ATTACHED; set_status++; } if (mask & CDNS_MCP_SLAVE_INTSTAT_ALERT) { + dev_dbg(cdns->dev, "Slave %d ALERT\n", i); status[i] = SDW_SLAVE_ALERT; set_status++; } if (mask & CDNS_MCP_SLAVE_INTSTAT_NPRESENT) { + dev_dbg(cdns->dev, "Slave %d UNATTACHED\n", i); status[i] = SDW_SLAVE_UNATTACHED; set_status++; } From 0ac77a9273075a513c5649f21661bac681f13253 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 6 Dec 2019 12:31:08 -0600 Subject: [PATCH 161/170] soundwire: bus: add traces for Slave state changes Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 99bbe4979de03a..b58df4de3f817e 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -710,7 +710,7 @@ static void sdw_modify_slave_status(struct sdw_slave *slave, { mutex_lock(&slave->bus->bus_lock); - dev_vdbg(&slave->dev, + dev_dbg(&slave->dev, "%s: changing status slave %d status %d new status %d\n", __func__, slave->dev_num, slave->status, status); From 15b4888eccc165a0074fe353c2eff0316bb638b2 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 9 Dec 2019 16:35:31 -0600 Subject: [PATCH 162/170] soundwire: cadence_master: add traces for clock_restart Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 145834f428b959..cf96532b0a8e0d 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -1332,6 +1332,8 @@ int sdw_cdns_clock_restart(struct sdw_cdns *cdns, bool bus_reset) { int ret; + dev_dbg(cdns->dev, "%s: start\n", __func__); + ret = cdns_clear_bit(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CLK_STOP_CLR); if (ret < 0) { @@ -1377,6 +1379,8 @@ int sdw_cdns_clock_restart(struct sdw_cdns *cdns, bool bus_reset) if (ret < 0) dev_err(cdns->dev, "bus failed to exit clock stop %d\n", ret); + dev_dbg(cdns->dev, "%s: end\n", __func__); + return ret; } EXPORT_SYMBOL(sdw_cdns_clock_restart); From 255682d38de55ca1e4c8a7efc9ce1053c0b6ac40 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Wed, 11 Dec 2019 15:03:20 +0800 Subject: [PATCH 163/170] soundwire: intel: refine function for wakeen event processing if a slave has been attached to a bus, the slave->dev_num_sticky should be non-zero, so we can check this value to skip the ghost devices defined in ACPI table but not populated in hardware. Signed-off-by: Rander Wang --- drivers/soundwire/intel.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index fedac935b4555b..2c505a30df06f1 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1492,13 +1492,13 @@ static void intel_master_process_wakeen_event(struct sdw_master_device *md) * the wakeen event and let codec driver check codec status */ list_for_each_entry(slave, &bus->slaves, node) { - if (slave->prop.wake_capable) { - if (slave->status != SDW_SLAVE_ATTACHED && - slave->status != SDW_SLAVE_ALERT) - continue; - + /* + * discard devices that are defined in ACPI tables but + * not physically present and devices that cannot + * generate wakes + */ + if (slave->dev_num_sticky && slave->prop.wake_capable) pm_request_resume(&slave->dev); - } } } From 93672db36c645623399c29ae6d9569db9a57e0fa Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 18 Dec 2019 18:16:43 -0600 Subject: [PATCH 164/170] soundwire: stream: add traces to debug "transport params" error Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/stream.c | 49 ++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 509286e3bdb5c1..e58013fbe6b15b 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -138,8 +138,12 @@ static int sdw_program_slave_port_params(struct sdw_bus *bus, dpn_prop = sdw_get_slave_dpn_prop(s_rt->slave, s_rt->direction, t_params->port_num); - if (!dpn_prop) + if (!dpn_prop) { + dev_err(&s_rt->slave->dev, + "invalid dpn_prop direction %d port_num %d\n", + s_rt->direction, t_params->port_num); return -EINVAL; + } addr1 = SDW_DPN_PORTCTRL(t_params->port_num); addr2 = SDW_DPN_BLOCKCTRL1(t_params->port_num); @@ -245,12 +249,21 @@ static int sdw_program_master_port_params(struct sdw_bus *bus, ret = bus->port_ops->dpn_set_port_transport_params(bus, &p_rt->transport_params, bus->params.next_bank); - if (ret < 0) + if (ret < 0) { + dev_err(bus->dev, "%s dpn_set_port_transport_params failed: %d\n", + __func__, ret); return ret; + } - return bus->port_ops->dpn_set_port_params(bus, - &p_rt->port_params, - bus->params.next_bank); + ret = bus->port_ops->dpn_set_port_params(bus, + &p_rt->port_params, + bus->params.next_bank); + + if (ret < 0) + dev_err(bus->dev, "%s dpn_set_port_params failed: %d\n", + __func__, ret); + + return ret; } /** @@ -270,16 +283,20 @@ static int sdw_program_port_params(struct sdw_master_runtime *m_rt) list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { list_for_each_entry(p_rt, &s_rt->port_list, port_node) { ret = sdw_program_slave_port_params(bus, s_rt, p_rt); - if (ret < 0) + if (ret < 0) { + pr_err("sdw_program_slave_port_params failed %d\n", ret); return ret; + } } } /* Program transport & port parameters for Master(s) */ list_for_each_entry(p_rt, &m_rt->port_list, port_node) { ret = sdw_program_master_port_params(bus, p_rt); - if (ret < 0) + if (ret < 0) { + pr_err("sdw_program_master_port_params failed %d\n", ret); return ret; + } } return 0; @@ -454,8 +471,12 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus, if (prep && intr) { ret = sdw_configure_dpn_intr(s_rt->slave, p_rt->num, prep, dpn_prop->imp_def_interrupts); - if (ret < 0) + if (ret < 0) { + dev_err(&s_rt->slave->dev, + "sdw_configure_dpn_intr failed %d\n", ret); + return ret; + } } /* Inform slave about the impending port prepare */ @@ -495,10 +516,16 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus, sdw_do_port_prep(s_rt, prep_ch, SDW_OPS_PORT_POST_PREP); /* Disable interrupt after Port de-prepare */ - if (!prep && intr) + if (!prep && intr) { ret = sdw_configure_dpn_intr(s_rt->slave, p_rt->num, prep, dpn_prop->imp_def_interrupts); + if (ret < 0) { + dev_err(&s_rt->slave->dev, + "sdw_configure_dpn_intr failed %d\n", ret); + } + } + return ret; } @@ -580,8 +607,10 @@ static int sdw_notify_config(struct sdw_master_runtime *m_rt) if (bus->ops->set_bus_conf) { ret = bus->ops->set_bus_conf(bus, &bus->params); - if (ret < 0) + if (ret < 0) { + dev_err(bus->dev, "%s: set_bus_conf failed %d\n", __func__, ret); return ret; + } } list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { From 1e4871c401473760f0504ff22c99461e372afb9d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 7 Jan 2020 15:31:15 -0600 Subject: [PATCH 165/170] ASoC: Intel: sdw-rt711-rt1308-rt715: move to new CODEC_CONF representation Follow upstream move and convert to .dlc = COMP_CODEC_CONF Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c index af0bd2b8f23fb8..67e2b0ec5fa18d 100644 --- a/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c +++ b/sound/soc/intel/boards/sdw_rt711_rt1308_rt715.c @@ -311,19 +311,19 @@ SND_SOC_DAILINK_DEF(platform, static struct snd_soc_codec_conf codec_conf[] = { { - .dev_name = "sdw:0:25d:711:0", + .dlc = COMP_CODEC_CONF("sdw:0:25d:711:0"), .name_prefix = "rt711", }, { - .dev_name = "sdw:1:25d:1308:0", + .dlc = COMP_CODEC_CONF("sdw:1:25d:1308:0"), .name_prefix = "rt1308-1", }, { - .dev_name = "sdw:3:25d:715:0", + .dlc = COMP_CODEC_CONF("sdw:3:25d:715:0"), .name_prefix = "rt715", }, { - .dev_name = "sdw:2:25d:1308:0", + .dlc = COMP_CODEC_CONF("sdw:2:25d:1308:0"), .name_prefix = "rt1308-2", }, From 674830a2f69ad1b60571013ceccd3eadeb1d2a42 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 18 Dec 2019 20:52:23 -0600 Subject: [PATCH 166/170] soundwire: cadence_master: filter out bad interrupts If somehow we read the interrupt status while the IP is not powered the result is probably undefined or 0xffffffff. We do know that some of the bits are reserved and read as zero, so use as a filter to discard invalid configurations. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index cf96532b0a8e0d..974b9162116411 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -75,6 +75,7 @@ MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask"); #define CDNS_MCP_INTMASK 0x48 #define CDNS_MCP_INT_IRQ BIT(31) +#define CDNS_MCP_INT_RESERVED1 GENMASK(30, 17) #define CDNS_MCP_INT_WAKEUP BIT(16) #define CDNS_MCP_INT_SLAVE_RSVD BIT(15) #define CDNS_MCP_INT_SLAVE_ALERT BIT(14) @@ -86,10 +87,12 @@ MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask"); #define CDNS_MCP_INT_DATA_CLASH BIT(9) #define CDNS_MCP_INT_PARITY BIT(8) #define CDNS_MCP_INT_CMD_ERR BIT(7) +#define CDNS_MCP_INT_RESERVED2 GENMASK(6, 4) #define CDNS_MCP_INT_RX_NE BIT(3) #define CDNS_MCP_INT_RX_WL BIT(2) #define CDNS_MCP_INT_TXE BIT(1) #define CDNS_MCP_INT_TXF BIT(0) +#define CDNS_MCP_INT_RESERVED (CDNS_MCP_INT_RESERVED1 | CDNS_MCP_INT_RESERVED2) #define CDNS_MCP_INTSET 0x4C @@ -752,6 +755,10 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id) int_status = cdns_readl(cdns, CDNS_MCP_INTSTAT); + /* check for reserved values read as zero */ + if (int_status & CDNS_MCP_INT_RESERVED) + return IRQ_NONE; + if (!(int_status & CDNS_MCP_INT_IRQ)) return IRQ_NONE; From a200933f157dc6a13055d9f485acc67d1bb52ff9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 19 Dec 2019 18:47:30 -0600 Subject: [PATCH 167/170] soundwire: cadence: disable exit_clock_stop when doing a bus_reset This function is broken due to race conditions and bad ideas... When a bus reset occurs, the clock stop is no longer prepared. FIXME: the clock stop mode1 needs to wait for the device to re-enumerate. Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/cadence_master.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 974b9162116411..e0e0b6e263682f 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -1382,9 +1382,11 @@ int sdw_cdns_clock_restart(struct sdw_cdns *cdns, bool bus_reset) cdns_update_config(cdns); - ret = sdw_bus_exit_clk_stop(&cdns->bus); - if (ret < 0) - dev_err(cdns->dev, "bus failed to exit clock stop %d\n", ret); + if (!bus_reset) { + ret = sdw_bus_exit_clk_stop(&cdns->bus); + if (ret < 0) + dev_err(cdns->dev, "bus failed to exit clock stop %d\n", ret); + } dev_dbg(cdns->dev, "%s: end\n", __func__); From cfb157900cbe8990f163088865106f9b1ca72d11 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 19 Dec 2019 20:05:12 -0600 Subject: [PATCH 168/170] soundwire: intel: don't program Sync registers in clock stop mode This leads to a "Failed to set sync period" error log, but the values don't need to be reprogrammed. FIXME: is this the right fix? Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 2c505a30df06f1..5e06fae52312ad 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -326,11 +326,11 @@ static int intel_link_power_up(struct sdw_intel *sdw) return 0; } -static int intel_shim_init(struct sdw_intel *sdw) +static int intel_shim_init(struct sdw_intel *sdw, bool clock_stop) { void __iomem *shim = sdw->link_res->shim; unsigned int link_id = sdw->instance; - int sync_reg, ret; + int sync_reg, ret = 0; u16 ioctl = 0, act = 0; mutex_lock(sdw->link_res->shim_lock); @@ -370,19 +370,21 @@ static int intel_shim_init(struct sdw_intel *sdw) act |= SDW_SHIM_CTMCTL_DODS; intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act); - /* Now set SyncPRD period */ - sync_reg = intel_readl(shim, SDW_SHIM_SYNC); - sync_reg |= (SDW_SHIM_SYNC_SYNCPRD_VAL << - SDW_REG_SHIFT(SDW_SHIM_SYNC_SYNCPRD)); + if (!clock_stop) { + /* Now set SyncPRD period */ + sync_reg = intel_readl(shim, SDW_SHIM_SYNC); + sync_reg |= (SDW_SHIM_SYNC_SYNCPRD_VAL << + SDW_REG_SHIFT(SDW_SHIM_SYNC_SYNCPRD)); - /* Set SyncCPU bit */ - sync_reg |= SDW_SHIM_SYNC_SYNCCPU; - ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg, - SDW_SHIM_SYNC_SYNCCPU); - mutex_unlock(sdw->link_res->shim_lock); + /* Set SyncCPU bit */ + sync_reg |= SDW_SHIM_SYNC_SYNCCPU; + ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg, + SDW_SHIM_SYNC_SYNCCPU); + if (ret < 0) + dev_err(sdw->cdns.dev, "Failed to set sync period: %d\n", ret); - if (ret < 0) - dev_err(sdw->cdns.dev, "Failed to set sync period: %d\n", ret); + } + mutex_unlock(sdw->link_res->shim_lock); return ret; } @@ -1271,11 +1273,16 @@ static struct sdw_master_ops sdw_intel_ops = { static int intel_init(struct sdw_intel *sdw) { + bool clock_stop; + /* Initialize shim and controller */ intel_link_power_up(sdw); - intel_shim_init(sdw); - if (sdw_cdns_is_clock_stop(&sdw->cdns)) + clock_stop = sdw_cdns_is_clock_stop(&sdw->cdns); + + intel_shim_init(sdw, clock_stop); + + if (clock_stop) return 0; return sdw_cdns_init(&sdw->cdns); From 688a947bc1805fe5d6bbb3da20390ace7aeac096 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 19 Dec 2019 19:07:12 -0600 Subject: [PATCH 169/170] soundwire: intel: add traces to track clock restart mode Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/intel.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 5e06fae52312ad..e2be9d7cf8ef34 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1722,6 +1722,9 @@ static int intel_resume_runtime(struct device *dev) clock_stop_quirks = sdw->link_res->clock_stop_quirks; if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) { + + dev_dbg(dev, "%s resume from TEARDOWN\n", __func__); + ret = intel_init(sdw); if (ret) { dev_err(dev, "%s failed: %d", __func__, ret); @@ -1747,6 +1750,9 @@ static int intel_resume_runtime(struct device *dev) return ret; } } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) { + + dev_dbg(dev, "%s resume from BUS_RESET\n", __func__); + ret = intel_init(sdw); if (ret) { dev_err(dev, "%s failed: %d", __func__, ret); @@ -1783,6 +1789,9 @@ static int intel_resume_runtime(struct device *dev) return ret; } } else if (!clock_stop_quirks) { + + dev_err(dev, "%s resume with no quirks\n", __func__); + ret = intel_init(sdw); if (ret) { dev_err(dev, "%s failed: %d", __func__, ret); From 0971aff9ca579989688617969b6b207416c7ff7f Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Tue, 7 Jan 2020 15:56:08 -0600 Subject: [PATCH 170/170] soundwire: stream: don't program ports when a stream that has not been prepared In multi-pipelines test case, there are two pipelines for playback and capture on the same bus. The test fails with an error when setting port params:. [ 599.224812] rt711 sdw:0:25d:711:0: invalid dpn_prop direction 1 port_num 0 [ 599.224815] sdw_program_slave_port_params failed -22 [ 599.224819] intel-sdw sdw-master-0: Program transport params failed: -22 [ 599.224822] intel-sdw sdw-master-0: Program params failed: -22 [ 599.224828] sdw_enable_stream: SDW0 Pin2-Playback: done This problem is root-caused to an invalid state for the stream, the calling sequence is: (1) hw_params for playback. The playback stream provide the port information to Bus. (2) stream_prepare for playback, Transport and port parameters are computed for playback. (3) hw_params for capture. The capture stream provide the port information to Bus, but it has not been prepared so is not accounted for in the bandwidth allocation. (4) stream_enable for playback. Program transport and port parameters for all masters and slaves. Since the transport and port parameters are not computed for capture stream, sdw_program_slave_port_params will generate a error when setting port params for capture. in step (4), we should only program the ports for the stream that have been prepared. A stream that is only in CONFIGURED state should be ignored, its ports will be programmed when it becomes PREPARED. Tested on Comet Lake. GitHub issue: https://github.com/thesofproject/linux/issues/1637 Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- drivers/soundwire/stream.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index e58013fbe6b15b..528ad9fc62d8ea 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -635,12 +635,23 @@ static int sdw_notify_config(struct sdw_master_runtime *m_rt) * * @bus: SDW bus instance */ -static int sdw_program_params(struct sdw_bus *bus) +static int sdw_program_params(struct sdw_bus *bus, bool prepare) { struct sdw_master_runtime *m_rt; int ret = 0; list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + + /* + * this loop walks through all master runtimes for a + * bus, but the ports can only be configured while + * explicitly preparing a stream or handling an + * already-prepared stream otherwise. + */ + if (!prepare && + m_rt->stream->state == SDW_STREAM_CONFIGURED) + continue; + ret = sdw_program_port_params(m_rt); if (ret < 0) { dev_err(bus->dev, @@ -1533,7 +1544,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream, program_params: /* Program params */ - ret = sdw_program_params(bus); + ret = sdw_program_params(bus, true); if (ret < 0) { dev_err(bus->dev, "Program params failed: %d\n", ret); goto restore_params; @@ -1636,7 +1647,7 @@ static int _sdw_enable_stream(struct sdw_stream_runtime *stream) bus = m_rt->bus; /* Program params */ - ret = sdw_program_params(bus); + ret = sdw_program_params(bus, false); if (ret < 0) { dev_err(bus->dev, "Program params failed: %d\n", ret); return ret; @@ -1723,7 +1734,7 @@ static int _sdw_disable_stream(struct sdw_stream_runtime *stream) struct sdw_bus *bus = m_rt->bus; /* Program params */ - ret = sdw_program_params(bus); + ret = sdw_program_params(bus, false); if (ret < 0) { dev_err(bus->dev, "Program params failed: %d\n", ret); return ret; @@ -1818,7 +1829,7 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) } /* Program params */ - ret = sdw_program_params(bus); + ret = sdw_program_params(bus, false); if (ret < 0) { dev_err(bus->dev, "Program params failed: %d\n", ret); return ret;