summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-10-07 16:13:55 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2022-10-07 16:13:55 -0700
commit881eccbef52563feb4fde0d19d375884798783f7 (patch)
tree6186c622d558bc0ad8649d1e5dd0128108402311
parent33e591dee915832c618cf68bb1058c8e7d296128 (diff)
parent503ae285944a5e99ad3e0c36852ffe2680288418 (diff)
downloadlinux-881eccbef52563feb4fde0d19d375884798783f7.tar.gz
linux-881eccbef52563feb4fde0d19d375884798783f7.tar.bz2
linux-881eccbef52563feb4fde0d19d375884798783f7.zip
Merge tag 'soundwire-6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire
Pull soundwire updates from Vinod Koul: "Updates for Intel, Cadence and Qualcomm drivers: - another round of Intel driver cleanup to prepare for future code reorg which is expected in next cycle (Pierre-Louis Bossart) - bus unattach notifications processing during re-enumeration along with Cadence driver updates for this (Richard Fitzgerald) - Qualcomm driver updates to handle device0 status (Srinivas Kandagatla)" * tag 'soundwire-6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire: (42 commits) soundwire: intel: add helper to stop bus soundwire: intel: introduce helpers to start bus soundwire: intel: introduce intel_shim_check_wake() helper soundwire: intel: simplify read ops assignment soundwire: intel: remove intel_init() wrapper soundwire: intel: move shim initialization before power up/down soundwire: intel: remove clock_stop parameter in intel_shim_init() soundwire: intel: move all PDI initialization under intel_register_dai() soundwire: intel: move DAI registration and debugfs init earlier soundwire: intel: simplify flow and use devm_ for DAI registration soundwire: intel: fix error handling on dai registration issues soundwire: cadence: Simplify error paths in cdns_xfer_msg() soundwire: cadence: Fix error check in cdns_xfer_msg() soundwire: cadence: Write to correct address for each FIFO chunk soundwire: bus: Fix wrong port number in sdw_handle_slave_alerts() soundwire: qcom: do not send status of device 0 during alert soundwire: qcom: update status from device id 1 soundwire: cadence: Don't overwrite msg->buf during write commands soundwire: bus: Don't exit early if no device IDs were programmed soundwire: cadence: Fix lost ATTACHED interrupts when enumerating ...
-rw-r--r--drivers/soundwire/bus.c94
-rw-r--r--drivers/soundwire/cadence_master.c104
-rw-r--r--drivers/soundwire/dmi-quirks.c27
-rw-r--r--drivers/soundwire/intel.c734
-rw-r--r--drivers/soundwire/intel_init.c2
-rw-r--r--drivers/soundwire/qcom.c9
-rw-r--r--include/linux/soundwire/sdw.h4
-rw-r--r--include/linux/soundwire/sdw_intel.h63
8 files changed, 557 insertions, 480 deletions
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c
index d95b07896a3e..76515c33e639 100644
--- a/drivers/soundwire/bus.c
+++ b/drivers/soundwire/bus.c
@@ -11,11 +11,12 @@
#include "bus.h"
#include "sysfs_local.h"
-static DEFINE_IDA(sdw_ida);
+static DEFINE_IDA(sdw_bus_ida);
+static DEFINE_IDA(sdw_peripheral_ida);
static int sdw_get_id(struct sdw_bus *bus)
{
- int rc = ida_alloc(&sdw_ida, GFP_KERNEL);
+ int rc = ida_alloc(&sdw_bus_ida, GFP_KERNEL);
if (rc < 0)
return rc;
@@ -75,7 +76,6 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent,
/*
* Initialize multi_link flag
- * TODO: populate this flag by reading property from FW node
*/
bus->multi_link = false;
if (bus->ops->read_prop) {
@@ -157,9 +157,11 @@ static int sdw_delete_slave(struct device *dev, void *data)
mutex_lock(&bus->bus_lock);
- if (slave->dev_num) /* clear dev_num if assigned */
+ if (slave->dev_num) { /* clear dev_num if assigned */
clear_bit(slave->dev_num, bus->assigned);
-
+ if (bus->dev_num_ida_min)
+ ida_free(&sdw_peripheral_ida, slave->dev_num);
+ }
list_del_init(&slave->node);
mutex_unlock(&bus->bus_lock);
@@ -179,7 +181,7 @@ void sdw_bus_master_delete(struct sdw_bus *bus)
sdw_master_device_del(bus);
sdw_bus_debugfs_exit(bus);
- ida_free(&sdw_ida, bus->id);
+ ida_free(&sdw_bus_ida, bus->id);
}
EXPORT_SYMBOL(sdw_bus_master_delete);
@@ -671,10 +673,18 @@ static int sdw_get_device_num(struct sdw_slave *slave)
{
int bit;
- bit = find_first_zero_bit(slave->bus->assigned, SDW_MAX_DEVICES);
- if (bit == SDW_MAX_DEVICES) {
- bit = -ENODEV;
- goto err;
+ if (slave->bus->dev_num_ida_min) {
+ bit = ida_alloc_range(&sdw_peripheral_ida,
+ slave->bus->dev_num_ida_min, SDW_MAX_DEVICES,
+ GFP_KERNEL);
+ if (bit < 0)
+ goto err;
+ } else {
+ bit = find_first_zero_bit(slave->bus->assigned, SDW_MAX_DEVICES);
+ if (bit == SDW_MAX_DEVICES) {
+ bit = -ENODEV;
+ goto err;
+ }
}
/*
@@ -751,7 +761,7 @@ void sdw_extract_slave_id(struct sdw_bus *bus,
}
EXPORT_SYMBOL(sdw_extract_slave_id);
-static int sdw_program_device_num(struct sdw_bus *bus)
+static int sdw_program_device_num(struct sdw_bus *bus, bool *programmed)
{
u8 buf[SDW_NUM_DEV_ID_REGISTERS] = {0};
struct sdw_slave *slave, *_s;
@@ -761,6 +771,8 @@ static int sdw_program_device_num(struct sdw_bus *bus)
int count = 0, ret;
u64 addr;
+ *programmed = false;
+
/* No Slave, so use raw xfer api */
ret = sdw_fill_msg(&msg, NULL, SDW_SCP_DEVID_0,
SDW_NUM_DEV_ID_REGISTERS, 0, SDW_MSG_FLAG_READ, buf);
@@ -796,6 +808,16 @@ static int sdw_program_device_num(struct sdw_bus *bus)
found = true;
/*
+ * To prevent skipping state-machine stages don't
+ * program a device until we've seen it UNATTACH.
+ * Must return here because no other device on #0
+ * can be detected until this one has been
+ * assigned a device ID.
+ */
+ if (slave->status != SDW_SLAVE_UNATTACHED)
+ return 0;
+
+ /*
* Assign a new dev_num to this Slave and
* not mark it present. It will be marked
* present after it reports ATTACHED on new
@@ -809,6 +831,8 @@ static int sdw_program_device_num(struct sdw_bus *bus)
return ret;
}
+ *programmed = true;
+
break;
}
}
@@ -848,13 +872,13 @@ static void sdw_modify_slave_status(struct sdw_slave *slave,
mutex_lock(&bus->bus_lock);
dev_vdbg(bus->dev,
- "%s: changing status slave %d status %d new status %d\n",
- __func__, slave->dev_num, slave->status, status);
+ "changing status slave %d status %d new status %d\n",
+ slave->dev_num, slave->status, status);
if (status == SDW_SLAVE_UNATTACHED) {
dev_dbg(&slave->dev,
- "%s: initializing enumeration and init completion for Slave %d\n",
- __func__, slave->dev_num);
+ "initializing enumeration and init completion for Slave %d\n",
+ slave->dev_num);
init_completion(&slave->enumeration_complete);
init_completion(&slave->initialization_complete);
@@ -862,8 +886,8 @@ static void sdw_modify_slave_status(struct sdw_slave *slave,
} else if ((status == SDW_SLAVE_ATTACHED) &&
(slave->status == SDW_SLAVE_UNATTACHED)) {
dev_dbg(&slave->dev,
- "%s: signaling enumeration completion for Slave %d\n",
- __func__, slave->dev_num);
+ "signaling enumeration completion for Slave %d\n",
+ slave->dev_num);
complete(&slave->enumeration_complete);
}
@@ -1630,7 +1654,7 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
port = buf2[0] & SDW_SCP_INTSTAT2_PORT4_10;
for_each_set_bit(bit, &port, 8) {
/* scp2 ports start from 4 */
- port_num = bit + 3;
+ port_num = bit + 4;
sdw_handle_port_interrupt(slave,
port_num,
&port_status[port_num]);
@@ -1642,7 +1666,7 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
port = buf2[1] & SDW_SCP_INTSTAT3_PORT11_14;
for_each_set_bit(bit, &port, 8) {
/* scp3 ports start from 11 */
- port_num = bit + 10;
+ port_num = bit + 11;
sdw_handle_port_interrupt(slave,
port_num,
&port_status[port_num]);
@@ -1768,7 +1792,7 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
{
enum sdw_slave_status prev_status;
struct sdw_slave *slave;
- bool attached_initializing;
+ bool attached_initializing, id_programmed;
int i, ret = 0;
/* first check if any Slaves fell off the bus */
@@ -1789,19 +1813,33 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
dev_warn(&slave->dev, "Slave %d state check1: UNATTACHED, status was %d\n",
i, slave->status);
sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED);
+
+ /* Ensure driver knows that peripheral unattached */
+ ret = sdw_update_slave_status(slave, status[i]);
+ if (ret < 0)
+ dev_warn(&slave->dev, "Update Slave status failed:%d\n", ret);
}
}
if (status[0] == SDW_SLAVE_ATTACHED) {
dev_dbg(bus->dev, "Slave attached, programming device number\n");
- ret = sdw_program_device_num(bus);
- if (ret < 0)
- dev_err(bus->dev, "Slave attach failed: %d\n", ret);
+
/*
- * programming a device number will have side effects,
- * so we deal with other devices at a later time
+ * Programming a device number will have side effects,
+ * so we deal with other devices at a later time.
+ * This relies on those devices reporting ATTACHED, which will
+ * trigger another call to this function. This will only
+ * happen if at least one device ID was programmed.
+ * Error returns from sdw_program_device_num() are currently
+ * ignored because there's no useful recovery that can be done.
+ * Returning the error here could result in the current status
+ * of other devices not being handled, because if no device IDs
+ * were programmed there's nothing to guarantee a status change
+ * to trigger another call to this function.
*/
- return ret;
+ sdw_program_device_num(bus, &id_programmed);
+ if (id_programmed)
+ return 0;
}
/* Continue to check other slave statuses */
@@ -1870,8 +1908,8 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
"Update Slave status failed:%d\n", ret);
if (attached_initializing) {
dev_dbg(&slave->dev,
- "%s: signaling initialization completion for Slave %d\n",
- __func__, slave->dev_num);
+ "signaling initialization completion for Slave %d\n",
+ slave->dev_num);
complete(&slave->initialization_complete);
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c
index 615b0b63a3e1..93929f19d083 100644
--- a/drivers/soundwire/cadence_master.c
+++ b/drivers/soundwire/cadence_master.c
@@ -544,9 +544,12 @@ cdns_fill_msg_resp(struct sdw_cdns *cdns,
return SDW_CMD_IGNORED;
}
- /* fill response */
- for (i = 0; i < count; i++)
- msg->buf[i + offset] = FIELD_GET(CDNS_MCP_RESP_RDATA, cdns->response_buf[i]);
+ if (msg->flags == SDW_MSG_FLAG_READ) {
+ /* fill response */
+ for (i = 0; i < count; i++)
+ msg->buf[i + offset] = FIELD_GET(CDNS_MCP_RESP_RDATA,
+ cdns->response_buf[i]);
+ }
return SDW_CMD_OK;
}
@@ -566,7 +569,7 @@ _cdns_xfer_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int cmd,
}
base = CDNS_MCP_CMD_BASE;
- addr = msg->addr;
+ addr = msg->addr + offset;
for (i = 0; i < count; i++) {
data = FIELD_PREP(CDNS_MCP_CMD_DEV_ADDR, msg->dev_num);
@@ -705,18 +708,15 @@ cdns_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg)
for (i = 0; i < msg->len / CDNS_MCP_CMD_LEN; i++) {
ret = _cdns_xfer_msg(cdns, msg, cmd, i * CDNS_MCP_CMD_LEN,
CDNS_MCP_CMD_LEN, false);
- if (ret < 0)
- goto exit;
+ if (ret != SDW_CMD_OK)
+ return ret;
}
if (!(msg->len % CDNS_MCP_CMD_LEN))
- goto exit;
-
- ret = _cdns_xfer_msg(cdns, msg, cmd, i * CDNS_MCP_CMD_LEN,
- msg->len % CDNS_MCP_CMD_LEN, false);
+ return SDW_CMD_OK;
-exit:
- return ret;
+ return _cdns_xfer_msg(cdns, msg, cmd, i * CDNS_MCP_CMD_LEN,
+ msg->len % CDNS_MCP_CMD_LEN, false);
}
EXPORT_SYMBOL(cdns_xfer_msg);
@@ -790,6 +790,7 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns,
enum sdw_slave_status status[SDW_MAX_DEVICES + 1];
bool is_slave = false;
u32 mask;
+ u32 val;
int i, set_status;
memset(status, 0, sizeof(status));
@@ -797,41 +798,38 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns,
for (i = 0; i <= SDW_MAX_DEVICES; i++) {
mask = (slave_intstat >> (i * CDNS_MCP_SLAVE_STATUS_NUM)) &
CDNS_MCP_SLAVE_STATUS_BITS;
- if (!mask)
- continue;
- is_slave = true;
set_status = 0;
- if (mask & CDNS_MCP_SLAVE_INTSTAT_RESERVED) {
- status[i] = SDW_SLAVE_RESERVED;
- set_status++;
- }
-
- if (mask & CDNS_MCP_SLAVE_INTSTAT_ATTACHED) {
- status[i] = SDW_SLAVE_ATTACHED;
- set_status++;
- }
+ if (mask) {
+ is_slave = true;
- if (mask & CDNS_MCP_SLAVE_INTSTAT_ALERT) {
- status[i] = SDW_SLAVE_ALERT;
- set_status++;
- }
+ if (mask & CDNS_MCP_SLAVE_INTSTAT_RESERVED) {
+ status[i] = SDW_SLAVE_RESERVED;
+ set_status++;
+ }
- if (mask & CDNS_MCP_SLAVE_INTSTAT_NPRESENT) {
- status[i] = SDW_SLAVE_UNATTACHED;
- set_status++;
- }
+ if (mask & CDNS_MCP_SLAVE_INTSTAT_ATTACHED) {
+ status[i] = SDW_SLAVE_ATTACHED;
+ set_status++;
+ }
- /* first check if Slave reported multiple status */
- if (set_status > 1) {
- u32 val;
+ if (mask & CDNS_MCP_SLAVE_INTSTAT_ALERT) {
+ status[i] = SDW_SLAVE_ALERT;
+ set_status++;
+ }
- dev_warn_ratelimited(cdns->dev,
- "Slave %d reported multiple Status: %d\n",
- i, mask);
+ if (mask & CDNS_MCP_SLAVE_INTSTAT_NPRESENT) {
+ status[i] = SDW_SLAVE_UNATTACHED;
+ set_status++;
+ }
+ }
- /* check latest status extracted from PING commands */
+ /*
+ * check that there was a single reported Slave status and when
+ * there is not use the latest status extracted from PING commands
+ */
+ if (set_status != 1) {
val = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT);
val >>= (i * 2);
@@ -850,11 +848,6 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns,
status[i] = SDW_SLAVE_RESERVED;
break;
}
-
- dev_warn_ratelimited(cdns->dev,
- "Slave %d status updated to %d\n",
- i, status[i]);
-
}
}
@@ -969,9 +962,22 @@ static void cdns_update_slave_status_work(struct work_struct *work)
u32 device0_status;
int retry_count = 0;
+ /*
+ * Clear main interrupt first so we don't lose any assertions
+ * that happen during this function.
+ */
+ cdns_writel(cdns, CDNS_MCP_INTSTAT, CDNS_MCP_INT_SLAVE_MASK);
+
slave0 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT0);
slave1 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT1);
+ /*
+ * Clear the bits before handling so we don't lose any
+ * bits that re-assert.
+ */
+ cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT0, slave0);
+ cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT1, slave1);
+
/* combine the two status */
slave_intstat = ((u64)slave1 << 32) | slave0;
@@ -979,8 +985,6 @@ static void cdns_update_slave_status_work(struct work_struct *work)
update_status:
cdns_update_slave_status(cdns, slave_intstat);
- cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT0, slave0);
- cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT1, slave1);
/*
* When there is more than one peripheral per link, it's
@@ -997,6 +1001,11 @@ update_status:
* attention with PING commands. There is no need to check for
* ALERTS since they are not allowed until a non-zero
* device_number is assigned.
+ *
+ * Do not clear the INTSTAT0/1. While looping to enumerate devices on
+ * #0 there could be status changes on other devices - these must
+ * be kept in the INTSTAT so they can be handled when all #0 devices
+ * have been handled.
*/
device0_status = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT);
@@ -1016,8 +1025,7 @@ update_status:
}
}
- /* clear and unmask Slave interrupt now */
- cdns_writel(cdns, CDNS_MCP_INTSTAT, CDNS_MCP_INT_SLAVE_MASK);
+ /* unmask Slave interrupt now */
cdns_updatel(cdns, CDNS_MCP_INTMASK,
CDNS_MCP_INT_SLAVE_MASK, CDNS_MCP_INT_SLAVE_MASK);
diff --git a/drivers/soundwire/dmi-quirks.c b/drivers/soundwire/dmi-quirks.c
index 747983743a14..f81cdd83ec26 100644
--- a/drivers/soundwire/dmi-quirks.c
+++ b/drivers/soundwire/dmi-quirks.c
@@ -55,7 +55,26 @@ static const struct adr_remap dell_sku_0A3E[] = {
{}
};
+/*
+ * The HP Omen 16-k0005TX does not expose the correct version of RT711 on link0
+ * and does not expose a RT1316 on link3
+ */
+static const struct adr_remap hp_omen_16[] = {
+ /* rt711-sdca on link0 */
+ {
+ 0x000020025d071100ull,
+ 0x000030025d071101ull
+ },
+ /* rt1316-sdca on link3 */
+ {
+ 0x000120025d071100ull,
+ 0x000330025d131601ull
+ },
+ {}
+};
+
static const struct dmi_system_id adr_remap_quirk_table[] = {
+ /* TGL devices */
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "HP"),
@@ -78,6 +97,14 @@ static const struct dmi_system_id adr_remap_quirk_table[] = {
},
.driver_data = (void *)dell_sku_0A3E,
},
+ /* ADL devices */
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "OMEN by HP Gaming Laptop 16-k0xxx"),
+ },
+ .driver_data = (void *)hp_omen_16,
+ },
{}
};
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c
index a5965e8827b9..244209358784 100644
--- a/drivers/soundwire/intel.c
+++ b/drivers/soundwire/intel.c
@@ -22,6 +22,9 @@
#include "bus.h"
#include "intel.h"
+/* IDA min selected to avoid conflicts with HDaudio/iDISP SDI values */
+#define INTEL_DEV_NUM_IDA_MIN 4
+
#define INTEL_MASTER_SUSPEND_DELAY_MS 3000
#define INTEL_MASTER_RESET_ITERATIONS 10
@@ -135,7 +138,7 @@ static int intel_reg_show(struct seq_file *s_file, void *data)
if (!buf)
return -ENOMEM;
- links = intel_readl(s, SDW_SHIM_LCAP) & GENMASK(2, 0);
+ links = intel_readl(s, SDW_SHIM_LCAP) & SDW_SHIM_LCAP_LCOUNT_MASK;
ret = scnprintf(buf, RD_BUF, "Register Value\n");
ret += scnprintf(buf + ret, RD_BUF - ret, "\nShim\n");
@@ -167,9 +170,8 @@ static int intel_reg_show(struct seq_file *s_file, void *data)
ret += intel_sprintf(s, false, buf, ret,
SDW_SHIM_PCMSYCHC(i, j));
}
- ret += scnprintf(buf + ret, RD_BUF - ret, "\n PDMSCAP, IOCTL, CTMCTL\n");
+ ret += scnprintf(buf + ret, RD_BUF - ret, "\n IOCTL, CTMCTL\n");
- ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PDMSCAP(i));
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_IOCTL(i));
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTMCTL(i));
}
@@ -258,86 +260,6 @@ static void intel_debugfs_exit(struct sdw_intel *sdw) {}
/*
* shim ops
*/
-
-static int intel_link_power_up(struct sdw_intel *sdw)
-{
- unsigned int link_id = sdw->instance;
- void __iomem *shim = sdw->link_res->shim;
- u32 *shim_mask = sdw->link_res->shim_mask;
- struct sdw_bus *bus = &sdw->cdns.bus;
- struct sdw_master_prop *prop = &bus->prop;
- u32 spa_mask, cpa_mask;
- u32 link_control;
- int ret = 0;
- u32 syncprd;
- u32 sync_reg;
-
- mutex_lock(sdw->link_res->shim_lock);
-
- /*
- * The hardware relies on an internal counter, typically 4kHz,
- * to generate the SoundWire SSP - which defines a 'safe'
- * synchronization point between commands and audio transport
- * and allows for multi link synchronization. The SYNCPRD value
- * is only dependent on the oscillator clock provided to
- * the IP, so adjust based on _DSD properties reported in DSDT
- * tables. The values reported are based on either 24MHz
- * (CNL/CML) or 38.4 MHz (ICL/TGL+).
- */
- if (prop->mclk_freq % 6000000)
- syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4;
- else
- syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24;
-
- if (!*shim_mask) {
- dev_dbg(sdw->cdns.dev, "%s: powering up all links\n", __func__);
-
- /* we first need to program the SyncPRD/CPU registers */
- dev_dbg(sdw->cdns.dev,
- "%s: first link up, programming SYNCPRD\n", __func__);
-
- /* set SyncPRD period */
- sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
- u32p_replace_bits(&sync_reg, syncprd, SDW_SHIM_SYNC_SYNCPRD);
-
- /* Set SyncCPU bit */
- sync_reg |= SDW_SHIM_SYNC_SYNCCPU;
- intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
-
- /* Link power up sequence */
- link_control = intel_readl(shim, SDW_SHIM_LCTL);
-
- /* only power-up enabled links */
- spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, sdw->link_res->link_mask);
- cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask);
-
- link_control |= spa_mask;
-
- ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
- if (ret < 0) {
- dev_err(sdw->cdns.dev, "Failed to power up link: %d\n", ret);
- goto out;
- }
-
- /* SyncCPU will change once link is active */
- ret = intel_wait_bit(shim, SDW_SHIM_SYNC,
- SDW_SHIM_SYNC_SYNCCPU, 0);
- if (ret < 0) {
- dev_err(sdw->cdns.dev,
- "Failed to set SHIM_SYNC: %d\n", ret);
- goto out;
- }
- }
-
- *shim_mask |= BIT(link_id);
-
- sdw->cdns.link_up = true;
-out:
- mutex_unlock(sdw->link_res->shim_lock);
-
- return ret;
-}
-
/* this needs to be called with shim_lock */
static void intel_shim_glue_to_master_ip(struct sdw_intel *sdw)
{
@@ -389,15 +311,13 @@ static void intel_shim_master_ip_to_glue(struct sdw_intel *sdw)
/* at this point Integration Glue has full control of the I/Os */
}
-static int intel_shim_init(struct sdw_intel *sdw, bool clock_stop)
+/* this needs to be called with shim_lock */
+static void intel_shim_init(struct sdw_intel *sdw)
{
void __iomem *shim = sdw->link_res->shim;
unsigned int link_id = sdw->instance;
- int ret = 0;
u16 ioctl = 0, act = 0;
- mutex_lock(sdw->link_res->shim_lock);
-
/* Initialize Shim */
ioctl |= SDW_SHIM_IOCTL_BKE;
intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);
@@ -422,10 +342,17 @@ static int intel_shim_init(struct sdw_intel *sdw, bool clock_stop)
act |= SDW_SHIM_CTMCTL_DODS;
intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act);
usleep_range(10, 15);
+}
- mutex_unlock(sdw->link_res->shim_lock);
+static int intel_shim_check_wake(struct sdw_intel *sdw)
+{
+ void __iomem *shim;
+ u16 wake_sts;
- return ret;
+ shim = sdw->link_res->shim;
+ wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS);
+
+ return wake_sts & BIT(sdw->instance);
}
static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable)
@@ -454,6 +381,88 @@ static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable)
mutex_unlock(sdw->link_res->shim_lock);
}
+static int intel_link_power_up(struct sdw_intel *sdw)
+{
+ unsigned int link_id = sdw->instance;
+ void __iomem *shim = sdw->link_res->shim;
+ u32 *shim_mask = sdw->link_res->shim_mask;
+ struct sdw_bus *bus = &sdw->cdns.bus;
+ struct sdw_master_prop *prop = &bus->prop;
+ u32 spa_mask, cpa_mask;
+ u32 link_control;
+ int ret = 0;
+ u32 syncprd;
+ u32 sync_reg;
+
+ mutex_lock(sdw->link_res->shim_lock);
+
+ /*
+ * The hardware relies on an internal counter, typically 4kHz,
+ * to generate the SoundWire SSP - which defines a 'safe'
+ * synchronization point between commands and audio transport
+ * and allows for multi link synchronization. The SYNCPRD value
+ * is only dependent on the oscillator clock provided to
+ * the IP, so adjust based on _DSD properties reported in DSDT
+ * tables. The values reported are based on either 24MHz
+ * (CNL/CML) or 38.4 MHz (ICL/TGL+).
+ */
+ if (prop->mclk_freq % 6000000)
+ syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4;
+ else
+ syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24;
+
+ if (!*shim_mask) {
+ dev_dbg(sdw->cdns.dev, "powering up all links\n");
+
+ /* we first need to program the SyncPRD/CPU registers */
+ dev_dbg(sdw->cdns.dev,
+ "first link up, programming SYNCPRD\n");
+
+ /* set SyncPRD period */
+ sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
+ u32p_replace_bits(&sync_reg, syncprd, SDW_SHIM_SYNC_SYNCPRD);
+
+ /* Set SyncCPU bit */
+ sync_reg |= SDW_SHIM_SYNC_SYNCCPU;
+ intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
+
+ /* Link power up sequence */
+ link_control = intel_readl(shim, SDW_SHIM_LCTL);
+
+ /* only power-up enabled links */
+ spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, sdw->link_res->link_mask);
+ cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask);
+
+ link_control |= spa_mask;
+
+ ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);
+ if (ret < 0) {
+ dev_err(sdw->cdns.dev, "Failed to power up link: %d\n", ret);
+ goto out;
+ }
+
+ /* SyncCPU will change once link is active */
+ ret = intel_wait_bit(shim, SDW_SHIM_SYNC,
+ SDW_SHIM_SYNC_SYNCCPU, 0);
+ if (ret < 0) {
+ dev_err(sdw->cdns.dev,
+ "Failed to set SHIM_SYNC: %d\n", ret);
+ goto out;
+ }
+ }
+
+ *shim_mask |= BIT(link_id);
+
+ sdw->cdns.link_up = true;
+
+ intel_shim_init(sdw);
+
+out:
+ mutex_unlock(sdw->link_res->shim_lock);
+
+ return ret;
+}
+
static int intel_link_power_down(struct sdw_intel *sdw)
{
u32 link_control, spa_mask, cpa_mask;
@@ -476,7 +485,7 @@ static int intel_link_power_down(struct sdw_intel *sdw)
if (!*shim_mask) {
- dev_dbg(sdw->cdns.dev, "%s: powering down all links\n", __func__);
+ dev_dbg(sdw->cdns.dev, "powering down all links\n");
/* Link power down sequence */
link_control = intel_readl(shim, SDW_SHIM_LCTL);
@@ -1169,11 +1178,20 @@ static int intel_create_dai(struct sdw_cdns *cdns,
static int intel_register_dai(struct sdw_intel *sdw)
{
+ 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, ret, 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_ch_update(sdw);
+
/* DAIs are created based on total number of PDIs supported */
num_dai = cdns->pcm.num_pdi;
@@ -1201,8 +1219,208 @@ static int intel_register_dai(struct sdw_intel *sdw)
if (ret)
return ret;
- return snd_soc_register_component(cdns->dev, &dai_component,
- dais, num_dai);
+ return devm_snd_soc_register_component(cdns->dev, &dai_component,
+ dais, num_dai);
+}
+
+static int intel_start_bus(struct sdw_intel *sdw)
+{
+ struct device *dev = sdw->cdns.dev;
+ struct sdw_cdns *cdns = &sdw->cdns;
+ 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
+ */
+ if (bus->multi_link)
+ intel_shim_sync_arm(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;
+ }
+
+ 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;
+ }
+
+ if (bus->multi_link) {
+ ret = intel_shim_sync_go(sdw);
+ if (ret < 0) {
+ dev_err(dev, "%s: sync go failed: %d\n", __func__, ret);
+ goto err_interrupt;
+ }
+ }
+ 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;
+}
+
+static int intel_start_bus_after_reset(struct sdw_intel *sdw)
+{
+ struct device *dev = sdw->cdns.dev;
+ struct sdw_cdns *cdns = &sdw->cdns;
+ struct sdw_bus *bus = &cdns->bus;
+ bool clock_stop0;
+ int status;
+ int ret;
+
+ /*
+ * An exception condition occurs for the CLK_STOP_BUS_RESET
+ * case if one or more masters remain active. In this condition,
+ * all the masters are powered on for they are in the same power
+ * domain. Master can preserve its context for clock stop0, so
+ * there is no need to clear slave status and reset bus.
+ */
+ clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns);
+
+ if (!clock_stop0) {
+
+ /*
+ * make sure all Slaves are tagged as UNATTACHED and
+ * provide reason for reinitialization
+ */
+
+ 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
+ */
+ if (bus->multi_link)
+ intel_shim_sync_arm(sdw);
+
+ /*
+ * Re-initialize the IP since it was powered-off
+ */
+ sdw_cdns_init(&sdw->cdns);
+
+ } else {
+ ret = sdw_cdns_enable_interrupt(cdns, true);
+ if (ret < 0) {
+ dev_err(dev, "cannot enable interrupts during resume\n");
+ return ret;
+ }
+ }
+
+ ret = sdw_cdns_clock_restart(cdns, !clock_stop0);
+ if (ret < 0) {
+ dev_err(dev, "unable to restart clock during resume\n");
+ goto err_interrupt;
+ }
+
+ 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;
+ }
+
+ if (bus->multi_link) {
+ ret = intel_shim_sync_go(sdw);
+ if (ret < 0) {
+ dev_err(sdw->cdns.dev, "sync go failed during resume\n");
+ goto err_interrupt;
+ }
+ }
+ }
+ 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;
+}
+
+static void intel_check_clock_stop(struct sdw_intel *sdw)
+{
+ struct device *dev = sdw->cdns.dev;
+ bool clock_stop0;
+
+ clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns);
+ if (!clock_stop0)
+ dev_err(dev, "%s: invalid configuration, clock was not stopped\n", __func__);
+}
+
+static int intel_start_bus_after_clock_stop(struct sdw_intel *sdw)
+{
+ struct device *dev = sdw->cdns.dev;
+ struct sdw_cdns *cdns = &sdw->cdns;
+ 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;
+ }
+
+ 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);
+
+ return 0;
+}
+
+static int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop)
+{
+ struct device *dev = sdw->cdns.dev;
+ struct sdw_cdns *cdns = &sdw->cdns;
+ bool wake_enable = false;
+ int ret;
+
+ if (clock_stop) {
+ ret = sdw_cdns_clock_stop(cdns, true);
+ if (ret < 0)
+ dev_err(dev, "%s: cannot stop clock: %d\n", __func__, ret);
+ else
+ wake_enable = true;
+ }
+
+ ret = sdw_cdns_enable_interrupt(cdns, false);
+ if (ret < 0) {
+ dev_err(dev, "%s: cannot disable interrupts: %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = intel_link_power_down(sdw);
+ if (ret) {
+ dev_err(dev, "%s: Link power down failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ intel_shim_wake(sdw, wake_enable);
+
+ return 0;
}
static int sdw_master_read_intel_prop(struct sdw_bus *bus)
@@ -1254,7 +1472,7 @@ static int intel_prop_read(struct sdw_bus *bus)
}
static struct sdw_master_ops sdw_intel_ops = {
- .read_prop = sdw_master_read_prop,
+ .read_prop = intel_prop_read,
.override_adr = sdw_dmi_override_adr,
.xfer_msg = cdns_xfer_msg,
.xfer_msg_defer = cdns_xfer_msg_defer,
@@ -1265,20 +1483,6 @@ static struct sdw_master_ops sdw_intel_ops = {
.read_ping_status = cdns_read_ping_status,
};
-static int intel_init(struct sdw_intel *sdw)
-{
- bool clock_stop;
-
- /* Initialize shim and controller */
- intel_link_power_up(sdw);
-
- clock_stop = sdw_cdns_is_clock_stop(&sdw->cdns);
-
- intel_shim_init(sdw, clock_stop);
-
- return 0;
-}
-
/*
* probe and init (aux_dev_id argument is required by function prototype but not used)
*/
@@ -1308,11 +1512,11 @@ static int intel_link_probe(struct auxiliary_device *auxdev,
cdns->msg_count = 0;
bus->link_id = auxdev->id;
+ bus->dev_num_ida_min = INTEL_DEV_NUM_IDA_MIN;
sdw_cdns_probe(cdns);
- /* Set property read ops */
- sdw_intel_ops.read_prop = intel_prop_read;
+ /* Set ops */
bus->ops = &sdw_intel_ops;
/* set driver data, accessed by snd_soc_dai_get_drvdata() */
@@ -1345,7 +1549,6 @@ static int intel_link_probe(struct auxiliary_device *auxdev,
int intel_link_startup(struct auxiliary_device *auxdev)
{
- struct sdw_cdns_stream_config config;
struct device *dev = &auxdev->dev;
struct sdw_cdns *cdns = auxiliary_get_drvdata(auxdev);
struct sdw_intel *sdw = cdns_to_intel(cdns);
@@ -1366,7 +1569,6 @@ int intel_link_startup(struct auxili