// SPDX-License-Identifier: GPL-2.0// Copyright (c) 2019, Linaro Limited #include <linux/clk.h>#include <linux/completion.h>#include <linux/interrupt.h>#include <linux/io.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/debugfs.h>#include <linux/of.h>#include <linux/of_irq.h>#include <linux/pm_runtime.h>#include <linux/regmap.h>#include <linux/reset.h>#include <linux/slab.h>#include <linux/pm_wakeirq.h>#include <linux/slimbus.h>#include <linux/soundwire/sdw.h>#include <linux/soundwire/sdw_registers.h>#include <sound/pcm_params.h>#include <sound/soc.h>#include "bus.h" #define SWRM_COMP_SW_RESET 0x008#define SWRM_COMP_STATUS 0x014#define SWRM_LINK_MANAGER_EE 0x018#define SWRM_EE_CPU 1#define SWRM_FRM_GEN_ENABLED BIT(0)#define SWRM_VERSION_1_3_0 0x01030000#define SWRM_VERSION_1_5_1 0x01050001#define SWRM_VERSION_1_7_0 0x01070000#define SWRM_VERSION_2_0_0 0x02000000#define SWRM_VERSION_3_1_0 0x03010000#define SWRM_COMP_HW_VERSION 0x00#define SWRM_COMP_CFG_ADDR 0x04#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK BIT(1)#define SWRM_COMP_CFG_ENABLE_MSK BIT(0)#define SWRM_COMP_PARAMS 0x100#define SWRM_COMP_PARAMS_WR_FIFO_DEPTH GENMASK(14, 10)#define SWRM_COMP_PARAMS_RD_FIFO_DEPTH GENMASK(19, 15)#define SWRM_COMP_PARAMS_DOUT_PORTS_MASK GENMASK(4, 0)#define SWRM_COMP_PARAMS_DIN_PORTS_MASK GENMASK(9, 5)#define SWRM_V3_COMP_PARAMS_WR_FIFO_DEPTH GENMASK(17, 10)#define SWRM_V3_COMP_PARAMS_RD_FIFO_DEPTH GENMASK(23, 18) #define SWRM_COMP_MASTER_ID 0x104#define SWRM_V1_3_INTERRUPT_STATUS 0x200#define SWRM_V2_0_INTERRUPT_STATUS 0x5000#define SWRM_INTERRUPT_STATUS_RMSK GENMASK(16, 0)#define SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ BIT(0)#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED BIT(1)#define SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS BIT(2)#define SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET BIT(3)#define SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW BIT(4)#define SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW BIT(5)#define SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW BIT(6)#define SWRM_INTERRUPT_STATUS_CMD_ERROR BIT(7)#define SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION BIT(8)#define SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH BIT(9)#define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED BIT(10)#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED BIT(11)#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL BIT(12)#define SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED_V2 BIT(13)#define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED_V2 BIT(14)#define SWRM_INTERRUPT_STATUS_EXT_CLK_STOP_WAKEUP BIT(16)#define SWRM_INTERRUPT_STATUS_CMD_IGNORED_AND_EXEC_CONTINUED BIT(19)#define SWRM_INTERRUPT_MAX 17#define SWRM_V1_3_INTERRUPT_MASK_ADDR 0x204#define SWRM_V1_3_INTERRUPT_CLEAR 0x208#define SWRM_V2_0_INTERRUPT_CLEAR 0x5008#define SWRM_V1_3_INTERRUPT_CPU_EN 0x210#define SWRM_V2_0_INTERRUPT_CPU_EN 0x5004#define SWRM_V1_3_CMD_FIFO_WR_CMD 0x300#define SWRM_V2_0_CMD_FIFO_WR_CMD 0x5020#define SWRM_V1_3_CMD_FIFO_RD_CMD 0x304#define SWRM_V2_0_CMD_FIFO_RD_CMD 0x5024#define SWRM_CMD_FIFO_CMD 0x308#define SWRM_CMD_FIFO_FLUSH 0x1#define SWRM_V1_3_CMD_FIFO_STATUS 0x30C#define SWRM_V2_0_CMD_FIFO_STATUS 0x5050#define SWRM_RD_CMD_FIFO_CNT_MASK GENMASK(20, 16)#define SWRM_WR_CMD_FIFO_CNT_MASK GENMASK(12, 8)#define SWRM_CMD_FIFO_CFG_ADDR 0x314#define SWRM_CONTINUE_EXEC_ON_CMD_IGNORE BIT(31)#define SWRM_RD_WR_CMD_RETRIES 0x7#define SWRM_V1_3_CMD_FIFO_RD_FIFO_ADDR 0x318#define SWRM_V2_0_CMD_FIFO_RD_FIFO_ADDR 0x5040#define SWRM_RD_FIFO_CMD_ID_MASK GENMASK(11, 8)#define SWRM_ENUMERATOR_CFG_ADDR 0x500#define SWRM_ENUMERATOR_SLAVE_DEV_ID_1(m) (0x530 + 0x8 * (m))#define SWRM_ENUMERATOR_SLAVE_DEV_ID_2(m) (0x534 + 0x8 * (m))#define SWRM_MCP_FRAME_CTRL_BANK_ADDR(m) (0x101C + 0x40 * (m))#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK GENMASK(2, 0)#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK GENMASK(7, 3)#define SWRM_MCP_BUS_CTRL 0x1044#define SWRM_MCP_BUS_CLK_START BIT(1)#define SWRM_MCP_CFG_ADDR 0x1048#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK GENMASK(21, 17)#define SWRM_DEF_CMD_NO_PINGS 0x1f#define SWRM_MCP_STATUS 0x104C#define SWRM_MCP_STATUS_BANK_NUM_MASK BIT(0)#define SWRM_MCP_SLV_STATUS 0x1090#define SWRM_MCP_SLV_STATUS_MASK GENMASK(1, 0)#define SWRM_MCP_SLV_STATUS_SZ 2 #define SWRM_DPn_PORT_CTRL_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m)#define SWRM_DPn_PORT_CTRL_2_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m)#define SWRM_DPn_BLOCK_CTRL_1(offset, n) (offset + 0x100 * (n - 1))#define SWRM_DPn_BLOCK_CTRL2_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m)#define SWRM_DPn_PORT_HCTRL_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m)#define SWRM_DPn_BLOCK_CTRL3_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m)#define SWRM_DPn_SAMPLECTRL2_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m) #define SWR_V1_3_MSTR_MAX_REG_ADDR 0x1740#define SWR_V2_0_MSTR_MAX_REG_ADDR 0x50ac #define SWRM_V2_0_CLK_CTRL 0x5060#define SWRM_V2_0_CLK_CTRL_CLK_START BIT(0)#define SWRM_V2_0_LINK_STATUS 0x5064 #define SWRM_DP_PORT_CTRL_EN_CHAN_SHFT 0x18#define SWRM_DP_PORT_CTRL_OFFSET2_SHFT 0x10#define SWRM_DP_PORT_CTRL_OFFSET1_SHFT 0x08#define SWRM_AHB_BRIDGE_WR_DATA_0 0xc85#define SWRM_AHB_BRIDGE_WR_ADDR_0 0xc89#define SWRM_AHB_BRIDGE_RD_ADDR_0 0xc8d#define SWRM_AHB_BRIDGE_RD_DATA_0 0xc91 #define SWRM_REG_VAL_PACK(data, dev, id, reg) \ ((reg) | ((id) << 16) | ((dev) << 20) | ((data) << 24)) #define MAX_FREQ_NUM 1#define TIMEOUT_MS 100#define QCOM_SWRM_MAX_RD_LEN 0x1#define DEFAULT_CLK_FREQ 9600000#define SWR_INVALID_PARAM 0xFF#define SWR_HSTOP_MAX_VAL 0xF#define SWR_HSTART_MIN_VAL 0x0#define SWR_BROADCAST_CMD_ID 0x0F#define SWR_MAX_CMD_ID 14#define MAX_FIFO_RD_RETRY 3#define SWR_OVERFLOW_RETRY_COUNT 30#define SWRM_LINK_STATUS_RETRY_CNT 100 enum { MASTER_ID_WSA = 1, MASTER_ID_RX, MASTER_ID_TX}; struct qcom_swrm_port_config { u16 si; u8 off1; u8 off2; u8 bp_mode; u8 hstart; u8 hstop; u8 word_length; u8 blk_group_count; u8 lane_control;}; /* * Internal IDs for different register layouts. Only few registers differ per * each variant, so the list of IDs below does not include all of registers. */enum { SWRM_REG_FRAME_GEN_ENABLED, SWRM_REG_INTERRUPT_STATUS, SWRM_REG_INTERRUPT_MASK_ADDR, SWRM_REG_INTERRUPT_CLEAR, SWRM_REG_INTERRUPT_CPU_EN, SWRM_REG_CMD_FIFO_WR_CMD, SWRM_REG_CMD_FIFO_RD_CMD, SWRM_REG_CMD_FIFO_STATUS, SWRM_REG_CMD_FIFO_RD_FIFO_ADDR, SWRM_OFFSET_DP_PORT_CTRL_BANK, SWRM_OFFSET_DP_PORT_CTRL_2_BANK, SWRM_OFFSET_DP_BLOCK_CTRL_1, SWRM_OFFSET_DP_BLOCK_CTRL2_BANK, SWRM_OFFSET_DP_PORT_HCTRL_BANK, SWRM_OFFSET_DP_BLOCK_CTRL3_BANK, SWRM_OFFSET_DP_SAMPLECTRL2_BANK,}; struct qcom_swrm_ctrl { struct sdw_bus bus; struct device *dev; struct regmap *regmap; u32 max_reg; const unsigned int *reg_layout; void __iomem *mmio; struct reset_control *audio_cgcr;#ifdef CONFIG_DEBUG_FS struct dentry *debugfs;#endif struct completion broadcast; struct completion enumeration; /* Port alloc/free lock */ struct mutex port_lock; struct clk *hclk; int irq; unsigned int version; int wake_irq; int num_din_ports; int num_dout_ports; int nports; int cols_index; int rows_index; unsigned long port_mask; u32 intr_mask; u8 rcmd_id; u8 wcmd_id; /* Port numbers are 1 - 14 */ struct qcom_swrm_port_config *pconfig; struct sdw_stream_runtime **sruntime; enum sdw_slave_status status[SDW_MAX_DEVICES + 1]; int (*reg_read)(struct qcom_swrm_ctrl *ctrl, int reg, u32 *val); int (*reg_write)(struct qcom_swrm_ctrl *ctrl, int reg, int val); u32 slave_status; u32 wr_fifo_depth; bool clock_stop_not_supported;}; struct qcom_swrm_data { u32 default_cols; u32 default_rows; bool sw_clk_gate_required; u32 max_reg; const unsigned int *reg_layout;}; static const unsigned int swrm_v1_3_reg_layout[] = { [SWRM_REG_FRAME_GEN_ENABLED] = SWRM_COMP_STATUS, [SWRM_REG_INTERRUPT_STATUS] = SWRM_V1_3_INTERRUPT_STATUS, [SWRM_REG_INTERRUPT_MASK_ADDR] = SWRM_V1_3_INTERRUPT_MASK_ADDR, [SWRM_REG_INTERRUPT_CLEAR] = SWRM_V1_3_INTERRUPT_CLEAR, [SWRM_REG_INTERRUPT_CPU_EN] = SWRM_V1_3_INTERRUPT_CPU_EN, [SWRM_REG_CMD_FIFO_WR_CMD] = SWRM_V1_3_CMD_FIFO_WR_CMD, [SWRM_REG_CMD_FIFO_RD_CMD] = SWRM_V1_3_CMD_FIFO_RD_CMD, [SWRM_REG_CMD_FIFO_STATUS] = SWRM_V1_3_CMD_FIFO_STATUS, [SWRM_REG_CMD_FIFO_RD_FIFO_ADDR] = SWRM_V1_3_CMD_FIFO_RD_FIFO_ADDR, [SWRM_OFFSET_DP_PORT_CTRL_BANK] = 0x1124, [SWRM_OFFSET_DP_PORT_CTRL_2_BANK] = 0x1128, [SWRM_OFFSET_DP_BLOCK_CTRL_1] = 0x112c, [SWRM_OFFSET_DP_BLOCK_CTRL2_BANK] = 0x1130, [SWRM_OFFSET_DP_PORT_HCTRL_BANK] = 0x1134, [SWRM_OFFSET_DP_BLOCK_CTRL3_BANK] = 0x1138, [SWRM_OFFSET_DP_SAMPLECTRL2_BANK] = 0x113c,}; static const struct qcom_swrm_data swrm_v1_3_data = { .default_rows = 48, .default_cols = 16, .max_reg = SWR_V1_3_MSTR_MAX_REG_ADDR, .reg_layout = swrm_v1_3_reg_layout,}; static const struct qcom_swrm_data swrm_v1_5_data = { .default_rows = 50, .default_cols = 16, .max_reg = SWR_V1_3_MSTR_MAX_REG_ADDR, .reg_layout = swrm_v1_3_reg_layout,}; static const struct qcom_swrm_data swrm_v1_6_data = { .default_rows = 50, .default_cols = 16, .sw_clk_gate_required = true, .max_reg = SWR_V1_3_MSTR_MAX_REG_ADDR, .reg_layout = swrm_v1_3_reg_layout,}; static const unsigned int swrm_v2_0_reg_layout[] = { [SWRM_REG_FRAME_GEN_ENABLED] = SWRM_V2_0_LINK_STATUS, [SWRM_REG_INTERRUPT_STATUS] = SWRM_V2_0_INTERRUPT_STATUS, [SWRM_REG_INTERRUPT_MASK_ADDR] = 0, /* Not present */ [SWRM_REG_INTERRUPT_CLEAR] = SWRM_V2_0_INTERRUPT_CLEAR, [SWRM_REG_INTERRUPT_CPU_EN] = SWRM_V2_0_INTERRUPT_CPU_EN, [SWRM_REG_CMD_FIFO_WR_CMD] = SWRM_V2_0_CMD_FIFO_WR_CMD, [SWRM_REG_CMD_FIFO_RD_CMD] = SWRM_V2_0_CMD_FIFO_RD_CMD, [SWRM_REG_CMD_FIFO_STATUS] = SWRM_V2_0_CMD_FIFO_STATUS, [SWRM_REG_CMD_FIFO_RD_FIFO_ADDR] = SWRM_V2_0_CMD_FIFO_RD_FIFO_ADDR, [SWRM_OFFSET_DP_PORT_CTRL_BANK] = 0x1124, [SWRM_OFFSET_DP_PORT_CTRL_2_BANK] = 0x1128, [SWRM_OFFSET_DP_BLOCK_CTRL_1] = 0x112c, [SWRM_OFFSET_DP_BLOCK_CTRL2_BANK] = 0x1130, [SWRM_OFFSET_DP_PORT_HCTRL_BANK] = 0x1134, [SWRM_OFFSET_DP_BLOCK_CTRL3_BANK] = 0x1138, [SWRM_OFFSET_DP_SAMPLECTRL2_BANK] = 0x113c,}; static const struct qcom_swrm_data swrm_v2_0_data = { .default_rows = 50, .default_cols = 16, .sw_clk_gate_required = true, .max_reg = SWR_V2_0_MSTR_MAX_REG_ADDR, .reg_layout = swrm_v2_0_reg_layout,}; static const unsigned int swrm_v3_0_reg_layout[] = { [SWRM_REG_FRAME_GEN_ENABLED] = SWRM_V2_0_LINK_STATUS, [SWRM_REG_INTERRUPT_STATUS] = SWRM_V2_0_INTERRUPT_STATUS, [SWRM_REG_INTERRUPT_MASK_ADDR] = 0, /* Not present */ [SWRM_REG_INTERRUPT_CLEAR] = SWRM_V2_0_INTERRUPT_CLEAR, [SWRM_REG_INTERRUPT_CPU_EN] = SWRM_V2_0_INTERRUPT_CPU_EN, [SWRM_REG_CMD_FIFO_WR_CMD] = SWRM_V2_0_CMD_FIFO_WR_CMD, [SWRM_REG_CMD_FIFO_RD_CMD] = SWRM_V2_0_CMD_FIFO_RD_CMD, [SWRM_REG_CMD_FIFO_STATUS] = SWRM_V2_0_CMD_FIFO_STATUS, [SWRM_REG_CMD_FIFO_RD_FIFO_ADDR] = SWRM_V2_0_CMD_FIFO_RD_FIFO_ADDR, [SWRM_OFFSET_DP_PORT_CTRL_BANK] = 0x1224, [SWRM_OFFSET_DP_PORT_CTRL_2_BANK] = 0x1228, [SWRM_OFFSET_DP_BLOCK_CTRL_1] = 0x122c, [SWRM_OFFSET_DP_BLOCK_CTRL2_BANK] = 0x1230, [SWRM_OFFSET_DP_PORT_HCTRL_BANK] = 0x1234, [SWRM_OFFSET_DP_BLOCK_CTRL3_BANK] = 0x1238, [SWRM_OFFSET_DP_SAMPLECTRL2_BANK] = 0x123c,}; static const struct qcom_swrm_data swrm_v3_0_data = { .default_rows = 50, .default_cols = 16, .sw_clk_gate_required = true, .max_reg = SWR_V2_0_MSTR_MAX_REG_ADDR, .reg_layout = swrm_v3_0_reg_layout,};#define to_qcom_sdw(b) container_of(b, struct qcom_swrm_ctrl, bus) static int qcom_swrm_ahb_reg_read(struct qcom_swrm_ctrl *ctrl, int reg, u32 *val){ struct regmap *wcd_regmap = ctrl->regmap; int ret; /* pg register + offset */ ret = regmap_bulk_write(wcd_regmap, SWRM_AHB_BRIDGE_RD_ADDR_0, (u8 *)®, 4); if (ret < 0) return SDW_CMD_FAIL; ret = regmap_bulk_read(wcd_regmap, SWRM_AHB_BRIDGE_RD_DATA_0, val, 4); if (ret < 0) return SDW_CMD_FAIL; return SDW_CMD_OK;} static int qcom_swrm_ahb_reg_write(struct qcom_swrm_ctrl *ctrl, int reg, int val){ struct regmap *wcd_regmap = ctrl->regmap; int ret; /* pg register + offset */ ret = regmap_bulk_write(wcd_regmap, SWRM_AHB_BRIDGE_WR_DATA_0, (u8 *)&val, 4); if (ret) return SDW_CMD_FAIL; /* write address register */ ret = regmap_bulk_write(wcd_regmap, SWRM_AHB_BRIDGE_WR_ADDR_0, (u8 *)®, 4); if (ret) return SDW_CMD_FAIL; return SDW_CMD_OK;} static int qcom_swrm_cpu_reg_read(struct qcom_swrm_ctrl *ctrl, int reg, u32 *val){ *val = readl(ctrl->mmio + reg); return SDW_CMD_OK;} static int qcom_swrm_cpu_reg_write(struct qcom_swrm_ctrl *ctrl, int reg, int val){ writel(val, ctrl->mmio + reg); return SDW_CMD_OK;} static u32 swrm_get_packed_reg_val(u8 *cmd_id, u8 cmd_data, u8 dev_addr, u16 reg_addr){ u32 val; u8 id = *cmd_id; if (id != SWR_BROADCAST_CMD_ID) { if (id < SWR_MAX_CMD_ID) id += 1; else id = 0; *cmd_id = id; } val = SWRM_REG_VAL_PACK(cmd_data, dev_addr, id, reg_addr); return val;} static int swrm_wait_for_rd_fifo_avail(struct qcom_swrm_ctrl *ctrl){ u32 fifo_outstanding_data, value; int fifo_retry_count = SWR_OVERFLOW_RETRY_COUNT; do { /* Check for fifo underflow during read */ ctrl->reg_read(ctrl, ctrl->reg_layout[SWRM_REG_CMD_FIFO_STATUS], &value); fifo_outstanding_data = FIELD_GET(SWRM_RD_CMD_FIFO_CNT_MASK, value); /* Check if read data is available in read fifo */ if (fifo_outstanding_data > 0) return 0; usleep_range(500, 510); } while (fifo_retry_count--); if (fifo_outstanding_data == 0) { dev_err_ratelimited(ctrl->dev, "%s err read underflow\n", __func__); return -EIO; } return 0;} static int swrm_wait_for_wr_fifo_avail(struct qcom_swrm_ctrl *ctrl){ u32 fifo_outstanding_cmds, value; int fifo_retry_count = SWR_OVERFLOW_RETRY_COUNT; do { /* Check for fifo overflow during write */ ctrl->reg_read(ctrl, ctrl->reg_layout[SWRM_REG_CMD_FIFO_STATUS], &value); fifo_outstanding_cmds = FIELD_GET(SWRM_WR_CMD_FIFO_CNT_MASK, value); /* Check for space in write fifo before writing */ if (fifo_outstanding_cmds < ctrl->wr_fifo_depth) return 0; usleep_range(500, 510); } while (fifo_retry_count--); if (fifo_outstanding_cmds == ctrl->wr_fifo_depth) { dev_err_ratelimited(ctrl->dev, "%s err write overflow\n", __func__); return -EIO; } return 0;} static bool swrm_wait_for_wr_fifo_done(struct qcom_swrm_ctrl *ctrl){ u32 fifo_outstanding_cmds, value; int fifo_retry_count = SWR_OVERFLOW_RETRY_COUNT; /* Check for fifo overflow during write */ ctrl->reg_read(ctrl, ctrl->reg_layout[SWRM_REG_CMD_FIFO_STATUS], &value); fifo_outstanding_cmds = FIELD_GET(SWRM_WR_CMD_FIFO_CNT_MASK, value); if (fifo_outstanding_cmds) { while (fifo_retry_count) { usleep_range(500, 510); ctrl->reg_read(ctrl, ctrl->reg_layout[SWRM_REG_CMD_FIFO_STATUS], &value); fifo_outstanding_cmds = FIELD_GET(SWRM_WR_CMD_FIFO_CNT_MASK, value); fifo_retry_count--; if (fifo_outstanding_cmds == 0) return true; } } else { return true; } return false;} static int qcom_swrm_cmd_fifo_wr_cmd(struct qcom_swrm_ctrl *ctrl, u8 cmd_data, u8 dev_addr, u16 reg_addr){ u32 val; int ret = 0; u8 cmd_id = 0x0; if (dev_addr == SDW_BROADCAST_DEV_NUM) { cmd_id = SWR_BROADCAST_CMD_ID; val = swrm_get_packed_reg_val(&cmd_id, cmd_data, dev_addr, reg_addr); } else { val = swrm_get_packed_reg_val(&ctrl->wcmd_id, cmd_data, dev_addr, reg_addr); } if (swrm_wait_for_wr_fifo_avail(ctrl)) return SDW_CMD_FAIL_OTHER; if (cmd_id == SWR_BROADCAST_CMD_ID)Lines 1–500 of 1856. Load more
// SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2019, Linaro Limited #include <linux/clk.h> #include <linux/completion.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/debugfs.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/reset.h> #include <linux/slab.h> #include <linux/pm_wakeirq.h> #include <linux/slimbus.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_registers.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include "bus.h" #define SWRM_COMP_SW_RESET 0x008 #define SWRM_COMP_STATUS 0x014 #define SWRM_LINK_MANAGER_EE 0x018 #define SWRM_EE_CPU 1 #define SWRM_FRM_GEN_ENABLED BIT(0) #define SWRM_VERSION_1_3_0 0x01030000 #define SWRM_VERSION_1_5_1 0x01050001 #define SWRM_VERSION_1_7_0 0x01070000 #define SWRM_VERSION_2_0_0 0x02000000 #define SWRM_VERSION_3_1_0 0x03010000 #define SWRM_COMP_HW_VERSION 0x00 #define SWRM_COMP_CFG_ADDR 0x04 #define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK BIT(1) #define SWRM_COMP_CFG_ENABLE_MSK BIT(0) #define SWRM_COMP_PARAMS 0x100 #define SWRM_COMP_PARAMS_WR_FIFO_DEPTH GENMASK(14, 10) #define SWRM_COMP_PARAMS_RD_FIFO_DEPTH GENMASK(19, 15) #define SWRM_COMP_PARAMS_DOUT_PORTS_MASK GENMASK(4, 0) #define SWRM_COMP_PARAMS_DIN_PORTS_MASK GENMASK(9, 5) #define SWRM_V3_COMP_PARAMS_WR_FIFO_DEPTH GENMASK(17, 10) #define SWRM_V3_COMP_PARAMS_RD_FIFO_DEPTH GENMASK(23, 18) #define SWRM_COMP_MASTER_ID 0x104 #define SWRM_V1_3_INTERRUPT_STATUS 0x200 #define SWRM_V2_0_INTERRUPT_STATUS 0x5000 #define SWRM_INTERRUPT_STATUS_RMSK GENMASK(16, 0) #define SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ BIT(0) #define SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED BIT(1) #define SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS BIT(2) #define SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET BIT(3) #define SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW BIT(4) #define SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW BIT(5) #define SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW BIT(6) #define SWRM_INTERRUPT_STATUS_CMD_ERROR BIT(7) #define SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION BIT(8) #define SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH BIT(9) #define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED BIT(10) #define SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED BIT(11) #define SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL BIT(12) #define SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED_V2 BIT(13) #define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED_V2 BIT(14) #define SWRM_INTERRUPT_STATUS_EXT_CLK_STOP_WAKEUP BIT(16) #define SWRM_INTERRUPT_STATUS_CMD_IGNORED_AND_EXEC_CONTINUED BIT(19) #define SWRM_INTERRUPT_MAX 17 #define SWRM_V1_3_INTERRUPT_MASK_ADDR 0x204 #define SWRM_V1_3_INTERRUPT_CLEAR 0x208 #define SWRM_V2_0_INTERRUPT_CLEAR 0x5008 #define SWRM_V1_3_INTERRUPT_CPU_EN 0x210 #define SWRM_V2_0_INTERRUPT_CPU_EN 0x5004 #define SWRM_V1_3_CMD_FIFO_WR_CMD 0x300 #define SWRM_V2_0_CMD_FIFO_WR_CMD 0x5020 #define SWRM_V1_3_CMD_FIFO_RD_CMD 0x304 #define SWRM_V2_0_CMD_FIFO_RD_CMD 0x5024 #define SWRM_CMD_FIFO_CMD 0x308 #define SWRM_CMD_FIFO_FLUSH 0x1 #define SWRM_V1_3_CMD_FIFO_STATUS 0x30C #define SWRM_V2_0_CMD_FIFO_STATUS 0x5050 #define SWRM_RD_CMD_FIFO_CNT_MASK GENMASK(20, 16) #define SWRM_WR_CMD_FIFO_CNT_MASK GENMASK(12, 8) #define SWRM_CMD_FIFO_CFG_ADDR 0x314 #define SWRM_CONTINUE_EXEC_ON_CMD_IGNORE BIT(31) #define SWRM_RD_WR_CMD_RETRIES 0x7 #define SWRM_V1_3_CMD_FIFO_RD_FIFO_ADDR 0x318 #define SWRM_V2_0_CMD_FIFO_RD_FIFO_ADDR 0x5040 #define SWRM_RD_FIFO_CMD_ID_MASK GENMASK(11, 8) #define SWRM_ENUMERATOR_CFG_ADDR 0x500 #define SWRM_ENUMERATOR_SLAVE_DEV_ID_1(m) (0x530 + 0x8 * (m)) #define SWRM_ENUMERATOR_SLAVE_DEV_ID_2(m) (0x534 + 0x8 * (m)) #define SWRM_MCP_FRAME_CTRL_BANK_ADDR(m) (0x101C + 0x40 * (m)) #define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK GENMASK(2, 0) #define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK GENMASK(7, 3) #define SWRM_MCP_BUS_CTRL 0x1044 #define SWRM_MCP_BUS_CLK_START BIT(1) #define SWRM_MCP_CFG_ADDR 0x1048 #define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK GENMASK(21, 17) #define SWRM_DEF_CMD_NO_PINGS 0x1f #define SWRM_MCP_STATUS 0x104C #define SWRM_MCP_STATUS_BANK_NUM_MASK BIT(0) #define SWRM_MCP_SLV_STATUS 0x1090 #define SWRM_MCP_SLV_STATUS_MASK GENMASK(1, 0) #define SWRM_MCP_SLV_STATUS_SZ 2 #define SWRM_DPn_PORT_CTRL_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m) #define SWRM_DPn_PORT_CTRL_2_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m) #define SWRM_DPn_BLOCK_CTRL_1(offset, n) (offset + 0x100 * (n - 1)) #define SWRM_DPn_BLOCK_CTRL2_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m) #define SWRM_DPn_PORT_HCTRL_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m) #define SWRM_DPn_BLOCK_CTRL3_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m) #define SWRM_DPn_SAMPLECTRL2_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m) #define SWR_V1_3_MSTR_MAX_REG_ADDR 0x1740 #define SWR_V2_0_MSTR_MAX_REG_ADDR 0x50ac #define SWRM_V2_0_CLK_CTRL 0x5060 #define SWRM_V2_0_CLK_CTRL_CLK_START BIT(0) #define SWRM_V2_0_LINK_STATUS 0x5064 #define SWRM_DP_PORT_CTRL_EN_CHAN_SHFT 0x18 #define SWRM_DP_PORT_CTRL_OFFSET2_SHFT 0x10 #define SWRM_DP_PORT_CTRL_OFFSET1_SHFT 0x08 #define SWRM_AHB_BRIDGE_WR_DATA_0 0xc85 #define SWRM_AHB_BRIDGE_WR_ADDR_0 0xc89 #define SWRM_AHB_BRIDGE_RD_ADDR_0 0xc8d #define SWRM_AHB_BRIDGE_RD_DATA_0 0xc91 #define SWRM_REG_VAL_PACK(data, dev, id, reg) \ ((reg) | ((id) << 16) | ((dev) << 20) | ((data) << 24)) #define MAX_FREQ_NUM 1 #define TIMEOUT_MS 100 #define QCOM_SWRM_MAX_RD_LEN 0x1 #define DEFAULT_CLK_FREQ 9600000 #define SWR_INVALID_PARAM 0xFF #define SWR_HSTOP_MAX_VAL 0xF #define SWR_HSTART_MIN_VAL 0x0 #define SWR_BROADCAST_CMD_ID 0x0F #define SWR_MAX_CMD_ID 14 #define MAX_FIFO_RD_RETRY 3 #define SWR_OVERFLOW_RETRY_COUNT 30 #define SWRM_LINK_STATUS_RETRY_CNT 100 enum { MASTER_ID_WSA = 1, MASTER_ID_RX, MASTER_ID_TX }; struct qcom_swrm_port_config { u16 si; u8 off1; u8 off2; u8 bp_mode; u8 hstart; u8 hstop; u8 word_length; u8 blk_group_count; u8 lane_control; }; /* * Internal IDs for different register layouts. Only few registers differ per * each variant, so the list of IDs below does not include all of registers. */ enum { SWRM_REG_FRAME_GEN_ENABLED, SWRM_REG_INTERRUPT_STATUS, SWRM_REG_INTERRUPT_MASK_ADDR, SWRM_REG_INTERRUPT_CLEAR, SWRM_REG_INTERRUPT_CPU_EN, SWRM_REG_CMD_FIFO_WR_CMD, SWRM_REG_CMD_FIFO_RD_CMD, SWRM_REG_CMD_FIFO_STATUS, SWRM_REG_CMD_FIFO_RD_FIFO_ADDR, SWRM_OFFSET_DP_PORT_CTRL_BANK, SWRM_OFFSET_DP_PORT_CTRL_2_BANK, SWRM_OFFSET_DP_BLOCK_CTRL_1, SWRM_OFFSET_DP_BLOCK_CTRL2_BANK, SWRM_OFFSET_DP_PORT_HCTRL_BANK, SWRM_OFFSET_DP_BLOCK_CTRL3_BANK, SWRM_OFFSET_DP_SAMPLECTRL2_BANK, }; struct qcom_swrm_ctrl { struct sdw_bus bus; struct device *dev; struct regmap *regmap; u32 max_reg; const unsigned int *reg_layout; void __iomem *mmio; struct reset_control *audio_cgcr; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; #endif struct completion broadcast; struct completion enumeration; /* Port alloc/free lock */ struct mutex port_lock; struct clk *hclk; int irq; unsigned int version; int wake_irq; int num_din_ports; int num_dout_ports; int nports; int cols_index; int rows_index; unsigned long port_mask; u32 intr_mask; u8 rcmd_id; u8 wcmd_id; /* Port numbers are 1 - 14 */ struct qcom_swrm_port_config *pconfig; struct sdw_stream_runtime **sruntime; enum sdw_slave_status status[SDW_MAX_DEVICES + 1]; int (*reg_read)(struct qcom_swrm_ctrl *ctrl, int reg, u32 *val); int (*reg_write)(struct qcom_swrm_ctrl *ctrl, int reg, int val); u32 slave_status; u32 wr_fifo_depth; bool clock_stop_not_supported; }; struct qcom_swrm_data { u32 default_cols; u32 default_rows; bool sw_clk_gate_required; u32 max_reg; const unsigned int *reg_layout; }; static const unsigned int swrm_v1_3_reg_layout[] = { [SWRM_REG_FRAME_GEN_ENABLED] = SWRM_COMP_STATUS, [SWRM_REG_INTERRUPT_STATUS] = SWRM_V1_3_INTERRUPT_STATUS, [SWRM_REG_INTERRUPT_MASK_ADDR] = SWRM_V1_3_INTERRUPT_MASK_ADDR, [SWRM_REG_INTERRUPT_CLEAR] = SWRM_V1_3_INTERRUPT_CLEAR, [SWRM_REG_INTERRUPT_CPU_EN] = SWRM_V1_3_INTERRUPT_CPU_EN, [SWRM_REG_CMD_FIFO_WR_CMD] = SWRM_V1_3_CMD_FIFO_WR_CMD, [SWRM_REG_CMD_FIFO_RD_CMD] = SWRM_V1_3_CMD_FIFO_RD_CMD, [SWRM_REG_CMD_FIFO_STATUS] = SWRM_V1_3_CMD_FIFO_STATUS, [SWRM_REG_CMD_FIFO_RD_FIFO_ADDR] = SWRM_V1_3_CMD_FIFO_RD_FIFO_ADDR, [SWRM_OFFSET_DP_PORT_CTRL_BANK] = 0x1124, [SWRM_OFFSET_DP_PORT_CTRL_2_BANK] = 0x1128, [SWRM_OFFSET_DP_BLOCK_CTRL_1] = 0x112c, [SWRM_OFFSET_DP_BLOCK_CTRL2_BANK] = 0x1130, [SWRM_OFFSET_DP_PORT_HCTRL_BANK] = 0x1134, [SWRM_OFFSET_DP_BLOCK_CTRL3_BANK] = 0x1138, [SWRM_OFFSET_DP_SAMPLECTRL2_BANK] = 0x113c, }; static const struct qcom_swrm_data swrm_v1_3_data = { .default_rows = 48, .default_cols = 16, .max_reg = SWR_V1_3_MSTR_MAX_REG_ADDR, .reg_layout = swrm_v1_3_reg_layout, }; static const struct qcom_swrm_data swrm_v1_5_data = { .default_rows = 50, .default_cols = 16, .max_reg = SWR_V1_3_MSTR_MAX_REG_ADDR, .reg_layout = swrm_v1_3_reg_layout, }; static const struct qcom_swrm_data swrm_v1_6_data = { .default_rows = 50, .default_cols = 16, .sw_clk_gate_required = true, .max_reg = SWR_V1_3_MSTR_MAX_REG_ADDR, .reg_layout = swrm_v1_3_reg_layout, }; static const unsigned int swrm_v2_0_reg_layout[] = { [SWRM_REG_FRAME_GEN_ENABLED] = SWRM_V2_0_LINK_STATUS, [SWRM_REG_INTERRUPT_STATUS] = SWRM_V2_0_INTERRUPT_STATUS, [SWRM_REG_INTERRUPT_MASK_ADDR] = 0, /* Not present */ [SWRM_REG_INTERRUPT_CLEAR] = SWRM_V2_0_INTERRUPT_CLEAR, [SWRM_REG_INTERRUPT_CPU_EN] = SWRM_V2_0_INTERRUPT_CPU_EN, [SWRM_REG_CMD_FIFO_WR_CMD] = SWRM_V2_0_CMD_FIFO_WR_CMD, [SWRM_REG_CMD_FIFO_RD_CMD] = SWRM_V2_0_CMD_FIFO_RD_CMD, [SWRM_REG_CMD_FIFO_STATUS] = SWRM_V2_0_CMD_FIFO_STATUS, [SWRM_REG_CMD_FIFO_RD_FIFO_ADDR] = SWRM_V2_0_CMD_FIFO_RD_FIFO_ADDR, [SWRM_OFFSET_DP_PORT_CTRL_BANK] = 0x1124, [SWRM_OFFSET_DP_PORT_CTRL_2_BANK] = 0x1128, [SWRM_OFFSET_DP_BLOCK_CTRL_1] = 0x112c, [SWRM_OFFSET_DP_BLOCK_CTRL2_BANK] = 0x1130, [SWRM_OFFSET_DP_PORT_HCTRL_BANK] = 0x1134, [SWRM_OFFSET_DP_BLOCK_CTRL3_BANK] = 0x1138, [SWRM_OFFSET_DP_SAMPLECTRL2_BANK] = 0x113c, }; static const struct qcom_swrm_data swrm_v2_0_data = { .default_rows = 50, .default_cols = 16, .sw_clk_gate_required = true, .max_reg = SWR_V2_0_MSTR_MAX_REG_ADDR, .reg_layout = swrm_v2_0_reg_layout, }; static const unsigned int swrm_v3_0_reg_layout[] = { [SWRM_REG_FRAME_GEN_ENABLED] = SWRM_V2_0_LINK_STATUS, [SWRM_REG_INTERRUPT_STATUS] = SWRM_V2_0_INTERRUPT_STATUS, [SWRM_REG_INTERRUPT_MASK_ADDR] = 0, /* Not present */ [SWRM_REG_INTERRUPT_CLEAR] = SWRM_V2_0_INTERRUPT_CLEAR, [SWRM_REG_INTERRUPT_CPU_EN] = SWRM_V2_0_INTERRUPT_CPU_EN, [SWRM_REG_CMD_FIFO_WR_CMD] = SWRM_V2_0_CMD_FIFO_WR_CMD, [SWRM_REG_CMD_FIFO_RD_CMD] = SWRM_V2_0_CMD_FIFO_RD_CMD, [SWRM_REG_CMD_FIFO_STATUS] = SWRM_V2_0_CMD_FIFO_STATUS, [SWRM_REG_CMD_FIFO_RD_FIFO_ADDR] = SWRM_V2_0_CMD_FIFO_RD_FIFO_ADDR, [SWRM_OFFSET_DP_PORT_CTRL_BANK] = 0x1224, [SWRM_OFFSET_DP_PORT_CTRL_2_BANK] = 0x1228, [SWRM_OFFSET_DP_BLOCK_CTRL_1] = 0x122c, [SWRM_OFFSET_DP_BLOCK_CTRL2_BANK] = 0x1230, [SWRM_OFFSET_DP_PORT_HCTRL_BANK] = 0x1234, [SWRM_OFFSET_DP_BLOCK_CTRL3_BANK] = 0x1238, [SWRM_OFFSET_DP_SAMPLECTRL2_BANK] = 0x123c, }; static const struct qcom_swrm_data swrm_v3_0_data = { .default_rows = 50, .default_cols = 16, .sw_clk_gate_required = true, .max_reg = SWR_V2_0_MSTR_MAX_REG_ADDR, .reg_layout = swrm_v3_0_reg_layout, }; #define to_qcom_sdw(b) container_of(b, struct qcom_swrm_ctrl, bus) static int qcom_swrm_ahb_reg_read(struct qcom_swrm_ctrl *ctrl, int reg, u32 *val) { struct regmap *wcd_regmap = ctrl->regmap; int ret; /* pg register + offset */ ret = regmap_bulk_write(wcd_regmap, SWRM_AHB_BRIDGE_RD_ADDR_0, (u8 *)®, 4); if (ret < 0) return SDW_CMD_FAIL; ret = regmap_bulk_read(wcd_regmap, SWRM_AHB_BRIDGE_RD_DATA_0, val, 4); if (ret < 0) return SDW_CMD_FAIL; return SDW_CMD_OK; } static int qcom_swrm_ahb_reg_write(struct qcom_swrm_ctrl *ctrl, int reg, int val) { struct regmap *wcd_regmap = ctrl->regmap; int ret; /* pg register + offset */ ret = regmap_bulk_write(wcd_regmap, SWRM_AHB_BRIDGE_WR_DATA_0, (u8 *)&val, 4); if (ret) return SDW_CMD_FAIL; /* write address register */ ret = regmap_bulk_write(wcd_regmap, SWRM_AHB_BRIDGE_WR_ADDR_0, (u8 *)®, 4); if (ret) return SDW_CMD_FAIL; return SDW_CMD_OK; } static int qcom_swrm_cpu_reg_read(struct qcom_swrm_ctrl *ctrl, int reg, u32 *val) { *val = readl(ctrl->mmio + reg); return SDW_CMD_OK; } static int qcom_swrm_cpu_reg_write(struct qcom_swrm_ctrl *ctrl, int reg, int val) { writel(val, ctrl->mmio + reg); return SDW_CMD_OK; } static u32 swrm_get_packed_reg_val(u8 *cmd_id, u8 cmd_data, u8 dev_addr, u16 reg_addr) { u32 val; u8 id = *cmd_id; if (id != SWR_BROADCAST_CMD_ID) { if (id < SWR_MAX_CMD_ID) id += 1; else id = 0; *cmd_id = id; } val = SWRM_REG_VAL_PACK(cmd_data, dev_addr, id, reg_addr); return val; } static int swrm_wait_for_rd_fifo_avail(struct qcom_swrm_ctrl *ctrl) { u32 fifo_outstanding_data, value; int fifo_retry_count = SWR_OVERFLOW_RETRY_COUNT; do { /* Check for fifo underflow during read */ ctrl->reg_read(ctrl, ctrl->reg_layout[SWRM_REG_CMD_FIFO_STATUS], &value); fifo_outstanding_data = FIELD_GET(SWRM_RD_CMD_FIFO_CNT_MASK, value); /* Check if read data is available in read fifo */ if (fifo_outstanding_data > 0) return 0; usleep_range(500, 510); } while (fifo_retry_count--); if (fifo_outstanding_data == 0) { dev_err_ratelimited(ctrl->dev, "%s err read underflow\n", __func__); return -EIO; } return 0; } static int swrm_wait_for_wr_fifo_avail(struct qcom_swrm_ctrl *ctrl) { u32 fifo_outstanding_cmds, value; int fifo_retry_count = SWR_OVERFLOW_RETRY_COUNT; do { /* Check for fifo overflow during write */ ctrl->reg_read(ctrl, ctrl->reg_layout[SWRM_REG_CMD_FIFO_STATUS], &value); fifo_outstanding_cmds = FIELD_GET(SWRM_WR_CMD_FIFO_CNT_MASK, value); /* Check for space in write fifo before writing */ if (fifo_outstanding_cmds < ctrl->wr_fifo_depth) return 0; usleep_range(500, 510); } while (fifo_retry_count--); if (fifo_outstanding_cmds == ctrl->wr_fifo_depth) { dev_err_ratelimited(ctrl->dev, "%s err write overflow\n", __func__); return -EIO; } return 0; } static bool swrm_wait_for_wr_fifo_done(struct qcom_swrm_ctrl *ctrl) { u32 fifo_outstanding_cmds, value; int fifo_retry_count = SWR_OVERFLOW_RETRY_COUNT; /* Check for fifo overflow during write */ ctrl->reg_read(ctrl, ctrl->reg_layout[SWRM_REG_CMD_FIFO_STATUS], &value); fifo_outstanding_cmds = FIELD_GET(SWRM_WR_CMD_FIFO_CNT_MASK, value); if (fifo_outstanding_cmds) { while (fifo_retry_count) { usleep_range(500, 510); ctrl->reg_read(ctrl, ctrl->reg_layout[SWRM_REG_CMD_FIFO_STATUS], &value); fifo_outstanding_cmds = FIELD_GET(SWRM_WR_CMD_FIFO_CNT_MASK, value); fifo_retry_count--; if (fifo_outstanding_cmds == 0) return true; } } else { return true; } return false; } static int qcom_swrm_cmd_fifo_wr_cmd(struct qcom_swrm_ctrl *ctrl, u8 cmd_data, u8 dev_addr, u16 reg_addr) { u32 val; int ret = 0; u8 cmd_id = 0x0; if (dev_addr == SDW_BROADCAST_DEV_NUM) { cmd_id = SWR_BROADCAST_CMD_ID; val = swrm_get_packed_reg_val(&cmd_id, cmd_data, dev_addr, reg_addr); } else { val = swrm_get_packed_reg_val(&ctrl->wcmd_id, cmd_data, dev_addr, reg_addr); } if (swrm_wait_for_wr_fifo_avail(ctrl)) return SDW_CMD_FAIL_OTHER; if (cmd_id == SWR_BROADCAST_CMD_ID)