soundwire updates for 6.5
- Stream handling and slave alert handling - Qualcomm Soundwire v2.0.0 controller support - Intel ACE2.x initial support and code reorganization -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmSlHY4ACgkQfBQHDyUj g0fHJw//XRkP6BDfB3D/dnfJTPtWpcIBChdrImhNlL52czqUiyZXfBtkQkaGcDdy AFFcsvqYZkgQTXcyAoT/g5E/4M92RnrXHdxcQREsziBU1xnGNAtUDfK8EyPE+0I5 xBVUmtPGC9NS24UVDXKlOnq/6hPw0Ab4JVzFRMziqC8iIXTA4vj9xenjI2XX4K8d J5ajVBA7bGDeAN/mPJsCdCnT4i1si23/vUgk93pC/onCO3phZqh4TK4pY/qbwXzx tFwCh6qam0iY70Ga1T0HVc5wCMxXcmZFJuM8HnpTOYArubGpW7bOamxlItZtv4vL CEEqWgMWBE5r50fgaxe3zJ278nQSBQ8Gx5IP+OCPdt9FdqfEFFzVv+LWb+BVaKdJ N2IRT97t89PrMqU8zcm05HnR7lkgLvwle7eFcNLZaG3FleGp+P5nixCa4+tyvq+b a7/YtQIbkkXUFxKNMY8fVquk+to9H6xxeLrPmuufwwnO2DiYMuIPina0zU5/gEor qhzg5zTDK5lyO7P4AC/HWt50jl0IYDLIqBfdEVQ94G+QopPRVOGXQWfqipWC9f// WxDTi5IKCtL7QXJTbpUKBqu5N84LFqJpPNFA0GG9Wy9+hsUOwI+kFQpB5TQfCETD 7rwncBhrzJNhtZmLv54EMs5hISWl/CDEiyvItdho2r904/AcIWM= =gjRz -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
fe1de55167
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
|
@ -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 */
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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_clock_restart(cdns, false);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s: unable to restart clock: %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;
|
||||
}
|
||||
|
||||
ret = sdw_cdns_clock_restart(cdns, false);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s: unable to restart clock: %d\n", __func__, ret);
|
||||
sdw_cdns_enable_interrupt(cdns, false);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
ctrl->reg_write(ctrl, ctrl->reg_layout[SWRM_REG_INTERRUPT_CLEAR],
|
||||
0xFFFFFFFF);
|
||||
|
||||
/* enable CPU IRQs */
|
||||
if (ctrl->mmio) {
|
||||
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);
|
||||
|
||||
/* enable CPU IRQs */
|
||||
if (ctrl->mmio) {
|
||||
ctrl->reg_write(ctrl, SWRM_INTERRUPT_CPU_EN,
|
||||
SWRM_INTERRUPT_STATUS_RMSK);
|
||||
}
|
||||
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 */},
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
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;
|
||||
}
|
||||
|
||||
alloc_master_rt = true;
|
||||
}
|
||||
skip_alloc_master_rt:
|
||||
|
||||
if (sdw_master_port_allocated(m_rt))
|
||||
goto skip_alloc_master_port;
|
||||
if (!sdw_master_port_allocated(m_rt)) {
|
||||
ret = sdw_master_port_alloc(m_rt, num_ports);
|
||||
if (ret)
|
||||
goto alloc_error;
|
||||
|
||||
ret = sdw_master_port_alloc(m_rt, num_ports);
|
||||
if (ret)
|
||||
goto alloc_error;
|
||||
|
||||
stream->m_rt_count++;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
alloc_master_rt = true;
|
||||
}
|
||||
|
||||
skip_alloc_master_rt:
|
||||
s_rt = sdw_slave_rt_find(slave, stream);
|
||||
if (s_rt) {
|
||||
alloc_slave_rt = false;
|
||||
goto skip_alloc_slave_rt;
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
alloc_slave_rt = true;
|
||||
}
|
||||
|
||||
skip_alloc_slave_rt:
|
||||
if (sdw_slave_port_allocated(s_rt))
|
||||
goto skip_port_alloc;
|
||||
if (!sdw_slave_port_allocated(s_rt)) {
|
||||
ret = sdw_slave_port_alloc(slave, s_rt, num_ports);
|
||||
if (ret)
|
||||
goto alloc_error;
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#define __SOUNDWIRE_H
|
||||
|
||||
#include <linux/bug.h>
|
||||
#include <linux/lockdep_types.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
|
@ -846,6 +847,7 @@ struct sdw_defer {
|
|||
* @post_bank_switch: Callback for post bank switch
|
||||
* @read_ping_status: Read status from PING frames, reported with two bits per Device.
|
||||
* Bits 31:24 are reserved.
|
||||
* @new_peripheral_assigned: Callback to handle enumeration of new peripheral.
|
||||
*/
|
||||
struct sdw_master_ops {
|
||||
int (*read_prop)(struct sdw_bus *bus);
|
||||
|
@ -860,7 +862,7 @@ struct sdw_master_ops {
|
|||
int (*pre_bank_switch)(struct sdw_bus *bus);
|
||||
int (*post_bank_switch)(struct sdw_bus *bus);
|
||||
u32 (*read_ping_status)(struct sdw_bus *bus);
|
||||
|
||||
void (*new_peripheral_assigned)(struct sdw_bus *bus, int dev_num);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -906,7 +908,9 @@ struct sdw_bus {
|
|||
struct list_head slaves;
|
||||
DECLARE_BITMAP(assigned, SDW_MAX_DEVICES);
|
||||
struct mutex bus_lock;
|
||||
struct lock_class_key bus_lock_key;
|
||||
struct mutex msg_lock;
|
||||
struct lock_class_key msg_lock_key;
|
||||
int (*compute_params)(struct sdw_bus *bus);
|
||||
const struct sdw_master_ops *ops;
|
||||
const struct sdw_master_port_ops *port_ops;
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
#include <linux/irqreturn.h>
|
||||
#include <linux/soundwire/sdw.h>
|
||||
|
||||
/*********************************************************************
|
||||
* cAVS and ACE1.x definitions
|
||||
*********************************************************************/
|
||||
|
||||
#define SDW_SHIM_BASE 0x2C000
|
||||
#define SDW_ALH_BASE 0x2C800
|
||||
#define SDW_SHIM_BASE_ACE 0x38000
|
||||
|
@ -101,13 +105,84 @@
|
|||
#define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0)
|
||||
#define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16)
|
||||
|
||||
/*********************************************************************
|
||||
* ACE2.x definitions for SHIM registers - only accessible when the
|
||||
* HDAudio extended link LCTL.SPA/CPA = 1.
|
||||
*********************************************************************/
|
||||
/* x variable is link index */
|
||||
#define SDW_SHIM2_GENERIC_BASE(x) (0x00030000 + 0x8000 * (x))
|
||||
#define SDW_IP_BASE(x) (0x00030100 + 0x8000 * (x))
|
||||
#define SDW_SHIM2_VS_BASE(x) (0x00036000 + 0x8000 * (x))
|
||||
|
||||
/* SHIM2 Generic Registers */
|
||||
/* Read-only capabilities */
|
||||
#define SDW_SHIM2_LECAP 0x00
|
||||
#define SDW_SHIM2_LECAP_HDS BIT(0) /* unset -> Host mode */
|
||||
#define SDW_SHIM2_LECAP_MLC GENMASK(3, 1) /* Number of Lanes */
|
||||
|
||||
/* PCM Stream capabilities */
|
||||
#define SDW_SHIM2_PCMSCAP 0x10
|
||||
#define SDW_SHIM2_PCMSCAP_ISS GENMASK(3, 0) /* Input-only streams */
|
||||
#define SDW_SHIM2_PCMSCAP_OSS GENMASK(7, 4) /* Output-only streams */
|
||||
#define SDW_SHIM2_PCMSCAP_BSS GENMASK(12, 8) /* Bidirectional streams */
|
||||
|
||||
/* Read-only PCM Stream Channel Count, y variable is stream */
|
||||
#define SDW_SHIM2_PCMSYCHC(y) (0x14 + (0x4 * (y)))
|
||||
#define SDW_SHIM2_PCMSYCHC_CS GENMASK(3, 0) /* Channels Supported */
|
||||
|
||||
/* PCM Stream Channel Map */
|
||||
#define SDW_SHIM2_PCMSYCHM(y) (0x16 + (0x4 * (y)))
|
||||
#define SDW_SHIM2_PCMSYCHM_LCHAN GENMASK(3, 0) /* Lowest channel used by the FIFO port */
|
||||
#define SDW_SHIM2_PCMSYCHM_HCHAN GENMASK(7, 4) /* Lowest channel used by the FIFO port */
|
||||
#define SDW_SHIM2_PCMSYCHM_STRM GENMASK(13, 8) /* HDaudio stream tag */
|
||||
#define SDW_SHIM2_PCMSYCHM_DIR BIT(15) /* HDaudio stream direction */
|
||||
|
||||
/* SHIM2 vendor-specific registers */
|
||||
#define SDW_SHIM2_INTEL_VS_LVSCTL 0x04
|
||||
#define SDW_SHIM2_INTEL_VS_LVSCTL_FCG BIT(26)
|
||||
#define SDW_SHIM2_INTEL_VS_LVSCTL_MLCS GENMASK(29, 27)
|
||||
#define SDW_SHIM2_INTEL_VS_LVSCTL_DCGD BIT(30)
|
||||
#define SDW_SHIM2_INTEL_VS_LVSCTL_ICGD BIT(31)
|
||||
|
||||
#define SDW_SHIM2_MLCS_XTAL_CLK 0x0
|
||||
#define SDW_SHIM2_MLCS_CARDINAL_CLK 0x1
|
||||
#define SDW_SHIM2_MLCS_AUDIO_PLL_CLK 0x2
|
||||
#define SDW_SHIM2_MLCS_MCLK_INPUT_CLK 0x3
|
||||
#define SDW_SHIM2_MLCS_WOV_RING_OSC_CLK 0x4
|
||||
|
||||
#define SDW_SHIM2_INTEL_VS_WAKEEN 0x08
|
||||
#define SDW_SHIM2_INTEL_VS_WAKEEN_PWE BIT(0)
|
||||
|
||||
#define SDW_SHIM2_INTEL_VS_WAKESTS 0x0A
|
||||
#define SDW_SHIM2_INTEL_VS_WAKEEN_PWS BIT(0)
|
||||
|
||||
#define SDW_SHIM2_INTEL_VS_IOCTL 0x0C
|
||||
#define SDW_SHIM2_INTEL_VS_IOCTL_MIF BIT(0)
|
||||
#define SDW_SHIM2_INTEL_VS_IOCTL_CO BIT(1)
|
||||
#define SDW_SHIM2_INTEL_VS_IOCTL_COE BIT(2)
|
||||
#define SDW_SHIM2_INTEL_VS_IOCTL_DO BIT(3)
|
||||
#define SDW_SHIM2_INTEL_VS_IOCTL_DOE BIT(4)
|
||||
#define SDW_SHIM2_INTEL_VS_IOCTL_BKE BIT(5)
|
||||
#define SDW_SHIM2_INTEL_VS_IOCTL_WPDD BIT(6)
|
||||
#define SDW_SHIM2_INTEL_VS_IOCTL_ODC BIT(7)
|
||||
#define SDW_SHIM2_INTEL_VS_IOCTL_CIBD BIT(8)
|
||||
#define SDW_SHIM2_INTEL_VS_IOCTL_DIBD BIT(9)
|
||||
#define SDW_SHIM2_INTEL_VS_IOCTL_HAMIFD BIT(10)
|
||||
|
||||
#define SDW_SHIM2_INTEL_VS_ACTMCTL 0x0E
|
||||
#define SDW_SHIM2_INTEL_VS_ACTMCTL_DACTQE BIT(0)
|
||||
#define SDW_SHIM2_INTEL_VS_ACTMCTL_DODS BIT(1)
|
||||
#define SDW_SHIM2_INTEL_VS_ACTMCTL_DODSE BIT(2)
|
||||
#define SDW_SHIM2_INTEL_VS_ACTMCTL_DOAIS GENMASK(4, 3)
|
||||
#define SDW_SHIM2_INTEL_VS_ACTMCTL_DOAISE BIT(5)
|
||||
|
||||
/**
|
||||
* struct sdw_intel_stream_params_data: configuration passed during
|
||||
* the @params_stream callback, e.g. for interaction with DSP
|
||||
* firmware.
|
||||
*/
|
||||
struct sdw_intel_stream_params_data {
|
||||
int stream;
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_soc_dai *dai;
|
||||
struct snd_pcm_hw_params *hw_params;
|
||||
int link_id;
|
||||
|
@ -120,7 +195,7 @@ struct sdw_intel_stream_params_data {
|
|||
* firmware.
|
||||
*/
|
||||
struct sdw_intel_stream_free_data {
|
||||
int stream;
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_soc_dai *dai;
|
||||
int link_id;
|
||||
};
|
||||
|
@ -134,7 +209,7 @@ struct sdw_intel_ops {
|
|||
struct sdw_intel_stream_params_data *params_data);
|
||||
int (*free_stream)(struct device *dev,
|
||||
struct sdw_intel_stream_free_data *free_data);
|
||||
int (*trigger)(struct snd_soc_dai *dai, int cmd, int stream);
|
||||
int (*trigger)(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -194,6 +269,8 @@ struct sdw_intel_slave_id {
|
|||
struct sdw_slave_id id;
|
||||
};
|
||||
|
||||
struct hdac_bus;
|
||||
|
||||
/**
|
||||
* struct sdw_intel_ctx - context allocated by the controller
|
||||
* driver probe
|
||||
|
@ -248,6 +325,10 @@ struct sdw_intel_ctx {
|
|||
* DSP driver. The quirks are common for all links for now.
|
||||
* @shim_base: sdw shim base.
|
||||
* @alh_base: sdw alh base.
|
||||
* @ext: extended HDaudio link support
|
||||
* @hbus: hdac_bus pointer, needed for power management
|
||||
* @eml_lock: mutex protecting shared registers in the HDaudio multi-link
|
||||
* space
|
||||
*/
|
||||
struct sdw_intel_res {
|
||||
const struct sdw_intel_hw_ops *hw_ops;
|
||||
|
@ -262,6 +343,9 @@ struct sdw_intel_res {
|
|||
u32 clock_stop_quirks;
|
||||
u32 shim_base;
|
||||
u32 alh_base;
|
||||
bool ext;
|
||||
struct hdac_bus *hbus;
|
||||
struct mutex *eml_lock;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -315,6 +399,7 @@ struct sdw_intel;
|
|||
* @sync_go: helper for multi-link synchronization
|
||||
* @sync_check_cmdsync_unlocked: helper for multi-link synchronization
|
||||
* and bank switch - shim_lock is assumed to be locked at higher level
|
||||
* @program_sdi: helper for codec command/control based on dev_num
|
||||
*/
|
||||
struct sdw_intel_hw_ops {
|
||||
void (*debugfs_init)(struct sdw_intel *sdw);
|
||||
|
@ -341,8 +426,11 @@ struct sdw_intel_hw_ops {
|
|||
int (*sync_go_unlocked)(struct sdw_intel *sdw);
|
||||
int (*sync_go)(struct sdw_intel *sdw);
|
||||
bool (*sync_check_cmdsync_unlocked)(struct sdw_intel *sdw);
|
||||
|
||||
void (*program_sdi)(struct sdw_intel *sdw, int dev_num);
|
||||
};
|
||||
|
||||
extern const struct sdw_intel_hw_ops sdw_intel_cnl_hw_ops;
|
||||
extern const struct sdw_intel_hw_ops sdw_intel_lnl_hw_ops;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -94,7 +94,7 @@ static int sdw_params_stream(struct device *dev,
|
|||
struct sdw_intel_stream_params_data *params_data)
|
||||
{
|
||||
struct snd_soc_dai *d = params_data->dai;
|
||||
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(d, params_data->stream);
|
||||
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(d, params_data->substream->stream);
|
||||
struct snd_sof_dai_config_data data = { 0 };
|
||||
|
||||
data.dai_index = (params_data->link_id << 8) | d->id;
|
||||
|
@ -158,6 +158,7 @@ static int hda_sdw_acpi_scan(struct snd_sof_dev *sdev)
|
|||
|
||||
static int hda_sdw_probe(struct snd_sof_dev *sdev)
|
||||
{
|
||||
const struct sof_intel_dsp_desc *chip;
|
||||
struct sof_intel_hda_dev *hdev;
|
||||
struct sdw_intel_res res;
|
||||
void *sdw;
|
||||
|
@ -166,16 +167,38 @@ static int hda_sdw_probe(struct snd_sof_dev *sdev)
|
|||
|
||||
memset(&res, 0, sizeof(res));
|
||||
|
||||
res.hw_ops = &sdw_intel_cnl_hw_ops;
|
||||
res.mmio_base = sdev->bar[HDA_DSP_BAR];
|
||||
res.shim_base = hdev->desc->sdw_shim_base;
|
||||
res.alh_base = hdev->desc->sdw_alh_base;
|
||||
chip = get_chip_info(sdev->pdata);
|
||||
if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) {
|
||||
res.mmio_base = sdev->bar[HDA_DSP_BAR];
|
||||
res.hw_ops = &sdw_intel_cnl_hw_ops;
|
||||
res.shim_base = hdev->desc->sdw_shim_base;
|
||||
res.alh_base = hdev->desc->sdw_alh_base;
|
||||
res.ext = false;
|
||||
} else {
|
||||
/*
|
||||
* retrieve eml_lock needed to protect shared registers
|
||||
* in the HDaudio multi-link areas
|
||||
*/
|
||||
res.eml_lock = hdac_bus_eml_get_mutex(sof_to_bus(sdev), true,
|
||||
AZX_REG_ML_LEPTR_ID_SDW);
|
||||
if (!res.eml_lock)
|
||||
return -ENODEV;
|
||||
|
||||
res.mmio_base = sdev->bar[HDA_DSP_HDA_BAR];
|
||||
/*
|
||||
* the SHIM and SoundWire register offsets are link-specific
|
||||
* and will be determined when adding auxiliary devices
|
||||
*/
|
||||
res.hw_ops = &sdw_intel_lnl_hw_ops;
|
||||
res.ext = true;
|
||||
}
|
||||
res.irq = sdev->ipc_irq;
|
||||
res.handle = hdev->info.handle;
|
||||
res.parent = sdev->dev;
|
||||
res.ops = &sdw_callback;
|
||||
res.dev = sdev->dev;
|
||||
res.clock_stop_quirks = sdw_clock_stop_quirks;
|
||||
res.hbus = sof_to_bus(sdev);
|
||||
|
||||
/*
|
||||
* ops and arg fields are not populated for now,
|
||||
|
|
|
@ -21,6 +21,7 @@ enum sof_intel_hw_ip_version {
|
|||
SOF_INTEL_CAVS_2_0, /* IceLake, JasperLake */
|
||||
SOF_INTEL_CAVS_2_5, /* TigerLake, AlderLake */
|
||||
SOF_INTEL_ACE_1_0, /* MeteorLake */
|
||||
SOF_INTEL_ACE_2_0, /* LunarLake */
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue