@@ -10,6 +10,7 @@
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/slimbus.h>
@@ -19,6 +20,8 @@
#include <sound/soc.h>
#include "bus.h"
+#define SWRM_COMP_SW_RESET (0x008)
+#define SWRM_COMP_STATUS (0x014)
#define SWRM_COMP_HW_VERSION 0x00
#define SWRM_COMP_CFG_ADDR 0x04
#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK BIT(1)
@@ -408,6 +411,9 @@ static int qcom_swrm_enumerate(struct sdw_bus *bus)
}
}
}
+ pm_runtime_get_sync(ctrl->dev);
+ pm_runtime_mark_last_busy(ctrl->dev);
+ pm_runtime_put_autosuspend(ctrl->dev);
complete(&ctrl->enumeration);
return 0;
@@ -421,6 +427,7 @@ static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id)
u8 devnum = 0;
int ret = IRQ_HANDLED;
+ clk_prepare_enable(swrm->hclk);
swrm->reg_read(swrm, SWRM_INTERRUPT_STATUS, &intr_sts);
intr_sts_masked = intr_sts & swrm->intr_mask;
@@ -529,6 +536,7 @@ static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id)
intr_sts_masked = intr_sts & swrm->intr_mask;
} while (intr_sts_masked);
+ clk_disable_unprepare(swrm->hclk);
return ret;
}
@@ -587,6 +595,8 @@ static enum sdw_command_response qcom_swrm_xfer_msg(struct sdw_bus *bus,
struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus);
int ret, i, len;
+ pm_runtime_get_sync(ctrl->dev);
+
if (msg->flags == SDW_MSG_FLAG_READ) {
for (i = 0; i < msg->len;) {
if ((msg->len - i) < QCOM_SWRM_MAX_RD_LEN)
@@ -598,7 +608,7 @@ static enum sdw_command_response qcom_swrm_xfer_msg(struct sdw_bus *bus,
msg->addr + i, len,
&msg->buf[i]);
if (ret)
- return ret;
+ goto done;
i = i + len;
}
@@ -607,12 +617,20 @@ static enum sdw_command_response qcom_swrm_xfer_msg(struct sdw_bus *bus,
ret = qcom_swrm_cmd_fifo_wr_cmd(ctrl, msg->buf[i],
msg->dev_num,
msg->addr + i);
- if (ret)
- return SDW_CMD_IGNORED;
+ if (ret) {
+ ret = SDW_CMD_IGNORED;
+ goto done;
+ }
}
}
+ pm_runtime_put_autosuspend(ctrl->dev);
+ pm_runtime_mark_last_busy(ctrl->dev);
return SDW_CMD_OK;
+done:
+ pm_runtime_mark_last_busy(ctrl->dev);
+ pm_runtime_put_autosuspend(ctrl->dev);
+ return ret;
}
static int qcom_swrm_pre_bank_switch(struct sdw_bus *bus)
@@ -620,13 +638,19 @@ static int qcom_swrm_pre_bank_switch(struct sdw_bus *bus)
u32 reg = SWRM_MCP_FRAME_CTRL_BANK_ADDR(bus->params.next_bank);
struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus);
u32 val;
+ int ret;
+ pm_runtime_get_sync(ctrl->dev);
ctrl->reg_read(ctrl, reg, &val);
u32p_replace_bits(&val, ctrl->cols_index, SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK);
u32p_replace_bits(&val, ctrl->rows_index, SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK);
- return ctrl->reg_write(ctrl, reg, val);
+ ret = ctrl->reg_write(ctrl, reg, val);
+ pm_runtime_mark_last_busy(ctrl->dev);
+ pm_runtime_put_autosuspend(ctrl->dev);
+
+ return ret;
}
static int qcom_swrm_port_params(struct sdw_bus *bus,
@@ -634,13 +658,18 @@ static int qcom_swrm_port_params(struct sdw_bus *bus,
unsigned int bank)
{
struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus);
+ int ret = 0;
+ pm_runtime_get_sync(ctrl->dev);
if (p_params->bps != SWR_INVALID_PARAM)
- return ctrl->reg_write(ctrl,
+ ret = ctrl->reg_write(ctrl,
SWRM_DP_BLOCK_CTRL_1(p_params->num),
p_params->bps - 1);
+ pm_runtime_mark_last_busy(ctrl->dev);
+ pm_runtime_put_autosuspend(ctrl->dev);
- return 0;
+
+ return ret;
}
static int qcom_swrm_transport_params(struct sdw_bus *bus,
@@ -651,6 +680,7 @@ static int qcom_swrm_transport_params(struct sdw_bus *bus,
u32 value;
int reg = SWRM_DP_PORT_CTRL_BANK((params->port_num), bank);
int ret;
+ pm_runtime_get_sync(ctrl->dev);
value = params->offset1 << SWRM_DP_PORT_CTRL_OFFSET1_SHFT;
value |= params->offset2 << SWRM_DP_PORT_CTRL_OFFSET2_SHFT;
@@ -685,6 +715,9 @@ static int qcom_swrm_transport_params(struct sdw_bus *bus,
reg = SWRM_DP_BLOCK_CTRL3_BANK(params->port_num, bank);
ret = ctrl->reg_write(ctrl, reg, params->blk_pkg_mode);
}
+ pm_runtime_mark_last_busy(ctrl->dev);
+ pm_runtime_put_autosuspend(ctrl->dev);
+
return ret;
}
@@ -696,6 +729,9 @@ static int qcom_swrm_port_enable(struct sdw_bus *bus,
u32 reg = SWRM_DP_PORT_CTRL_BANK(enable_ch->port_num, bank);
struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus);
u32 val;
+ int ret;
+
+ pm_runtime_get_sync(ctrl->dev);
ctrl->reg_read(ctrl, reg, &val);
@@ -704,7 +740,11 @@ static int qcom_swrm_port_enable(struct sdw_bus *bus,
else
val &= ~(0xff << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT);
- return ctrl->reg_write(ctrl, reg, val);
+ ret = ctrl->reg_write(ctrl, reg, val);
+ pm_runtime_mark_last_busy(ctrl->dev);
+ pm_runtime_put_autosuspend(ctrl->dev);
+
+ return ret;
}
static const struct sdw_master_port_ops qcom_swrm_port_ops = {
@@ -1194,6 +1234,13 @@ static int qcom_swrm_probe(struct platform_device *pdev)
(ctrl->version >> 24) & 0xff, (ctrl->version >> 16) & 0xff,
ctrl->version & 0xffff);
+ pm_runtime_set_autosuspend_delay(dev, 30000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
return 0;
err_master_add:
@@ -1214,6 +1261,47 @@ static int qcom_swrm_remove(struct platform_device *pdev)
return 0;
}
+static int swrm_runtime_resume(struct device *dev)
+{
+ struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dev);
+
+ reinit_completion(&ctrl->enumeration);
+ clk_prepare_enable(ctrl->hclk);
+ ctrl->reg_write(ctrl, SWRM_COMP_SW_RESET, 0x01);
+ qcom_swrm_get_device_status(ctrl);
+ sdw_handle_slave_status(&ctrl->bus, ctrl->status);
+ qcom_swrm_init(ctrl);
+ wait_for_completion_timeout(&ctrl->enumeration,
+ msecs_to_jiffies(TIMEOUT_MS));
+ usleep_range(100, 105);
+
+ pm_runtime_mark_last_busy(dev);
+
+ return 0;
+}
+
+static int __maybe_unused swrm_runtime_suspend(struct device *dev)
+{
+ struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dev);
+
+ /* Mask bus clash interrupt */
+ ctrl->intr_mask &= ~SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET;
+ ctrl->reg_write(ctrl, SWRM_INTERRUPT_MASK_ADDR, ctrl->intr_mask);
+ ctrl->reg_write(ctrl, SWRM_INTERRUPT_CPU_EN, ctrl->intr_mask);
+ /* clock stop sequence */
+ qcom_swrm_cmd_fifo_wr_cmd(ctrl, 0x2, 0xF, SDW_SCP_CTRL);
+
+ clk_disable_unprepare(ctrl->hclk);
+
+ usleep_range(100, 105);
+
+ return 0;
+}
+
+static const struct dev_pm_ops swrm_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(swrm_runtime_suspend, swrm_runtime_resume, NULL)
+};
+
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 },
@@ -1228,6 +1316,7 @@ static struct platform_driver qcom_swrm_driver = {
.driver = {
.name = "qcom-soundwire",
.of_match_table = qcom_swrm_of_match,
+ .pm = &swrm_dev_pm_ops,
}
};
module_platform_driver(qcom_swrm_driver);
Add Clock stop feature support using runtime PM. Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> --- drivers/soundwire/qcom.c | 103 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 96 insertions(+), 7 deletions(-)