// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)/* * SoundWire AMD Manager driver * * Copyright 2023-24 Advanced Micro Devices, Inc. */ #include <linux/completion.h>#include <linux/cleanup.h>#include <linux/device.h>#include <linux/io.h>#include <linux/jiffies.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/soundwire/sdw.h>#include <linux/soundwire/sdw_registers.h>#include <linux/pm_runtime.h>#include <linux/wait.h>#include <sound/pcm_params.h>#include <sound/soc.h>#include "bus.h"#include "amd_init.h"#include "amd_manager.h" #define DRV_NAME "amd_sdw_manager" #define to_amd_sdw(b) container_of(b, struct amd_sdw_manager, bus) static int amd_sdw_clk_init_ctrl(struct amd_sdw_manager *amd_manager){ struct sdw_bus *bus = &amd_manager->bus; struct sdw_master_prop *prop = &bus->prop; u32 divider; dev_dbg(amd_manager->dev, "mclk %d max %d row %d col %d frame_rate:%d\n", prop->mclk_freq, prop->max_clk_freq, prop->default_row, prop->default_col, prop->default_frame_rate); if (!prop->default_frame_rate || !prop->default_row) { dev_err(amd_manager->dev, "Default frame_rate %d or row %d is invalid\n", prop->default_frame_rate, prop->default_row); return -EINVAL; } /* Set clock divider */ divider = (prop->mclk_freq / bus->params.curr_dr_freq); writel(divider, amd_manager->mmio + ACP_SW_CLK_FREQUENCY_CTRL); /* Set frame shape base on the actual bus frequency. */ prop->default_col = bus->params.curr_dr_freq / prop->default_frame_rate / prop->default_row; amd_manager->cols_index = sdw_find_col_index(prop->default_col); amd_manager->rows_index = sdw_find_row_index(prop->default_row); bus->params.col = prop->default_col; bus->params.row = prop->default_row; return 0;} static int amd_init_sdw_manager(struct amd_sdw_manager *amd_manager){ u32 val; int ret; writel(AMD_SDW_ENABLE, amd_manager->mmio + ACP_SW_EN); ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_EN_STATUS, val, val, ACP_DELAY_US, AMD_SDW_TIMEOUT); if (ret) return ret; /* SoundWire manager bus reset */ writel(AMD_SDW_BUS_RESET_REQ, amd_manager->mmio + ACP_SW_BUS_RESET_CTRL); ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_BUS_RESET_CTRL, val, (val & AMD_SDW_BUS_RESET_DONE), ACP_DELAY_US, AMD_SDW_TIMEOUT); if (ret) return ret; writel(AMD_SDW_BUS_RESET_CLEAR_REQ, amd_manager->mmio + ACP_SW_BUS_RESET_CTRL); ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_BUS_RESET_CTRL, val, !val, ACP_DELAY_US, AMD_SDW_TIMEOUT); if (ret) { dev_err(amd_manager->dev, "Failed to reset SoundWire manager instance%d\n", amd_manager->instance); return ret; } writel(AMD_SDW_DISABLE, amd_manager->mmio + ACP_SW_EN); return readl_poll_timeout(amd_manager->mmio + ACP_SW_EN_STATUS, val, !val, ACP_DELAY_US, AMD_SDW_TIMEOUT);} static int amd_enable_sdw_manager(struct amd_sdw_manager *amd_manager){ u32 val; writel(AMD_SDW_ENABLE, amd_manager->mmio + ACP_SW_EN); return readl_poll_timeout(amd_manager->mmio + ACP_SW_EN_STATUS, val, val, ACP_DELAY_US, AMD_SDW_TIMEOUT);} static int amd_disable_sdw_manager(struct amd_sdw_manager *amd_manager){ u32 val; writel(AMD_SDW_DISABLE, amd_manager->mmio + ACP_SW_EN); /* * After invoking manager disable sequence, check whether * manager has executed clock stop sequence. In this case, * manager should ignore checking enable status register. */ val = readl(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL); if (val) return 0; return readl_poll_timeout(amd_manager->mmio + ACP_SW_EN_STATUS, val, !val, ACP_DELAY_US, AMD_SDW_TIMEOUT);} static void amd_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager){ u32 val; mutex_lock(amd_manager->acp_sdw_lock); val = sdw_manager_reg_mask_array[amd_manager->instance]; amd_updatel(amd_manager->acp_mmio, ACP_EXTERNAL_INTR_CNTL(amd_manager->instance), val, val); mutex_unlock(amd_manager->acp_sdw_lock); writel(AMD_SDW_IRQ_MASK_0TO7, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_0TO7); writel(AMD_SDW_IRQ_MASK_8TO11, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11); writel(AMD_SDW_IRQ_ERROR_MASK, amd_manager->mmio + ACP_SW_ERROR_INTR_MASK);} static void amd_disable_sdw_interrupts(struct amd_sdw_manager *amd_manager){ u32 irq_mask; mutex_lock(amd_manager->acp_sdw_lock); irq_mask = sdw_manager_reg_mask_array[amd_manager->instance]; amd_updatel(amd_manager->acp_mmio, ACP_EXTERNAL_INTR_CNTL(amd_manager->instance), irq_mask, 0); mutex_unlock(amd_manager->acp_sdw_lock); writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_0TO7); writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11); writel(0x00, amd_manager->mmio + ACP_SW_ERROR_INTR_MASK);} static int amd_deinit_sdw_manager(struct amd_sdw_manager *amd_manager){ amd_disable_sdw_interrupts(amd_manager); return amd_disable_sdw_manager(amd_manager);} static void amd_sdw_set_frameshape(struct amd_sdw_manager *amd_manager){ u32 frame_size; frame_size = (amd_manager->rows_index << 3) | amd_manager->cols_index; writel(frame_size, amd_manager->mmio + ACP_SW_FRAMESIZE);} static void amd_sdw_wake_enable(struct amd_sdw_manager *amd_manager, bool enable){ u32 wake_ctrl; wake_ctrl = readl(amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11); if (enable) wake_ctrl |= AMD_SDW_WAKE_INTR_MASK; else wake_ctrl &= ~AMD_SDW_WAKE_INTR_MASK; writel(wake_ctrl, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11);} static int amd_sdw_set_device_state(struct amd_sdw_manager *amd_manager, u32 target_device_state){ u32 sdw_dev_state; sdw_dev_state = readl(amd_manager->acp_mmio + AMD_SDW_DEVICE_STATE); switch (amd_manager->instance) { case ACP_SDW0: u32p_replace_bits(&sdw_dev_state, target_device_state, AMD_SDW0_DEVICE_STATE_MASK); break; case ACP_SDW1: u32p_replace_bits(&sdw_dev_state, target_device_state, AMD_SDW1_DEVICE_STATE_MASK); break; default: return -EINVAL; } writel(sdw_dev_state, amd_manager->acp_mmio + AMD_SDW_DEVICE_STATE); sdw_dev_state = readl(amd_manager->acp_mmio + AMD_SDW_DEVICE_STATE); dev_dbg(amd_manager->dev, "AMD_SDW_DEVICE_STATE:0x%x\n", sdw_dev_state); return 0;} static int amd_sdw_host_wake_enable(struct amd_sdw_manager *amd_manager, bool enable){ u32 intr_cntl1; u32 sdw_host_wake_irq_mask; if (!amd_manager->wake_en_mask) return 0; switch (amd_manager->instance) { case ACP_SDW0: sdw_host_wake_irq_mask = AMD_SDW0_HOST_WAKE_INTR_MASK; break; case ACP_SDW1: sdw_host_wake_irq_mask = AMD_SDW1_HOST_WAKE_INTR_MASK; break; default: return -EINVAL; } intr_cntl1 = readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(ACP_SDW1)); if (enable) intr_cntl1 |= sdw_host_wake_irq_mask; else intr_cntl1 &= ~sdw_host_wake_irq_mask; writel(intr_cntl1, amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(ACP_SDW1)); return 0;} static void amd_sdw_ctl_word_prep(u32 *lower_word, u32 *upper_word, struct sdw_msg *msg, int cmd_offset){ u32 upper_data; u32 lower_data = 0; u16 addr; u8 upper_addr, lower_addr; u8 data = 0; addr = msg->addr + cmd_offset; upper_addr = (addr & 0xFF00) >> 8; lower_addr = addr & 0xFF; if (msg->flags == SDW_MSG_FLAG_WRITE) data = msg->buf[cmd_offset]; upper_data = FIELD_PREP(AMD_SDW_MCP_CMD_DEV_ADDR, msg->dev_num); upper_data |= FIELD_PREP(AMD_SDW_MCP_CMD_COMMAND, msg->flags + 2); upper_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_ADDR_HIGH, upper_addr); lower_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_ADDR_LOW, lower_addr); lower_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_DATA, data); *upper_word = upper_data; *lower_word = lower_data;} static u64 amd_sdw_send_cmd_get_resp(struct amd_sdw_manager *amd_manager, u32 lower_data, u32 upper_data){ u64 resp; u32 lower_resp, upper_resp; u32 sts; int ret; ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_IMM_CMD_STS, sts, !(sts & AMD_SDW_IMM_CMD_BUSY), ACP_DELAY_US, AMD_SDW_TIMEOUT); if (ret) { dev_err(amd_manager->dev, "SDW%x previous cmd status clear failed\n", amd_manager->instance); return ret; } if (sts & AMD_SDW_IMM_RES_VALID) { dev_err(amd_manager->dev, "SDW%x manager is in bad state\n", amd_manager->instance); writel(AMD_SDW_IMM_RES_VALID, amd_manager->mmio + ACP_SW_IMM_CMD_STS); } writel(upper_data, amd_manager->mmio + ACP_SW_IMM_CMD_UPPER_WORD); writel(lower_data, amd_manager->mmio + ACP_SW_IMM_CMD_LOWER_QWORD); ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_IMM_CMD_STS, sts, (sts & AMD_SDW_IMM_RES_VALID), ACP_DELAY_US, AMD_SDW_TIMEOUT); if (ret) { dev_err(amd_manager->dev, "SDW%x cmd response timeout occurred\n", amd_manager->instance); return ret; } upper_resp = readl(amd_manager->mmio + ACP_SW_IMM_RESP_UPPER_WORD); lower_resp = readl(amd_manager->mmio + ACP_SW_IMM_RESP_LOWER_QWORD); writel(AMD_SDW_IMM_RES_VALID, amd_manager->mmio + ACP_SW_IMM_CMD_STS); ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_IMM_CMD_STS, sts, !(sts & AMD_SDW_IMM_RES_VALID), ACP_DELAY_US, AMD_SDW_TIMEOUT); if (ret) { dev_err(amd_manager->dev, "SDW%x cmd status retry failed\n", amd_manager->instance); return ret; } resp = upper_resp; resp = (resp << 32) | lower_resp; return resp;} static enum sdw_command_responseamd_program_scp_addr(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg){ struct sdw_msg scp_msg = {0}; u64 response_buf[2] = {0}; u32 upper_data = 0, lower_data = 0; int index; scp_msg.dev_num = msg->dev_num; scp_msg.addr = SDW_SCP_ADDRPAGE1; scp_msg.buf = &msg->addr_page1; scp_msg.flags = SDW_MSG_FLAG_WRITE; amd_sdw_ctl_word_prep(&lower_data, &upper_data, &scp_msg, 0); response_buf[0] = amd_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data); scp_msg.addr = SDW_SCP_ADDRPAGE2; scp_msg.buf = &msg->addr_page2; amd_sdw_ctl_word_prep(&lower_data, &upper_data, &scp_msg, 0); response_buf[1] = amd_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data); for (index = 0; index < 2; index++) { if (response_buf[index] == -ETIMEDOUT) { dev_err_ratelimited(amd_manager->dev, "SCP_addrpage command timeout for Slave %d\n", msg->dev_num); return SDW_CMD_TIMEOUT; } else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) { if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) { dev_err_ratelimited(amd_manager->dev, "SCP_addrpage NACKed for Slave %d\n", msg->dev_num); return SDW_CMD_FAIL; } dev_dbg_ratelimited(amd_manager->dev, "SCP_addrpage ignored for Slave %d\n", msg->dev_num); return SDW_CMD_IGNORED; } } return SDW_CMD_OK;} static int amd_prep_msg(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg){ int ret; if (msg->page) { ret = amd_program_scp_addr(amd_manager, msg); if (ret) { msg->len = 0; return ret; } } switch (msg->flags) { case SDW_MSG_FLAG_READ: case SDW_MSG_FLAG_WRITE: break; default: dev_err(amd_manager->dev, "Invalid msg cmd: %d\n", msg->flags); return -EINVAL; } return 0;} static enum sdw_command_response amd_sdw_fill_msg_resp(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg, u64 response, int offset){ if (response & AMD_SDW_MCP_RESP_ACK) { if (msg->flags == SDW_MSG_FLAG_READ) msg->buf[offset] = FIELD_GET(AMD_SDW_MCP_RESP_RDATA, response); } else { if (response == -ETIMEDOUT) { dev_err_ratelimited(amd_manager->dev, "command timeout for Slave %d\n", msg->dev_num); return SDW_CMD_TIMEOUT; } else if (response & AMD_SDW_MCP_RESP_NACK) { dev_err_ratelimited(amd_manager->dev, "command response NACK received for Slave %d\n", msg->dev_num); return SDW_CMD_FAIL; } dev_dbg_ratelimited(amd_manager->dev, "command is ignored for Slave %d\n", msg->dev_num); return SDW_CMD_IGNORED; } return SDW_CMD_OK;} static unsigned int _amd_sdw_xfer_msg(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg, int cmd_offset){ u64 response; u32 upper_data = 0, lower_data = 0; amd_sdw_ctl_word_prep(&lower_data, &upper_data, msg, cmd_offset); response = amd_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data); return amd_sdw_fill_msg_resp(amd_manager, msg, response, cmd_offset);} static enum sdw_command_response amd_sdw_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg){ struct amd_sdw_manager *amd_manager = to_amd_sdw(bus); int ret, i; ret = amd_prep_msg(amd_manager, msg); if (ret) return SDW_CMD_FAIL_OTHER; for (i = 0; i < msg->len; i++) { ret = _amd_sdw_xfer_msg(amd_manager, msg, i); if (ret) return ret; } return SDW_CMD_OK;} static void amd_sdw_fill_slave_status(struct amd_sdw_manager *amd_manager, u16 index, u32 status){ switch (status) { case SDW_SLAVE_ATTACHED: case SDW_SLAVE_UNATTACHED: case SDW_SLAVE_ALERT: amd_manager->status[index] = status; break; default: amd_manager->status[index] = SDW_SLAVE_RESERVED; break; }} static void amd_sdw_process_ping_status(u64 response, struct amd_sdw_manager *amd_manager){ u64 slave_stat; u32 val; u16 dev_index; /* slave status response */ slave_stat = FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_0_3, response); slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8; dev_dbg(amd_manager->dev, "slave_stat:0x%llx\n", slave_stat); for (dev_index = 0; dev_index <= SDW_MAX_DEVICES; ++dev_index) { val = (slave_stat >> (dev_index * 2)) & AMD_SDW_MCP_SLAVE_STATUS_MASK; dev_dbg(amd_manager->dev, "val:0x%x\n", val); amd_sdw_fill_slave_status(amd_manager, dev_index, val); }} static void amd_sdw_read_and_process_ping_status(struct amd_sdw_manager *amd_manager){ u64 response; mutex_lock(&amd_manager->bus.msg_lock); response = amd_sdw_send_cmd_get_resp(amd_manager, 0, 0); mutex_unlock(&amd_manager->bus.msg_lock); amd_sdw_process_ping_status(response, amd_manager);} static u32 amd_sdw_read_ping_status(struct sdw_bus *bus){ struct amd_sdw_manager *amd_manager = to_amd_sdw(bus); u64 response; u32 slave_stat; response = amd_sdw_send_cmd_get_resp(amd_manager, 0, 0); /* slave status from ping response */ slave_stat = FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_0_3, response); slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8; dev_dbg(amd_manager->dev, "slave_stat:0x%x\n", slave_stat); return slave_stat;} static int amd_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream){ struct amd_sdw_manager *amd_manager = to_amd_sdw(bus); struct sdw_transport_data t_data = {0}; struct sdw_master_runtime *m_rt; struct sdw_port_runtime *p_rt; struct sdw_bus_params *b_params = &bus->params; int port_bo, hstart, hstop, sample_int; unsigned int rate, bps, channels; unsigned int stream_slot_size, max_slots; static unsigned int next_offset[AMD_SDW_MAX_MANAGER_COUNT] = {1}; unsigned int inst_id = amd_manager->instance; port_bo = 0; hstart = 1; hstop = bus->params.col - 1; t_data.hstop = hstop; t_data.hstart = hstart; list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { rate = m_rt->stream->params.rate; bps = m_rt->stream->params.bps; channels = m_rt->stream->params.ch_count; sample_int = (bus->params.curr_dr_freq / rate); /* Compute slots required for this stream dynamically */ stream_slot_size = bps * channels; list_for_each_entry(p_rt, &m_rt->port_list, port_node) { if (p_rt->num >= amd_manager->max_ports) { dev_err(bus->dev, "Port %d exceeds max ports %d\n", p_rt->num, amd_manager->max_ports); return -EINVAL;Lines 1–500 of 1462. Load more
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) /* * SoundWire AMD Manager driver * * Copyright 2023-24 Advanced Micro Devices, Inc. */ #include <linux/completion.h> #include <linux/cleanup.h> #include <linux/device.h> #include <linux/io.h> #include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_registers.h> #include <linux/pm_runtime.h> #include <linux/wait.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include "bus.h" #include "amd_init.h" #include "amd_manager.h" #define DRV_NAME "amd_sdw_manager" #define to_amd_sdw(b) container_of(b, struct amd_sdw_manager, bus) static int amd_sdw_clk_init_ctrl(struct amd_sdw_manager *amd_manager) { struct sdw_bus *bus = &amd_manager->bus; struct sdw_master_prop *prop = &bus->prop; u32 divider; dev_dbg(amd_manager->dev, "mclk %d max %d row %d col %d frame_rate:%d\n", prop->mclk_freq, prop->max_clk_freq, prop->default_row, prop->default_col, prop->default_frame_rate); if (!prop->default_frame_rate || !prop->default_row) { dev_err(amd_manager->dev, "Default frame_rate %d or row %d is invalid\n", prop->default_frame_rate, prop->default_row); return -EINVAL; } /* Set clock divider */ divider = (prop->mclk_freq / bus->params.curr_dr_freq); writel(divider, amd_manager->mmio + ACP_SW_CLK_FREQUENCY_CTRL); /* Set frame shape base on the actual bus frequency. */ prop->default_col = bus->params.curr_dr_freq / prop->default_frame_rate / prop->default_row; amd_manager->cols_index = sdw_find_col_index(prop->default_col); amd_manager->rows_index = sdw_find_row_index(prop->default_row); bus->params.col = prop->default_col; bus->params.row = prop->default_row; return 0; } static int amd_init_sdw_manager(struct amd_sdw_manager *amd_manager) { u32 val; int ret; writel(AMD_SDW_ENABLE, amd_manager->mmio + ACP_SW_EN); ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_EN_STATUS, val, val, ACP_DELAY_US, AMD_SDW_TIMEOUT); if (ret) return ret; /* SoundWire manager bus reset */ writel(AMD_SDW_BUS_RESET_REQ, amd_manager->mmio + ACP_SW_BUS_RESET_CTRL); ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_BUS_RESET_CTRL, val, (val & AMD_SDW_BUS_RESET_DONE), ACP_DELAY_US, AMD_SDW_TIMEOUT); if (ret) return ret; writel(AMD_SDW_BUS_RESET_CLEAR_REQ, amd_manager->mmio + ACP_SW_BUS_RESET_CTRL); ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_BUS_RESET_CTRL, val, !val, ACP_DELAY_US, AMD_SDW_TIMEOUT); if (ret) { dev_err(amd_manager->dev, "Failed to reset SoundWire manager instance%d\n", amd_manager->instance); return ret; } writel(AMD_SDW_DISABLE, amd_manager->mmio + ACP_SW_EN); return readl_poll_timeout(amd_manager->mmio + ACP_SW_EN_STATUS, val, !val, ACP_DELAY_US, AMD_SDW_TIMEOUT); } static int amd_enable_sdw_manager(struct amd_sdw_manager *amd_manager) { u32 val; writel(AMD_SDW_ENABLE, amd_manager->mmio + ACP_SW_EN); return readl_poll_timeout(amd_manager->mmio + ACP_SW_EN_STATUS, val, val, ACP_DELAY_US, AMD_SDW_TIMEOUT); } static int amd_disable_sdw_manager(struct amd_sdw_manager *amd_manager) { u32 val; writel(AMD_SDW_DISABLE, amd_manager->mmio + ACP_SW_EN); /* * After invoking manager disable sequence, check whether * manager has executed clock stop sequence. In this case, * manager should ignore checking enable status register. */ val = readl(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL); if (val) return 0; return readl_poll_timeout(amd_manager->mmio + ACP_SW_EN_STATUS, val, !val, ACP_DELAY_US, AMD_SDW_TIMEOUT); } static void amd_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager) { u32 val; mutex_lock(amd_manager->acp_sdw_lock); val = sdw_manager_reg_mask_array[amd_manager->instance]; amd_updatel(amd_manager->acp_mmio, ACP_EXTERNAL_INTR_CNTL(amd_manager->instance), val, val); mutex_unlock(amd_manager->acp_sdw_lock); writel(AMD_SDW_IRQ_MASK_0TO7, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_0TO7); writel(AMD_SDW_IRQ_MASK_8TO11, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11); writel(AMD_SDW_IRQ_ERROR_MASK, amd_manager->mmio + ACP_SW_ERROR_INTR_MASK); } static void amd_disable_sdw_interrupts(struct amd_sdw_manager *amd_manager) { u32 irq_mask; mutex_lock(amd_manager->acp_sdw_lock); irq_mask = sdw_manager_reg_mask_array[amd_manager->instance]; amd_updatel(amd_manager->acp_mmio, ACP_EXTERNAL_INTR_CNTL(amd_manager->instance), irq_mask, 0); mutex_unlock(amd_manager->acp_sdw_lock); writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_0TO7); writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11); writel(0x00, amd_manager->mmio + ACP_SW_ERROR_INTR_MASK); } static int amd_deinit_sdw_manager(struct amd_sdw_manager *amd_manager) { amd_disable_sdw_interrupts(amd_manager); return amd_disable_sdw_manager(amd_manager); } static void amd_sdw_set_frameshape(struct amd_sdw_manager *amd_manager) { u32 frame_size; frame_size = (amd_manager->rows_index << 3) | amd_manager->cols_index; writel(frame_size, amd_manager->mmio + ACP_SW_FRAMESIZE); } static void amd_sdw_wake_enable(struct amd_sdw_manager *amd_manager, bool enable) { u32 wake_ctrl; wake_ctrl = readl(amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11); if (enable) wake_ctrl |= AMD_SDW_WAKE_INTR_MASK; else wake_ctrl &= ~AMD_SDW_WAKE_INTR_MASK; writel(wake_ctrl, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11); } static int amd_sdw_set_device_state(struct amd_sdw_manager *amd_manager, u32 target_device_state) { u32 sdw_dev_state; sdw_dev_state = readl(amd_manager->acp_mmio + AMD_SDW_DEVICE_STATE); switch (amd_manager->instance) { case ACP_SDW0: u32p_replace_bits(&sdw_dev_state, target_device_state, AMD_SDW0_DEVICE_STATE_MASK); break; case ACP_SDW1: u32p_replace_bits(&sdw_dev_state, target_device_state, AMD_SDW1_DEVICE_STATE_MASK); break; default: return -EINVAL; } writel(sdw_dev_state, amd_manager->acp_mmio + AMD_SDW_DEVICE_STATE); sdw_dev_state = readl(amd_manager->acp_mmio + AMD_SDW_DEVICE_STATE); dev_dbg(amd_manager->dev, "AMD_SDW_DEVICE_STATE:0x%x\n", sdw_dev_state); return 0; } static int amd_sdw_host_wake_enable(struct amd_sdw_manager *amd_manager, bool enable) { u32 intr_cntl1; u32 sdw_host_wake_irq_mask; if (!amd_manager->wake_en_mask) return 0; switch (amd_manager->instance) { case ACP_SDW0: sdw_host_wake_irq_mask = AMD_SDW0_HOST_WAKE_INTR_MASK; break; case ACP_SDW1: sdw_host_wake_irq_mask = AMD_SDW1_HOST_WAKE_INTR_MASK; break; default: return -EINVAL; } intr_cntl1 = readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(ACP_SDW1)); if (enable) intr_cntl1 |= sdw_host_wake_irq_mask; else intr_cntl1 &= ~sdw_host_wake_irq_mask; writel(intr_cntl1, amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(ACP_SDW1)); return 0; } static void amd_sdw_ctl_word_prep(u32 *lower_word, u32 *upper_word, struct sdw_msg *msg, int cmd_offset) { u32 upper_data; u32 lower_data = 0; u16 addr; u8 upper_addr, lower_addr; u8 data = 0; addr = msg->addr + cmd_offset; upper_addr = (addr & 0xFF00) >> 8; lower_addr = addr & 0xFF; if (msg->flags == SDW_MSG_FLAG_WRITE) data = msg->buf[cmd_offset]; upper_data = FIELD_PREP(AMD_SDW_MCP_CMD_DEV_ADDR, msg->dev_num); upper_data |= FIELD_PREP(AMD_SDW_MCP_CMD_COMMAND, msg->flags + 2); upper_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_ADDR_HIGH, upper_addr); lower_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_ADDR_LOW, lower_addr); lower_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_DATA, data); *upper_word = upper_data; *lower_word = lower_data; } static u64 amd_sdw_send_cmd_get_resp(struct amd_sdw_manager *amd_manager, u32 lower_data, u32 upper_data) { u64 resp; u32 lower_resp, upper_resp; u32 sts; int ret; ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_IMM_CMD_STS, sts, !(sts & AMD_SDW_IMM_CMD_BUSY), ACP_DELAY_US, AMD_SDW_TIMEOUT); if (ret) { dev_err(amd_manager->dev, "SDW%x previous cmd status clear failed\n", amd_manager->instance); return ret; } if (sts & AMD_SDW_IMM_RES_VALID) { dev_err(amd_manager->dev, "SDW%x manager is in bad state\n", amd_manager->instance); writel(AMD_SDW_IMM_RES_VALID, amd_manager->mmio + ACP_SW_IMM_CMD_STS); } writel(upper_data, amd_manager->mmio + ACP_SW_IMM_CMD_UPPER_WORD); writel(lower_data, amd_manager->mmio + ACP_SW_IMM_CMD_LOWER_QWORD); ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_IMM_CMD_STS, sts, (sts & AMD_SDW_IMM_RES_VALID), ACP_DELAY_US, AMD_SDW_TIMEOUT); if (ret) { dev_err(amd_manager->dev, "SDW%x cmd response timeout occurred\n", amd_manager->instance); return ret; } upper_resp = readl(amd_manager->mmio + ACP_SW_IMM_RESP_UPPER_WORD); lower_resp = readl(amd_manager->mmio + ACP_SW_IMM_RESP_LOWER_QWORD); writel(AMD_SDW_IMM_RES_VALID, amd_manager->mmio + ACP_SW_IMM_CMD_STS); ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_IMM_CMD_STS, sts, !(sts & AMD_SDW_IMM_RES_VALID), ACP_DELAY_US, AMD_SDW_TIMEOUT); if (ret) { dev_err(amd_manager->dev, "SDW%x cmd status retry failed\n", amd_manager->instance); return ret; } resp = upper_resp; resp = (resp << 32) | lower_resp; return resp; } static enum sdw_command_response amd_program_scp_addr(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg) { struct sdw_msg scp_msg = {0}; u64 response_buf[2] = {0}; u32 upper_data = 0, lower_data = 0; int index; scp_msg.dev_num = msg->dev_num; scp_msg.addr = SDW_SCP_ADDRPAGE1; scp_msg.buf = &msg->addr_page1; scp_msg.flags = SDW_MSG_FLAG_WRITE; amd_sdw_ctl_word_prep(&lower_data, &upper_data, &scp_msg, 0); response_buf[0] = amd_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data); scp_msg.addr = SDW_SCP_ADDRPAGE2; scp_msg.buf = &msg->addr_page2; amd_sdw_ctl_word_prep(&lower_data, &upper_data, &scp_msg, 0); response_buf[1] = amd_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data); for (index = 0; index < 2; index++) { if (response_buf[index] == -ETIMEDOUT) { dev_err_ratelimited(amd_manager->dev, "SCP_addrpage command timeout for Slave %d\n", msg->dev_num); return SDW_CMD_TIMEOUT; } else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) { if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) { dev_err_ratelimited(amd_manager->dev, "SCP_addrpage NACKed for Slave %d\n", msg->dev_num); return SDW_CMD_FAIL; } dev_dbg_ratelimited(amd_manager->dev, "SCP_addrpage ignored for Slave %d\n", msg->dev_num); return SDW_CMD_IGNORED; } } return SDW_CMD_OK; } static int amd_prep_msg(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg) { int ret; if (msg->page) { ret = amd_program_scp_addr(amd_manager, msg); if (ret) { msg->len = 0; return ret; } } switch (msg->flags) { case SDW_MSG_FLAG_READ: case SDW_MSG_FLAG_WRITE: break; default: dev_err(amd_manager->dev, "Invalid msg cmd: %d\n", msg->flags); return -EINVAL; } return 0; } static enum sdw_command_response amd_sdw_fill_msg_resp(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg, u64 response, int offset) { if (response & AMD_SDW_MCP_RESP_ACK) { if (msg->flags == SDW_MSG_FLAG_READ) msg->buf[offset] = FIELD_GET(AMD_SDW_MCP_RESP_RDATA, response); } else { if (response == -ETIMEDOUT) { dev_err_ratelimited(amd_manager->dev, "command timeout for Slave %d\n", msg->dev_num); return SDW_CMD_TIMEOUT; } else if (response & AMD_SDW_MCP_RESP_NACK) { dev_err_ratelimited(amd_manager->dev, "command response NACK received for Slave %d\n", msg->dev_num); return SDW_CMD_FAIL; } dev_dbg_ratelimited(amd_manager->dev, "command is ignored for Slave %d\n", msg->dev_num); return SDW_CMD_IGNORED; } return SDW_CMD_OK; } static unsigned int _amd_sdw_xfer_msg(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg, int cmd_offset) { u64 response; u32 upper_data = 0, lower_data = 0; amd_sdw_ctl_word_prep(&lower_data, &upper_data, msg, cmd_offset); response = amd_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data); return amd_sdw_fill_msg_resp(amd_manager, msg, response, cmd_offset); } static enum sdw_command_response amd_sdw_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg) { struct amd_sdw_manager *amd_manager = to_amd_sdw(bus); int ret, i; ret = amd_prep_msg(amd_manager, msg); if (ret) return SDW_CMD_FAIL_OTHER; for (i = 0; i < msg->len; i++) { ret = _amd_sdw_xfer_msg(amd_manager, msg, i); if (ret) return ret; } return SDW_CMD_OK; } static void amd_sdw_fill_slave_status(struct amd_sdw_manager *amd_manager, u16 index, u32 status) { switch (status) { case SDW_SLAVE_ATTACHED: case SDW_SLAVE_UNATTACHED: case SDW_SLAVE_ALERT: amd_manager->status[index] = status; break; default: amd_manager->status[index] = SDW_SLAVE_RESERVED; break; } } static void amd_sdw_process_ping_status(u64 response, struct amd_sdw_manager *amd_manager) { u64 slave_stat; u32 val; u16 dev_index; /* slave status response */ slave_stat = FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_0_3, response); slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8; dev_dbg(amd_manager->dev, "slave_stat:0x%llx\n", slave_stat); for (dev_index = 0; dev_index <= SDW_MAX_DEVICES; ++dev_index) { val = (slave_stat >> (dev_index * 2)) & AMD_SDW_MCP_SLAVE_STATUS_MASK; dev_dbg(amd_manager->dev, "val:0x%x\n", val); amd_sdw_fill_slave_status(amd_manager, dev_index, val); } } static void amd_sdw_read_and_process_ping_status(struct amd_sdw_manager *amd_manager) { u64 response; mutex_lock(&amd_manager->bus.msg_lock); response = amd_sdw_send_cmd_get_resp(amd_manager, 0, 0); mutex_unlock(&amd_manager->bus.msg_lock); amd_sdw_process_ping_status(response, amd_manager); } static u32 amd_sdw_read_ping_status(struct sdw_bus *bus) { struct amd_sdw_manager *amd_manager = to_amd_sdw(bus); u64 response; u32 slave_stat; response = amd_sdw_send_cmd_get_resp(amd_manager, 0, 0); /* slave status from ping response */ slave_stat = FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_0_3, response); slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8; dev_dbg(amd_manager->dev, "slave_stat:0x%x\n", slave_stat); return slave_stat; } static int amd_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream) { struct amd_sdw_manager *amd_manager = to_amd_sdw(bus); struct sdw_transport_data t_data = {0}; struct sdw_master_runtime *m_rt; struct sdw_port_runtime *p_rt; struct sdw_bus_params *b_params = &bus->params; int port_bo, hstart, hstop, sample_int; unsigned int rate, bps, channels; unsigned int stream_slot_size, max_slots; static unsigned int next_offset[AMD_SDW_MAX_MANAGER_COUNT] = {1}; unsigned int inst_id = amd_manager->instance; port_bo = 0; hstart = 1; hstop = bus->params.col - 1; t_data.hstop = hstop; t_data.hstart = hstart; list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { rate = m_rt->stream->params.rate; bps = m_rt->stream->params.bps; channels = m_rt->stream->params.ch_count; sample_int = (bus->params.curr_dr_freq / rate); /* Compute slots required for this stream dynamically */ stream_slot_size = bps * channels; list_for_each_entry(p_rt, &m_rt->port_list, port_node) { if (p_rt->num >= amd_manager->max_ports) { dev_err(bus->dev, "Port %d exceeds max ports %d\n", p_rt->num, amd_manager->max_ports); return -EINVAL;