diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-07-05 10:54:43 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-07-05 10:54:43 -0700 |
commit | fe1de55167963a1c0ebe1579e37a8a41495f0a81 (patch) | |
tree | 5fad0fa391dd217a332c62f075df88e4177581e2 /drivers | |
parent | 15ac468614e5e4fee82e1eb32568f427b0e51adc (diff) | |
parent | a4857d1afdd1fa7ff763e1d07b1c2db521a5f9b1 (diff) |
Merge tag 'soundwire-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire
Pull soundwire updates from Vinod Koul:
- Stream handling and slave alert handling
- Qualcomm Soundwire v2.0.0 controller support
- Intel ACE2.x initial support and code reorganization
* tag 'soundwire-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire: (55 commits)
soundwire: stream: Make master_list ordered to prevent deadlocks
soundwire: bus: Prevent lockdep asserts when stream has multiple buses
soundwire: qcom: fix storing port config out-of-bounds
soundwire: intel_ace2x: fix SND_SOC_SOF_HDA_MLINK dependency
soundwire: debugfs: Add missing SCP registers
soundwire: stream: Remove unnecessary gotos
soundwire: stream: Invert logic on runtime alloc flags
soundwire: stream: Remove unneeded checks for NULL bus
soundwire: bandwidth allocation: Remove pointless variable
soundwire: cadence: revisit parity injection
soundwire: intel/cadence: update hardware reset sequence
soundwire: intel_bus_common: enable interrupts last
soundwire: intel_bus_common: update error log
soundwire: amd: Improve error message in remove callback
soundwire: debugfs: fix unbalanced pm_runtime_put()
soundwire: qcom: fix unbalanced pm_runtime_put()
soundwire: qcom: set clk stop need reset flag at runtime
soundwire: qcom: add software workaround for bus clash interrupt assertion
soundwire: qcom: wait for fifo to be empty before suspend
soundwire: qcom: drop unused struct qcom_swrm_ctrl members
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/soundwire/Kconfig | 1 | ||||
-rw-r--r-- | drivers/soundwire/Makefile | 3 | ||||
-rw-r--r-- | drivers/soundwire/amd_manager.c | 9 | ||||
-rw-r--r-- | drivers/soundwire/bus.c | 30 | ||||
-rw-r--r-- | drivers/soundwire/cadence_master.c | 50 | ||||
-rw-r--r-- | drivers/soundwire/cadence_master.h | 5 | ||||
-rw-r--r-- | drivers/soundwire/debugfs.c | 12 | ||||
-rw-r--r-- | drivers/soundwire/generic_bandwidth_allocation.c | 10 | ||||
-rw-r--r-- | drivers/soundwire/intel.c | 57 | ||||
-rw-r--r-- | drivers/soundwire/intel.h | 16 | ||||
-rw-r--r-- | drivers/soundwire/intel_ace2x.c | 393 | ||||
-rw-r--r-- | drivers/soundwire/intel_ace2x_debugfs.c | 147 | ||||
-rw-r--r-- | drivers/soundwire/intel_auxdevice.c | 17 | ||||
-rw-r--r-- | drivers/soundwire/intel_bus_common.c | 92 | ||||
-rw-r--r-- | drivers/soundwire/intel_init.c | 21 | ||||
-rw-r--r-- | drivers/soundwire/qcom.c | 496 | ||||
-rw-r--r-- | drivers/soundwire/stream.c | 161 |
17 files changed, 1136 insertions, 384 deletions
diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig index fa71c9a36df7..4d8f3b7024ae 100644 --- a/drivers/soundwire/Kconfig +++ b/drivers/soundwire/Kconfig @@ -37,6 +37,7 @@ config SOUNDWIRE_INTEL select SOUNDWIRE_GENERIC_ALLOCATION select AUXILIARY_BUS depends on ACPI && SND_SOC + depends on SND_SOC_SOF_HDA_MLINK || !SND_SOC_SOF_HDA_MLINK help SoundWire Intel Master driver. If you have an Intel platform which has a SoundWire Master then diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index 925566ff4272..c3d3ab3262d3 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -24,7 +24,8 @@ soundwire-cadence-y := cadence_master.o obj-$(CONFIG_SOUNDWIRE_CADENCE) += soundwire-cadence.o #Intel driver -soundwire-intel-y := intel.o intel_auxdevice.o intel_init.o dmi-quirks.o \ +soundwire-intel-y := intel.o intel_ace2x.o intel_ace2x_debugfs.o \ + intel_auxdevice.o intel_init.o dmi-quirks.o \ intel_bus_common.o obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel.o diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c index 9fb7f91ca182..08aeb7ed00e1 100644 --- a/drivers/soundwire/amd_manager.c +++ b/drivers/soundwire/amd_manager.c @@ -972,15 +972,18 @@ static int amd_sdw_manager_probe(struct platform_device *pdev) return 0; } -static int amd_sdw_manager_remove(struct platform_device *pdev) +static void amd_sdw_manager_remove(struct platform_device *pdev) { struct amd_sdw_manager *amd_manager = dev_get_drvdata(&pdev->dev); + int ret; pm_runtime_disable(&pdev->dev); cancel_work_sync(&amd_manager->probe_work); amd_disable_sdw_interrupts(amd_manager); sdw_bus_master_delete(&amd_manager->bus); - return amd_disable_sdw_manager(amd_manager); + ret = amd_disable_sdw_manager(amd_manager); + if (ret) + dev_err(&pdev->dev, "Failed to disable device (%pe)\n", ERR_PTR(ret)); } static int amd_sdw_clock_stop(struct amd_sdw_manager *amd_manager) @@ -1194,7 +1197,7 @@ static const struct dev_pm_ops amd_pm = { static struct platform_driver amd_sdw_driver = { .probe = &amd_sdw_manager_probe, - .remove = &amd_sdw_manager_remove, + .remove_new = &amd_sdw_manager_remove, .driver = { .name = "amd_sdw_manager", .pm = &amd_pm, diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 1ea6a64f8c4a..dba920ec88f6 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -69,8 +69,17 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, return -EINVAL; } - mutex_init(&bus->msg_lock); - mutex_init(&bus->bus_lock); + /* + * Give each bus_lock and msg_lock a unique key so that lockdep won't + * trigger a deadlock warning when the locks of several buses are + * grabbed during configuration of a multi-bus stream. + */ + lockdep_register_key(&bus->msg_lock_key); + __mutex_init(&bus->msg_lock, "msg_lock", &bus->msg_lock_key); + + lockdep_register_key(&bus->bus_lock_key); + __mutex_init(&bus->bus_lock, "bus_lock", &bus->bus_lock_key); + INIT_LIST_HEAD(&bus->slaves); INIT_LIST_HEAD(&bus->m_rt_list); @@ -181,6 +190,8 @@ void sdw_bus_master_delete(struct sdw_bus *bus) sdw_master_device_del(bus); sdw_bus_debugfs_exit(bus); + lockdep_unregister_key(&bus->bus_lock_key); + lockdep_unregister_key(&bus->msg_lock_key); ida_free(&sdw_bus_ida, bus->id); } EXPORT_SYMBOL(sdw_bus_master_delete); @@ -769,6 +780,9 @@ static int sdw_assign_device_num(struct sdw_slave *slave) /* After xfer of msg, restore dev_num */ slave->dev_num = slave->dev_num_sticky; + if (bus->ops && bus->ops->new_peripheral_assigned) + bus->ops->new_peripheral_assigned(bus, dev_num); + return 0; } @@ -1588,7 +1602,7 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) unsigned long port; bool slave_notify; u8 sdca_cascade = 0; - u8 buf, buf2[2], _buf, _buf2[2]; + u8 buf, buf2[2]; bool parity_check; bool parity_quirk; @@ -1745,9 +1759,9 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) "SDW_SCP_INT1 recheck read failed:%d\n", ret); goto io_err; } - _buf = ret; + buf = ret; - ret = sdw_nread_no_pm(slave, SDW_SCP_INTSTAT2, 2, _buf2); + ret = sdw_nread_no_pm(slave, SDW_SCP_INTSTAT2, 2, buf2); if (ret < 0) { dev_err(&slave->dev, "SDW_SCP_INT2/3 recheck read failed:%d\n", ret); @@ -1765,12 +1779,8 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) } /* - * Make sure no interrupts are pending, but filter to limit loop - * to interrupts identified in the first status read + * Make sure no interrupts are pending */ - buf &= _buf; - buf2[0] &= _buf2[0]; - buf2[1] &= _buf2[1]; stat = buf || buf2[0] || buf2[1] || sdca_cascade; /* diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 39502bc75712..0efc1c3bee5f 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -283,6 +283,29 @@ static int cdns_config_update(struct sdw_cdns *cdns) return ret; } +/** + * sdw_cdns_config_update() - Update configurations + * @cdns: Cadence instance + */ +void sdw_cdns_config_update(struct sdw_cdns *cdns) +{ + /* commit changes */ + cdns_writel(cdns, CDNS_MCP_CONFIG_UPDATE, CDNS_MCP_CONFIG_UPDATE_BIT); +} +EXPORT_SYMBOL(sdw_cdns_config_update); + +/** + * sdw_cdns_config_update_set_wait() - wait until configuration update bit is self-cleared + * @cdns: Cadence instance + */ +int sdw_cdns_config_update_set_wait(struct sdw_cdns *cdns) +{ + /* the hardware recommendation is to wait at least 300us */ + return cdns_set_wait(cdns, CDNS_MCP_CONFIG_UPDATE, + CDNS_MCP_CONFIG_UPDATE_BIT, 0); +} +EXPORT_SYMBOL(sdw_cdns_config_update_set_wait); + /* * debugfs */ @@ -433,9 +456,9 @@ static int cdns_parity_error_injection(void *data, u64 value) CDNS_IP_MCP_CMDCTRL_INSERT_PARITY_ERR); /* commit changes */ - cdns_updatel(cdns, CDNS_MCP_CONFIG_UPDATE, - CDNS_MCP_CONFIG_UPDATE_BIT, - CDNS_MCP_CONFIG_UPDATE_BIT); + ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE, CDNS_MCP_CONFIG_UPDATE_BIT); + if (ret < 0) + goto unlock; /* do a broadcast dummy read to avoid bus clashes */ ret = sdw_bread_no_pm_unlocked(&cdns->bus, 0xf, SDW_SCP_DEVID_0); @@ -447,16 +470,17 @@ static int cdns_parity_error_injection(void *data, u64 value) 0); /* commit changes */ - cdns_updatel(cdns, CDNS_MCP_CONFIG_UPDATE, - CDNS_MCP_CONFIG_UPDATE_BIT, - CDNS_MCP_CONFIG_UPDATE_BIT); - - /* Continue bus operation with parity error injection disabled */ - mutex_unlock(&bus->bus_lock); + ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE, CDNS_MCP_CONFIG_UPDATE_BIT); + if (ret < 0) + goto unlock; /* Userspace changed the hardware state behind the kernel's back */ add_taint(TAINT_USER, LOCKDEP_STILL_OK); +unlock: + /* Continue bus operation with parity error injection disabled */ + mutex_unlock(&bus->bus_lock); + /* * allow Master device to enter pm_runtime suspend. This may * also result in Slave devices suspending. @@ -1116,13 +1140,7 @@ int sdw_cdns_exit_reset(struct sdw_cdns *cdns) CDNS_MCP_CONTROL_HW_RST); /* commit changes */ - cdns_updatel(cdns, CDNS_MCP_CONFIG_UPDATE, - CDNS_MCP_CONFIG_UPDATE_BIT, - CDNS_MCP_CONFIG_UPDATE_BIT); - - /* don't wait here */ - return 0; - + return cdns_config_update(cdns); } EXPORT_SYMBOL(sdw_cdns_exit_reset); diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 27c56274217f..bc84435e420f 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -14,6 +14,8 @@ */ #define CDNS_MCP_IP_MAX_CMD_LEN 32 +#define SDW_CADENCE_MCP_IP_OFFSET 0x4000 + /** * struct sdw_cdns_pdi: PDI (Physical Data Interface) instance * @@ -197,4 +199,7 @@ int cdns_set_sdw_stream(struct snd_soc_dai *dai, void sdw_cdns_check_self_clearing_bits(struct sdw_cdns *cdns, const char *string, bool initial_delay, int reset_iterations); +void sdw_cdns_config_update(struct sdw_cdns *cdns); +int sdw_cdns_config_update_set_wait(struct sdw_cdns *cdns); + #endif /* __SDW_CADENCE_H */ diff --git a/drivers/soundwire/debugfs.c b/drivers/soundwire/debugfs.c index dea782e0edc4..d1553cb77187 100644 --- a/drivers/soundwire/debugfs.c +++ b/drivers/soundwire/debugfs.c @@ -56,8 +56,9 @@ static int sdw_slave_reg_show(struct seq_file *s_file, void *data) if (!buf) return -ENOMEM; - ret = pm_runtime_resume_and_get(&slave->dev); + ret = pm_runtime_get_sync(&slave->dev); if (ret < 0 && ret != -EACCES) { + pm_runtime_put_noidle(&slave->dev); kfree(buf); return ret; } @@ -85,10 +86,17 @@ static int sdw_slave_reg_show(struct seq_file *s_file, void *data) /* SCP registers */ ret += scnprintf(buf + ret, RD_BUF - ret, "\nSCP\n"); - for (i = SDW_SCP_INT1; i <= SDW_SCP_BANKDELAY; i++) + for (i = SDW_SCP_INT1; i <= SDW_SCP_BUS_CLOCK_BASE; i++) ret += sdw_sprintf(slave, buf, ret, i); for (i = SDW_SCP_DEVID_0; i <= SDW_SCP_DEVID_5; i++) ret += sdw_sprintf(slave, buf, ret, i); + for (i = SDW_SCP_FRAMECTRL_B0; i <= SDW_SCP_BUSCLOCK_SCALE_B0; i++) + ret += sdw_sprintf(slave, buf, ret, i); + for (i = SDW_SCP_FRAMECTRL_B1; i <= SDW_SCP_BUSCLOCK_SCALE_B1; i++) + ret += sdw_sprintf(slave, buf, ret, i); + for (i = SDW_SCP_PHY_OUT_CTRL_0; i <= SDW_SCP_PHY_OUT_CTRL_7; i++) + ret += sdw_sprintf(slave, buf, ret, i); + /* * SCP Bank 0/1 registers are read-only and cannot be diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index 325c475b6a66..31162f2b5638 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -139,20 +139,16 @@ static void _sdw_compute_port_params(struct sdw_bus *bus, { struct sdw_master_runtime *m_rt; int hstop = bus->params.col - 1; - int block_offset, port_bo, i; + int 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); + 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; + port_bo += m_rt->ch_count * m_rt->stream->params.bps; } hstop = hstop - params[i].hwidth; diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 238acf5c97a9..26d8485427dd 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -260,7 +260,7 @@ static void intel_shim_init(struct sdw_intel *sdw) { void __iomem *shim = sdw->link_res->shim; unsigned int link_id = sdw->instance; - u16 ioctl = 0, act = 0; + u16 ioctl = 0, act; /* Initialize Shim */ ioctl |= SDW_SHIM_IOCTL_BKE; @@ -281,6 +281,7 @@ static void intel_shim_init(struct sdw_intel *sdw) intel_shim_glue_to_master_ip(sdw); + act = intel_readw(shim, SDW_SHIM_CTMCTL(link_id)); u16p_replace_bits(&act, 0x1, SDW_SHIM_CTMCTL_DOAIS); act |= SDW_SHIM_CTMCTL_DACTQE; act |= SDW_SHIM_CTMCTL_DODS; @@ -643,7 +644,7 @@ intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) } static int intel_params_stream(struct sdw_intel *sdw, - int stream, + struct snd_pcm_substream *substream, struct snd_soc_dai *dai, struct snd_pcm_hw_params *hw_params, int link_id, int alh_stream_id) @@ -651,7 +652,7 @@ static int intel_params_stream(struct sdw_intel *sdw, struct sdw_intel_link_res *res = sdw->link_res; struct sdw_intel_stream_params_data params_data; - params_data.stream = stream; /* direction */ + params_data.substream = substream; params_data.dai = dai; params_data.hw_params = hw_params; params_data.link_id = link_id; @@ -663,25 +664,6 @@ static int intel_params_stream(struct sdw_intel *sdw, return -EIO; } -static int intel_free_stream(struct sdw_intel *sdw, - int stream, - 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.stream = stream; /* direction */ - 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; -} - /* * DAI routines */ @@ -727,7 +709,7 @@ static int intel_hw_params(struct snd_pcm_substream *substream, dai_runtime->pdi = pdi; /* Inform DSP about PDI stream number */ - ret = intel_params_stream(sdw, substream->stream, dai, params, + ret = intel_params_stream(sdw, substream, dai, params, sdw->instance, pdi->intel_alh_id); if (ret) @@ -804,7 +786,7 @@ static int intel_prepare(struct snd_pcm_substream *substream, sdw_cdns_config_stream(cdns, ch, dir, dai_runtime->pdi); /* Inform DSP about PDI stream number */ - ret = intel_params_stream(sdw, substream->stream, dai, + ret = intel_params_stream(sdw, substream, dai, hw_params, sdw->instance, dai_runtime->pdi->intel_alh_id); @@ -817,7 +799,6 @@ 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_dai_runtime *dai_runtime; int ret; @@ -838,12 +819,6 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) return ret; } - ret = intel_free_stream(sdw, substream->stream, dai, sdw->instance); - if (ret < 0) { - dev_err(dai->dev, "intel_free_stream: failed %d\n", ret); - return ret; - } - dai_runtime->pdi = NULL; return 0; @@ -871,19 +846,9 @@ static void *intel_get_sdw_stream(struct snd_soc_dai *dai, static int intel_trigger(struct snd_pcm_substream *substream, int cmd, 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_intel_link_res *res = sdw->link_res; struct sdw_cdns_dai_runtime *dai_runtime; int ret = 0; - /* - * The .trigger callback is used to send required IPC to audio - * firmware. The .free_stream callback will still be called - * by intel_free_stream() in the TRIGGER_SUSPEND case. - */ - if (res->ops && res->ops->trigger) - res->ops->trigger(dai, cmd, substream->stream); - dai_runtime = cdns->dai_runtime_array[dai->id]; if (!dai_runtime) { dev_err(dai->dev, "failed to get dai runtime in %s\n", @@ -903,7 +868,6 @@ static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct sn dai_runtime->suspended = true; - ret = intel_free_stream(sdw, substream->stream, dai, sdw->instance); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: @@ -949,9 +913,7 @@ static int intel_component_dais_suspend(struct snd_soc_component *component) */ for_each_component_dais(component, dai) { struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); - struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_cdns_dai_runtime *dai_runtime; - int ret; dai_runtime = cdns->dai_runtime_array[dai->id]; @@ -961,13 +923,8 @@ static int intel_component_dais_suspend(struct snd_soc_component *component) if (dai_runtime->suspended) continue; - if (dai_runtime->paused) { + if (dai_runtime->paused) dai_runtime->suspended = true; - - ret = intel_free_stream(sdw, dai_runtime->direction, dai, sdw->instance); - if (ret < 0) - return ret; - } } return 0; diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index 09d479f2c77b..511932c55216 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -4,13 +4,17 @@ #ifndef __SDW_INTEL_LOCAL_H #define __SDW_INTEL_LOCAL_H +struct hdac_bus; + /** * struct sdw_intel_link_res - Soundwire Intel link resource structure, * typically populated by the controller driver. * @hw_ops: platform-specific ops * @mmio_base: mmio base of SoundWire registers * @registers: Link IO registers base + * @ip_offset: offset for MCP_IP registers * @shim: Audio shim pointer + * @shim_vs: Audio vendor-specific shim pointer * @alh: ALH (Audio Link Hub) pointer * @irq: Interrupt line * @ops: Shim callback ops @@ -21,13 +25,16 @@ * @link_mask: global mask needed for power-up/down sequences * @cdns: Cadence master descriptor * @list: used to walk-through all masters exposed by the same controller + * @hbus: hdac_bus pointer, needed for power management */ struct sdw_intel_link_res { const struct sdw_intel_hw_ops *hw_ops; void __iomem *mmio_base; /* not strictly needed, useful for debug */ void __iomem *registers; + u32 ip_offset; void __iomem *shim; + void __iomem *shim_vs; void __iomem *alh; int irq; const struct sdw_intel_ops *ops; @@ -38,6 +45,7 @@ struct sdw_intel_link_res { u32 link_mask; struct sdw_cdns *cdns; struct list_head list; + struct hdac_bus *hbus; }; struct sdw_intel { @@ -87,6 +95,14 @@ static inline void intel_writew(void __iomem *base, int offset, u16 value) (sdw)->link_res->hw_ops->cb) #define SDW_INTEL_OPS(sdw, cb) ((sdw)->link_res->hw_ops->cb) +#ifdef CONFIG_DEBUG_FS +void intel_ace2x_debugfs_init(struct sdw_intel *sdw); +void intel_ace2x_debugfs_exit(struct sdw_intel *sdw); +#else +static inline void intel_ace2x_debugfs_init(struct sdw_intel *sdw) {} +static inline void intel_ace2x_debugfs_exit(struct sdw_intel *sdw) {} +#endif + static inline void sdw_intel_debugfs_init(struct sdw_intel *sdw) { if (SDW_INTEL_CHECK_OPS(sdw, debugfs_init)) diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c new file mode 100644 index 000000000000..1be0bea5f40f --- /dev/null +++ b/drivers/soundwire/intel_ace2x.c @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// Copyright(c) 2023 Intel Corporation. All rights reserved. + +/* + * Soundwire Intel ops for LunarLake + */ + +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_intel.h> +#include <sound/hda-mlink.h> +#include "cadence_master.h" +#include "bus.h" +#include "intel.h" + +/* + * shim vendor-specific (vs) ops + */ + +static void intel_shim_vs_init(struct sdw_intel *sdw) +{ + void __iomem *shim_vs = sdw->link_res->shim_vs; + u16 act = 0; + + u16p_replace_bits(&act, 0x1, SDW_SHIM2_INTEL_VS_ACTMCTL_DOAIS); + act |= SDW_SHIM2_INTEL_VS_ACTMCTL_DACTQE; + act |= SDW_SHIM2_INTEL_VS_ACTMCTL_DODS; + intel_writew(shim_vs, SDW_SHIM2_INTEL_VS_ACTMCTL, act); + usleep_range(10, 15); +} + +static int intel_shim_check_wake(struct sdw_intel *sdw) +{ + void __iomem *shim_vs; + u16 wake_sts; + + shim_vs = sdw->link_res->shim_vs; + wake_sts = intel_readw(shim_vs, SDW_SHIM2_INTEL_VS_WAKESTS); + + return wake_sts & SDW_SHIM2_INTEL_VS_WAKEEN_PWS; +} + +static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) +{ + void __iomem *shim_vs = sdw->link_res->shim_vs; + u16 wake_en; + u16 wake_sts; + + wake_en = intel_readw(shim_vs, SDW_SHIM2_INTEL_VS_WAKEEN); + + if (wake_enable) { + /* Enable the wakeup */ + wake_en |= SDW_SHIM2_INTEL_VS_WAKEEN_PWE; + intel_writew(shim_vs, SDW_SHIM2_INTEL_VS_WAKEEN, wake_en); + } else { + /* Disable the wake up interrupt */ + wake_en &= ~SDW_SHIM2_INTEL_VS_WAKEEN_PWE; + intel_writew(shim_vs, SDW_SHIM2_INTEL_VS_WAKEEN, wake_en); + + /* Clear wake status (W1C) */ + wake_sts = intel_readw(shim_vs, SDW_SHIM2_INTEL_VS_WAKESTS); + wake_sts |= SDW_SHIM2_INTEL_VS_WAKEEN_PWS; + intel_writew(shim_vs, SDW_SHIM2_INTEL_VS_WAKESTS, wake_sts); + } +} + +static int intel_link_power_up(struct sdw_intel *sdw) +{ + struct sdw_bus *bus = &sdw->cdns.bus; + struct sdw_master_prop *prop = &bus->prop; + u32 *shim_mask = sdw->link_res->shim_mask; + unsigned int link_id = sdw->instance; + u32 syncprd; + int ret; + + mutex_lock(sdw->link_res->shim_lock); + + if (!*shim_mask) { + /* we first need to program the SyncPRD/CPU registers */ + dev_dbg(sdw->cdns.dev, "first link up, programming SYNCPRD\n"); + + if (prop->mclk_freq % 6000000) + syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4; + else + syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24; + + ret = hdac_bus_eml_sdw_set_syncprd_unlocked(sdw->link_res->hbus, syncprd); + if (ret < 0) { + dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_set_syncprd failed: %d\n", + __func__, ret); + goto out; + } + } + + ret = hdac_bus_eml_sdw_power_up_unlocked(sdw->link_res->hbus, link_id); + if (ret < 0) { + dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_power_up failed: %d\n", + __func__, ret); + goto out; + } + + if (!*shim_mask) { + /* SYNCPU will change once link is active */ + ret = hdac_bus_eml_sdw_wait_syncpu_unlocked(sdw->link_res->hbus); + if (ret < 0) { + dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_wait_syncpu failed: %d\n", + __func__, ret); + goto out; + } + } + + *shim_mask |= BIT(link_id); + + sdw->cdns.link_up = true; + + intel_shim_vs_init(sdw); + +out: + mutex_unlock(sdw->link_res->shim_lock); + + return ret; +} + +static int intel_link_power_down(struct sdw_intel *sdw) +{ + u32 *shim_mask = sdw->link_res->shim_mask; + unsigned int link_id = sdw->instance; + int ret; + + mutex_lock(sdw->link_res->shim_lock); + + sdw->cdns.link_up = false; + + *shim_mask &= ~BIT(link_id); + + ret = hdac_bus_eml_sdw_power_down_unlocked(sdw->link_res->hbus, link_id); + if (ret < 0) { + dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_power_down failed: %d\n", + __func__, ret); + + /* + * we leave the sdw->cdns.link_up flag as false since we've disabled + * the link at this point and cannot handle interrupts any longer. + */ + } + + mutex_unlock(sdw->link_res->shim_lock); + + return ret; +} + +static void intel_sync_arm(struct sdw_intel *sdw) +{ + unsigned int link_id = sdw->instance; + + mutex_lock(sdw->link_res->shim_lock); + + hdac_bus_eml_sdw_sync_arm_unlocked(sdw->link_res->hbus, link_id); + + mutex_unlock(sdw->link_res->shim_lock); +} + +static int intel_sync_go_unlocked(struct sdw_intel *sdw) +{ + int ret; + + ret = hdac_bus_eml_sdw_sync_go_unlocked(sdw->link_res->hbus); + if (ret < 0) + dev_err(sdw->cdns.dev, "%s: SyncGO clear failed: %d\n", __func__, ret); + + return ret; +} + +static int intel_sync_go(struct sdw_intel *sdw) +{ + int ret; + + mutex_lock(sdw->link_res->shim_lock); + + ret = intel_sync_go_unlocked(sdw); + + mutex_unlock(sdw->link_res->shim_lock); + + return ret; +} + +static bool intel_check_cmdsync_unlocked(struct sdw_intel *sdw) +{ + return hdac_bus_eml_sdw_check_cmdsync_unlocked(sdw->link_res->hbus); +} + +/* + * DAI operations + */ +static const struct snd_soc_dai_ops intel_pcm_dai_ops = { +}; + +static const struct snd_soc_component_driver dai_component = { + .name = "soundwire", +}; + +/* + * PDI routines + */ +static void intel_pdi_init(struct sdw_intel *sdw, + struct sdw_cdns_stream_config *config) +{ + void __iomem *shim = sdw->link_res->shim; + int pcm_cap; + + /* PCM Stream Capability */ + pcm_cap = intel_readw(shim, SDW_SHIM2_PCMSCAP); + + config->pcm_bd = FIELD_GET(SDW_SHIM2_PCMSCAP_BSS, pcm_cap); + config->pcm_in = FIELD_GET(SDW_SHIM2_PCMSCAP_ISS, pcm_cap); + config->pcm_out = FIELD_GET(SDW_SHIM2_PCMSCAP_ISS, pcm_cap); + + dev_dbg(sdw->cdns.dev, "PCM cap bd:%d in:%d out:%d\n", + config->pcm_bd, config->pcm_in, config->pcm_out); +} + +static int +intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num) +{ + void __iomem *shim = sdw->link_res->shim; + + /* zero based values for channel count in register */ + return intel_readw(shim, SDW_SHIM2_PCMSYCHC(pdi_num)) + 1; +} + +static void intel_pdi_get_ch_update(struct sdw_intel *sdw, + struct sdw_cdns_pdi *pdi, + unsigned int num_pdi, + unsigned int *num_ch) +{ + int ch_count = 0; + int i; + + for (i = 0; i < num_pdi; i++) { + pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num); + ch_count += pdi->ch_count; + pdi++; + } + + *num_ch = ch_count; +} + +static void intel_pdi_stream_ch_update(struct sdw_intel *sdw, + struct sdw_cdns_streams *stream) +{ + intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd, + &stream->num_ch_bd); + + intel_pdi_get_ch_update(sdw, stream->in, stream->num_in, + &stream->num_ch_in); + + intel_pdi_get_ch_update(sdw, stream->out, stream->num_out, + &stream->num_ch_out); +} + +static int intel_create_dai(struct sdw_cdns *cdns, + struct snd_soc_dai_driver *dais, + enum intel_pdi_type type, + u32 num, u32 off, u32 max_ch) +{ + int i; + + if (!num) + return 0; + + for (i = off; i < (off + num); i++) { + dais[i].name = devm_kasprintf(cdns->dev, GFP_KERNEL, + "SDW%d Pin%d", + cdns->instance, i); + if (!dais[i].name) + return -ENOMEM; + + if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) { + dais[i].playback.channels_min = 1; + dais[i].playback.channels_max = max_ch; + } + + if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) { + dais[i].capture.channels_min = 1; + dais[i].capture.channels_max = max_ch; + } + + dais[i].ops = &intel_pcm_dai_ops; + } + + return 0; +} + +static int intel_register_dai(struct sdw_intel *sdw) +{ + struct sdw_cdns_dai_runtime **dai_runtime_array; + struct sdw_cdns_stream_config config; + struct sdw_cdns *cdns = &sdw->cdns; + struct sdw_cdns_streams *stream; + struct snd_soc_dai_driver *dais; + int num_dai; + int ret; + int off = 0; + + /* Read the PDI config and initialize cadence PDI */ + intel_pdi_init(sdw, &config); + ret = sdw_cdns_pdi_init(cdns, config); + if (ret) + return ret; + + intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm); + + /* DAIs are created based on total number of PDIs supported */ + num_dai = cdns->pcm.num_pdi; + + dai_runtime_array = devm_kcalloc(cdns->dev, num_dai, + sizeof(struct sdw_cdns_dai_runtime *), + GFP_KERNEL); + if (!dai_runtime_array) + return -ENOMEM; + cdns->dai_runtime_array = dai_runtime_array; + + dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL); + if (!dais) + return -ENOMEM; + + /* Create PCM DAIs */ + stream = &cdns->pcm; + + ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, cdns->pcm.num_in, + off, stream->num_ch_in); + if (ret) + return ret; + + off += cdns->pcm.num_in; + ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, cdns->pcm.num_out, + off, stream->num_ch_out); + if (ret) + return ret; + + off += cdns->pcm.num_out; + ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, cdns->pcm.num_bd, + off, stream->num_ch_bd); + if (ret) + return ret; + + return devm_snd_soc_register_component(cdns->dev, &dai_component, + dais, num_dai); +} + +static void intel_program_sdi(struct sdw_intel *sdw, int dev_num) +{ + int ret; + + ret = hdac_bus_eml_sdw_set_lsdiid(sdw->link_res->hbus, sdw->instance, dev_num); + if (ret < 0) + dev_err(sdw->cdns.dev, "%s: could not set lsdiid for link %d %d\n", + __func__, sdw->instance, dev_num); +} + +const struct sdw_intel_hw_ops sdw_intel_lnl_hw_ops = { + .debugfs_init = intel_ace2x_debugfs_init, + .debugfs_exit = intel_ace2x_debugfs_exit, + + .register_dai = intel_register_dai, + + .check_clock_stop = intel_check_clock_stop, + .start_bus = intel_start_bus, + .start_bus_after_reset = intel_start_bus_after_reset, + .start_bus_after_clock_stop = intel_start_bus_after_clock_stop, + .stop_bus = intel_stop_bus, + + .link_power_up = intel_link_power_up, + .link_power_down = intel_link_power_down, + + .shim_check_wake = intel_shim_check_wake, + .shim_wake = intel_shim_wake, + + .pre_bank_switch = intel_pre_bank_switch, + .post_bank_switch = intel_post_bank_switch, + + .sync_arm = intel_sync_arm, + .sync_go_unlocked = intel_sync_go_unlocked, + .sync_go = intel_sync_go, + .sync_check_cmdsync_unlocked = intel_check_cmdsync_unlocked, + + .program_sdi = intel_program_sdi, +}; +EXPORT_SYMBOL_NS(sdw_intel_lnl_hw_ops, SOUNDWIRE_INTEL); + +MODULE_IMPORT_NS(SND_SOC_SOF_HDA_MLINK); diff --git a/drivers/soundwire/intel_ace2x_debugfs.c b/drivers/soundwire/intel_ace2x_debugfs.c new file mode 100644 index 000000000000..3d24661ffd37 --- /dev/null +++ b/drivers/soundwire/intel_ace2x_debugfs.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright(c) 2023 Intel Corporation. All rights reserved. + +#include <linux/acpi.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/pm_runtime.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_intel.h> +#include <linux/soundwire/sdw_registers.h> +#include "bus.h" +#include "cadence_master.h" +#include "intel.h" + +/* + * debugfs + */ +#ifdef CONFIG_DEBUG_FS + +#define RD_BUF (2 * PAGE_SIZE) + +static ssize_t intel_sprintf(void __iomem *mem, bool l, + char *buf, size_t pos, unsigned int reg) +{ + int value; + + if (l) + value = intel_readl(mem, reg); + else + value = intel_readw(mem, reg); + + return scnprintf(buf + pos, RD_BUF - pos, "%4x\t%4x\n", reg, value); +} + +static int intel_reg_show(struct seq_file *s_file, void *data) +{ + struct sdw_intel *sdw = s_file->private; + void __iomem *s = sdw->link_res->shim; + void __iomem *vs_s = sdw->link_res->shim_vs; + ssize_t ret; + u32 pcm_cap; + int pcm_bd; + char *buf; + int j; + + buf = kzalloc(RD_BUF, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = scnprintf(buf, RD_BUF, "Register Value\n"); + ret += scnprintf(buf + ret, RD_BUF - ret, "\nShim\n"); + + ret += intel_sprintf(s, true, buf, ret, SDW_SHIM2_LECAP); + ret += intel_sprintf(s, false, buf, ret, SDW_SHIM2_PCMSCAP); + + pcm_cap = intel_readw(s, SDW_SHIM2_PCMSCAP); + pcm_bd = FIELD_GET(SDW_SHIM2_PCMSCAP_BSS, pcm_cap); + + for (j = 0; j < pcm_bd; j++) { + ret += intel_sprintf(s, false, buf, ret, + SDW_SHIM2_PCMSYCHM(j)); + ret += intel_sprintf(s, false, buf, ret, + SDW_SHIM2_PCMSYCHC(j)); + } + + ret += scnprintf(buf + ret, RD_BUF - ret, "\nVS CLK controls\n"); + ret += intel_sprintf(vs_s, true, buf, ret, SDW_SHIM2_INTEL_VS_LVSCTL); + + ret += scnprintf(buf + ret, RD_BUF - ret, "\nVS Wake registers\n"); + ret += intel_sprintf(vs_s, false, buf, ret, SDW_SHIM2_INTEL_VS_WAKEEN); + ret += intel_sprintf(vs_s, false, buf, ret, SDW_SHIM2_INTEL_VS_WAKESTS); + + ret += scnprintf(buf + ret, RD_BUF - ret, "\nVS IOCTL, ACTMCTL\n"); + ret += intel_sprintf(vs_s, false, buf, ret, SDW_SHIM2_INTEL_VS_IOCTL); + ret += intel_sprintf(vs_s, false, buf, ret, SDW_SHIM2_INTEL_VS_ACTMCTL); + + seq_printf(s_file, "%s", buf); + kfree(buf); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(intel_reg); + +static int intel_set_m_datamode(void *data, u64 value) +{ + struct sdw_intel *sdw = data; + struct sdw_bus *bus = &sdw->cdns.bus; + + if (value > SDW_PORT_DATA_MODE_STATIC_1) + return -EINVAL; + + /* Userspace changed the hardware state behind the kernel's back */ + add_taint(TAINT_USER, LOCKDEP_STILL_OK); + + bus->params.m_data_mode = value; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(intel_set_m_datamode_fops, NULL, + intel_set_m_datamode, "%llu\n"); + +static int intel_set_s_datamode(void *data, u64 value) +{ + struct sdw_intel *sdw = data; + struct sdw_bus *bus = &sdw->cdns.bus; + + if (value > SDW_PORT_DATA_MODE_STATIC_1) + return -EINVAL; + + /* Userspace changed the hardware state behind the kernel's back */ + add_taint(TAINT_USER, LOCKDEP_STILL_OK); + + bus->params.s_data_mode = value; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(intel_set_s_datamode_fops, NULL, + intel_set_s_datamode, "%llu\n"); + +void intel_ace2x_debugfs_init(struct sdw_intel *sdw) +{ + struct dentry *root = sdw->cdns.bus.debugfs; + + if (!root) + return; + + sdw->debugfs = debugfs_create_dir("intel-sdw", root); + + debugfs_create_file("intel-registers", 0400, sdw->debugfs, sdw, + &intel_reg_fops); + + debugfs_create_file("intel-m-datamode", 0200, sdw->debugfs, sdw, + &intel_set_m_datamode_fops); + + debugfs_create_file("intel-s-datamode", 0200, sdw->debugfs, sdw, + &intel_set_s_datamode_fops); + + sdw_cdns_debugfs_init(&sdw->cdns, sdw->debugfs); +} + +void intel_ace2x_debugfs_exit(struct sdw_intel *sdw) +{ + debugfs_remove_recursive(sdw->debugfs); +} +#endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index b21e86084f7b..0daa6ca9a224 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -60,6 +60,21 @@ static int generic_post_bank_switch(struct sdw_bus *bus) return sdw->link_res->hw_ops->post_bank_switch(sdw); } +static void generic_new_peripheral_assigned(struct sdw_bus *bus, int dev_num) +{ + struct sdw_cdns *cdns = bus_to_cdns(bus); + struct sdw_intel *sdw = cdns_to_intel(cdns); + + /* paranoia check, this should never happen */ + if (dev_num < INTEL_DEV_NUM_IDA_MIN || dev_num > SDW_MAX_DEVICES) { + dev_err(bus->dev, "%s: invalid dev_num %d\n", __func__, dev_num); + return; + } + + if (sdw->link_res->hw_ops->program_sdi) + sdw->link_res->hw_ops->program_sdi(sdw, dev_num); +} + static int sdw_master_read_intel_prop(struct sdw_bus *bus) { struct sdw_master_prop *prop = &bus->prop; @@ -117,6 +132,7 @@ static struct sdw_master_ops sdw_intel_ops = { .pre_bank_switch = generic_pre_bank_switch, .post_bank_switch = generic_post_bank_switch, .read_ping_status = cdns_read_ping_status, + .new_peripheral_assigned = generic_new_peripheral_assigned, }; /* @@ -144,6 +160,7 @@ static int intel_link_probe(struct auxiliary_device *auxdev, sdw->link_res = &ldev->link_res; cdns->dev = dev; cdns->registers = sdw->link_res->registers; + cdns->ip_offset = sdw->link_res->ip_offset; cdns->instance = sdw->instance; cdns->msg_count = 0; diff --git a/drivers/soundwire/intel_bus_common.c b/drivers/soundwire/intel_bus_common.c index f180e3bea989..e5ac3cc7cb79 100644 --- a/drivers/soundwire/intel_bus_common.c +++ b/drivers/soundwire/intel_bus_common.c @@ -16,12 +16,6 @@ int intel_start_bus(struct sdw_intel *sdw) struct sdw_bus *bus = &cdns->bus; int ret; - ret = sdw_cdns_enable_interrupt(cdns, true); - if (ret < 0) { - dev_err(dev, "%s: cannot enable interrupts: %d\n", __func__, ret); - return ret; - } - /* * follow recommended programming flows to avoid timeouts when * gsync is enabled @@ -32,30 +26,41 @@ int intel_start_bus(struct sdw_intel *sdw) ret = sdw_cdns_init(cdns); if (ret < 0) { dev_err(dev, "%s: unable to initialize Cadence IP: %d\n", __func__, ret); - goto err_interrupt; + return ret; } - ret = sdw_cdns_exit_reset(cdns); - if (ret < 0) { - dev_err(dev, "%s: unable to exit bus reset sequence: %d\n", __func__, ret); - goto err_interrupt; - } + sdw_cdns_config_update(cdns); if (bus->multi_link) { ret = sdw_intel_sync_go(sdw); if (ret < 0) { dev_err(dev, "%s: sync go failed: %d\n", __func__, ret); - goto err_interrupt; + return ret; } } + + ret = sdw_cdns_config_update_set_wait(cdns); + if (ret < 0) { + dev_err(dev, "%s: CONFIG_UPDATE BIT still set\n", __func__); + return ret; + } + + ret = sdw_cdns_exit_reset(cdns); + if (ret < 0) { + dev_err(dev, "%s: unable to exit bus reset sequence: %d\n", __func__, ret); + return ret; + } + + ret = sdw_cdns_enable_interrupt(cdns, true); + if (ret < 0) { + dev_err(dev, "%s: cannot enable interrupts: %d\n", __func__, ret); + return ret; + } + sdw_cdns_check_self_clearing_bits(cdns, __func__, true, INTEL_MASTER_RESET_ITERATIONS); return 0; - -err_interrupt: - sdw_cdns_enable_interrupt(cdns, false); - return ret; } int intel_start_bus_after_reset(struct sdw_intel *sdw) @@ -86,12 +91,6 @@ int intel_start_bus_after_reset(struct sdw_intel *sdw) status = SDW_UNATTACH_REQUEST_MASTER_RESET; sdw_clear_slave_status(bus, status); - ret = sdw_cdns_enable_interrupt(cdns, true); - if (ret < 0) { - dev_err(dev, "cannot enable interrupts during resume\n"); - return ret; - } - /* * follow recommended programming flows to avoid * timeouts when gsync is enabled @@ -115,31 +114,44 @@ int intel_start_bus_after_reset(struct sdw_intel *sdw) ret = sdw_cdns_clock_restart(cdns, !clock_stop0); if (ret < 0) { dev_err(dev, "unable to restart clock during resume\n"); - goto err_interrupt; + if (!clock_stop0) + sdw_cdns_enable_interrupt(cdns, false); + return ret; } if (!clock_stop0) { - ret = sdw_cdns_exit_reset(cdns); - if (ret < 0) { - dev_err(dev, "unable to exit bus reset sequence during resume\n"); - goto err_interrupt; - } + sdw_cdns_config_update(cdns); if (bus->multi_link) { ret = sdw_intel_sync_go(sdw); if (ret < 0) { dev_err(sdw->cdns.dev, "sync go failed during resume\n"); - goto err_interrupt; + return ret; } } + + ret = sdw_cdns_config_update_set_wait(cdns); + if (ret < 0) { + dev_err(dev, "%s: CONFIG_UPDATE BIT still set\n", __func__); + 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; + } + + ret = sdw_cdns_enable_interrupt(cdns, true); + if (ret < 0) { + dev_err(dev, "cannot enable interrupts during resume\n"); + return ret; + } + } sdw_cdns_check_self_clearing_bits(cdns, __func__, true, INTEL_MASTER_RESET_ITERATIONS); return 0; - -err_interrupt: - sdw_cdns_enable_interrupt(cdns, false); - return ret; } void intel_check_clock_stop(struct sdw_intel *sdw) @@ -158,21 +170,19 @@ int intel_start_bus_after_clock_stop(struct sdw_intel *sdw) struct sdw_cdns *cdns = &sdw->cdns; int ret; - ret = sdw_cdns_enable_interrupt(cdns, true); + ret = sdw_cdns_clock_restart(cdns, false); if (ret < 0) { - dev_err(dev, "%s: cannot enable interrupts: %d\n", __func__, ret); + dev_err(dev, "%s: unable to restart clock: %d\n", __func__, ret); return ret; } - ret = sdw_cdns_clock_restart(cdns, false); + ret = sdw_cdns_enable_interrupt(cdns, true); if (ret < 0) { - dev_err(dev, "%s: unable to restart clock: %d\n", __func__, ret); - sdw_cdns_enable_interrupt(cdns, false); + dev_err(dev, "%s: cannot enable interrupts: %d\n", __func__, ret); return ret; } - sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime no_quirks", - true, INTEL_MASTER_RESET_ITERATIONS); + sdw_cdns_check_self_clearing_bits(cdns, __func__, true, INTEL_MASTER_RESET_ITERATIONS); return 0; } diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index cbe56b993c6c..534c8795e7e8 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -63,19 +63,30 @@ static struct sdw_intel_link_dev *intel_link_dev_register(struct sdw_intel_res * link = &ldev->link_res; link->hw_ops = res->hw_ops; link->mmio_base = res->mmio_base; - link->registers = res->mmio_base + SDW_LINK_BASE - + (SDW_LINK_SIZE * link_id); - link->shim = res->mmio_base + res->shim_base; - link->alh = res->mmio_base + res->alh_base; + if (!res->ext) { + link->registers = res->mmio_base + SDW_LINK_BASE + + (SDW_LINK_SIZE * link_id); + link->ip_offset = 0; + link->shim = res->mmio_base + res->shim_base; + link->alh = res->mmio_base + res->alh_base; + link->shim_lock = &ctx->shim_lock; + } else { + link->registers = res->mmio_base + SDW_IP_BASE(link_id); + link->ip_offset = SDW_CADENCE_MCP_IP_OFFSET; + link->shim = res->mmio_base + SDW_SHIM2_GENERIC_BASE(link_id); + link->shim_vs = res->mmio_base + SDW_SHIM2_VS_BASE(link_id); + link->shim_lock = res->eml_lock; + } link->ops = res->ops; link->dev = res->dev; link->clock_stop_quirks = res->clock_stop_quirks; - link->shim_lock = &ctx->shim_lock; link->shim_mask = &ctx->shim_mask; link->link_mask = ctx->link_mask; + link->hbus = res->hbus; + /* now follow the two-step init/add sequence */ ret = auxiliary_device_init(auxdev); if (ret < 0) { diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 280455f047a3..7970fdb27ba0 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -31,6 +31,7 @@ #define SWRM_VERSION_1_3_0 0x01030000 #define SWRM_VERSION_1_5_1 0x01050001 #define SWRM_VERSION_1_7_0 0x01070000 +#define SWRM_VERSION_2_0_0 0x02000000 #define SWRM_COMP_HW_VERSION 0x00 #define SWRM_COMP_CFG_ADDR 0x04 #define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK BIT(1) @@ -41,7 +42,8 @@ #define SWRM_COMP_PARAMS_DOUT_PORTS_MASK GENMASK(4, 0) #define SWRM_COMP_PARAMS_DIN_PORTS_MASK GENMASK(9, 5) #define SWRM_COMP_MASTER_ID 0x104 -#define SWRM_INTERRUPT_STATUS 0x200 +#define SWRM_V1_3_INTERRUPT_STATUS 0x200 +#define SWRM_V2_0_INTERRUPT_STATUS 0x5000 #define SWRM_INTERRUPT_STATUS_RMSK GENMASK(16, 0) #define SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ BIT(0) #define SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED BIT(1) @@ -54,24 +56,32 @@ #define SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION BIT(8) #define SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH BIT(9) #define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED BIT(10) -#define SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED_V2 BIT(13) -#define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED_V2 BIT(14) -#define SWRM_INTERRUPT_STATUS_EXT_CLK_STOP_WAKEUP BIT(16) +#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED BIT(11) +#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL BIT(12) +#define SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED_V2 BIT(13) +#define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED_V2 BIT(14) +#define SWRM_INTERRUPT_STATUS_EXT_CLK_STOP_WAKEUP BIT(16) #define SWRM_INTERRUPT_MAX 17 -#define SWRM_INTERRUPT_MASK_ADDR 0x204 -#define SWRM_INTERRUPT_CLEAR 0x208 -#define SWRM_INTERRUPT_CPU_EN 0x210 -#define SWRM_CMD_FIFO_WR_CMD 0x300 -#define SWRM_CMD_FIFO_RD_CMD 0x304 +#define SWRM_V1_3_INTERRUPT_MASK_ADDR 0x204 +#define SWRM_V1_3_INTERRUPT_CLEAR 0x208 +#define SWRM_V2_0_INTERRUPT_CLEAR 0x5008 +#define SWRM_V1_3_INTERRUPT_CPU_EN 0x210 +#define SWRM_V2_0_INTERRUPT_CPU_EN 0x5004 +#define SWRM_V1_3_CMD_FIFO_WR_CMD 0x300 +#define SWRM_V2_0_CMD_FIFO_WR_CMD 0x5020 +#define SWRM_V1_3_CMD_FIFO_RD_CMD 0x304 +#define SWRM_V2_0_CMD_FIFO_RD_CMD 0x5024 #define SWRM_CMD_FIFO_CMD 0x308 #define SWRM_CMD_FIFO_FLUSH 0x1 -#define SWRM_CMD_FIFO_STATUS 0x30C +#define SWRM_V1_3_CMD_FIFO_STATUS 0x30C +#define SWRM_V2_0_CMD_FIFO_STATUS 0x5050 #define SWRM_RD_CMD_FIFO_CNT_MASK GENMASK(20, 16) #define SWRM_WR_CMD_FIFO_CNT_MASK GENMASK(12, 8) #define SWRM_CMD_FIFO_CFG_ADDR 0x314 #define SWRM_CONTINUE_EXEC_ON_CMD_IGNORE BIT(31) #define SWRM_RD_WR_CMD_RETRIES 0x7 -#define SWRM_CMD_FIFO_RD_FIFO_ADDR 0x318 +#define SWRM_V1_3_CMD_FIFO_RD_FIFO_ADDR 0x318 +#define SWRM_V2_0_CMD_FIFO_RD_FIFO_ADDR 0x5040 #define SWRM_RD_FIFO_CMD_ID_MASK GENMASK(11, 8) #define SWRM_ENUMERATOR_CFG_ADDR 0x500 #define SWRM_ENUMERATOR_SLAVE_DEV_ID_1(m) (0x530 + 0x8 * (m)) @@ -95,8 +105,14 @@ #define SWRM_DP_BLOCK_CTRL2_BANK(n, m) (0x1130 + 0x100 * (n - 1) + 0x40 * m) #define SWRM_DP_PORT_HCTRL_BANK(n, m) (0x1134 + 0x100 * (n - 1) + 0x40 * m) #define SWRM_DP_BLOCK_CTRL3_BANK(n, m) (0x1138 + 0x100 * (n - 1) + 0x40 * m) +#define SWRM_DP_SAMPLECTRL2_BANK(n, m) (0x113C + 0x100 * (n - 1) + 0x40 * m) #define SWRM_DIN_DPn_PCM_PORT_CTRL(n) (0x1054 + 0x100 * (n - 1)) -#define SWR_MSTR_MAX_REG_ADDR (0x1740) +#define SWR_V1_3_MSTR_MAX_REG_ADDR 0x1740 +#define SWR_V2_0_MSTR_MAX_REG_ADDR 0x50ac + +#define SWRM_V2_0_CLK_CTRL 0x5060 +#define SWRM_V2_0_CLK_CTRL_CLK_START BIT(0) +#define SWRM_V2_0_LINK_STATUS 0x5064 #define SWRM_DP_PORT_CTRL_EN_CHAN_SHFT 0x18 #define SWRM_DP_PORT_CTRL_OFFSET2_SHFT 0x10 @@ -109,20 +125,20 @@ #define SWRM_REG_VAL_PACK(data, dev, id, reg) \ ((reg) | ((id) << 16) | ((dev) << 20) | ((data) << 24)) -#define MAX_FREQ_NUM 1 -#define TIMEOUT_MS 100 -#define QCOM_SWRM_MAX_RD_LEN 0x1 -#define QCOM_SDW_MAX_PORTS 14 -#define DEFAULT_CLK_FREQ 9600000 -#define SWRM_MAX_DAIS 0xF -#define SWR_INVALID_PARAM 0xFF -#define SWR_HSTOP_MAX_VAL 0xF -#define SWR_HSTART_MIN_VAL 0x0 -#define SWR_BROADCAST_CMD_ID 0x0F -#define SWR_MAX_CMD_ID 14 -#define MAX_FIFO_RD_RETRY 3 -#define SWR_OVERFLOW_RETRY_COUNT 30 -#define SWRM_LINK_STATUS_RETRY_CNT 100 +#define MAX_FREQ_NUM 1 +#define TIMEOUT_MS 100 +#define QCOM_SWRM_MAX_RD_LEN 0x1 +#define QCOM_SDW_MAX_PORTS 14 +#define DEFAULT_CLK_FREQ 9600000 +#define SWRM_MAX_DAIS 0xF +#define SWR_INVALID_PARAM 0xFF +#define SWR_HSTOP_MAX_VAL 0xF +#define SWR_HSTART_MIN_VAL 0x0 +#define SWR_BROADCAST_CMD_ID 0x0F +#define SWR_MAX_CMD_ID 14 +#define MAX_FIFO_RD_RETRY 3 +#define SWR_OVERFLOW_RETRY_COUNT 30 +#define SWRM_LINK_STATUS_RETRY_CNT 100 enum { MASTER_ID_WSA = 1, @@ -131,7 +147,7 @@ enum { }; struct qcom_swrm_port_config { - u8 si; + u16 si; u8 off1; u8 off2; u8 bp_mode; @@ -142,10 +158,28 @@ struct qcom_swrm_port_config { u8 lane_control; }; +/* + * Internal IDs for different register layouts. Only few registers differ per + * each variant, so the list of IDs below does not include all of registers. + */ +enum { + SWRM_REG_FRAME_GEN_ENABLED, + SWRM_REG_INTERRUPT_STATUS, + SWRM_REG_INTERRUPT_MASK_ADDR, + SWRM_REG_INTERRUPT_CLEAR, + SWRM_REG_INTERRUPT_CPU_EN, + SWRM_REG_CMD_FIFO_WR_CMD, + SWRM_REG_CMD_FIFO_RD_CMD, + SWRM_REG_CMD_FIFO_STATUS, + SWRM_REG_CMD_FIFO_RD_FIFO_ADDR, +}; + struct qcom_swrm_ctrl { struct sdw_bus bus; struct device *dev; struct regmap *regmap; + u32 max_reg; + const unsigned int *reg_layout; void __iomem *mmio; struct reset_control *audio_cgcr; #ifdef CONFIG_DEBUG_FS @@ -153,12 +187,9 @@ struct qcom_swrm_ctrl { #endif struct completion broadcast; struct completion enumeration; - struct work_struct slave_work; /* Port alloc/free lock */ struct mutex port_lock; struct clk *hclk; - u8 wr_cmd_id; - u8 rd_cmd_id; int irq; unsigned int version; int wake_irq; @@ -171,7 +202,8 @@ struct qcom_swrm_ctrl { u32 intr_mask; u8 rcmd_id; u8 wcmd_id; - struct qcom_swrm_port_config pconfig[QCOM_SDW_MAX_PORTS]; + /* Port numbers are 1 - 14 */ + struct qcom_swrm_port_config pconfig[QCOM_SDW_MAX_PORTS + 1]; struct sdw_stream_runtime *sruntime[SWRM_MAX_DAIS]; enum sdw_slave_status status[SDW_MAX_DEVICES + 1]; int (*reg_read)(struct qcom_swrm_ctrl *ctrl, int reg, u32 *val); @@ -186,22 +218,62 @@ struct qcom_swrm_data { u32 default_cols; u32 default_rows; bool sw_clk_gate_required; + u32 max_reg; + const unsigned int *reg_layout; +}; + +static const unsigned int swrm_v1_3_reg_layout[] = { + [SWRM_REG_FRAME_GEN_ENABLED] = SWRM_COMP_STATUS, + [SWRM_REG_INTERRUPT_STATUS] = SWRM_V1_3_INTERRUPT_STATUS, + [SWRM_REG_INTERRUPT_MASK_ADDR] = SWRM_V1_3_INTERRUPT_MASK_ADDR, + [SWRM_REG_INTERRUPT_CLEAR] = SWRM_V1_3_INTERRUPT_CLEAR, + [SWRM_REG_INTERRUPT_CPU_EN] = SWRM_V1_3_INTERRUPT_CPU_EN, + [SWRM_REG_CMD_FIFO_WR_CMD] = SWRM_V1_3_CMD_FIFO_WR_CMD, + [SWRM_REG_CMD_FIFO_RD_CMD] = SWRM_V1_3_CMD_FIFO_RD_CMD, + [SWRM_REG_CMD_FIFO_STATUS] = SWRM_V1_3_CMD_FIFO_STATUS, + [SWRM_REG_CMD_FIFO_RD_FIFO_ADDR] = SWRM_V1_3_CMD_FIFO_RD_FIFO_ADDR, }; static const struct qcom_swrm_data swrm_v1_3_data = { .default_rows = 48, .default_cols = 16, + .max_reg = SWR_V1_3_MSTR_MAX_REG_ADDR, + .reg_layout = swrm_v1_3_reg_layout, }; static const struct qcom_swrm_data swrm_v1_5_data = { .default_rows = 50, .default_cols = 16, + .max_reg = SWR_V1_3_MSTR_MAX_REG_ADDR, + .reg_layout = swrm_v1_3_reg_layout, }; static const struct qcom_swrm_data swrm_v1_6_data = { .default_rows = 50, .default_cols = 16, .sw_clk_gate_required = true, + .max_reg = SWR_V1_3_MSTR_MAX_REG_ADDR, + .reg_layout = swrm_v1_3_reg_layout, +}; + +static const unsigned int swrm_v2_0_reg_layout[] = { + [SWRM_REG_FRAME_GEN_ENABLED] = SWRM_V2_0_LINK_STATUS, + [SWRM_REG_INTERRUPT_STATUS] = SWRM_V2_0_INTERRUPT_STATUS, + [SWRM_REG_INTERRUPT_MASK_ADDR] = 0, /* Not present */ + [SWRM_REG_INTERRUPT_CLEAR] = SWRM_V2_0_INTERRUPT_CLEAR, + [SWRM_REG_INTERRUPT_CPU_EN] = SWRM_V2_0_INTERRUPT_CPU_EN, + [SWRM_REG_CMD_FIFO_WR_CMD] = SWRM_V2_0_CMD_FIFO_WR_CMD, + [SWRM_REG_CMD_FIFO_RD_CMD] = SWRM_V2_0_CMD_FIFO_RD_CMD, + [SWRM_REG_CMD_FIFO_STATUS] = SWRM_V2_0_CMD_FIFO_STATUS, + [SWRM_REG_CMD_FIFO_RD_FIFO_ADDR] = SWRM_V2_0_CMD_FIFO_RD_FIFO_ADDR, +}; + +static const struct qcom_swrm_data swrm_v2_0_data = { + .default_rows = 50, + .default_cols = 16, + .sw_clk_gate_required = true, + .max_reg = SWR_V2_0_MSTR_MAX_REG_ADDR, + .reg_layout = swrm_v2_0_reg_layout, }; #define to_qcom_sdw(b) container_of(b, struct qcom_swrm_ctrl, bus) @@ -278,14 +350,15 @@ static u32 swrm_get_packed_reg_val(u8 *cmd_id, u8 cmd_data, return val; } -static int swrm_wait_for_rd_fifo_avail(struct qcom_swrm_ctrl *swrm) +static int swrm_wait_for_rd_fifo_avail(struct qcom_swrm_ctrl *ctrl) { u32 fifo_outstanding_data, value; int fifo_retry_count = SWR_OVERFLOW_RETRY_COUNT; do { /* Check for fifo underflow during read */ - swrm->reg_read(swrm, SWRM_CMD_FIFO_STATUS, &value); + ctrl->reg_read(ctrl, ctrl->reg_layout[SWRM_REG_CMD_FIFO_STATUS], + &value); fifo_outstanding_data = FIELD_GET(SWRM_RD_CMD_FIFO_CNT_MASK, value); /* Check if read data is available in read fifo */ @@ -296,39 +369,66 @@ static int swrm_wait_for_rd_fifo_avail(struct qcom_swrm_ctrl *swrm) } while (fifo_retry_count--); if (fifo_outstanding_data == 0) { - dev_err_ratelimited(swrm->dev, "%s err read underflow\n", __func__); + dev_err_ratelimited(ctrl->dev, "%s err read underflow\n", __func__); return -EIO; } return 0; } -static int swrm_wait_for_wr_fifo_avail(struct qcom_swrm_ctrl *swrm) +static int swrm_wait_for_wr_fifo_avail(struct qcom_swrm_ctrl *ctrl) { u32 fifo_outstanding_cmds, value; int fifo_retry_count = SWR_OVERFLOW_RETRY_COUNT; do { /* Check for fifo overflow during write */ - swrm->reg_read(swrm, SWRM_CMD_FIFO_STATUS, &value); + ctrl->reg_read(ctrl, ctrl->reg_layout[SWRM_REG_CMD_FIFO_STATUS], + &value); fifo_outstanding_cmds = FIELD_GET(SWRM_WR_CMD_FIFO_CNT_MASK, value); /* Check for space in write fifo before writing */ - if (fifo_outstanding_cmds < swrm->wr_fifo_depth) + if (fifo_outstanding_cmds < ctrl->wr_fifo_depth) return 0; usleep_range(500, 510); } while (fifo_retry_count--); - if (fifo_outstanding_cmds == swrm->wr_fifo_depth) { - dev_err_ratelimited(swrm->dev, "%s err write overflow\n", __func__); + if (fifo_outstanding_cmds == ctrl->wr_fifo_depth) { + dev_err_ratelimited(ctrl->dev, "%s err write overflow\n", __func__); return -EIO; } return 0; } -static int qcom_swrm_cmd_fifo_wr_cmd(struct qcom_swrm_ctrl *swrm, u8 cmd_data, +static bool swrm_wait_for_wr_fifo_done(struct qcom_swrm_ctrl *ctrl) +{ + u32 fifo_outstanding_cmds, value; + int fifo_retry_count = SWR_OVERFLOW_RETRY_COUNT; + + /* Check for fifo overflow during write */ + ctrl->reg_read(ctrl, ctrl->reg_layout[SWRM_REG_CMD_FIFO_STATUS], &value); + fifo_outstanding_cmds = FIELD_GET(SWRM_WR_CMD_FIFO_CNT_MASK, value); + + if (fifo_outstanding_cmds) { + while (fifo_retry_count) { + usleep_range(500, 510); + ctrl->reg_read(ctrl, ctrl->reg_layout[SWRM_REG_CMD_FIFO_STATUS], &value); + fifo_outstanding_cmds = FIELD_GET(SWRM_WR_CMD_FIFO_CNT_MASK, value); + fifo_retry_count--; + if (fifo_outstanding_cmds == 0) + return true; + } + } else { + return true; + } + + + return false; +} + +static int qcom_swrm_cmd_fifo_wr_cmd(struct qcom_swrm_ctrl *ctrl, u8 cmd_data, u8 dev_addr, u16 reg_addr) { @@ -341,28 +441,29 @@ static int qcom_swrm_cmd_fifo_wr_cmd(struct qcom_swrm_ctrl *swrm, u8 cmd_data, val = swrm_get_packed_reg_val(&cmd_id, cmd_data, dev_addr, reg_addr); } else { - val = swrm_get_packed_reg_val(&swrm->wcmd_id, cmd_data, + val = swrm_get_packed_reg_val(&ctrl->wcmd_id, cmd_data, dev_addr, reg_addr); } - if (swrm_wait_for_wr_fifo_avail(swrm)) + if (swrm_wait_for_wr_fifo_avail(ctrl)) return SDW_CMD_FAIL_OTHER; if (cmd_id == SWR_BROADCAST_CMD_ID) - reinit_completion(&swrm->broadcast); + reinit_completion(&ctrl->broadcast); /* Its assumed that write is okay as we do not get any status back */ - swrm->reg_write(swrm, SWRM_CMD_FIFO_WR_CMD, val); + ctrl->reg_write(ctrl, ctrl->reg_layout[SWRM_REG_CMD_FIFO_WR_CMD], val); - if (swrm->version <= SWRM_VERSION_1_3_0) + if (ctrl->version <= SWRM_VERSION_1_3_0) usleep_range(150, 155); if (cmd_id == SWR_BROADCAST_CMD_ID) { + swrm_wait_for_wr_fifo_done(ctrl); /* * sleep for 10ms for MSM soundwire variant to allow broadcast * command to complete. */ - ret = wait_for_completion_timeout(&swrm->broadcast, + ret = wait_for_completion_timeout(&ctrl->broadcast, msecs_to_jiffies(TIMEOUT_MS)); if (!ret) ret = SDW_CMD_IGNORED; @@ -375,41 +476,44 @@ static int qcom_swrm_cmd_fifo_wr_cmd(struct qcom_swrm_ctrl *swrm, u8 cmd_data, return ret; } -static int qcom_swrm_cmd_fifo_rd_cmd(struct qcom_swrm_ctrl *swrm, +static int qcom_swrm_cmd_fifo_rd_cmd(struct qcom_swrm_ctrl *ctrl, u8 dev_addr, u16 reg_addr, u32 len, u8 *rval) { u32 cmd_data, cmd_id, val, retry_attempt = 0; - val = swrm_get_packed_reg_val(&swrm->rcmd_id, len, dev_addr, reg_addr); + val = swrm_get_packed_reg_val(&ctrl->rcmd_id, len, dev_addr, reg_addr); /* * Check for outstanding cmd wrt. write fifo depth to avoid * overflow as read will also increase write fifo cnt. */ - swrm_wait_for_wr_fifo_avail(swrm); + swrm_wait_for_wr_fifo_avail(ctrl); /* wait for FIFO RD to complete to avoid overflow */ usleep_range(100, 105); - swrm->reg_write(swrm, SWRM_CMD_FIFO_RD_CMD, val); + ctrl->reg_write(ctrl, ctrl->reg_layout[SWRM_REG_CMD_FIFO_RD_CMD], val); /* wait for FIFO RD CMD complete to avoid overflow */ usleep_range(250, 255); - if (swrm_wait_for_rd_fifo_avail(swrm)) + if (swrm_wait_for_rd_fifo_avail(ctrl)) return SDW_CMD_FAIL_OTHER; do { - swrm->reg_read(swrm, SWRM_CMD_FIFO_RD_FIFO_ADDR, &cmd_data); + ctrl->reg_read(ctrl, ctrl->reg_layout[SWRM_REG_CMD_FIFO_RD_FIFO_ADDR], + &cmd_data); rval[0] = cmd_data & 0xFF; cmd_id = FIELD_GET(SWRM_RD_FIFO_CMD_ID_MASK, cmd_data); - if (cmd_id != swrm->rcmd_id) { + if (cmd_id != ctrl->rcmd_id) { if (retry_attempt < (MAX_FIFO_RD_RETRY - 1)) { /* wait 500 us before retry on fifo read failure */ usleep_range(500, 505); - swrm->reg_write(swrm, SWRM_CMD_FIFO_CMD, + ctrl->reg_write(ctrl, SWRM_CMD_FIFO_CMD, SWRM_CMD_FIFO_FLUSH); - swrm->reg_write(swrm, SWRM_CMD_FIFO_RD_CMD, val); + ctrl->reg_write(ctrl, + ctrl->reg_layout[SWRM_REG_CMD_FIFO_RD_CMD], + val); } retry_attempt++; } else { @@ -418,9 +522,9 @@ static int qcom_swrm_cmd_fifo_rd_cmd(struct qcom_swrm_ctrl *swrm, } while (retry_attempt < MAX_FIFO_RD_RETRY); - dev_err(swrm->dev, "failed to read fifo: reg: 0x%x, rcmd_id: 0x%x,\ + dev_err(ctrl->dev, "failed to read fifo: reg: 0x%x, rcmd_id: 0x%x,\ dev_num: 0x%x, cmd_data: 0x%x\n", - reg_addr, swrm->rcmd_id, dev_addr, cmd_data); + reg_addr, ctrl->rcmd_id, dev_addr, cmd_data); return SDW_CMD_IGNORED; } @@ -511,10 +615,14 @@ static int qcom_swrm_enumerate(struct sdw_bus *bus) sdw_extract_slave_id(bus, addr, &id); found = false; + ctrl->clock_stop_not_supported = false; /* Now compare with entries */ list_for_each_entry_safe(slave, _s, &bus->slaves, node) { if (sdw_compare_devid(slave, id) == 0) { qcom_swrm_set_slave_dev_num(bus, slave, i); + if (slave->prop.clk_stop_mode1) + ctrl->clock_stop_not_supported = true; + found = true; break; } @@ -532,39 +640,41 @@ static int qcom_swrm_enumerate(struct sdw_bus *bus) static irqreturn_t qcom_swrm_wake_irq_handler(int irq, void *dev_id) { - struct qcom_swrm_ctrl *swrm = dev_id; + struct qcom_swrm_ctrl *ctrl = dev_id; int ret; - ret = pm_runtime_resume_and_get(swrm->dev); + ret = pm_runtime_get_sync(ctrl->dev); if (ret < 0 && ret != -EACCES) { - dev_err_ratelimited(swrm->dev, - "pm_runtime_resume_and_get failed in %s, ret %d\n", + dev_err_ratelimited(ctrl->dev, + "pm_runtime_get_sync failed in %s, ret %d\n", __func__, ret); + pm_runtime_put_noidle(ctrl->dev); return ret; } - if (swrm->wake_irq > 0) { - if (!irqd_irq_disabled(irq_get_irq_data(swrm->wake_irq))) - disable_irq_nosync(swrm->wake_irq); + if (ctrl->wake_irq > 0) { + if (!irqd_irq_disabled(irq_get_irq_data(ctrl->wake_irq))) + disable_irq_nosync(ctrl->wake_irq); } - pm_runtime_mark_last_busy(swrm->dev); - pm_runtime_put_autosuspend(swrm->dev); + pm_runtime_mark_last_busy(ctrl->dev); + pm_runtime_put_autosuspend(ctrl->dev); return IRQ_HANDLED; } static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id) { - struct qcom_swrm_ctrl *swrm = dev_id; + struct qcom_swrm_ctrl *ctrl = dev_id; u32 value, intr_sts, intr_sts_masked, slave_status; u32 i; int devnum; int ret = IRQ_HANDLED; - clk_prepare_enable(swrm->hclk); + clk_prepare_enable(ctrl->hclk); - swrm->reg_read(swrm, SWRM_INTERRUPT_STATUS, &intr_sts); - intr_sts_masked = intr_sts & swrm->intr_mask; + ctrl->reg_read(ctrl, ctrl->reg_layout[SWRM_REG_INTERRUPT_STATUS], + &intr_sts); + intr_sts_masked = intr_sts & ctrl->intr_mask; do { for (i = 0; i < SWRM_INTERRUPT_MAX; i++) { @@ -574,80 +684,92 @@ static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id) switch (value) { case SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ: - devnum = qcom_swrm_get_alert_slave_dev_num(swrm); + devnum = qcom_swrm_get_alert_slave_dev_num(ctrl); if (devnum < 0) { - dev_err_ratelimited(swrm->dev, + dev_err_ratelimited(ctrl->dev, "no slave alert found.spurious interrupt\n"); } else { - sdw_handle_slave_status(&swrm->bus, swrm->status); + sdw_handle_slave_status(&ctrl->bus, ctrl->status); } break; case SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED: case SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS: - dev_dbg_ratelimited(swrm->dev, "SWR new slave attached\n"); - swrm->reg_read(swrm, SWRM_MCP_SLV_STATUS, &slave_status); - if (swrm->slave_status == slave_status) { - dev_dbg(swrm->dev, "Slave status not changed %x\n", + dev_dbg_ratelimited(ctrl->dev, "SWR new slave attached\n"); + ctrl->reg_read(ctrl, SWRM_MCP_SLV_STATUS, &slave_status); + if (ctrl->slave_status == slave_status) { + dev_dbg(ctrl->dev, "Slave status not changed %x\n", slave_status); } else { - qcom_swrm_get_device_status(swrm); - qcom_swrm_enumerate(&swrm->bus); - sdw_handle_slave_status(&swrm->bus, swrm->status); + qcom_swrm_get_device_status(ctrl); + qcom_swrm_enumerate(&ctrl->bus); + sdw_handle_slave_status(&ctrl->bus, ctrl->status); } break; case SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET: - dev_err_ratelimited(swrm->dev, + dev_err_ratelimited(ctrl->dev, "%s: SWR bus clsh detected\n", __func__); - swrm->intr_mask &= ~SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET; - swrm->reg_write(swrm, SWRM_INTERRUPT_CPU_EN, swrm->intr_mask); + ctrl->intr_mask &= ~SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET; + ctrl->reg_write(ctrl, + ctrl->reg_layout[SWRM_REG_INTERRUPT_CPU_EN], + ctrl->intr_mask); break; case SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW: - swrm->reg_read(swrm, SWRM_CMD_FIFO_STATUS, &value); - dev_err_ratelimited(swrm->dev, + ctrl->reg_read(ctrl, + ctrl->reg_layout[SWRM_REG_CMD_FIFO_STATUS], + &value); + dev_err_ratelimited(ctrl->dev, "%s: SWR read FIFO overflow fifo status 0x%x\n", __func__, value); break; case SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW: - swrm->reg_read(swrm, SWRM_CMD_FIFO_STATUS, &value); - dev_err_ratelimited(swrm->dev, + ctrl->reg_read(ctrl, + ctrl->reg_layout[SWRM_REG_CMD_FIFO_STATUS], + &value); + dev_err_ratelimited(ctrl->dev, "%s: SWR read FIFO underflow fifo status 0x%x\n", __func__, value); break; case SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW: - swrm->reg_read(swrm, SWRM_CMD_FIFO_STATUS, &value); - dev_err(swrm->dev, + ctrl->reg_read(ctrl, + ctrl->reg_layout[SWRM_REG_CMD_FIFO_STATUS], + &value); + dev_err(ctrl->dev, "%s: SWR write FIFO overflow fifo status %x\n", __func__, value); - swrm->reg_write(swrm, SWRM_CMD_FIFO_CMD, 0x1); + ctrl->reg_write(ctrl, SWRM_CMD_FIFO_CMD, 0x1); break; case SWRM_INTERRUPT_STATUS_CMD_ERROR: - swrm->reg_read(swrm, SWRM_CMD_FIFO_STATUS, &value); - dev_err_ratelimited(swrm->dev, + ctrl->reg_read(ctrl, + ctrl->reg_layout[SWRM_REG_CMD_FIFO_STATUS], + &value); + dev_err_ratelimited(ctrl->dev, "%s: SWR CMD error, fifo status 0x%x, flushing fifo\n", __func__, value); - swrm->reg_write(swrm, SWRM_CMD_FIFO_CMD, 0x1); + ctrl->reg_write(ctrl, SWRM_CMD_FIFO_CMD, 0x1); break; case SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION: - dev_err_ratelimited(swrm->dev, + dev_err_ratelimited(ctrl->dev, "%s: SWR Port collision detected\n", __func__); - swrm->intr_mask &= ~SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION; - swrm->reg_write(swrm, - SWRM_INTERRUPT_CPU_EN, swrm->intr_mask); + ctrl->intr_mask &= ~SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION; + ctrl->reg_write(ctrl, + ctrl->reg_layout[SWRM_REG_INTERRUPT_CPU_EN], + ctrl->intr_mask); break; case SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH: - dev_err_ratelimited(swrm->dev, + dev_err_ratelimited(ctrl->dev, "%s: SWR read enable valid mismatch\n", __func__); - swrm->intr_mask &= + ctrl->intr_mask &= ~SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH; - swrm->reg_write(swrm, - SWRM_INTERRUPT_CPU_EN, swrm->intr_mask); + ctrl->reg_write(ctrl, + ctrl->reg_layout[SWRM_REG_INTERRUPT_CPU_EN], + ctrl->intr_mask); break; case SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED: - complete(&swrm->broadcast); + complete(&ctrl->broadcast); break; case SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED_V2: break; @@ -656,22 +778,44 @@ static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id) case SWRM_INTERRUPT_STATUS_EXT_CLK_STOP_WAKEUP: break; default: - dev_err_ratelimited(swrm->dev, + dev_err_ratelimited(ctrl->dev, "%s: SWR unknown interrupt value: %d\n", __func__, value); ret = IRQ_NONE; break; } } - swrm->reg_write(swrm, SWRM_INTERRUPT_CLEAR, intr_sts); - swrm->reg_read(swrm, SWRM_INTERRUPT_STATUS, &intr_sts); - intr_sts_masked = intr_sts & swrm->intr_mask; + ctrl->reg_write(ctrl, ctrl->reg_layout[SWRM_REG_INTERRUPT_CLEAR], + intr_sts); + ctrl->reg_read(ctrl, ctrl->reg_layout[SWRM_REG_INTERRUPT_STATUS], + &intr_sts); + intr_sts_masked = intr_sts & ctrl->intr_mask; } while (intr_sts_masked); - clk_disable_unprepare(swrm->hclk); + clk_disable_unprepare(ctrl->hclk); return ret; } +static bool swrm_wait_for_frame_gen_enabled(struct qcom_swrm_ctrl *ctrl) +{ + int retry = SWRM_LINK_STATUS_RETRY_CNT; + int comp_sts; + + do { + ctrl->reg_read(ctrl, SWRM_COMP_STATUS, &comp_sts); + + if (comp_sts & SWRM_FRM_GEN_ENABLED) + return true; + + usleep_range(500, 510); + } while (retry--); + + dev_err(ctrl->dev, "%s: link status not %s\n", __func__, + comp_sts & SWRM_FRM_GEN_ENABLED ? "connected" : "disconnected"); + + return false; +} + static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl) { u32 val; @@ -689,18 +833,23 @@ static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl) ctrl->intr_mask = SWRM_INTERRUPT_STATUS_RMSK; /* Mask soundwire interrupts */ - ctrl->reg_write(ctrl, SWRM_INTERRUPT_MASK_ADDR, - SWRM_INTERRUPT_STATUS_RMSK); + if (ctrl->version < SWRM_VERSION_2_0_0) + ctrl->reg_write(ctrl, ctrl->reg_layout[SWRM_REG_INTERRUPT_MASK_ADDR], + SWRM_INTERRUPT_STATUS_RMSK); /* Configure No pings */ ctrl->reg_read(ctrl, SWRM_MCP_CFG_ADDR, &val); u32p_replace_bits(&val, SWRM_DEF_CMD_NO_PINGS, SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK); ctrl->reg_write(ctrl, SWRM_MCP_CFG_ADDR, val); - if (ctrl->version >= SWRM_VERSION_1_7_0) { + if (ctrl->version == SWRM_VERSION_1_7_0) { ctrl->reg_write(ctrl, SWRM_LINK_MANAGER_EE, SWRM_EE_CPU); ctrl->reg_write(ctrl, SWRM_MCP_BUS_CTRL, SWRM_MCP_BUS_CLK_START << SWRM_EE_CPU); + } else if (ctrl->version >= SWRM_VERSION_2_0_0) { + ctrl->reg_write(ctrl, SWRM_LINK_MANAGER_EE, SWRM_EE_CPU); + ctrl->reg_write(ctrl, SWRM_V2_0_CLK_CTRL, + SWRM_V2_0_CLK_CTRL_CLK_START); } else { ctrl->reg_write(ctrl, SWRM_MCP_BUS_CTRL, SWRM_MCP_BUS_CLK_START); } @@ -715,16 +864,28 @@ static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl) SWRM_RD_WR_CMD_RETRIES); } + /* COMP Enable */ + ctrl->reg_write(ctrl, SWRM_COMP_CFG_ADDR, SWRM_COMP_CFG_ENABLE_MSK); + /* Set IRQ to PULSE */ ctrl->reg_write(ctrl, SWRM_COMP_CFG_ADDR, - SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK | - SWRM_COMP_CFG_ENABLE_MSK); + SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK); + + ctrl->reg_write(ctrl, ctrl->reg_layout[SWRM_REG_INTERRUPT_CLEAR], + 0xFFFFFFFF); /* enable CPU IRQs */ if (ctrl->mmio) { - ctrl->reg_write(ctrl, SWRM_INTERRUPT_CPU_EN, + ctrl->reg_write(ctrl, ctrl->reg_layout[SWRM_REG_INTERRUPT_CPU_EN], SWRM_INTERRUPT_STATUS_RMSK); } + + /* Set IRQ to PULSE */ + ctrl->reg_write(ctrl, SWRM_COMP_CFG_ADDR, + SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK | + SWRM_COMP_CFG_ENABLE_MSK); + + swrm_wait_for_frame_gen_enabled(ctrl); ctrl->slave_status = 0; ctrl->reg_read(ctrl, SWRM_COMP_PARAMS, &val); ctrl->rd_fifo_depth = FIELD_GET(SWRM_COMP_PARAMS_RD_FIFO_DEPTH, val); @@ -806,12 +967,20 @@ static int qcom_swrm_transport_params(struct sdw_bus *bus, value = pcfg->off1 << SWRM_DP_PORT_CTRL_OFFSET1_SHFT; value |= pcfg->off2 << SWRM_DP_PORT_CTRL_OFFSET2_SHFT; - value |= pcfg->si; + value |= pcfg->si & 0xff; ret = ctrl->reg_write(ctrl, reg, value); if (ret) goto err; + if (pcfg->si > 0xff) { + value = (pcfg->si >> 8) & 0xff; + reg = SWRM_DP_SAMPLECTRL2_BANK(params->port_num, bank); + ret = ctrl->reg_write(ctrl, reg, value); + if (ret) + goto err; + } + if (pcfg->lane_control != SWR_INVALID_PARAM) { reg = SWRM_DP_PORT_CTRL_2_BANK(params->port_num, bank); value = pcfg->lane_control; @@ -1090,11 +1259,12 @@ static int qcom_swrm_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai; int ret, i; - ret = pm_runtime_resume_and_get(ctrl->dev); + ret = pm_runtime_get_sync(ctrl->dev); if (ret < 0 && ret != -EACCES) { dev_err_ratelimited(ctrl->dev, - "pm_runtime_resume_and_get failed in %s, ret %d\n", + "pm_runtime_get_sync failed in %s, ret %d\n", __func__, ret); + pm_runtime_put_noidle(ctrl->dev); return ret; } @@ -1132,6 +1302,7 @@ static void qcom_swrm_shutdown(struct snd_pcm_substream *substream, { struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dai->dev); + swrm_wait_for_wr_fifo_done(ctrl); sdw_release_stream(ctrl->sruntime[dai->id]); ctrl->sruntime[dai->id] = NULL; pm_runtime_mark_last_busy(ctrl->dev); @@ -1194,7 +1365,7 @@ static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl) struct device_node *np = ctrl->dev->of_node; u8 off1[QCOM_SDW_MAX_PORTS]; u8 off2[QCOM_SDW_MAX_PORTS]; - u8 si[QCOM_SDW_MAX_PORTS]; + u16 si[QCOM_SDW_MAX_PORTS]; u8 bp_mode[QCOM_SDW_MAX_PORTS] = { 0, }; u8 hstart[QCOM_SDW_MAX_PORTS]; u8 hstop[QCOM_SDW_MAX_PORTS]; @@ -1202,6 +1373,7 @@ static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl) u8 blk_group_count[QCOM_SDW_MAX_PORTS]; u8 lane_control[QCOM_SDW_MAX_PORTS]; int i, ret, nports, val; + bool si_16 = false; ctrl->reg_read(ctrl, SWRM_COMP_PARAMS, &val); @@ -1245,9 +1417,14 @@ static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl) return ret; ret = of_property_read_u8_array(np, "qcom,ports-sinterval-low", - si, nports); - if (ret) - return ret; + (u8 *)si, nports); + if (ret) { + ret = of_property_read_u16_array(np, "qcom,ports-sinterval", + si, nports); + if (ret) + return ret; + si_16 = true; + } ret = of_property_read_u8_array(np, "qcom,ports-block-pack-mode", bp_mode, nports); @@ -1275,7 +1452,10 @@ static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl) for (i = 0; i < nports; i++) { /* Valid port number range is from 1-14 */ - ctrl->pconfig[i + 1].si = si[i]; + if (si_16) + ctrl->pconfig[i + 1].si = si[i]; + else + ctrl->pconfig[i + 1].si = ((u8 *)si)[i]; ctrl->pconfig[i + 1].off1 = off1[i]; ctrl->pconfig[i + 1].off2 = off2[i]; ctrl->pconfig[i + 1].bp_mode = bp_mode[i]; @@ -1292,23 +1472,24 @@ static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl) #ifdef CONFIG_DEBUG_FS static int swrm_reg_show(struct seq_file *s_file, void *data) { - struct qcom_swrm_ctrl *swrm = s_file->private; + struct qcom_swrm_ctrl *ctrl = s_file->private; int reg, reg_val, ret; - ret = pm_runtime_resume_and_get(swrm->dev); + ret = pm_runtime_get_sync(ctrl->dev); if (ret < 0 && ret != -EACCES) { - dev_err_ratelimited(swrm->dev, - "pm_runtime_resume_and_get failed in %s, ret %d\n", + dev_err_ratelimited(ctrl->dev, + "pm_runtime_get_sync failed in %s, ret %d\n", __func__, ret); + pm_runtime_put_noidle(ctrl->dev); return ret; } - for (reg = 0; reg <= SWR_MSTR_MAX_REG_ADDR; reg += 4) { - swrm->reg_read(swrm, reg, ®_val); + for (reg = 0; reg <= ctrl->max_reg; reg += 4) { + ctrl->reg_read(ctrl, reg, ®_val); seq_printf(s_file, "0x%.3x: 0x%.2x\n", reg, reg_val); } - pm_runtime_mark_last_busy(swrm->dev); - pm_runtime_put_autosuspend(swrm->dev); + pm_runtime_mark_last_busy(ctrl->dev); + pm_runtime_put_autosuspend(ctrl->dev); return 0; @@ -1331,6 +1512,8 @@ static int qcom_swrm_probe(struct platform_device *pdev) return -ENOMEM; data = of_device_get_match_data(dev); + ctrl->max_reg = data->max_reg; + ctrl->reg_layout = data->reg_layout; ctrl->rows_index = sdw_find_row_index(data->default_rows); ctrl->cols_index = sdw_find_col_index(data->default_cols); #if IS_REACHABLE(CONFIG_SLIMBUS) @@ -1454,15 +1637,6 @@ static int qcom_swrm_probe(struct platform_device *pdev) pm_runtime_set_active(dev); pm_runtime_enable(dev); - /* Clk stop is not supported on WSA Soundwire masters */ - if (ctrl->version <= SWRM_VERSION_1_3_0) { - ctrl->clock_stop_not_supported = true; - } else { - ctrl->reg_read(ctrl, SWRM_COMP_MASTER_ID, &val); - if (val == MASTER_ID_WSA) - ctrl->clock_stop_not_supported = true; - } - #ifdef CONFIG_DEBUG_FS ctrl->debugfs = debugfs_create_dir("qualcomm-sdw", ctrl->bus.debugfs); debugfs_create_file("qualcomm-registers", 0400, ctrl->debugfs, ctrl, @@ -1489,26 +1663,6 @@ static int qcom_swrm_remove(struct platform_device *pdev) return 0; } -static bool swrm_wait_for_frame_gen_enabled(struct qcom_swrm_ctrl *swrm) -{ - int retry = SWRM_LINK_STATUS_RETRY_CNT; - int comp_sts; - - do { - swrm->reg_read(swrm, SWRM_COMP_STATUS, &comp_sts); - - if (comp_sts & SWRM_FRM_GEN_ENABLED) - return true; - - usleep_range(500, 510); - } while (retry--); - - dev_err(swrm->dev, "%s: link status not %s\n", __func__, - comp_sts & SWRM_FRM_GEN_ENABLED ? "connected" : "disconnected"); - - return false; -} - static int __maybe_unused swrm_runtime_resume(struct device *dev) { struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dev); @@ -1540,19 +1694,27 @@ static int __maybe_unused swrm_runtime_resume(struct device *dev) } else { reset_control_reset(ctrl->audio_cgcr); - if (ctrl->version >= SWRM_VERSION_1_7_0) { + if (ctrl->version == SWRM_VERSION_1_7_0) { ctrl->reg_write(ctrl, SWRM_LINK_MANAGER_EE, SWRM_EE_CPU); ctrl->reg_write(ctrl, SWRM_MCP_BUS_CTRL, SWRM_MCP_BUS_CLK_START << SWRM_EE_CPU); + } else if (ctrl->version >= SWRM_VERSION_2_0_0) { + ctrl->reg_write(ctrl, SWRM_LINK_MANAGER_EE, SWRM_EE_CPU); + ctrl->reg_write(ctrl, SWRM_V2_0_CLK_CTRL, + SWRM_V2_0_CLK_CTRL_CLK_START); } else { ctrl->reg_write(ctrl, SWRM_MCP_BUS_CTRL, SWRM_MCP_BUS_CLK_START); } - ctrl->reg_write(ctrl, SWRM_INTERRUPT_CLEAR, + ctrl->reg_write(ctrl, ctrl->reg_layout[SWRM_REG_INTERRUPT_CLEAR], SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET); ctrl->intr_mask |= SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET; - ctrl->reg_write(ctrl, SWRM_INTERRUPT_MASK_ADDR, ctrl->intr_mask); - ctrl->reg_write(ctrl, SWRM_INTERRUPT_CPU_EN, ctrl->intr_mask); + if (ctrl->version < SWRM_VERSION_2_0_0) + ctrl->reg_write(ctrl, + ctrl->reg_layout[SWRM_REG_INTERRUPT_MASK_ADDR], + ctrl->intr_mask); + ctrl->reg_write(ctrl, ctrl->reg_layout[SWRM_REG_INTERRUPT_CPU_EN], + ctrl->intr_mask); usleep_range(100, 105); if (!swrm_wait_for_frame_gen_enabled(ctrl)) @@ -1571,11 +1733,16 @@ static int __maybe_unused swrm_runtime_suspend(struct device *dev) struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dev); int ret; + swrm_wait_for_wr_fifo_done(ctrl); if (!ctrl->clock_stop_not_supported) { /* Mask bus clash interrupt */ ctrl->intr_mask &= ~SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET; - ctrl->reg_write(ctrl, SWRM_INTERRUPT_MASK_ADDR, ctrl->intr_mask); - ctrl->reg_write(ctrl, SWRM_INTERRUPT_CPU_EN, ctrl->intr_mask); + if (ctrl->version < SWRM_VERSION_2_0_0) + ctrl->reg_write(ctrl, + ctrl->reg_layout[SWRM_REG_INTERRUPT_MASK_ADDR], + ctrl->intr_mask); + ctrl->reg_write(ctrl, ctrl->reg_layout[SWRM_REG_INTERRUPT_CPU_EN], + ctrl->intr_mask); /* Prepare slaves for clock stop */ ret = sdw_bus_prep_clk_stop(&ctrl->bus); if (ret < 0 && ret != -ENODATA) { @@ -1611,6 +1778,7 @@ static const struct of_device_id qcom_swrm_of_match[] = { { .compatible = "qcom,soundwire-v1.5.1", .data = &swrm_v1_5_data }, { .compatible = "qcom,soundwire-v1.6.0", .data = &swrm_v1_6_data }, { .compatible = "qcom,soundwire-v1.7.0", .data = &swrm_v1_5_data }, + { .compatible = "qcom,soundwire-v2.0.0", .data = &swrm_v2_0_data }, {/* sentinel */}, }; diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 379228f22186..d77a8a0d42c8 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1150,7 +1150,8 @@ static struct sdw_master_runtime *sdw_master_rt_alloc(struct sdw_bus *bus, struct sdw_stream_runtime *stream) { - struct sdw_master_runtime *m_rt; + struct sdw_master_runtime *m_rt, *walk_m_rt; + struct list_head *insert_after; m_rt = kzalloc(sizeof(*m_rt), GFP_KERNEL); if (!m_rt) @@ -1159,7 +1160,20 @@ static struct sdw_master_runtime /* Initialization of Master runtime handle */ INIT_LIST_HEAD(&m_rt->port_list); INIT_LIST_HEAD(&m_rt->slave_rt_list); - list_add_tail(&m_rt->stream_node, &stream->master_list); + + /* + * Add in order of bus id so that when taking the bus_lock + * of multiple buses they will always be taken in the same + * order to prevent a mutex deadlock. + */ + insert_after = &stream->master_list; + list_for_each_entry_reverse(walk_m_rt, &stream->master_list, stream_node) { + if (walk_m_rt->bus->id < bus->id) { + insert_after = &walk_m_rt->stream_node; + break; + } + } + list_add(&m_rt->stream_node, insert_after); list_add_tail(&m_rt->bus_node, &bus->m_rt_list); @@ -1338,7 +1352,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream, bool update_params) { struct sdw_master_runtime *m_rt; - struct sdw_bus *bus = NULL; + struct sdw_bus *bus; struct sdw_master_prop *prop; struct sdw_bus_params params; int ret; @@ -1355,25 +1369,23 @@ 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 * - m_rt->ch_count * m_rt->stream->params.bps; + if (update_params) { + /* Increment cumulative bus bandwidth */ + /* TODO: Update this during Device-Device support */ + 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\n", - ret); - goto restore_params; + /* Compute params */ + if (bus->compute_params) { + ret = bus->compute_params(bus); + if (ret < 0) { + dev_err(bus->dev, "Compute params failed: %d\n", + ret); + goto restore_params; + } } } -program_params: /* Program params */ ret = sdw_program_params(bus, true); if (ret < 0) { @@ -1382,11 +1394,6 @@ program_params: } } - if (!bus) { - pr_err("Configuration error in %s\n", __func__); - return -EINVAL; - } - ret = do_bank_switch(stream); if (ret < 0) { pr_err("%s: do_bank_switch failed: %d\n", __func__, ret); @@ -1467,7 +1474,7 @@ EXPORT_SYMBOL(sdw_prepare_stream); static int _sdw_enable_stream(struct sdw_stream_runtime *stream) { struct sdw_master_runtime *m_rt; - struct sdw_bus *bus = NULL; + struct sdw_bus *bus; int ret; /* Enable Master(s) and Slave(s) port(s) associated with stream */ @@ -1490,11 +1497,6 @@ static int _sdw_enable_stream(struct sdw_stream_runtime *stream) } } - if (!bus) { - pr_err("Configuration error in %s\n", __func__); - return -EINVAL; - } - ret = do_bank_switch(stream); if (ret < 0) { pr_err("%s: do_bank_switch failed: %d\n", __func__, ret); @@ -1864,7 +1866,7 @@ int sdw_stream_add_master(struct sdw_bus *bus, struct sdw_stream_runtime *stream) { struct sdw_master_runtime *m_rt; - bool alloc_master_rt = true; + bool alloc_master_rt = false; int ret; mutex_lock(&bus->bus_lock); @@ -1886,30 +1888,25 @@ int sdw_stream_add_master(struct sdw_bus *bus, * it first), if so skip allocation and go to configuration */ m_rt = sdw_master_rt_find(bus, stream); - if (m_rt) { - alloc_master_rt = false; - goto skip_alloc_master_rt; - } - - m_rt = sdw_master_rt_alloc(bus, stream); if (!m_rt) { - dev_err(bus->dev, "%s: Master runtime alloc failed for stream:%s\n", - __func__, stream->name); - ret = -ENOMEM; - goto unlock; - } -skip_alloc_master_rt: - - if (sdw_master_port_allocated(m_rt)) - goto skip_alloc_master_port; + m_rt = sdw_master_rt_alloc(bus, stream); + if (!m_rt) { + dev_err(bus->dev, "%s: Master runtime alloc failed for stream:%s\n", + __func__, stream->name); + ret = -ENOMEM; + goto unlock; + } - ret = sdw_master_port_alloc(m_rt, num_ports); - if (ret) - goto alloc_error; + alloc_master_rt = true; + } - stream->m_rt_count++; + if (!sdw_master_port_allocated(m_rt)) { + ret = sdw_master_port_alloc(m_rt, num_ports); + if (ret) + goto alloc_error; -skip_alloc_master_port: + stream->m_rt_count++; + } ret = sdw_master_rt_config(m_rt, stream_config); if (ret < 0) @@ -1990,8 +1987,8 @@ int sdw_stream_add_slave(struct sdw_slave *slave, { struct sdw_slave_runtime *s_rt; struct sdw_master_runtime *m_rt; - bool alloc_master_rt = true; - bool alloc_slave_rt = true; + bool alloc_master_rt = false; + bool alloc_slave_rt = false; int ret; @@ -2002,47 +1999,41 @@ int sdw_stream_add_slave(struct sdw_slave *slave, * and go to configuration */ m_rt = sdw_master_rt_find(slave->bus, stream); - if (m_rt) { - alloc_master_rt = false; - goto skip_alloc_master_rt; - } - - /* - * If this API is invoked by Slave first then m_rt is not valid. - * So, allocate m_rt and add Slave to it. - */ - m_rt = sdw_master_rt_alloc(slave->bus, stream); if (!m_rt) { - dev_err(&slave->dev, "%s: Master runtime alloc failed for stream:%s\n", - __func__, stream->name); - ret = -ENOMEM; - goto unlock; - } + /* + * If this API is invoked by Slave first then m_rt is not valid. + * So, allocate m_rt and add Slave to it. + */ + m_rt = sdw_master_rt_alloc(slave->bus, stream); + if (!m_rt) { + dev_err(&slave->dev, "%s: Master runtime alloc failed for stream:%s\n", + __func__, stream->name); + ret = -ENOMEM; + goto unlock; + } -skip_alloc_master_rt: - s_rt = sdw_slave_rt_find(slave, stream); - if (s_rt) { - alloc_slave_rt = false; - goto skip_alloc_slave_rt; + alloc_master_rt = true; } - s_rt = sdw_slave_rt_alloc(slave, m_rt); + s_rt = sdw_slave_rt_find(slave, stream); if (!s_rt) { - dev_err(&slave->dev, "Slave runtime alloc failed for stream:%s\n", stream->name); - alloc_slave_rt = false; - ret = -ENOMEM; - goto alloc_error; - } + s_rt = sdw_slave_rt_alloc(slave, m_rt); + if (!s_rt) { + dev_err(&slave->dev, "Slave runtime alloc failed for stream:%s\n", + stream->name); + ret = -ENOMEM; + goto alloc_error; + } -skip_alloc_slave_rt: - if (sdw_slave_port_allocated(s_rt)) - goto skip_port_alloc; + alloc_slave_rt = true; + } - ret = sdw_slave_port_alloc(slave, s_rt, num_ports); - if (ret) - goto alloc_error; + if (!sdw_slave_port_allocated(s_rt)) { + ret = sdw_slave_port_alloc(slave, s_rt, num_ports); + if (ret) + goto alloc_error; + } -skip_port_alloc: ret = sdw_master_rt_config(m_rt, stream_config); if (ret) goto unlock; |