diff options
Diffstat (limited to 'drivers/soundwire')
-rw-r--r-- | drivers/soundwire/bus.c | 27 | ||||
-rw-r--r-- | drivers/soundwire/cadence_master.c | 42 | ||||
-rw-r--r-- | drivers/soundwire/intel.c | 11 | ||||
-rw-r--r-- | drivers/soundwire/qcom.c | 22 | ||||
-rw-r--r-- | drivers/soundwire/stream.c | 1 |
5 files changed, 76 insertions, 27 deletions
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 354d3f89366f..a2bfb0434a67 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -536,11 +536,9 @@ int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) { int ret; - ret = pm_runtime_get_sync(&slave->dev); - if (ret < 0 && ret != -EACCES) { - pm_runtime_put_noidle(&slave->dev); + ret = pm_runtime_resume_and_get(&slave->dev); + if (ret < 0 && ret != -EACCES) return ret; - } ret = sdw_nread_no_pm(slave, addr, count, val); @@ -562,11 +560,9 @@ int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, const u8 *val) { int ret; - ret = pm_runtime_get_sync(&slave->dev); - if (ret < 0 && ret != -EACCES) { - pm_runtime_put_noidle(&slave->dev); + ret = pm_runtime_resume_and_get(&slave->dev); + if (ret < 0 && ret != -EACCES) return ret; - } ret = sdw_nwrite_no_pm(slave, addr, count, val); @@ -1506,10 +1502,9 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) sdw_modify_slave_status(slave, SDW_SLAVE_ALERT); - ret = pm_runtime_get_sync(&slave->dev); + ret = pm_runtime_resume_and_get(&slave->dev); if (ret < 0 && ret != -EACCES) { dev_err(&slave->dev, "Failed to resume device: %d\n", ret); - pm_runtime_put_noidle(&slave->dev); return ret; } @@ -1838,6 +1833,18 @@ int sdw_handle_slave_status(struct sdw_bus *bus, __func__, slave->dev_num); complete(&slave->initialization_complete); + + /* + * If the manager became pm_runtime active, the peripherals will be + * restarted and attach, but their pm_runtime status may remain + * suspended. If the 'update_slave_status' callback initiates + * any sort of deferred processing, this processing would not be + * cancelled on pm_runtime suspend. + * To avoid such zombie states, we queue a request to resume. + * This would be a no-op in case the peripheral was being resumed + * by e.g. the ALSA/ASoC framework. + */ + pm_request_resume(&slave->dev); } } diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 558390af44b6..4fbb19557f5e 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -386,12 +386,11 @@ static int cdns_parity_error_injection(void *data, u64 value) * Resume Master device. If this results in a bus reset, the * Slave devices will re-attach and be re-enumerated. */ - ret = pm_runtime_get_sync(bus->dev); + ret = pm_runtime_resume_and_get(bus->dev); if (ret < 0 && ret != -EACCES) { dev_err_ratelimited(cdns->dev, - "pm_runtime_get_sync failed in %s, ret %d\n", + "pm_runtime_resume_and_get failed in %s, ret %d\n", __func__, ret); - pm_runtime_put_noidle(bus->dev); return ret; } @@ -959,6 +958,8 @@ static void cdns_update_slave_status_work(struct work_struct *work) container_of(work, struct sdw_cdns, work); u32 slave0, slave1; u64 slave_intstat; + u32 device0_status; + int retry_count = 0; slave0 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT0); slave1 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT1); @@ -968,10 +969,45 @@ static void cdns_update_slave_status_work(struct work_struct *work) dev_dbg_ratelimited(cdns->dev, "Slave status change: 0x%llx\n", slave_intstat); +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 + * possible that a deviceB becomes attached after we deal with + * the attachment of deviceA. Since the hardware does a + * logical AND, the attachment of the second device does not + * change the status seen by the driver. + * + * In that case, clearing the registers above would result in + * the deviceB never being detected - until a change of status + * is observed on the bus. + * + * To avoid this race condition, re-check if any device0 needs + * 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. + */ + + device0_status = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT); + device0_status &= 3; + + if (device0_status == SDW_SLAVE_ATTACHED) { + if (retry_count++ < SDW_MAX_DEVICES) { + dev_dbg_ratelimited(cdns->dev, + "Device0 detected after clearing status, iteration %d\n", + retry_count); + slave_intstat = CDNS_MCP_SLAVE_INTSTAT_ATTACHED; + goto update_status; + } else { + dev_err_ratelimited(cdns->dev, + "Device0 detected after %d iterations\n", + retry_count); + } + } + /* clear and unmask Slave interrupt now */ cdns_writel(cdns, CDNS_MCP_INTSTAT, CDNS_MCP_INT_SLAVE_MASK); cdns_updatel(cdns, CDNS_MCP_INTMASK, diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 63101f1ba271..505c5ef061e3 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -799,12 +799,11 @@ static int intel_startup(struct snd_pcm_substream *substream, struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); int ret; - ret = pm_runtime_get_sync(cdns->dev); + ret = pm_runtime_resume_and_get(cdns->dev); if (ret < 0 && ret != -EACCES) { dev_err_ratelimited(cdns->dev, - "pm_runtime_get_sync failed in %s, ret %d\n", + "pm_runtime_resume_and_get failed in %s, ret %d\n", __func__, ret); - pm_runtime_put_noidle(cdns->dev); return ret; } return 0; @@ -1293,6 +1292,9 @@ static int intel_link_probe(struct auxiliary_device *auxdev, /* use generic bandwidth allocation algorithm */ sdw->cdns.bus.compute_params = sdw_compute_params; + /* avoid resuming from pm_runtime suspend if it's not required */ + dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND); + ret = sdw_bus_master_add(bus, dev, dev->fwnode); if (ret) { dev_err(dev, "sdw_bus_master_add fail: %d\n", ret); @@ -1828,6 +1830,9 @@ static int __maybe_unused intel_resume_runtime(struct device *dev) return 0; } + /* 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); diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index da1ad7ebb1aa..22b706350ead 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -105,7 +105,7 @@ #define SWRM_SPECIAL_CMD_ID 0xF #define MAX_FREQ_NUM 1 -#define TIMEOUT_MS (2 * HZ) +#define TIMEOUT_MS 100 #define QCOM_SWRM_MAX_RD_LEN 0x1 #define QCOM_SDW_MAX_PORTS 14 #define DEFAULT_CLK_FREQ 9600000 @@ -510,12 +510,12 @@ static irqreturn_t qcom_swrm_wake_irq_handler(int irq, void *dev_id) struct qcom_swrm_ctrl *swrm = dev_id; int ret; - ret = pm_runtime_get_sync(swrm->dev); + ret = pm_runtime_resume_and_get(swrm->dev); if (ret < 0 && ret != -EACCES) { dev_err_ratelimited(swrm->dev, - "pm_runtime_get_sync failed in %s, ret %d\n", + "pm_runtime_resume_and_get failed in %s, ret %d\n", __func__, ret); - pm_runtime_put_noidle(swrm->dev); + return ret; } if (swrm->wake_irq > 0) { @@ -1058,12 +1058,11 @@ static int qcom_swrm_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai; int ret, i; - ret = pm_runtime_get_sync(ctrl->dev); + ret = pm_runtime_resume_and_get(ctrl->dev); if (ret < 0 && ret != -EACCES) { dev_err_ratelimited(ctrl->dev, - "pm_runtime_get_sync failed in %s, ret %d\n", + "pm_runtime_resume_and_get failed in %s, ret %d\n", __func__, ret); - pm_runtime_put_noidle(ctrl->dev); return ret; } @@ -1252,12 +1251,12 @@ static int swrm_reg_show(struct seq_file *s_file, void *data) struct qcom_swrm_ctrl *swrm = s_file->private; int reg, reg_val, ret; - ret = pm_runtime_get_sync(swrm->dev); + ret = pm_runtime_resume_and_get(swrm->dev); if (ret < 0 && ret != -EACCES) { dev_err_ratelimited(swrm->dev, - "pm_runtime_get_sync failed in %s, ret %d\n", + "pm_runtime_resume_and_get failed in %s, ret %d\n", __func__, ret); - pm_runtime_put_noidle(swrm->dev); + return ret; } for (reg = 0; reg <= SWR_MSTR_MAX_REG_ADDR; reg += 4) { @@ -1452,7 +1451,7 @@ static bool swrm_wait_for_frame_gen_enabled(struct qcom_swrm_ctrl *swrm) } while (retry--); dev_err(swrm->dev, "%s: link status not %s\n", __func__, - comp_sts && SWRM_FRM_GEN_ENABLED ? "connected" : "disconnected"); + comp_sts & SWRM_FRM_GEN_ENABLED ? "connected" : "disconnected"); return false; } @@ -1549,6 +1548,7 @@ static const struct dev_pm_ops swrm_dev_pm_ops = { static const struct of_device_id qcom_swrm_of_match[] = { { .compatible = "qcom,soundwire-v1.3.0", .data = &swrm_v1_3_data }, { .compatible = "qcom,soundwire-v1.5.1", .data = &swrm_v1_5_data }, + { .compatible = "qcom,soundwire-v1.6.0", .data = &swrm_v1_5_data }, {/* sentinel */}, }; diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index f273459b2023..d34150559142 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -822,6 +822,7 @@ static int do_bank_switch(struct sdw_stream_runtime *stream) } else if (multi_link) { dev_err(bus->dev, "Post bank switch ops not implemented\n"); + ret = -EINVAL; goto error; } |