Commit fe1de551 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 15ac4686 a4857d1a
......@@ -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, &params[i],
port_bo, hstop);
sdw_compute_master_ports(m_rt, &params[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))
......
// 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);
// 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;
}
sdw_cdns_check_self_clearing_bits(cdns, __func__, true, INTEL_MASTER_RESET_ITERATIONS);
return 0;
ret = sdw_cdns_exit_reset(cdns);
if (ret < 0) {
dev_err(dev, "unable to exit bus reset sequence during resume\n");
return ret;
}
err_interrupt:
sdw_cdns_enable_interrupt(cdns, false);
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;
}
void intel_check_clock_stop(struct sdw_intel *sdw)
......@@ -158,21 +170,19 @@ int intel_start_bus_after_clock_stop(struct sdw_intel *sdw)
struct sdw_cdns *cdns = &sdw->cdns;
int ret;
ret = sdw_cdns_enable_interrupt(cdns, true);
ret = sdw_cdns_clock_restart(cdns, false);
if (ret < 0) {
dev_err(dev, "%s: cannot enable interrupts: %d\n", __func__, ret);
dev_err(dev, "%s: unable to restart clock: %d\n", __func__, ret);
return ret;
}
ret = sdw_cdns_clock_restart(cdns, false);
ret = sdw_cdns_enable_interrupt(cdns, true);
if (ret < 0) {
dev_err(dev, "%s: unable to restart clock: %d\n", __func__, ret);
sdw_cdns_enable_interrupt(cdns, false);
dev_err(dev, "%s: cannot enable interrupts: %d\n", __func__, ret);
return ret;
}
sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime no_quirks",
true, INTEL_MASTER_RESET_ITERATIONS);
sdw_cdns_check_self_clearing_bits(cdns, __func__, true, INTEL_MASTER_RESET_ITERATIONS);
return 0;
}
......
......@@ -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;
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) {
......
This diff is collapsed.
......@@ -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,9 +1369,7 @@ 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 *
......@@ -1372,8 +1384,8 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream,
goto restore_params;
}
}
}
program_params:
/* Program params */
ret = sdw_program_params(bus, true);
if (ret < 0) {
......@@ -1382,11 +1394,6 @@ static int _sdw_prepare_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);
......@@ -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,11 +1888,7 @@ 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;
}
if (!m_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",
......@@ -1898,18 +1896,17 @@ int sdw_stream_add_master(struct sdw_bus *bus,
ret = -ENOMEM;
goto unlock;
}
skip_alloc_master_rt:
if (sdw_master_port_allocated(m_rt))
goto skip_alloc_master_port;
alloc_master_rt = true;
}
if (!sdw_master_port_allocated(m_rt)) {
ret = sdw_master_port_alloc(m_rt, num_ports);
if (ret)
goto alloc_error;
stream->m_rt_count++;
skip_alloc_master_port:
}
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,11 +1999,7 @@ 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 (!m_rt) {
/*
* If this API is invoked by Slave first then m_rt is not valid.
* So, allocate m_rt and add Slave to it.
......@@ -2019,30 +2012,28 @@ int sdw_stream_add_slave(struct sdw_slave *slave,
goto unlock;
}
skip_alloc_master_rt:
s_rt = sdw_slave_rt_find(slave, stream);
if (s_rt) {
alloc_slave_rt = false;
goto skip_alloc_slave_rt;
alloc_master_rt = true;
}
s_rt = sdw_slave_rt_find(slave, stream);
if (!s_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;
dev_err(&slave->dev, "Slave runtime alloc failed for stream:%s\n",
stream->name);
ret = -ENOMEM;
goto alloc_error;
}
skip_alloc_slave_rt:
if (sdw_slave_port_allocated(s_rt))
goto skip_port_alloc;
alloc_slave_rt = true;
}
if (!sdw_slave_port_allocated(s_rt)) {
ret = sdw_slave_port_alloc(slave, s_rt, num_ports);
if (ret)
goto alloc_error;
}
skip_port_alloc:
ret = sdw_master_rt_config(m_rt, stream_config);
if (ret)
goto unlock;
......
......@@ -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;
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 */
};
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment