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 | |
| parent | 15ac468614e5e4fee82e1eb32568f427b0e51adc (diff) | |
| parent | a4857d1afdd1fa7ff763e1d07b1c2db521a5f9b1 (diff) | |
| download | linux-fe1de55167963a1c0ebe1579e37a8a41495f0a81.tar.gz linux-fe1de55167963a1c0ebe1579e37a8a41495f0a81.tar.bz2 linux-fe1de55167963a1c0ebe1579e37a8a41495f0a81.zip | |
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
...
| -rw-r--r-- | Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml | 39 | ||||
| -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 | ||||
| -rw-r--r-- | include/linux/soundwire/sdw.h | 6 | ||||
| -rw-r--r-- | include/linux/soundwire/sdw_intel.h | 94 | ||||
| -rw-r--r-- | sound/soc/sof/intel/hda.c | 33 | ||||
| -rw-r--r-- | sound/soc/sof/intel/shim.h | 1 |
22 files changed, 1289 insertions, 404 deletions
diff --git a/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml b/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml index e4dba825ab11..fb44b89a754e 100644 --- a/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml +++ b/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml @@ -21,6 +21,7 @@ properties: - qcom,soundwire-v1.5.1 - qcom,soundwire-v1.6.0 - qcom,soundwire-v1.7.0 + - qcom,soundwire-v2.0.0 reg: maxItems: 1 @@ -80,18 +81,29 @@ properties: or applicable for the respective data port. More info in MIPI Alliance SoundWire 1.0 Specifications. minItems: 3 - maxItems: 8 + maxItems: 16 qcom,ports-sinterval-low: $ref: /schemas/types.yaml#/definitions/uint8-array description: - Sample interval low of each data port. + Sample interval (only lowest byte) of each data port. Out ports followed by In ports. Used for Sample Interval calculation. Value of 0xff indicates that this option is not implemented or applicable for the respective data port. More info in MIPI Alliance SoundWire 1.0 Specifications. minItems: 3 - maxItems: 8 + maxItems: 16 + + qcom,ports-sinterval: + $ref: /schemas/types.yaml#/definitions/uint16-array + description: + Sample interval of each data port. + Out ports followed by In ports. Used for Sample Interval calculation. + Value of 0xffff indicates that this option is not implemented + or applicable for the respective data port. + More info in MIPI Alliance SoundWire 1.0 Specifications. + minItems: 3 + maxItems: 16 qcom,ports-offset1: $ref: /schemas/types.yaml#/definitions/uint8-array @@ -102,7 +114,7 @@ properties: or applicable for the respective data port. More info in MIPI Alliance SoundWire 1.0 Specifications. minItems: 3 - maxItems: 8 + maxItems: 16 qcom,ports-offset2: $ref: /schemas/types.yaml#/definitions/uint8-array @@ -113,7 +125,7 @@ properties: or applicable for the respective data port. More info in MIPI Alliance SoundWire 1.0 Specifications. minItems: 3 - maxItems: 8 + maxItems: 16 qcom,ports-lane-control: $ref: /schemas/types.yaml#/definitions/uint8-array @@ -124,7 +136,7 @@ properties: or applicable for the respective data port. More info in MIPI Alliance SoundWire 1.0 Specifications. minItems: 3 - maxItems: 8 + maxItems: 16 qcom,ports-block-pack-mode: $ref: /schemas/types.yaml#/definitions/uint8-array @@ -137,7 +149,7 @@ properties: or applicable for the respective data port. More info in MIPI Alliance SoundWire 1.0 Specifications. minItems: 3 - maxItems: 8 + maxItems: 16 items: oneOf: - minimum: 0 @@ -154,7 +166,7 @@ properties: or applicable for the respective data port. More info in MIPI Alliance SoundWire 1.0 Specifications. minItems: 3 - maxItems: 8 + maxItems: 16 items: oneOf: - minimum: 0 @@ -171,7 +183,7 @@ properties: or applicable for the respective data port. More info in MIPI Alliance SoundWire 1.0 Specifications. minItems: 3 - maxItems: 8 + maxItems: 16 items: oneOf: - minimum: 0 @@ -187,7 +199,7 @@ properties: or applicable for the respective data port. More info in MIPI Alliance SoundWire 1.0 Specifications. minItems: 3 - maxItems: 8 + maxItems: 16 items: oneOf: - minimum: 0 @@ -219,10 +231,15 @@ required: - '#size-cells' - qcom,dout-ports - qcom,din-ports - - qcom,ports-sinterval-low - qcom,ports-offset1 - qcom,ports-offset2 +oneOf: + - required: + - qcom,ports-sinterval-low + - required: + - qcom,ports-sinterval + additionalProperties: false examples: 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) +{< |
