diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-10-07 16:13:55 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-10-07 16:13:55 -0700 |
commit | 881eccbef52563feb4fde0d19d375884798783f7 (patch) | |
tree | 6186c622d558bc0ad8649d1e5dd0128108402311 | |
parent | 33e591dee915832c618cf68bb1058c8e7d296128 (diff) | |
parent | 503ae285944a5e99ad3e0c36852ffe2680288418 (diff) |
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.c | 94 | ||||
-rw-r--r-- | drivers/soundwire/cadence_master.c | 104 | ||||
-rw-r--r-- | drivers/soundwire/dmi-quirks.c | 27 | ||||
-rw-r--r-- | drivers/soundwire/intel.c | 734 | ||||
-rw-r--r-- | drivers/soundwire/intel_init.c | 2 | ||||
-rw-r--r-- | drivers/soundwire/qcom.c | 9 | ||||
-rw-r--r-- | include/linux/soundwire/sdw.h | 4 | ||||
-rw-r--r-- | include/linux/soundwire/sdw_intel.h | 63 |
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 auxiliary_device *auxdev) multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK); if (!multi_link) { dev_dbg(dev, "Multi-link is disabled\n"); - bus->multi_link = false; } else { /* * hardware-based synchronization is required regardless @@ -1374,68 +1576,31 @@ int intel_link_startup(struct auxiliary_device *auxdev) * synchronization is gated by gsync when the multi-master * mode is set. */ - bus->multi_link = true; bus->hw_sync_min_links = 1; } + bus->multi_link = multi_link; /* Initialize shim, controller */ - ret = intel_init(sdw); - if (ret) - goto err_init; - - /* Read the PDI config and initialize cadence PDI */ - intel_pdi_init(sdw, &config); - ret = sdw_cdns_pdi_init(cdns, config); + ret = intel_link_power_up(sdw); if (ret) goto err_init; - intel_pdi_ch_update(sdw); - - ret = sdw_cdns_enable_interrupt(cdns, true); - if (ret < 0) { - dev_err(dev, "cannot enable interrupts\n"); - goto err_init; - } - - /* - * follow recommended programming flows to avoid timeouts when - * gsync is enabled - */ - if (multi_link) - intel_shim_sync_arm(sdw); - - ret = sdw_cdns_init(cdns); - if (ret < 0) { - dev_err(dev, "unable to initialize Cadence IP\n"); - goto err_interrupt; - } - - ret = sdw_cdns_exit_reset(cdns); - if (ret < 0) { - dev_err(dev, "unable to exit bus reset sequence\n"); - goto err_interrupt; - } - - if (multi_link) { - ret = intel_shim_sync_go(sdw); - if (ret < 0) { - dev_err(dev, "sync go failed: %d\n", ret); - goto err_interrupt; - } - } - sdw_cdns_check_self_clearing_bits(cdns, __func__, - true, INTEL_MASTER_RESET_ITERATIONS); - /* Register DAIs */ ret = intel_register_dai(sdw); if (ret) { dev_err(dev, "DAI registration failed: %d\n", ret); - snd_soc_unregister_component(dev); - goto err_interrupt; + goto err_power_up; } intel_debugfs_init(sdw); + /* start bus */ + ret = intel_start_bus(sdw); + if (ret) { + dev_err(dev, "bus start failed: %d\n", ret); + goto err_power_up; + } + /* Enable runtime PM */ if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) { pm_runtime_set_autosuspend_delay(dev, @@ -1480,15 +1645,14 @@ int intel_link_startup(struct auxiliary_device *auxdev) sdw->startup_done = true; return 0; -err_interrupt: - sdw_cdns_enable_interrupt(cdns, false); +err_power_up: + intel_link_power_down(sdw); err_init: return ret; } static void intel_link_remove(struct auxiliary_device *auxdev) { - struct device *dev = &auxdev->dev; struct sdw_cdns *cdns = auxiliary_get_drvdata(auxdev); struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_bus *bus = &cdns->bus; @@ -1501,7 +1665,6 @@ static void intel_link_remove(struct auxiliary_device *auxdev) if (!bus->prop.hw_disabled) { intel_debugfs_exit(sdw); sdw_cdns_enable_interrupt(cdns, false); - snd_soc_unregister_component(dev); } sdw_bus_master_delete(bus); } @@ -1511,8 +1674,6 @@ int intel_link_process_wakeen_event(struct auxiliary_device *auxdev) struct device *dev = &auxdev->dev; struct sdw_intel *sdw; struct sdw_bus *bus; - void __iomem *shim; - u16 wake_sts; sdw = auxiliary_get_drvdata(auxdev); bus = &sdw->cdns.bus; @@ -1523,10 +1684,7 @@ int intel_link_process_wakeen_event(struct auxiliary_device *auxdev) return 0; } - shim = sdw->link_res->shim; - wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); - - if (!(wake_sts & BIT(sdw->instance))) + if (!intel_shim_check_wake(sdw)) return 0; /* disable WAKEEN interrupt ASAP to prevent interrupt flood */ @@ -1554,11 +1712,11 @@ static int intel_resume_child_device(struct device *dev, void *data) struct sdw_slave *slave = dev_to_sdw_dev(dev); if (!slave->probed) { - dev_dbg(dev, "%s: skipping device, no probed driver\n", __func__); + dev_dbg(dev, "skipping device, no probed driver\n"); return 0; } if (!slave->dev_num_sticky) { - dev_dbg(dev, "%s: skipping device, never detected on bus\n", __func__); + dev_dbg(dev, "skipping device, never detected on bus\n"); return 0; } @@ -1644,7 +1802,7 @@ static int __maybe_unused intel_suspend(struct device *dev) } if (pm_runtime_suspended(dev)) { - dev_dbg(dev, "%s: pm_runtime status: suspended\n", __func__); + dev_dbg(dev, "pm_runtime status: suspended\n"); clock_stop_quirks = sdw->link_res->clock_stop_quirks; @@ -1665,20 +1823,12 @@ static int __maybe_unused intel_suspend(struct device *dev) return 0; } - ret = sdw_cdns_enable_interrupt(cdns, false); + ret = intel_stop_bus(sdw, false); if (ret < 0) { - dev_err(dev, "cannot disable interrupts on suspend\n"); - return ret; - } - - ret = intel_link_power_down(sdw); - if (ret) { - dev_err(dev, "Link power down failed: %d\n", ret); + dev_err(dev, "%s: cannot stop bus: %d\n", __func__, ret); return ret; } - intel_shim_wake(sdw, false); - return 0; } @@ -1699,44 +1849,19 @@ static int __maybe_unused intel_suspend_runtime(struct device *dev) clock_stop_quirks = sdw->link_res->clock_stop_quirks; if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) { - - ret = sdw_cdns_enable_interrupt(cdns, false); + ret = intel_stop_bus(sdw, false); if (ret < 0) { - dev_err(dev, "cannot disable interrupts on suspend\n"); - return ret; - } - - ret = intel_link_power_down(sdw); - if (ret) { - dev_err(dev, "Link power down failed: %d\n", ret); + dev_err(dev, "%s: cannot stop bus during teardown: %d\n", + __func__, ret); return ret; } - - intel_shim_wake(sdw, false); - - } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET || - !clock_stop_quirks) { - bool wake_enable = true; - - ret = sdw_cdns_clock_stop(cdns, true); - if (ret < 0) { - dev_err(dev, "cannot enable clock stop on suspend\n"); - wake_enable = false; - } - - ret = sdw_cdns_enable_interrupt(cdns, false); + } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET || !clock_stop_quirks) { + ret = intel_stop_bus(sdw, true); if (ret < 0) { - dev_err(dev, "cannot disable interrupts on suspend\n"); - return ret; - } - - ret = intel_link_power_down(sdw); - if (ret) { - dev_err(dev, "Link power down failed: %d\n", ret); + dev_err(dev, "%s: cannot stop bus during clock_stop: %d\n", + __func__, ret); return ret; } - - intel_shim_wake(sdw, wake_enable); } else { dev_err(dev, "%s clock_stop_quirks %x unsupported\n", __func__, clock_stop_quirks); @@ -1752,7 +1877,6 @@ static int __maybe_unused intel_resume(struct device *dev) struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_bus *bus = &cdns->bus; int link_flags; - bool multi_link; int ret; if (bus->prop.hw_disabled || !sdw->startup_done) { @@ -1762,10 +1886,9 @@ static int __maybe_unused intel_resume(struct device *dev) } link_flags = md_flags >> (bus->link_id * 8); - multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK); if (pm_runtime_suspended(dev)) { - dev_dbg(dev, "%s: pm_runtime status was suspended, forcing active\n", __func__); + dev_dbg(dev, "pm_runtime status was suspended, forcing active\n"); /* follow required sequence from runtime_pm.rst */ pm_runtime_disable(dev); @@ -1779,7 +1902,7 @@ static int __maybe_unused intel_resume(struct device *dev) pm_runtime_idle(dev); } - ret = intel_init(sdw); + ret = intel_link_power_up(sdw); if (ret) { dev_err(dev, "%s failed: %d\n", __func__, ret); return ret; @@ -1791,41 +1914,13 @@ static int __maybe_unused intel_resume(struct device *dev) */ sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET); - 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 (multi_link) - intel_shim_sync_arm(sdw); - - ret = sdw_cdns_init(&sdw->cdns); - if (ret < 0) { - dev_err(dev, "unable to initialize Cadence IP during resume\n"); - return ret; - } - - ret = sdw_cdns_exit_reset(cdns); + ret = intel_start_bus(sdw); if (ret < 0) { - dev_err(dev, "unable to exit bus reset sequence during resume\n"); + dev_err(dev, "cannot start bus during resume\n"); + intel_link_power_down(sdw); return ret; } - if (multi_link) { - ret = intel_shim_sync_go(sdw); - if (ret < 0) { - dev_err(dev, "sync go failed during resume\n"); - return ret; - } - } - sdw_cdns_check_self_clearing_bits(cdns, __func__, - true, INTEL_MASTER_RESET_ITERATIONS); - /* * after system resume, the pm_runtime suspend() may kick in * during the enumeration, before any children device force the @@ -1838,7 +1933,7 @@ static int __maybe_unused intel_resume(struct device *dev) */ pm_runtime_mark_last_busy(dev); - return ret; + return 0; } static int __maybe_unused intel_resume_runtime(struct device *dev) @@ -1847,10 +1942,6 @@ static int __maybe_unused intel_resume_runtime(struct device *dev) struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_bus *bus = &cdns->bus; u32 clock_stop_quirks; - bool clock_stop0; - int link_flags; - bool multi_link; - int status; int ret; if (bus->prop.hw_disabled || !sdw->startup_done) { @@ -1862,15 +1953,12 @@ static int __maybe_unused intel_resume_runtime(struct device *dev) /* unconditionally disable WAKEEN interrupt */ intel_shim_wake(sdw, false); - link_flags = md_flags >> (bus->link_id * 8); - multi_link = !(link_flags & SDW_INTEL_MASTER_DISABLE_MULTI_LINK); - clock_stop_quirks = sdw->link_res->clock_stop_quirks; if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) { - ret = intel_init(sdw); + ret = intel_link_power_up(sdw); if (ret) { - dev_err(dev, "%s failed: %d\n", __func__, ret); + dev_err(dev, "%s: power_up failed after teardown: %d\n", __func__, ret); return ret; } @@ -1880,145 +1968,45 @@ static int __maybe_unused intel_resume_runtime(struct device *dev) */ sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET); - 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 (multi_link) - intel_shim_sync_arm(sdw); - - ret = sdw_cdns_init(&sdw->cdns); - if (ret < 0) { - dev_err(dev, "unable to initialize Cadence IP during resume\n"); - return ret; - } - - ret = sdw_cdns_exit_reset(cdns); + ret = intel_start_bus(sdw); if (ret < 0) { - dev_err(dev, "unable to exit bus reset sequence during resume\n"); + dev_err(dev, "%s: cannot start bus after teardown: %d\n", __func__, ret); + intel_link_power_down(sdw); return ret; } - if (multi_link) { - ret = intel_shim_sync_go(sdw); - if (ret < 0) { - dev_err(dev, "sync go failed during resume\n"); - return ret; - } - } - sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime TEARDOWN", - true, INTEL_MASTER_RESET_ITERATIONS); } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) { - ret = intel_init(sdw); + ret = intel_link_power_up(sdw); if (ret) { - dev_err(dev, "%s failed: %d\n", __func__, ret); + dev_err(dev, "%s: power_up failed after bus reset: %d\n", __func__, ret); return 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 (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); + ret = intel_start_bus_after_reset(sdw); if (ret < 0) { - dev_err(dev, "unable to restart clock during resume\n"); + dev_err(dev, "%s: cannot start bus after reset: %d\n", __func__, ret); + intel_link_power_down(sdw); 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"); - return ret; - } - - if (multi_link) { - ret = intel_shim_sync_go(sdw); - if (ret < 0) { - dev_err(sdw->cdns.dev, "sync go failed during resume\n"); - return ret; - } - } - } - sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime BUS_RESET", - true, INTEL_MASTER_RESET_ITERATIONS); - } else if (!clock_stop_quirks) { - clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns); - if (!clock_stop0) - dev_err(dev, "%s invalid configuration, clock was not stopped", __func__); + intel_check_clock_stop(sdw); - ret = intel_init(sdw); + ret = intel_link_power_up(sdw); if (ret) { - dev_err(dev, "%s failed: %d\n", __func__, ret); + dev_err(dev, "%s: power_up failed: %d\n", __func__, ret); return ret; } - ret = sdw_cdns_enable_interrupt(cdns, true); + ret = intel_start_bus_after_clock_stop(sdw); if (ret < 0) { - dev_err(dev, "cannot enable interrupts during resume\n"); + dev_err(dev, "%s: cannot start bus after clock stop: %d\n", __func__, ret); + intel_link_power_down(sdw); return ret; } - - ret = sdw_cdns_clock_restart(cdns, false); - if (ret < 0) { - dev_err(dev, "unable to resume master during resume\n"); - return ret; - } - - sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime no_quirks", - true, INTEL_MASTER_RESET_ITERATIONS); } else { - dev_err(dev, "%s clock_stop_quirks %x unsupported\n", + dev_err(dev, "%s: clock_stop_quirks %x unsupported\n", __func__, clock_stop_quirks); ret = -EINVAL; } diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 824f4f32d4dc..d091513919df 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -306,7 +306,7 @@ sdw_intel_startup_controller(struct sdw_intel_ctx *ctx) /* Check SNDWLCAP.LCOUNT */ caps = ioread32(ctx->mmio_base + ctx->shim_base + SDW_SHIM_LCAP); - caps &= GENMASK(2, 0); + caps &= SDW_SHIM_LCAP_LCOUNT_MASK; /* Check HW supported vs property value */ if (caps < ctx->count) { diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 3a992a6478c3..b33d5db494a5 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -420,7 +420,7 @@ static int qcom_swrm_get_alert_slave_dev_num(struct qcom_swrm_ctrl *ctrl) ctrl->reg_read(ctrl, SWRM_MCP_SLV_STATUS, &val); - for (dev_num = 0; dev_num <= SDW_MAX_DEVICES; dev_num++) { + for (dev_num = 1; dev_num <= SDW_MAX_DEVICES; dev_num++) { status = (val >> (dev_num * SWRM_MCP_SLV_STATUS_SZ)); if ((status & SWRM_MCP_SLV_STATUS_MASK) == SDW_SLAVE_ALERT) { @@ -440,7 +440,7 @@ static void qcom_swrm_get_device_status(struct qcom_swrm_ctrl *ctrl) ctrl->reg_read(ctrl, SWRM_MCP_SLV_STATUS, &val); ctrl->slave_status = val; - for (i = 0; i <= SDW_MAX_DEVICES; i++) { + for (i = 1; i <= SDW_MAX_DEVICES; i++) { u32 s; s = (val >> (i * 2)); @@ -573,11 +573,10 @@ static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id) break; case SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED: case SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS: - dev_err_ratelimited(swrm->dev, "%s: SWR new slave attached\n", - __func__); + 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_err(swrm->dev, "Slave status not changed %x\n", + dev_dbg(swrm->dev, "Slave status not changed %x\n", slave_status); } else { qcom_swrm_get_device_status(swrm); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 822599957b35..9e4537f409c2 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -892,6 +892,9 @@ struct sdw_master_ops { * meaningful if multi_link is set. If set to 1, hardware-based * synchronization will be used even if a stream only uses a single * SoundWire segment. + * @dev_num_ida_min: if set, defines the minimum values for the IDA + * used to allocate system-unique device numbers. This value needs to be + * identical across all SoundWire bus in the system. */ struct sdw_bus { struct device *dev; @@ -916,6 +919,7 @@ struct sdw_bus { u32 bank_switch_timeout; bool multi_link; int hw_sync_min_links; + int dev_num_ida_min; }; int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h index ec16ae49e6a4..2e9fd91572d4 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -15,32 +15,21 @@ #define SDW_LINK_SIZE 0x10000 /* Intel SHIM Registers Definition */ +/* LCAP */ #define SDW_SHIM_LCAP 0x0 -#define SDW_SHIM_LCTL 0x4 -#define SDW_SHIM_IPPTR 0x8 -#define SDW_SHIM_SYNC 0xC - -#define SDW_SHIM_CTLSCAP(x) (0x010 + 0x60 * (x)) -#define SDW_SHIM_CTLS0CM(x) (0x012 + 0x60 * (x)) -#define SDW_SHIM_CTLS1CM(x) (0x014 + 0x60 * (x)) -#define SDW_SHIM_CTLS2CM(x) (0x016 + 0x60 * (x)) -#define SDW_SHIM_CTLS3CM(x) (0x018 + 0x60 * (x)) -#define SDW_SHIM_PCMSCAP(x) (0x020 + 0x60 * (x)) +#define SDW_SHIM_LCAP_LCOUNT_MASK GENMASK(2, 0) -#define SDW_SHIM_PCMSYCHM(x, y) (0x022 + (0x60 * (x)) + (0x2 * (y))) -#define SDW_SHIM_PCMSYCHC(x, y) (0x042 + (0x60 * (x)) + (0x2 * (y))) -#define SDW_SHIM_PDMSCAP(x) (0x062 + 0x60 * (x)) -#define SDW_SHIM_IOCTL(x) (0x06C + 0x60 * (x)) -#define SDW_SHIM_CTMCTL(x) (0x06E + 0x60 * (x)) - -#define SDW_SHIM_WAKEEN 0x190 -#define SDW_SHIM_WAKESTS 0x192 +/* LCTL */ +#define SDW_SHIM_LCTL 0x4 #define SDW_SHIM_LCTL_SPA BIT(0) #define SDW_SHIM_LCTL_SPA_MASK GENMASK(3, 0) #define SDW_SHIM_LCTL_CPA BIT(8) #define SDW_SHIM_LCTL_CPA_MASK GENMASK(11, 8) +/* SYNC */ +#define SDW_SHIM_SYNC 0xC + #define SDW_SHIM_SYNC_SYNCPRD_VAL_24 (24000 / SDW_CADENCE_GSYNC_KHZ - 1) #define SDW_SHIM_SYNC_SYNCPRD_VAL_38_4 (38400 / SDW_CADENCE_GSYNC_KHZ - 1) #define SDW_SHIM_SYNC_SYNCPRD GENMASK(14, 0) @@ -49,19 +38,33 @@ #define SDW_SHIM_SYNC_CMDSYNC BIT(16) #define SDW_SHIM_SYNC_SYNCGO BIT(24) +/* Control stream capabililities and channel mask */ +#define SDW_SHIM_CTLSCAP(x) (0x010 + 0x60 * (x)) +#define SDW_SHIM_CTLS0CM(x) (0x012 + 0x60 * (x)) +#define SDW_SHIM_CTLS1CM(x) (0x014 + 0x60 * (x)) +#define SDW_SHIM_CTLS2CM(x) (0x016 + 0x60 * (x)) +#define SDW_SHIM_CTLS3CM(x) (0x018 + 0x60 * (x)) + +/* PCM Stream capabilities */ +#define SDW_SHIM_PCMSCAP(x) (0x020 + 0x60 * (x)) + #define SDW_SHIM_PCMSCAP_ISS GENMASK(3, 0) #define SDW_SHIM_PCMSCAP_OSS GENMASK(7, 4) #define SDW_SHIM_PCMSCAP_BSS GENMASK(12, 8) +/* PCM Stream Channel Map */ +#define SDW_SHIM_PCMSYCHM(x, y) (0x022 + (0x60 * (x)) + (0x2 * (y))) + +/* PCM Stream Channel Count */ +#define SDW_SHIM_PCMSYCHC(x, y) (0x042 + (0x60 * (x)) + (0x2 * (y))) + #define SDW_SHIM_PCMSYCM_LCHN GENMASK(3, 0) #define SDW_SHIM_PCMSYCM_HCHN GENMASK(7, 4) #define SDW_SHIM_PCMSYCM_STREAM GENMASK(13, 8) #define SDW_SHIM_PCMSYCM_DIR BIT(15) -#define SDW_SHIM_PDMSCAP_ISS GENMASK(3, 0) -#define SDW_SHIM_PDMSCAP_OSS GENMASK(7, 4) -#define SDW_SHIM_PDMSCAP_BSS GENMASK(12, 8) -#define SDW_SHIM_PDMSCAP_CPSS GENMASK(15, 13) +/* IO control */ +#define SDW_SHIM_IOCTL(x) (0x06C + 0x60 * (x)) #define SDW_SHIM_IOCTL_MIF BIT(0) #define SDW_SHIM_IOCTL_CO BIT(1) @@ -73,13 +76,23 @@ #define SDW_SHIM_IOCTL_CIBD BIT(8) #define SDW_SHIM_IOCTL_DIBD BIT(9) -#define SDW_SHIM_CTMCTL_DACTQE BIT(0) -#define SDW_SHIM_CTMCTL_DODS BIT(1) -#define SDW_SHIM_CTMCTL_DOAIS GENMASK(4, 3) +/* Wake Enable*/ +#define SDW_SHIM_WAKEEN 0x190 #define SDW_SHIM_WAKEEN_ENABLE BIT(0) + +/* Wake Status */ +#define SDW_SHIM_WAKESTS 0x192 + #define SDW_SHIM_WAKESTS_STATUS BIT(0) +/* AC Timing control */ +#define SDW_SHIM_CTMCTL(x) (0x06E + 0x60 * (x)) + +#define SDW_SHIM_CTMCTL_DACTQE BIT(0) +#define SDW_SHIM_CTMCTL_DODS BIT(1) +#define SDW_SHIM_CTMCTL_DOAIS GENMASK(4, 3) + /* Intel ALH Register definitions */ #define SDW_ALH_STRMZCFG(x) (0x000 + (0x4 * (x))) #define SDW_ALH_NUM_STREAMS 64 |