Commit 4c9818d8 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'soundwire-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire

Pull soundwire updates from Vinod Koul:
 "This features AMD soundwire controller driver, a bunch of Intel
  changes for future platform support, sdw API updates etc:

   - Support for AMD soundwire controller

   - Intel driver updates to support future platforms

   - Core API sdw_nread/nwrite_no_pm updates to handle page boundaries"

* tag 'soundwire-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire: (38 commits)
  soundwire: intel_auxdevice: improve pm_prepare step
  soundwire: bus: Fix unbalanced pm_runtime_put() causing usage count underflow
  soundwire: intel: don't save hw_params for use in prepare
  soundwire: bus: Update sdw_nread/nwrite_no_pm to handle page boundaries
  soundwire: bus: Update kernel doc for no_pm functions
  soundwire: bus: Remove now outdated comments on no_pm IO
  soundwire: stream: uniquify dev_err() logs
  soundwire: stream: remove bus->dev from logs on multiple buses
  soundwire: amd: add pm_prepare callback and pm ops support
  soundwire: amd: handle SoundWire wake enable interrupt
  soundwire: amd: add runtime pm ops for AMD SoundWire manager driver
  soundwire: amd: add SoundWire manager interrupt handling
  soundwire: amd: enable build for AMD SoundWire manager driver
  soundwire: amd: register SoundWire manager dai ops
  soundwire: amd: Add support for AMD Manager driver
  soundwire: export sdw_compute_slave_ports() function
  soundwire: stream: restore cumulative bus bandwidth when compute_params callback failed
  soundwire: bandwidth allocation: Use hweight32() to calculate set bits
  soundwire: qcom: gracefully handle too many ports in DT
  soundwire: qcom: define hardcoded version magic numbers
  ...
parents 54bdf8a3 67572c8d
......@@ -18,6 +18,16 @@ if SOUNDWIRE
comment "SoundWire Devices"
config SOUNDWIRE_AMD
tristate "AMD SoundWire Manager driver"
select SOUNDWIRE_GENERIC_ALLOCATION
depends on ACPI && SND_SOC
help
SoundWire AMD Manager driver.
If you have an AMD platform which has a SoundWire Manager then
enable this config option to get the SoundWire support for that
device.
config SOUNDWIRE_CADENCE
tristate
......
......@@ -15,12 +15,17 @@ ifdef CONFIG_DEBUG_FS
soundwire-bus-y += debugfs.o
endif
#AMD driver
soundwire-amd-y := amd_manager.o
obj-$(CONFIG_SOUNDWIRE_AMD) += soundwire-amd.o
#Cadence Objs
soundwire-cadence-y := cadence_master.o
obj-$(CONFIG_SOUNDWIRE_CADENCE) += soundwire-cadence.o
#Intel driver
soundwire-intel-y := intel.o intel_auxdevice.o intel_init.o dmi-quirks.o
soundwire-intel-y := intel.o intel_auxdevice.o intel_init.o dmi-quirks.o \
intel_bus_common.o
obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel.o
#Qualcomm driver
......
// SPDX-License-Identifier: GPL-2.0+
/*
* SoundWire AMD Manager driver
*
* Copyright 2023 Advanced Micro Devices, Inc.
*/
#include <linux/completion.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_manager.h"
#define DRV_NAME "amd_sdw_manager"
#define to_amd_sdw(b) container_of(b, struct amd_sdw_manager, bus)
static void amd_enable_sdw_pads(struct amd_sdw_manager *amd_manager)
{
u32 sw_pad_pulldown_val;
u32 val;
mutex_lock(amd_manager->acp_sdw_lock);
val = readl(amd_manager->acp_mmio + ACP_SW_PAD_KEEPER_EN);
val |= amd_manager->reg_mask->sw_pad_enable_mask;
writel(val, amd_manager->acp_mmio + ACP_SW_PAD_KEEPER_EN);
usleep_range(1000, 1500);
sw_pad_pulldown_val = readl(amd_manager->acp_mmio + ACP_PAD_PULLDOWN_CTRL);
sw_pad_pulldown_val &= amd_manager->reg_mask->sw_pad_pulldown_mask;
writel(sw_pad_pulldown_val, amd_manager->acp_mmio + ACP_PAD_PULLDOWN_CTRL);
mutex_unlock(amd_manager->acp_sdw_lock);
}
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)
{
struct sdw_manager_reg_mask *reg_mask = amd_manager->reg_mask;
u32 val;
mutex_lock(amd_manager->acp_sdw_lock);
val = readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance));
val |= reg_mask->acp_sdw_intr_mask;
writel(val, amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance));
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)
{
struct sdw_manager_reg_mask *reg_mask = amd_manager->reg_mask;
u32 val;
mutex_lock(amd_manager->acp_sdw_lock);
val = readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance));
val &= ~reg_mask->acp_sdw_intr_mask;
writel(val, amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance));
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_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(0x00, 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_err_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_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;
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;
sample_int = (bus->params.curr_dr_freq / rate);
list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
port_bo = (p_rt->num * 64) + 1;
dev_dbg(bus->dev, "p_rt->num=%d hstart=%d hstop=%d port_bo=%d\n",
p_rt->num, hstart, hstop, port_bo);
sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
false, SDW_BLK_GRP_CNT_1, sample_int,
port_bo, port_bo >> 8, hstart, hstop,
SDW_BLK_PKG_PER_PORT, 0x0);
sdw_fill_port_params(&p_rt->port_params,
p_rt->num, bps,
SDW_PORT_FLOW_MODE_ISOCH,
b_params->m_data_mode);
t_data.hstart = hstart;
t_data.hstop = hstop;
t_data.block_offset = port_bo;
t_data.sub_block_offset = 0;
}
sdw_compute_slave_ports(m_rt, &t_data);
}
return 0;
}
static int amd_sdw_port_params(struct sdw_bus *bus, struct sdw_port_params *p_params,
unsigned int bank)
{
struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);
u32 frame_fmt_reg, dpn_frame_fmt;
dev_dbg(amd_manager->dev, "p_params->num:0x%x\n", p_params->num);
switch (amd_manager->instance) {
case ACP_SDW0:
frame_fmt_reg = sdw0_manager_dp_reg[p_params->num].frame_fmt_reg;
break;
case ACP_SDW1:
frame_fmt_reg = sdw1_manager_dp_reg[p_params->num].frame_fmt_reg;
break;
default:
return -EINVAL;
}
dpn_frame_fmt = readl(amd_manager->mmio + frame_fmt_reg);
u32p_replace_bits(&dpn_frame_fmt, p_params->flow_mode, AMD_DPN_FRAME_FMT_PFM);
u32p_replace_bits(&dpn_frame_fmt, p_params->data_mode, AMD_DPN_FRAME_FMT_PDM);
u32p_replace_bits(&dpn_frame_fmt, p_params->bps - 1, AMD_DPN_FRAME_FMT_WORD_LEN);
writel(dpn_frame_fmt, amd_manager->mmio + frame_fmt_reg);
return 0;
}
static int amd_sdw_transport_params(struct sdw_bus *bus,
struct sdw_transport_params *params,
enum sdw_reg_bank bank)
{
struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);
u32 dpn_frame_fmt;
u32 dpn_sampleinterval;
u32 dpn_hctrl;
u32 dpn_offsetctrl;
u32 dpn_lanectrl;
u32 frame_fmt_reg, sample_int_reg, hctrl_dp0_reg;
u32 offset_reg, lane_ctrl_ch_en_reg;
switch (amd_manager->instance) {
case ACP_SDW0:
frame_fmt_reg = sdw0_manager_dp_reg[params->port_num].frame_fmt_reg;
sample_int_reg = sdw0_manager_dp_reg[params->port_num].sample_int_reg;
hctrl_dp0_reg = sdw0_manager_dp_reg[params->port_num].hctrl_dp0_reg;
offset_reg = sdw0_manager_dp_reg[params->port_num].offset_reg;
lane_ctrl_ch_en_reg = sdw0_manager_dp_reg[params->port_num].lane_ctrl_ch_en_reg;
break;
case ACP_SDW1:
frame_fmt_reg = sdw1_manager_dp_reg[params->port_num].frame_fmt_reg;
sample_int_reg = sdw1_manager_dp_reg[params->port_num].sample_int_reg;
hctrl_dp0_reg = sdw1_manager_dp_reg[params->port_num].hctrl_dp0_reg;
offset_reg = sdw1_manager_dp_reg[params->port_num].offset_reg;
lane_ctrl_ch_en_reg = sdw1_manager_dp_reg[params->port_num].lane_ctrl_ch_en_reg;
break;
default:
return -EINVAL;
}
writel(AMD_SDW_SSP_COUNTER_VAL, amd_manager->mmio + ACP_SW_SSP_COUNTER);
dpn_frame_fmt = readl(amd_manager->mmio + frame_fmt_reg);
u32p_replace_bits(&dpn_frame_fmt, params->blk_pkg_mode, AMD_DPN_FRAME_FMT_BLK_PKG_MODE);
u32p_replace_bits(&dpn_frame_fmt, params->blk_grp_ctrl, AMD_DPN_FRAME_FMT_BLK_GRP_CTRL);
u32p_replace_bits(&dpn_frame_fmt, SDW_STREAM_PCM, AMD_DPN_FRAME_FMT_PCM_OR_PDM);
writel(dpn_frame_fmt, amd_manager->mmio + frame_fmt_reg);
dpn_sampleinterval = params->sample_interval - 1;
writel(dpn_sampleinterval, amd_manager->mmio + sample_int_reg);
dpn_hctrl = FIELD_PREP(AMD_DPN_HCTRL_HSTOP, params->hstop);
dpn_hctrl |= FIELD_PREP(AMD_DPN_HCTRL_HSTART, params->hstart);
writel(dpn_hctrl, amd_manager->mmio + hctrl_dp0_reg);
dpn_offsetctrl = FIELD_PREP(AMD_DPN_OFFSET_CTRL_1, params->offset1);
dpn_offsetctrl |= FIELD_PREP(AMD_DPN_OFFSET_CTRL_2, params->offset2);
writel(dpn_offsetctrl, amd_manager->mmio + offset_reg);
/*
* lane_ctrl_ch_en_reg will be used to program lane_ctrl and ch_mask
* parameters.
*/
dpn_lanectrl = readl(amd_manager->mmio + lane_ctrl_ch_en_reg);
u32p_replace_bits(&dpn_lanectrl, params->lane_ctrl, AMD_DPN_CH_EN_LCTRL);
writel(dpn_lanectrl, amd_manager->mmio + lane_ctrl_ch_en_reg);
return 0;
}
static int amd_sdw_port_enable(struct sdw_bus *bus,
struct sdw_enable_ch *enable_ch,
unsigned int bank)
{
struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);
u32 dpn_ch_enable;
u32 lane_ctrl_ch_en_reg;
switch (amd_manager->instance) {
case ACP_SDW0:
lane_ctrl_ch_en_reg = sdw0_manager_dp_reg[enable_ch->port_num].lane_ctrl_ch_en_reg;
break;
case ACP_SDW1:
lane_ctrl_ch_en_reg = sdw1_manager_dp_reg[enable_ch->port_num].lane_ctrl_ch_en_reg;
break;
default:
return -EINVAL;
}
/*
* lane_ctrl_ch_en_reg will be used to program lane_ctrl and ch_mask
* parameters.
*/
dpn_ch_enable = readl(amd_manager->mmio + lane_ctrl_ch_en_reg);
u32p_replace_bits(&dpn_ch_enable, enable_ch->ch_mask, AMD_DPN_CH_EN_CHMASK);
if (enable_ch->enable)
writel(dpn_ch_enable, amd_manager->mmio + lane_ctrl_ch_en_reg);
else
writel(0, amd_manager->mmio + lane_ctrl_ch_en_reg);
return 0;
}
static int sdw_master_read_amd_prop(struct sdw_bus *bus)
{
struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);
struct fwnode_handle *link;
struct sdw_master_prop *prop;
u32 quirk_mask = 0;
u32 wake_en_mask = 0;
u32 power_mode_mask = 0;
char name[32];
prop = &bus->prop;
/* Find manager handle */
snprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", bus->link_id);
link = device_get_named_child_node(bus->dev, name);
if (!link) {
dev_err(bus->dev, "Manager node %s not found\n", name);
return -EIO;
}
fwnode_property_read_u32(link, "amd-sdw-enable", &quirk_mask);
if (!(quirk_mask & AMD_SDW_QUIRK_MASK_BUS_ENABLE))
prop->hw_disabled = true;
prop->quirks = SDW_MASTER_QUIRKS_CLEAR_INITIAL_CLASH |
SDW_MASTER_QUIRKS_CLEAR_INITIAL_PARITY;
fwnode_property_read_u32(link, "amd-sdw-wakeup-enable", &wake_en_mask);
amd_manager->wake_en_mask = wake_en_mask;
fwnode_property_read_u32(link, "amd-sdw-power-mode", &power_mode_mask);
amd_manager->power_mode_mask = power_mode_mask;
return 0;
}
static int amd_prop_read(struct sdw_bus *bus)
{
sdw_master_read_prop(bus);
sdw_master_read_amd_prop(bus);
return 0;
}
static const struct sdw_master_port_ops amd_sdw_port_ops = {
.dpn_set_port_params = amd_sdw_port_params,
.dpn_set_port_transport_params = amd_sdw_transport_params,
.dpn_port_enable_ch = amd_sdw_port_enable,
};
static const struct sdw_master_ops amd_sdw_ops = {
.read_prop = amd_prop_read,
.xfer_msg = amd_sdw_xfer_msg,
.read_ping_status = amd_sdw_read_ping_status,
};
static int amd_sdw_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct amd_sdw_manager *amd_manager = snd_soc_dai_get_drvdata(dai);
struct sdw_amd_dai_runtime *dai_runtime;
struct sdw_stream_config sconfig;
struct sdw_port_config *pconfig;
int ch, dir;
int ret;
dai_runtime = amd_manager->dai_runtime_array[dai->id];
if (!dai_runtime)
return -EIO;
ch = params_channels(params);
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
dir = SDW_DATA_DIR_RX;
else
dir = SDW_DATA_DIR_TX;
dev_dbg(amd_manager->dev, "dir:%d dai->id:0x%x\n", dir, dai->id);
sconfig.direction = dir;
sconfig.ch_count = ch;
sconfig.frame_rate = params_rate(params);
sconfig.type = dai_runtime->stream_type;
sconfig.bps = snd_pcm_format_width(params_format(params));
/* Port configuration */
pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL);
if (!pconfig) {
ret = -ENOMEM;
goto error;
}
pconfig->num = dai->id;
pconfig->ch_mask = (1 << ch) - 1;
ret = sdw_stream_add_master(&amd_manager->bus, &sconfig,
pconfig, 1, dai_runtime->stream);
if (ret)
dev_err(amd_manager->dev, "add manager to stream failed:%d\n", ret);
kfree(pconfig);
error:
return ret;
}
static int amd_sdw_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct amd_sdw_manager *amd_manager = snd_soc_dai_get_drvdata(dai);
struct sdw_amd_dai_runtime *dai_runtime;
int ret;
dai_runtime = amd_manager->dai_runtime_array[dai->id];
if (!dai_runtime)
return -EIO;
ret = sdw_stream_remove_master(&amd_manager->bus, dai_runtime->stream);
if (ret < 0)
dev_err(dai->dev, "remove manager from stream %s failed: %d\n",
dai_runtime->stream->name, ret);
return ret;
}
static int amd_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
{
struct amd_sdw_manager *amd_manager = snd_soc_dai_get_drvdata(dai);
struct sdw_amd_dai_runtime *dai_runtime;
dai_runtime = amd_manager->dai_runtime_array[dai->id];
if (stream) {
/* first paranoia check */
if (dai_runtime) {
dev_err(dai->dev, "dai_runtime already allocated for dai %s\n", dai->name);
return -EINVAL;
}
/* allocate and set dai_runtime info */
dai_runtime = kzalloc(sizeof(*dai_runtime), GFP_KERNEL);
if (!dai_runtime)
return -ENOMEM;
dai_runtime->stream_type = SDW_STREAM_PCM;
dai_runtime->bus = &amd_manager->bus;
dai_runtime->stream = stream;
amd_manager->dai_runtime_array[dai->id] = dai_runtime;
} else {
/* second paranoia check */
if (!dai_runtime) {
dev_err(dai->dev, "dai_runtime not allocated for dai %s\n", dai->name);
return -EINVAL;
}
/* for NULL stream we release allocated dai_runtime */
kfree(dai_runtime);
amd_manager->dai_runtime_array[dai->id] = NULL;
}
return 0;
}
static int amd_pcm_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
{
return amd_set_sdw_stream(dai, stream, direction);
}
static void *amd_get_sdw_stream(struct snd_soc_dai *dai, int direction)
{
struct amd_sdw_manager *amd_manager = snd_soc_dai_get_drvdata(dai);
struct sdw_amd_dai_runtime *dai_runtime;
dai_runtime = amd_manager->dai_runtime_array[dai->id];
if (!dai_runtime)
return ERR_PTR(-EINVAL);
return dai_runtime->stream;
}
static const struct snd_soc_dai_ops amd_sdw_dai_ops = {
.hw_params = amd_sdw_hw_params,
.hw_free = amd_sdw_hw_free,
.set_stream = amd_pcm_set_sdw_stream,
.get_stream = amd_get_sdw_stream,
};
static const struct snd_soc_component_driver amd_sdw_dai_component = {
.name = "soundwire",
};
static int amd_sdw_register_dais(struct amd_sdw_manager *amd_manager)
{
struct sdw_amd_dai_runtime **dai_runtime_array;
struct snd_soc_dai_driver *dais;
struct snd_soc_pcm_stream *stream;
struct device *dev;
int i, num_dais;
dev = amd_manager->dev;
num_dais = amd_manager->num_dout_ports + amd_manager->num_din_ports;
dais = devm_kcalloc(dev, num_dais, sizeof(*dais), GFP_KERNEL);
if (!dais)
return -ENOMEM;
dai_runtime_array = devm_kcalloc(dev, num_dais,
sizeof(struct sdw_amd_dai_runtime *),
GFP_KERNEL);
if (!dai_runtime_array)
return -ENOMEM;
amd_manager->dai_runtime_array = dai_runtime_array;
for (i = 0; i < num_dais; i++) {
dais[i].name = devm_kasprintf(dev, GFP_KERNEL, "SDW%d Pin%d", amd_manager->instance,
i);
if (!dais[i].name)
return -ENOMEM;
if (i < amd_manager->num_dout_ports)
stream = &dais[i].playback;
else
stream = &dais[i].capture;
stream->channels_min = 2;
stream->channels_max = 2;
stream->rates = SNDRV_PCM_RATE_48000;
stream->formats = SNDRV_PCM_FMTBIT_S16_LE;
dais[i].ops = &amd_sdw_dai_ops;
dais[i].id = i;
}
return devm_snd_soc_register_component(dev, &amd_sdw_dai_component,
dais, num_dais);
}
static void amd_sdw_update_slave_status_work(struct work_struct *work)
{
struct amd_sdw_manager *amd_manager =
container_of(work, struct amd_sdw_manager, amd_sdw_work);
int retry_count = 0;
if (amd_manager->status[0] == SDW_SLAVE_ATTACHED) {
writel(0, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_0TO7);
writel(0, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11);
}
update_status:
sdw_handle_slave_status(&amd_manager->bus, amd_manager->status);
/*
* During the peripheral enumeration sequence, the SoundWire manager interrupts
* are masked. Once the device number programming is done for all peripherals,
* interrupts will be unmasked. Read the peripheral device status from ping command
* and process the response. This sequence will ensure all peripheral devices enumerated
* and initialized properly.
*/
if (amd_manager->status[0] == SDW_SLAVE_ATTACHED) {
if (retry_count++ < SDW_MAX_DEVICES) {
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);
amd_sdw_read_and_process_ping_status(amd_manager);
goto update_status;
} else {
dev_err_ratelimited(amd_manager->dev,
"Device0 detected after %d iterations\n",
retry_count);
}
}
}
static void amd_sdw_update_slave_status(u32 status_change_0to7, u32 status_change_8to11,
struct amd_sdw_manager *amd_manager)
{
u64 slave_stat;
u32 val;
int dev_index;
if (status_change_0to7 == AMD_SDW_SLAVE_0_ATTACHED)
memset(amd_manager->status, 0, sizeof(amd_manager->status));
slave_stat = status_change_0to7;
slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STATUS_8TO_11, status_change_8to11) << 32;
dev_dbg(amd_manager->dev, "status_change_0to7:0x%x status_change_8to11:0x%x\n",
status_change_0to7, status_change_8to11);
if (slave_stat) {
for (dev_index = 0; dev_index <= SDW_MAX_DEVICES; ++dev_index) {
if (slave_stat & AMD_SDW_MCP_SLAVE_STATUS_VALID_MASK(dev_index)) {
val = (slave_stat >> AMD_SDW_MCP_SLAVE_STAT_SHIFT_MASK(dev_index)) &
AMD_SDW_MCP_SLAVE_STATUS_MASK;
amd_sdw_fill_slave_status(amd_manager, dev_index, val);
}
}
}
}
static void amd_sdw_process_wake_event(struct amd_sdw_manager *amd_manager)
{
pm_request_resume(amd_manager->dev);
writel(0x00, amd_manager->acp_mmio + ACP_SW_WAKE_EN(amd_manager->instance));
writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_8TO11);
}
static void amd_sdw_irq_thread(struct work_struct *work)
{
struct amd_sdw_manager *amd_manager =
container_of(work, struct amd_sdw_manager, amd_sdw_irq_thread);
u32 status_change_8to11;
u32 status_change_0to7;
status_change_8to11 = readl(amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_8TO11);
status_change_0to7 = readl(amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_0TO7);
dev_dbg(amd_manager->dev, "[SDW%d] SDW INT: 0to7=0x%x, 8to11=0x%x\n",
amd_manager->instance, status_change_0to7, status_change_8to11);
if (status_change_8to11 & AMD_SDW_WAKE_STAT_MASK)
return amd_sdw_process_wake_event(amd_manager);
if (status_change_8to11 & AMD_SDW_PREQ_INTR_STAT) {
amd_sdw_read_and_process_ping_status(amd_manager);
} else {
/* Check for the updated status on peripheral device */
amd_sdw_update_slave_status(status_change_0to7, status_change_8to11, amd_manager);
}
if (status_change_8to11 || status_change_0to7)
schedule_work(&amd_manager->amd_sdw_work);
writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_8TO11);
writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_0TO7);
}
static void amd_sdw_probe_work(struct work_struct *work)
{
struct amd_sdw_manager *amd_manager = container_of(work, struct amd_sdw_manager,
probe_work);
struct sdw_master_prop *prop;
int ret;
prop = &amd_manager->bus.prop;
if (!prop->hw_disabled) {
amd_enable_sdw_pads(amd_manager);
ret = amd_init_sdw_manager(amd_manager);
if (ret)
return;
amd_enable_sdw_interrupts(amd_manager);
ret = amd_enable_sdw_manager(amd_manager);
if (ret)
return;
amd_sdw_set_frameshape(amd_manager);
}
/* Enable runtime PM */
pm_runtime_set_autosuspend_delay(amd_manager->dev, AMD_SDW_MASTER_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(amd_manager->dev);
pm_runtime_mark_last_busy(amd_manager->dev);
pm_runtime_set_active(amd_manager->dev);
pm_runtime_enable(amd_manager->dev);
}
static int amd_sdw_manager_probe(struct platform_device *pdev)
{
const struct acp_sdw_pdata *pdata = pdev->dev.platform_data;
struct resource *res;
struct device *dev = &pdev->dev;
struct sdw_master_prop *prop;
struct sdw_bus_params *params;
struct amd_sdw_manager *amd_manager;
int ret;
amd_manager = devm_kzalloc(dev, sizeof(struct amd_sdw_manager), GFP_KERNEL);
if (!amd_manager)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENOMEM;
amd_manager->acp_mmio = devm_ioremap(dev, res->start, resource_size(res));
if (IS_ERR(amd_manager->mmio)) {
dev_err(dev, "mmio not found\n");
return PTR_ERR(amd_manager->mmio);
}
amd_manager->instance = pdata->instance;
amd_manager->mmio = amd_manager->acp_mmio +
(amd_manager->instance * SDW_MANAGER_REG_OFFSET);
amd_manager->acp_sdw_lock = pdata->acp_sdw_lock;
amd_manager->cols_index = sdw_find_col_index(AMD_SDW_DEFAULT_COLUMNS);
amd_manager->rows_index = sdw_find_row_index(AMD_SDW_DEFAULT_ROWS);
amd_manager->dev = dev;
amd_manager->bus.ops = &amd_sdw_ops;
amd_manager->bus.port_ops = &amd_sdw_port_ops;
amd_manager->bus.compute_params = &amd_sdw_compute_params;
amd_manager->bus.clk_stop_timeout = 200;
amd_manager->bus.link_id = amd_manager->instance;
switch (amd_manager->instance) {
case ACP_SDW0:
amd_manager->num_dout_ports = AMD_SDW0_MAX_TX_PORTS;
amd_manager->num_din_ports = AMD_SDW0_MAX_RX_PORTS;
break;
case ACP_SDW1:
amd_manager->num_dout_ports = AMD_SDW1_MAX_TX_PORTS;
amd_manager->num_din_ports = AMD_SDW1_MAX_RX_PORTS;
break;
default:
return -EINVAL;
}
amd_manager->reg_mask = &sdw_manager_reg_mask_array[amd_manager->instance];
params = &amd_manager->bus.params;
params->max_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
params->curr_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
params->col = AMD_SDW_DEFAULT_COLUMNS;
params->row = AMD_SDW_DEFAULT_ROWS;
prop = &amd_manager->bus.prop;
prop->clk_freq = &amd_sdw_freq_tbl[0];
prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ;
ret = sdw_bus_master_add(&amd_manager->bus, dev, dev->fwnode);
if (ret) {
dev_err(dev, "Failed to register SoundWire manager(%d)\n", ret);
return ret;
}
ret = amd_sdw_register_dais(amd_manager);
if (ret) {
dev_err(dev, "CPU DAI registration failed\n");
sdw_bus_master_delete(&amd_manager->bus);
return ret;
}
dev_set_drvdata(dev, amd_manager);
INIT_WORK(&amd_manager->amd_sdw_irq_thread, amd_sdw_irq_thread);
INIT_WORK(&amd_manager->amd_sdw_work, amd_sdw_update_slave_status_work);
INIT_WORK(&amd_manager->probe_work, amd_sdw_probe_work);
/*
* Instead of having lengthy probe sequence, use deferred probe.
*/
schedule_work(&amd_manager->probe_work);
return 0;
}
static int amd_sdw_manager_remove(struct platform_device *pdev)
{
struct amd_sdw_manager *amd_manager = dev_get_drvdata(&pdev->dev);
pm_runtime_disable(&pdev->dev);
cancel_work_sync(&amd_manager->probe_work);
amd_disable_sdw_interrupts(amd_manager);
sdw_bus_master_delete(&amd_manager->bus);
return amd_disable_sdw_manager(amd_manager);
}
static int amd_sdw_clock_stop(struct amd_sdw_manager *amd_manager)
{
u32 val;
int ret;
ret = sdw_bus_prep_clk_stop(&amd_manager->bus);
if (ret < 0 && ret != -ENODATA) {
dev_err(amd_manager->dev, "prepare clock stop failed %d", ret);
return 0;
}
ret = sdw_bus_clk_stop(&amd_manager->bus);
if (ret < 0 && ret != -ENODATA) {
dev_err(amd_manager->dev, "bus clock stop failed %d", ret);
return 0;
}
ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL, val,
(val & AMD_SDW_CLK_STOP_DONE), ACP_DELAY_US, AMD_SDW_TIMEOUT);
if (ret) {
dev_err(amd_manager->dev, "SDW%x clock stop failed\n", amd_manager->instance);
return 0;
}
amd_manager->clk_stopped = true;
if (amd_manager->wake_en_mask)
writel(0x01, amd_manager->acp_mmio + ACP_SW_WAKE_EN(amd_manager->instance));
dev_dbg(amd_manager->dev, "SDW%x clock stop successful\n", amd_manager->instance);
return 0;
}
static int amd_sdw_clock_stop_exit(struct amd_sdw_manager *amd_manager)
{
int ret;
u32 val;
if (amd_manager->clk_stopped) {
val = readl(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL);
val |= AMD_SDW_CLK_RESUME_REQ;
writel(val, amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL);
ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL, val,
(val & AMD_SDW_CLK_RESUME_DONE), ACP_DELAY_US,
AMD_SDW_TIMEOUT);
if (val & AMD_SDW_CLK_RESUME_DONE) {
writel(0, amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL);
ret = sdw_bus_exit_clk_stop(&amd_manager->bus);
if (ret < 0)
dev_err(amd_manager->dev, "bus failed to exit clock stop %d\n",
ret);
amd_manager->clk_stopped = false;
}
}
if (amd_manager->clk_stopped) {
dev_err(amd_manager->dev, "SDW%x clock stop exit failed\n", amd_manager->instance);
return 0;
}
dev_dbg(amd_manager->dev, "SDW%x clock stop exit successful\n", amd_manager->instance);
return 0;
}
static int amd_resume_child_device(struct device *dev, void *data)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
int ret;
if (!slave->probed) {
dev_dbg(dev, "skipping device, no probed driver\n");
return 0;
}
if (!slave->dev_num_sticky) {
dev_dbg(dev, "skipping device, never detected on bus\n");
return 0;
}
ret = pm_request_resume(dev);
if (ret < 0) {
dev_err(dev, "pm_request_resume failed: %d\n", ret);
return ret;
}
return 0;
}
static int __maybe_unused amd_pm_prepare(struct device *dev)
{
struct amd_sdw_manager *amd_manager = dev_get_drvdata(dev);
struct sdw_bus *bus = &amd_manager->bus;
int ret;
if (bus->prop.hw_disabled) {
dev_dbg(bus->dev, "SoundWire manager %d is disabled, ignoring\n",
bus->link_id);
return 0;
}
/*
* When multiple peripheral devices connected over the same link, if SoundWire manager
* device is not in runtime suspend state, observed that device alerts are missing
* without pm_prepare on AMD platforms in clockstop mode0.
*/
if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
ret = pm_request_resume(dev);
if (ret < 0) {
dev_err(bus->dev, "pm_request_resume failed: %d\n", ret);
return 0;
}
}
/* To force peripheral devices to system level suspend state, resume the devices
* from runtime suspend state first. Without that unable to dispatch the alert
* status to peripheral driver during system level resume as they are in runtime
* suspend state.
*/
ret = device_for_each_child(bus->dev, NULL, amd_resume_child_device);
if (ret < 0)
dev_err(dev, "amd_resume_child_device failed: %d\n", ret);
return 0;
}
static int __maybe_unused amd_suspend(struct device *dev)
{
struct amd_sdw_manager *amd_manager = dev_get_drvdata(dev);
struct sdw_bus *bus = &amd_manager->bus;
int ret;
if (bus->prop.hw_disabled) {
dev_dbg(bus->dev, "SoundWire manager %d is disabled, ignoring\n",
bus->link_id);
return 0;
}
if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
return amd_sdw_clock_stop(amd_manager);
} else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
/*
* As per hardware programming sequence on AMD platforms,
* clock stop should be invoked first before powering-off
*/
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
return amd_deinit_sdw_manager(amd_manager);
}
return 0;
}
static int __maybe_unused amd_suspend_runtime(struct device *dev)
{
struct amd_sdw_manager *amd_manager = dev_get_drvdata(dev);
struct sdw_bus *bus = &amd_manager->bus;
int ret;
if (bus->prop.hw_disabled) {
dev_dbg(bus->dev, "SoundWire manager %d is disabled,\n",
bus->link_id);
return 0;
}
if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
return amd_sdw_clock_stop(amd_manager);
} else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
return amd_deinit_sdw_manager(amd_manager);
}
return 0;
}
static int __maybe_unused amd_resume_runtime(struct device *dev)
{
struct amd_sdw_manager *amd_manager = dev_get_drvdata(dev);
struct sdw_bus *bus = &amd_manager->bus;
int ret;
u32 val;
if (bus->prop.hw_disabled) {
dev_dbg(bus->dev, "SoundWire manager %d is disabled, ignoring\n",
bus->link_id);
return 0;
}
if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
return amd_sdw_clock_stop_exit(amd_manager);
} else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
val = readl(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL);
if (val) {
val |= AMD_SDW_CLK_RESUME_REQ;
writel(val, amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL);
ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL, val,
(val & AMD_SDW_CLK_RESUME_DONE), ACP_DELAY_US,
AMD_SDW_TIMEOUT);
if (val & AMD_SDW_CLK_RESUME_DONE) {
writel(0, amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL);
amd_manager->clk_stopped = false;
}
}
sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
amd_init_sdw_manager(amd_manager);
amd_enable_sdw_interrupts(amd_manager);
ret = amd_enable_sdw_manager(amd_manager);
if (ret)
return ret;
amd_sdw_set_frameshape(amd_manager);
}
return 0;
}
static const struct dev_pm_ops amd_pm = {
.prepare = amd_pm_prepare,
SET_SYSTEM_SLEEP_PM_OPS(amd_suspend, amd_resume_runtime)
SET_RUNTIME_PM_OPS(amd_suspend_runtime, amd_resume_runtime, NULL)
};
static struct platform_driver amd_sdw_driver = {
.probe = &amd_sdw_manager_probe,
.remove = &amd_sdw_manager_remove,
.driver = {
.name = "amd_sdw_manager",
.pm = &amd_pm,
}
};
module_platform_driver(amd_sdw_driver);
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
MODULE_DESCRIPTION("AMD SoundWire driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
*/
#ifndef __AMD_MANAGER_H
#define __AMD_MANAGER_H
#include <linux/soundwire/sdw_amd.h>
#define SDW_MANAGER_REG_OFFSET 0xc00
#define AMD_SDW_DEFAULT_ROWS 50
#define AMD_SDW_DEFAULT_COLUMNS 10
#define ACP_PAD_PULLDOWN_CTRL 0x0001448
#define ACP_SW_PAD_KEEPER_EN 0x0001454
#define ACP_SW0_WAKE_EN 0x0001458
#define ACP_EXTERNAL_INTR_CNTL0 0x0001a04
#define ACP_EXTERNAL_INTR_STAT0 0x0001a0c
#define ACP_EXTERNAL_INTR_CNTL(i) (ACP_EXTERNAL_INTR_CNTL0 + ((i) * 4))
#define ACP_EXTERNAL_INTR_STAT(i) (ACP_EXTERNAL_INTR_STAT0 + ((i) * 4))
#define ACP_SW_WAKE_EN(i) (ACP_SW0_WAKE_EN + ((i) * 8))
#define ACP_SW_EN 0x0003000
#define ACP_SW_EN_STATUS 0x0003004
#define ACP_SW_FRAMESIZE 0x0003008
#define ACP_SW_SSP_COUNTER 0x000300c
#define ACP_SW_AUDIO0_TX_EN 0x0003010
#define ACP_SW_AUDIO0_TX_EN_STATUS 0x0003014
#define ACP_SW_AUDIO0_TX_FRAME_FORMAT 0x0003018
#define ACP_SW_AUDIO0_TX_SAMPLEINTERVAL 0x000301c
#define ACP_SW_AUDIO0_TX_HCTRL_DP0 0x0003020
#define ACP_SW_AUDIO0_TX_HCTRL_DP1 0x0003024
#define ACP_SW_AUDIO0_TX_HCTRL_DP2 0x0003028
#define ACP_SW_AUDIO0_TX_HCTRL_DP3 0x000302c
#define ACP_SW_AUDIO0_TX_OFFSET_DP0 0x0003030
#define ACP_SW_AUDIO0_TX_OFFSET_DP1 0x0003034
#define ACP_SW_AUDIO0_TX_OFFSET_DP2 0x0003038
#define ACP_SW_AUDIO0_TX_OFFSET_DP3 0x000303c
#define ACP_SW_AUDIO0_TX_CHANNEL_ENABLE_DP0 0x0003040
#define ACP_SW_AUDIO0_TX_CHANNEL_ENABLE_DP1 0x0003044
#define ACP_SW_AUDIO0_TX_CHANNEL_ENABLE_DP2 0x0003048
#define ACP_SW_AUDIO0_TX_CHANNEL_ENABLE_DP3 0x000304c
#define ACP_SW_AUDIO1_TX_EN 0x0003050
#define ACP_SW_AUDIO1_TX_EN_STATUS 0x0003054
#define ACP_SW_AUDIO1_TX_FRAME_FORMAT 0x0003058
#define ACP_SW_AUDIO1_TX_SAMPLEINTERVAL 0x000305c
#define ACP_SW_AUDIO1_TX_HCTRL 0x0003060
#define ACP_SW_AUDIO1_TX_OFFSET 0x0003064
#define ACP_SW_AUDIO1_TX_CHANNEL_ENABLE_DP0 0x0003068
#define ACP_SW_AUDIO2_TX_EN 0x000306c
#define ACP_SW_AUDIO2_TX_EN_STATUS 0x0003070
#define ACP_SW_AUDIO2_TX_FRAME_FORMAT 0x0003074
#define ACP_SW_AUDIO2_TX_SAMPLEINTERVAL 0x0003078
#define ACP_SW_AUDIO2_TX_HCTRL 0x000307c
#define ACP_SW_AUDIO2_TX_OFFSET 0x0003080
#define ACP_SW_AUDIO2_TX_CHANNEL_ENABLE_DP0 0x0003084
#define ACP_SW_AUDIO0_RX_EN 0x0003088
#define ACP_SW_AUDIO0_RX_EN_STATUS 0x000308c
#define ACP_SW_AUDIO0_RX_FRAME_FORMAT 0x0003090
#define ACP_SW_AUDIO0_RX_SAMPLEINTERVAL 0x0003094
#define ACP_SW_AUDIO0_RX_HCTRL_DP0 0x0003098
#define ACP_SW_AUDIO0_RX_HCTRL_DP1 0x000309c
#define ACP_SW_AUDIO0_RX_HCTRL_DP2 0x0003100
#define ACP_SW_AUDIO0_RX_HCTRL_DP3 0x0003104
#define ACP_SW_AUDIO0_RX_OFFSET_DP0 0x0003108
#define ACP_SW_AUDIO0_RX_OFFSET_DP1 0x000310c
#define ACP_SW_AUDIO0_RX_OFFSET_DP2 0x0003110
#define ACP_SW_AUDIO0_RX_OFFSET_DP3 0x0003114
#define ACP_SW_AUDIO0_RX_CHANNEL_ENABLE_DP0 0x0003118
#define ACP_SW_AUDIO0_RX_CHANNEL_ENABLE_DP1 0x000311c
#define ACP_SW_AUDIO0_RX_CHANNEL_ENABLE_DP2 0x0003120
#define ACP_SW_AUDIO0_RX_CHANNEL_ENABLE_DP3 0x0003124
#define ACP_SW_AUDIO1_RX_EN 0x0003128
#define ACP_SW_AUDIO1_RX_EN_STATUS 0x000312c
#define ACP_SW_AUDIO1_RX_FRAME_FORMAT 0x0003130
#define ACP_SW_AUDIO1_RX_SAMPLEINTERVAL 0x0003134
#define ACP_SW_AUDIO1_RX_HCTRL 0x0003138
#define ACP_SW_AUDIO1_RX_OFFSET 0x000313c
#define ACP_SW_AUDIO1_RX_CHANNEL_ENABLE_DP0 0x0003140
#define ACP_SW_AUDIO2_RX_EN 0x0003144
#define ACP_SW_AUDIO2_RX_EN_STATUS 0x0003148
#define ACP_SW_AUDIO2_RX_FRAME_FORMAT 0x000314c
#define ACP_SW_AUDIO2_RX_SAMPLEINTERVAL 0x0003150
#define ACP_SW_AUDIO2_RX_HCTRL 0x0003154
#define ACP_SW_AUDIO2_RX_OFFSET 0x0003158
#define ACP_SW_AUDIO2_RX_CHANNEL_ENABLE_DP0 0x000315c
#define ACP_SW_BPT_PORT_EN 0x0003160
#define ACP_SW_BPT_PORT_EN_STATUS 0x0003164
#define ACP_SW_BPT_PORT_FRAME_FORMAT 0x0003168
#define ACP_SW_BPT_PORT_SAMPLEINTERVAL 0x000316c
#define ACP_SW_BPT_PORT_HCTRL 0x0003170
#define ACP_SW_BPT_PORT_OFFSET 0x0003174
#define ACP_SW_BPT_PORT_CHANNEL_ENABLE 0x0003178
#define ACP_SW_BPT_PORT_FIRST_BYTE_ADDR 0x000317c
#define ACP_SW_CLK_RESUME_CTRL 0x0003180
#define ACP_SW_CLK_RESUME_DELAY_CNTR 0x0003184
#define ACP_SW_BUS_RESET_CTRL 0x0003188
#define ACP_SW_PRBS_ERR_STATUS 0x000318c
#define ACP_SW_IMM_CMD_UPPER_WORD 0x0003230
#define ACP_SW_IMM_CMD_LOWER_QWORD 0x0003234
#define ACP_SW_IMM_RESP_UPPER_WORD 0x0003238
#define ACP_SW_IMM_RESP_LOWER_QWORD 0x000323c
#define ACP_SW_IMM_CMD_STS 0x0003240
#define ACP_SW_BRA_BASE_ADDRESS 0x0003244
#define ACP_SW_BRA_TRANSFER_SIZE 0x0003248
#define ACP_SW_BRA_DMA_BUSY 0x000324c
#define ACP_SW_BRA_RESP 0x0003250
#define ACP_SW_BRA_RESP_FRAME_ADDR 0x0003254
#define ACP_SW_BRA_CURRENT_TRANSFER_SIZE 0x0003258
#define ACP_SW_STATE_CHANGE_STATUS_0TO7 0x000325c
#define ACP_SW_STATE_CHANGE_STATUS_8TO11 0x0003260
#define ACP_SW_STATE_CHANGE_STATUS_MASK_0TO7 0x0003264
#define ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11 0x0003268
#define ACP_SW_CLK_FREQUENCY_CTRL 0x000326c
#define ACP_SW_ERROR_INTR_MASK 0x0003270
#define ACP_SW_PHY_TEST_MODE_DATA_OFF 0x0003274
#define ACP_DELAY_US 10
#define AMD_SDW_TIMEOUT 1000
#define AMD_SDW_DEFAULT_CLK_FREQ 12000000
#define AMD_SDW_MCP_RESP_ACK BIT(0)
#define AMD_SDW_MCP_RESP_NACK BIT(1)
#define AMD_SDW_MCP_RESP_RDATA GENMASK(14, 7)
#define AMD_SDW_MCP_CMD_SSP_TAG BIT(31)
#define AMD_SDW_MCP_CMD_COMMAND GENMASK(14, 12)
#define AMD_SDW_MCP_CMD_DEV_ADDR GENMASK(11, 8)
#define AMD_SDW_MCP_CMD_REG_ADDR_HIGH GENMASK(7, 0)
#define AMD_SDW_MCP_CMD_REG_ADDR_LOW GENMASK(31, 24)
#define AMD_SDW_MCP_CMD_REG_DATA GENMASK(14, 7)
#define AMD_SDW_MCP_SLAVE_STAT_0_3 GENMASK(14, 7)
#define AMD_SDW_MCP_SLAVE_STAT_4_11 GENMASK_ULL(39, 24)
#define AMD_SDW_MCP_SLAVE_STATUS_MASK GENMASK(1, 0)
#define AMD_SDW_MCP_SLAVE_STATUS_BITS GENMASK(3, 2)
#define AMD_SDW_MCP_SLAVE_STATUS_8TO_11 GENMASK_ULL(15, 0)
#define AMD_SDW_MCP_SLAVE_STATUS_VALID_MASK(x) BIT(((x) * 4))
#define AMD_SDW_MCP_SLAVE_STAT_SHIFT_MASK(x) (((x) * 4) + 1)
#define AMD_SDW_MASTER_SUSPEND_DELAY_MS 2000
#define AMD_SDW_QUIRK_MASK_BUS_ENABLE BIT(0)
#define AMD_SDW_IMM_RES_VALID 1
#define AMD_SDW_IMM_CMD_BUSY 2
#define AMD_SDW_ENABLE 1
#define AMD_SDW_DISABLE 0
#define AMD_SDW_BUS_RESET_CLEAR_REQ 0
#define AMD_SDW_BUS_RESET_REQ 1
#define AMD_SDW_BUS_RESET_DONE 2
#define AMD_SDW_BUS_BASE_FREQ 24000000
#define AMD_SDW0_EXT_INTR_MASK 0x200000
#define AMD_SDW1_EXT_INTR_MASK 4
#define AMD_SDW_IRQ_MASK_0TO7 0x77777777
#define AMD_SDW_IRQ_MASK_8TO11 0x000d7777
#define AMD_SDW_IRQ_ERROR_MASK 0xff
#define AMD_SDW_MAX_FREQ_NUM 1
#define AMD_SDW0_MAX_TX_PORTS 3
#define AMD_SDW0_MAX_RX_PORTS 3
#define AMD_SDW1_MAX_TX_PORTS 1
#define AMD_SDW1_MAX_RX_PORTS 1
#define AMD_SDW0_MAX_DAI 6
#define AMD_SDW1_MAX_DAI 2
#define AMD_SDW_SLAVE_0_ATTACHED 5
#define AMD_SDW_SSP_COUNTER_VAL 3
#define AMD_DPN_FRAME_FMT_PFM GENMASK(1, 0)
#define AMD_DPN_FRAME_FMT_PDM GENMASK(3, 2)
#define AMD_DPN_FRAME_FMT_BLK_PKG_MODE BIT(4)
#define AMD_DPN_FRAME_FMT_BLK_GRP_CTRL GENMASK(6, 5)
#define AMD_DPN_FRAME_FMT_WORD_LEN GENMASK(12, 7)
#define AMD_DPN_FRAME_FMT_PCM_OR_PDM BIT(13)
#define AMD_DPN_HCTRL_HSTOP GENMASK(3, 0)
#define AMD_DPN_HCTRL_HSTART GENMASK(7, 4)
#define AMD_DPN_OFFSET_CTRL_1 GENMASK(7, 0)
#define AMD_DPN_OFFSET_CTRL_2 GENMASK(15, 8)
#define AMD_DPN_CH_EN_LCTRL GENMASK(2, 0)
#define AMD_DPN_CH_EN_CHMASK GENMASK(10, 3)
#define AMD_SDW_STAT_MAX_RETRY_COUNT 100
#define AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK 0x7f9f
#define AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK 0x7ffa
#define AMD_SDW0_PAD_PULLDOWN_CTRL_DISABLE_MASK 0x60
#define AMD_SDW1_PAD_PULLDOWN_CTRL_DISABLE_MASK 5
#define AMD_SDW0_PAD_KEEPER_EN_MASK 1
#define AMD_SDW1_PAD_KEEPER_EN_MASK 0x10
#define AMD_SDW0_PAD_KEEPER_DISABLE_MASK 0x1e
#define AMD_SDW1_PAD_KEEPER_DISABLE_MASK 0xf
#define AMD_SDW_PREQ_INTR_STAT BIT(19)
#define AMD_SDW_CLK_STOP_DONE 1
#define AMD_SDW_CLK_RESUME_REQ 2
#define AMD_SDW_CLK_RESUME_DONE 3
#define AMD_SDW_WAKE_STAT_MASK BIT(16)
static u32 amd_sdw_freq_tbl[AMD_SDW_MAX_FREQ_NUM] = {
AMD_SDW_DEFAULT_CLK_FREQ,
};
struct sdw_manager_dp_reg {
u32 frame_fmt_reg;
u32 sample_int_reg;
u32 hctrl_dp0_reg;
u32 offset_reg;
u32 lane_ctrl_ch_en_reg;
};
/*
* SDW0 Manager instance registers 6 CPU DAI (3 TX & 3 RX Ports)
* whereas SDW1 Manager Instance registers 2 CPU DAI (one TX & one RX port)
* Below is the CPU DAI <->Manager port number mapping
* i.e SDW0 Pin0 -> port number 0 -> AUDIO0 TX
* SDW0 Pin1 -> Port number 1 -> AUDIO1 TX
* SDW0 Pin2 -> Port number 2 -> AUDIO2 TX
* SDW0 Pin3 -> port number 3 -> AUDIO0 RX
* SDW0 Pin4 -> Port number 4 -> AUDIO1 RX
* SDW0 Pin5 -> Port number 5 -> AUDIO2 RX
* Whereas for SDW1 instance
* SDW1 Pin0 -> port number 0 -> AUDIO1 TX
* SDW1 Pin1 -> Port number 1 -> AUDIO1 RX
* Same mapping should be used for programming DMA controller registers in SoundWire DMA driver.
* i.e if AUDIO0 TX channel is selected then we need to use AUDIO0 TX registers for DMA programming
* in SoundWire DMA driver.
*/
static struct sdw_manager_dp_reg sdw0_manager_dp_reg[AMD_SDW0_MAX_DAI] = {
{ACP_SW_AUDIO0_TX_FRAME_FORMAT, ACP_SW_AUDIO0_TX_SAMPLEINTERVAL, ACP_SW_AUDIO0_TX_HCTRL_DP0,
ACP_SW_AUDIO0_TX_OFFSET_DP0, ACP_SW_AUDIO0_TX_CHANNEL_ENABLE_DP0},
{ACP_SW_AUDIO1_TX_FRAME_FORMAT, ACP_SW_AUDIO1_TX_SAMPLEINTERVAL, ACP_SW_AUDIO1_TX_HCTRL,
ACP_SW_AUDIO1_TX_OFFSET, ACP_SW_AUDIO1_TX_CHANNEL_ENABLE_DP0},
{ACP_SW_AUDIO2_TX_FRAME_FORMAT, ACP_SW_AUDIO2_TX_SAMPLEINTERVAL, ACP_SW_AUDIO2_TX_HCTRL,
ACP_SW_AUDIO2_TX_OFFSET, ACP_SW_AUDIO2_TX_CHANNEL_ENABLE_DP0},
{ACP_SW_AUDIO0_RX_FRAME_FORMAT, ACP_SW_AUDIO0_RX_SAMPLEINTERVAL, ACP_SW_AUDIO0_RX_HCTRL_DP0,
ACP_SW_AUDIO0_RX_OFFSET_DP0, ACP_SW_AUDIO0_RX_CHANNEL_ENABLE_DP0},
{ACP_SW_AUDIO1_RX_FRAME_FORMAT, ACP_SW_AUDIO1_RX_SAMPLEINTERVAL, ACP_SW_AUDIO1_RX_HCTRL,
ACP_SW_AUDIO1_RX_OFFSET, ACP_SW_AUDIO1_RX_CHANNEL_ENABLE_DP0},
{ACP_SW_AUDIO2_RX_FRAME_FORMAT, ACP_SW_AUDIO2_RX_SAMPLEINTERVAL, ACP_SW_AUDIO2_RX_HCTRL,
ACP_SW_AUDIO2_RX_OFFSET, ACP_SW_AUDIO2_RX_CHANNEL_ENABLE_DP0},
};
static struct sdw_manager_dp_reg sdw1_manager_dp_reg[AMD_SDW1_MAX_DAI] = {
{ACP_SW_AUDIO1_TX_FRAME_FORMAT, ACP_SW_AUDIO1_TX_SAMPLEINTERVAL, ACP_SW_AUDIO1_TX_HCTRL,
ACP_SW_AUDIO1_TX_OFFSET, ACP_SW_AUDIO1_TX_CHANNEL_ENABLE_DP0},
{ACP_SW_AUDIO1_RX_FRAME_FORMAT, ACP_SW_AUDIO1_RX_SAMPLEINTERVAL, ACP_SW_AUDIO1_RX_HCTRL,
ACP_SW_AUDIO1_RX_OFFSET, ACP_SW_AUDIO1_RX_CHANNEL_ENABLE_DP0}
};
static struct sdw_manager_reg_mask sdw_manager_reg_mask_array[2] = {
{
AMD_SDW0_PAD_KEEPER_EN_MASK,
AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK,
AMD_SDW0_EXT_INTR_MASK
},
{
AMD_SDW1_PAD_KEEPER_EN_MASK,
AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK,
AMD_SDW1_EXT_INTR_MASK
}
};
#endif
......@@ -384,45 +384,73 @@ int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave,
/*
* Read/Write IO functions.
* no_pm versions can only be called by the bus, e.g. while enumerating or
* handling suspend-resume sequences.
* all clients need to use the pm versions
*/
int sdw_nread_no_pm(struct sdw_slave *slave, u32 addr, size_t count, u8 *val)
static int sdw_ntransfer_no_pm(struct sdw_slave *slave, u32 addr, u8 flags,
size_t count, u8 *val)
{
struct sdw_msg msg;
size_t size;
int ret;
ret = sdw_fill_msg(&msg, slave, addr, count,
slave->dev_num, SDW_MSG_FLAG_READ, val);
if (ret < 0)
return ret;
while (count) {
// Only handle bytes up to next page boundary
size = min_t(size_t, count, (SDW_REGADDR + 1) - (addr & SDW_REGADDR));
ret = sdw_transfer(slave->bus, &msg);
if (slave->is_mockup_device)
ret = 0;
return ret;
ret = sdw_fill_msg(&msg, slave, addr, size, slave->dev_num, flags, val);
if (ret < 0)
return ret;
ret = sdw_transfer(slave->bus, &msg);
if (ret < 0 && !slave->is_mockup_device)
return ret;
addr += size;
val += size;
count -= size;
}
return 0;
}
/**
* sdw_nread_no_pm() - Read "n" contiguous SDW Slave registers with no PM
* @slave: SDW Slave
* @addr: Register address
* @count: length
* @val: Buffer for values to be read
*
* Note that if the message crosses a page boundary each page will be
* transferred under a separate invocation of the msg_lock.
*/
int sdw_nread_no_pm(struct sdw_slave *slave, u32 addr, size_t count, u8 *val)
{
return sdw_ntransfer_no_pm(slave, addr, SDW_MSG_FLAG_READ, count, val);
}
EXPORT_SYMBOL(sdw_nread_no_pm);
/**
* sdw_nwrite_no_pm() - Write "n" contiguous SDW Slave registers with no PM
* @slave: SDW Slave
* @addr: Register address
* @count: length
* @val: Buffer for values to be written
*
* Note that if the message crosses a page boundary each page will be
* transferred under a separate invocation of the msg_lock.
*/
int sdw_nwrite_no_pm(struct sdw_slave *slave, u32 addr, size_t count, const u8 *val)
{
struct sdw_msg msg;
int ret;
ret = sdw_fill_msg(&msg, slave, addr, count,
slave->dev_num, SDW_MSG_FLAG_WRITE, (u8 *)val);
if (ret < 0)
return ret;
ret = sdw_transfer(slave->bus, &msg);
if (slave->is_mockup_device)
ret = 0;
return ret;
return sdw_ntransfer_no_pm(slave, addr, SDW_MSG_FLAG_WRITE, count, (u8 *)val);
}
EXPORT_SYMBOL(sdw_nwrite_no_pm);
/**
* sdw_write_no_pm() - Write a SDW Slave register with no PM
* @slave: SDW Slave
* @addr: Register address
* @value: Register value
*/
int sdw_write_no_pm(struct sdw_slave *slave, u32 addr, u8 value)
{
return sdw_nwrite_no_pm(slave, addr, 1, &value);
......@@ -495,6 +523,11 @@ int sdw_bwrite_no_pm_unlocked(struct sdw_bus *bus, u16 dev_num, u32 addr, u8 val
}
EXPORT_SYMBOL(sdw_bwrite_no_pm_unlocked);
/**
* sdw_read_no_pm() - Read a SDW Slave register with no PM
* @slave: SDW Slave
* @addr: Register address
*/
int sdw_read_no_pm(struct sdw_slave *slave, u32 addr)
{
u8 buf;
......@@ -541,14 +574,21 @@ EXPORT_SYMBOL(sdw_update);
* @addr: Register address
* @count: length
* @val: Buffer for values to be read
*
* This version of the function will take a PM reference to the slave
* device.
* Note that if the message crosses a page boundary each page will be
* transferred under a separate invocation of the msg_lock.
*/
int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val)
{
int ret;
ret = pm_runtime_resume_and_get(&slave->dev);
if (ret < 0 && ret != -EACCES)
ret = pm_runtime_get_sync(&slave->dev);
if (ret < 0 && ret != -EACCES) {
pm_runtime_put_noidle(&slave->dev);
return ret;
}
ret = sdw_nread_no_pm(slave, addr, count, val);
......@@ -565,14 +605,21 @@ EXPORT_SYMBOL(sdw_nread);
* @addr: Register address
* @count: length
* @val: Buffer for values to be written
*
* This version of the function will take a PM reference to the slave
* device.
* Note that if the message crosses a page boundary each page will be
* transferred under a separate invocation of the msg_lock.
*/
int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, const u8 *val)
{
int ret;
ret = pm_runtime_resume_and_get(&slave->dev);
if (ret < 0 && ret != -EACCES)
ret = pm_runtime_get_sync(&slave->dev);
if (ret < 0 && ret != -EACCES) {
pm_runtime_put_noidle(&slave->dev);
return ret;
}
ret = sdw_nwrite_no_pm(slave, addr, count, val);
......@@ -587,6 +634,9 @@ EXPORT_SYMBOL(sdw_nwrite);
* sdw_read() - Read a SDW Slave register
* @slave: SDW Slave
* @addr: Register address
*
* This version of the function will take a PM reference to the slave
* device.
*/
int sdw_read(struct sdw_slave *slave, u32 addr)
{
......@@ -606,6 +656,9 @@ EXPORT_SYMBOL(sdw_read);
* @slave: SDW Slave
* @addr: Register address
* @value: Register value
*
* This version of the function will take a PM reference to the slave
* device.
*/
int sdw_write(struct sdw_slave *slave, u32 addr, u8 value)
{
......@@ -1541,9 +1594,10 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
sdw_modify_slave_status(slave, SDW_SLAVE_ALERT);
ret = pm_runtime_resume_and_get(&slave->dev);
ret = pm_runtime_get_sync(&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;
}
......
......@@ -144,6 +144,13 @@ struct sdw_master_runtime {
struct list_head bus_node;
};
struct sdw_transport_data {
int hstart;
int hstop;
int block_offset;
int sub_block_offset;
};
struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave,
enum sdw_data_direction direction,
unsigned int port_num);
......@@ -158,17 +165,6 @@ int sdw_transfer_defer(struct sdw_bus *bus, struct sdw_msg *msg);
int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave,
u32 addr, size_t count, u16 dev_num, u8 flags, u8 *buf);
/* Retrieve and return channel count from channel mask */
static inline int sdw_ch_mask_to_ch(int ch_mask)
{
int c = 0;
for (c = 0; ch_mask; ch_mask >>= 1)
c += ch_mask & 1;
return c;
}
/* Fill transport parameter data structure */
static inline void sdw_fill_xport_params(struct sdw_transport_params *params,
int port_num, bool grp_ctrl_valid,
......@@ -212,5 +208,7 @@ int sdw_bwrite_no_pm_unlocked(struct sdw_bus *bus, u16 dev_num, u32 addr, u8 val
void sdw_clear_slave_status(struct sdw_bus *bus, u32 request);
int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size);
void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt,
struct sdw_transport_data *t_data);
#endif /* __SDW_BUS_H */
......@@ -27,32 +27,36 @@ module_param_named(cnds_mcp_int_mask, interrupt_mask, int, 0444);
MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask");
#define CDNS_MCP_CONFIG 0x0
#define CDNS_MCP_CONFIG_MCMD_RETRY GENMASK(27, 24)
#define CDNS_MCP_CONFIG_MPREQ_DELAY GENMASK(20, 16)
#define CDNS_MCP_CONFIG_MMASTER BIT(7)
#define CDNS_MCP_CONFIG_BUS_REL BIT(6)
#define CDNS_MCP_CONFIG_SNIFFER BIT(5)
#define CDNS_MCP_CONFIG_SSPMOD BIT(4)
#define CDNS_MCP_CONFIG_CMD BIT(3)
#define CDNS_MCP_CONFIG_OP GENMASK(2, 0)
#define CDNS_MCP_CONFIG_OP_NORMAL 0
#define CDNS_IP_MCP_CONFIG 0x0 /* IP offset added at run-time */
#define CDNS_IP_MCP_CONFIG_MCMD_RETRY GENMASK(27, 24)
#define CDNS_IP_MCP_CONFIG_MPREQ_DELAY GENMASK(20, 16)
#define CDNS_IP_MCP_CONFIG_MMASTER BIT(7)
#define CDNS_IP_MCP_CONFIG_SNIFFER BIT(5)
#define CDNS_IP_MCP_CONFIG_CMD BIT(3)
#define CDNS_IP_MCP_CONFIG_OP GENMASK(2, 0)
#define CDNS_IP_MCP_CONFIG_OP_NORMAL 0
#define CDNS_MCP_CONTROL 0x4
#define CDNS_MCP_CONTROL_RST_DELAY GENMASK(10, 8)
#define CDNS_MCP_CONTROL_CMD_RST BIT(7)
#define CDNS_MCP_CONTROL_SOFT_RST BIT(6)
#define CDNS_MCP_CONTROL_SW_RST BIT(5)
#define CDNS_MCP_CONTROL_HW_RST BIT(4)
#define CDNS_MCP_CONTROL_CLK_PAUSE BIT(3)
#define CDNS_MCP_CONTROL_CLK_STOP_CLR BIT(2)
#define CDNS_MCP_CONTROL_CMD_ACCEPT BIT(1)
#define CDNS_MCP_CONTROL_BLOCK_WAKEUP BIT(0)
#define CDNS_MCP_CMDCTRL 0x8
#define CDNS_IP_MCP_CONTROL 0x4 /* IP offset added at run-time */
#define CDNS_IP_MCP_CONTROL_RST_DELAY GENMASK(10, 8)
#define CDNS_IP_MCP_CONTROL_SW_RST BIT(5)
#define CDNS_IP_MCP_CONTROL_CLK_PAUSE BIT(3)
#define CDNS_IP_MCP_CONTROL_CMD_ACCEPT BIT(1)
#define CDNS_IP_MCP_CONTROL_BLOCK_WAKEUP BIT(0)
#define CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR BIT(2)
#define CDNS_IP_MCP_CMDCTRL 0x8 /* IP offset added at run-time */
#define CDNS_IP_MCP_CMDCTRL_INSERT_PARITY_ERR BIT(2)
#define CDNS_MCP_SSPSTAT 0xC
#define CDNS_MCP_FRAME_SHAPE 0x10
......@@ -125,8 +129,8 @@ MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask");
#define CDNS_MCP_FIFOSTAT 0x7C
#define CDNS_MCP_RX_FIFO_AVAIL GENMASK(5, 0)
#define CDNS_MCP_CMD_BASE 0x80
#define CDNS_MCP_RESP_BASE 0x80
#define CDNS_IP_MCP_CMD_BASE 0x80 /* IP offset added at run-time */
#define CDNS_IP_MCP_RESP_BASE 0x80 /* IP offset added at run-time */
/* FIFO can hold 8 commands */
#define CDNS_MCP_CMD_LEN 8
#define CDNS_MCP_CMD_WORD_LEN 0x4
......@@ -206,6 +210,16 @@ static inline void cdns_writel(struct sdw_cdns *cdns, int offset, u32 value)
writel(value, cdns->registers + offset);
}
static inline u32 cdns_ip_readl(struct sdw_cdns *cdns, int offset)
{
return cdns_readl(cdns, cdns->ip_offset + offset);
}
static inline void cdns_ip_writel(struct sdw_cdns *cdns, int offset, u32 value)
{
return cdns_writel(cdns, cdns->ip_offset + offset, value);
}
static inline void cdns_updatel(struct sdw_cdns *cdns,
int offset, u32 mask, u32 val)
{
......@@ -216,6 +230,12 @@ static inline void cdns_updatel(struct sdw_cdns *cdns,
cdns_writel(cdns, offset, tmp);
}
static inline void cdns_ip_updatel(struct sdw_cdns *cdns,
int offset, u32 mask, u32 val)
{
cdns_updatel(cdns, cdns->ip_offset + offset, mask, val);
}
static int cdns_set_wait(struct sdw_cdns *cdns, int offset, u32 mask, u32 value)
{
int timeout = 10;
......@@ -408,9 +428,9 @@ static int cdns_parity_error_injection(void *data, u64 value)
mutex_lock(&bus->bus_lock);
/* program hardware to inject parity error */
cdns_updatel(cdns, CDNS_MCP_CMDCTRL,
CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR,
CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR);
cdns_ip_updatel(cdns, CDNS_IP_MCP_CMDCTRL,
CDNS_IP_MCP_CMDCTRL_INSERT_PARITY_ERR,
CDNS_IP_MCP_CMDCTRL_INSERT_PARITY_ERR);
/* commit changes */
cdns_updatel(cdns, CDNS_MCP_CONFIG_UPDATE,
......@@ -422,9 +442,9 @@ static int cdns_parity_error_injection(void *data, u64 value)
dev_info(cdns->dev, "parity error injection, read: %d\n", ret);
/* program hardware to disable parity error */
cdns_updatel(cdns, CDNS_MCP_CMDCTRL,
CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR,
0);
cdns_ip_updatel(cdns, CDNS_IP_MCP_CMDCTRL,
CDNS_IP_MCP_CMDCTRL_INSERT_PARITY_ERR,
0);
/* commit changes */
cdns_updatel(cdns, CDNS_MCP_CONFIG_UPDATE,
......@@ -570,10 +590,10 @@ static void cdns_read_response(struct sdw_cdns *cdns)
num_resp = ARRAY_SIZE(cdns->response_buf);
}
cmd_base = CDNS_MCP_CMD_BASE;
cmd_base = CDNS_IP_MCP_CMD_BASE;
for (i = 0; i < num_resp; i++) {
cdns->response_buf[i] = cdns_readl(cdns, cmd_base);
cdns->response_buf[i] = cdns_ip_readl(cdns, cmd_base);
cmd_base += CDNS_MCP_CMD_WORD_LEN;
}
}
......@@ -592,7 +612,7 @@ _cdns_xfer_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int cmd,
cdns->msg_count = count;
}
base = CDNS_MCP_CMD_BASE;
base = CDNS_IP_MCP_CMD_BASE;
addr = msg->addr + offset;
for (i = 0; i < count; i++) {
......@@ -605,7 +625,7 @@ _cdns_xfer_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int cmd,
data |= msg->buf[i + offset];
data |= FIELD_PREP(CDNS_MCP_CMD_SSP_TAG, msg->ssp_sync);
cdns_writel(cdns, base, data);
cdns_ip_writel(cdns, base, data);
base += CDNS_MCP_CMD_WORD_LEN;
}
......@@ -653,10 +673,10 @@ cdns_program_scp_addr(struct sdw_cdns *cdns, struct sdw_msg *msg)
data[0] |= msg->addr_page1;
data[1] |= msg->addr_page2;
base = CDNS_MCP_CMD_BASE;
cdns_writel(cdns, base, data[0]);
base = CDNS_IP_MCP_CMD_BASE;
cdns_ip_writel(cdns, base, data[0]);
base += CDNS_MCP_CMD_WORD_LEN;
cdns_writel(cdns, base, data[1]);
cdns_ip_writel(cdns, base, data[1]);
time = wait_for_completion_timeout(&cdns->tx_complete,
msecs_to_jiffies(CDNS_TX_TIMEOUT));
......@@ -1033,6 +1053,7 @@ static void cdns_update_slave_status_work(struct work_struct *work)
void sdw_cdns_check_self_clearing_bits(struct sdw_cdns *cdns, const char *string,
bool initial_delay, int reset_iterations)
{
u32 ip_mcp_control;
u32 mcp_control;
u32 mcp_config_update;
int i;
......@@ -1040,6 +1061,12 @@ void sdw_cdns_check_self_clearing_bits(struct sdw_cdns *cdns, const char *string
if (initial_delay)
usleep_range(1000, 1500);
ip_mcp_control = cdns_ip_readl(cdns, CDNS_IP_MCP_CONTROL);
/* the following bits should be cleared immediately */
if (ip_mcp_control & CDNS_IP_MCP_CONTROL_SW_RST)
dev_err(cdns->dev, "%s failed: IP_MCP_CONTROL_SW_RST is not cleared\n", string);
mcp_control = cdns_readl(cdns, CDNS_MCP_CONTROL);
/* the following bits should be cleared immediately */
......@@ -1047,10 +1074,9 @@ void sdw_cdns_check_self_clearing_bits(struct sdw_cdns *cdns, const char *string
dev_err(cdns->dev, "%s failed: MCP_CONTROL_CMD_RST is not cleared\n", string);
if (mcp_control & CDNS_MCP_CONTROL_SOFT_RST)
dev_err(cdns->dev, "%s failed: MCP_CONTROL_SOFT_RST is not cleared\n", string);
if (mcp_control & CDNS_MCP_CONTROL_SW_RST)
dev_err(cdns->dev, "%s failed: MCP_CONTROL_SW_RST is not cleared\n", string);
if (mcp_control & CDNS_MCP_CONTROL_CLK_STOP_CLR)
dev_err(cdns->dev, "%s failed: MCP_CONTROL_CLK_STOP_CLR is not cleared\n", string);
mcp_config_update = cdns_readl(cdns, CDNS_MCP_CONFIG_UPDATE);
if (mcp_config_update & CDNS_MCP_CONFIG_UPDATE_BIT)
dev_err(cdns->dev, "%s failed: MCP_CONFIG_UPDATE_BIT is not cleared\n", string);
......@@ -1327,34 +1353,39 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
CDNS_MCP_CONTROL_CMD_RST);
/* Set cmd accept mode */
cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT,
CDNS_MCP_CONTROL_CMD_ACCEPT);
cdns_ip_updatel(cdns, CDNS_IP_MCP_CONTROL, CDNS_IP_MCP_CONTROL_CMD_ACCEPT,
CDNS_IP_MCP_CONTROL_CMD_ACCEPT);
/* Configure mcp config */
val = cdns_readl(cdns, CDNS_MCP_CONFIG);
/* Disable auto bus release */
val &= ~CDNS_MCP_CONFIG_BUS_REL;
cdns_writel(cdns, CDNS_MCP_CONFIG, val);
/* Configure IP mcp config */
val = cdns_ip_readl(cdns, CDNS_IP_MCP_CONFIG);
/* enable bus operations with clock and data */
val &= ~CDNS_MCP_CONFIG_OP;
val |= CDNS_MCP_CONFIG_OP_NORMAL;
val &= ~CDNS_IP_MCP_CONFIG_OP;
val |= CDNS_IP_MCP_CONFIG_OP_NORMAL;
/* Set cmd mode for Tx and Rx cmds */
val &= ~CDNS_MCP_CONFIG_CMD;
val &= ~CDNS_IP_MCP_CONFIG_CMD;
/* Disable sniffer mode */
val &= ~CDNS_MCP_CONFIG_SNIFFER;
/* Disable auto bus release */
val &= ~CDNS_MCP_CONFIG_BUS_REL;
val &= ~CDNS_IP_MCP_CONFIG_SNIFFER;
if (cdns->bus.multi_link)
/* Set Multi-master mode to take gsync into account */
val |= CDNS_MCP_CONFIG_MMASTER;
val |= CDNS_IP_MCP_CONFIG_MMASTER;
/* leave frame delay to hardware default of 0x1F */
/* leave command retry to hardware default of 0 */
cdns_writel(cdns, CDNS_MCP_CONFIG, val);
cdns_ip_writel(cdns, CDNS_IP_MCP_CONFIG, val);
/* changes will be committed later */
return 0;
......@@ -1584,9 +1615,9 @@ int sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake)
* in clock stop state
*/
if (block_wake)
cdns_updatel(cdns, CDNS_MCP_CONTROL,
CDNS_MCP_CONTROL_BLOCK_WAKEUP,
CDNS_MCP_CONTROL_BLOCK_WAKEUP);
cdns_ip_updatel(cdns, CDNS_IP_MCP_CONTROL,
CDNS_IP_MCP_CONTROL_BLOCK_WAKEUP,
CDNS_IP_MCP_CONTROL_BLOCK_WAKEUP);
list_for_each_entry(slave, &cdns->bus.slaves, node) {
if (slave->status == SDW_SLAVE_ATTACHED ||
......@@ -1659,18 +1690,18 @@ int sdw_cdns_clock_restart(struct sdw_cdns *cdns, bool bus_reset)
return ret;
}
cdns_updatel(cdns, CDNS_MCP_CONTROL,
CDNS_MCP_CONTROL_BLOCK_WAKEUP, 0);
cdns_ip_updatel(cdns, CDNS_IP_MCP_CONTROL,
CDNS_IP_MCP_CONTROL_BLOCK_WAKEUP, 0);
cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT,
CDNS_MCP_CONTROL_CMD_ACCEPT);
cdns_ip_updatel(cdns, CDNS_IP_MCP_CONTROL, CDNS_IP_MCP_CONTROL_CMD_ACCEPT,
CDNS_IP_MCP_CONTROL_CMD_ACCEPT);
if (!bus_reset) {
/* enable bus operations with clock and data */
cdns_updatel(cdns, CDNS_MCP_CONFIG,
CDNS_MCP_CONFIG_OP,
CDNS_MCP_CONFIG_OP_NORMAL);
cdns_ip_updatel(cdns, CDNS_IP_MCP_CONFIG,
CDNS_IP_MCP_CONFIG_OP,
CDNS_IP_MCP_CONFIG_OP_NORMAL);
ret = cdns_config_update(cdns);
if (ret < 0) {
......
......@@ -84,7 +84,6 @@ struct sdw_cdns_stream_config {
* @bus: Bus handle
* @stream_type: Stream type
* @link_id: Master link id
* @hw_params: hw_params to be applied in .prepare step
* @suspended: status set when suspended, to be used in .prepare
* @paused: status set in .trigger, to be used in suspend
* @direction: stream direction
......@@ -96,7 +95,6 @@ struct sdw_cdns_dai_runtime {
struct sdw_bus *bus;
enum sdw_stream_type stream_type;
int link_id;
struct snd_pcm_hw_params *hw_params;
bool suspended;
bool paused;
int direction;
......@@ -107,6 +105,7 @@ struct sdw_cdns_dai_runtime {
* @dev: Linux device
* @bus: Bus handle
* @instance: instance number
* @ip_offset: version-dependent offset to access IP_MCP registers and fields
* @response_buf: SoundWire response buffer
* @tx_complete: Tx completion
* @ports: Data ports
......@@ -122,6 +121,8 @@ struct sdw_cdns {
struct sdw_bus bus;
unsigned int instance;
u32 ip_offset;
/*
* The datasheet says the RX FIFO AVAIL can be 2 entries more
* than the FIFO capacity, so allow for this.
......
......@@ -73,6 +73,23 @@ static const struct adr_remap hp_omen_16[] = {
{}
};
/*
* Intel NUC M15 LAPRC510 and LAPRC710
*/
static const struct adr_remap intel_rooks_county[] = {
/* rt711-sdca on link0 */
{
0x000020025d071100ull,
0x000030025d071101ull
},
/* rt1316-sdca on link2 */
{
0x000120025d071100ull,
0x000230025d131601ull
},
{}
};
static const struct dmi_system_id adr_remap_quirk_table[] = {
/* TGL devices */
{
......@@ -98,6 +115,14 @@ static const struct dmi_system_id adr_remap_quirk_table[] = {
},
.driver_data = (void *)intel_tgl_bios,
},
{
/* quirk used for NUC15 'Rooks County' LAPRC510 and LAPRC710 skews */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"),
DMI_MATCH(DMI_PRODUCT_NAME, "LAPRC"),
},
.driver_data = (void *)intel_rooks_county,
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
......
......@@ -6,6 +6,7 @@
*
*/
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
......@@ -28,15 +29,8 @@ struct sdw_group {
unsigned int *rates;
};
struct sdw_transport_data {
int hstart;
int hstop;
int block_offset;
int sub_block_offset;
};
static void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt,
struct sdw_transport_data *t_data)
void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt,
struct sdw_transport_data *t_data)
{
struct sdw_slave_runtime *s_rt = NULL;
struct sdw_port_runtime *p_rt;
......@@ -54,7 +48,7 @@ static void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt,
slave_total_ch = 0;
list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
ch = sdw_ch_mask_to_ch(p_rt->ch_mask);
ch = hweight32(p_rt->ch_mask);
sdw_fill_xport_params(&p_rt->transport_params,
p_rt->num, false,
......@@ -85,6 +79,7 @@ static void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt,
}
}
}
EXPORT_SYMBOL(sdw_compute_slave_ports);
static void sdw_compute_master_ports(struct sdw_master_runtime *m_rt,
struct sdw_group_params *params,
......
......@@ -19,38 +19,6 @@
#include "bus.h"
#include "intel.h"
enum intel_pdi_type {
INTEL_PDI_IN = 0,
INTEL_PDI_OUT = 1,
INTEL_PDI_BD = 2,
};
#define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns)
/*
* Read, write helpers for HW registers
*/
static inline int intel_readl(void __iomem *base, int offset)
{
return readl(base + offset);
}
static inline void intel_writel(void __iomem *base, int offset, int value)
{
writel(value, base + offset);
}
static inline u16 intel_readw(void __iomem *base, int offset)
{
return readw(base + offset);
}
static inline void intel_writew(void __iomem *base, int offset, u16 value)
{
writew(value, base + offset);
}
static int intel_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
{
int timeout = 10;
......@@ -357,6 +325,15 @@ static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable)
mutex_unlock(sdw->link_res->shim_lock);
}
static bool intel_check_cmdsync_unlocked(struct sdw_intel *sdw)
{
void __iomem *shim = sdw->link_res->shim;
int sync_reg;
sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
return !!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK);
}
static int intel_link_power_up(struct sdw_intel *sdw)
{
unsigned int link_id = sdw->instance;
......@@ -507,7 +484,6 @@ static int intel_shim_sync_go_unlocked(struct sdw_intel *sdw)
{
void __iomem *shim = sdw->link_res->shim;
u32 sync_reg;
int ret;
/* Read SYNC register */
sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
......@@ -519,13 +495,9 @@ static int intel_shim_sync_go_unlocked(struct sdw_intel *sdw)
*/
sync_reg |= SDW_SHIM_SYNC_SYNCGO;
ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg,
SDW_SHIM_SYNC_SYNCGO);
if (ret < 0)
dev_err(sdw->cdns.dev, "SyncGO clear failed: %d\n", ret);
intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
return ret;
return 0;
}
static int intel_shim_sync_go(struct sdw_intel *sdw)
......@@ -618,13 +590,6 @@ static int intel_pdi_stream_ch_update(struct sdw_intel *sdw,
return 0;
}
static int intel_pdi_ch_update(struct sdw_intel *sdw)
{
intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm);
return 0;
}
static void
intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
{
......@@ -717,63 +682,6 @@ static int intel_free_stream(struct sdw_intel *sdw,
return 0;
}
/*
* bank switch routines
*/
static int intel_pre_bank_switch(struct sdw_intel *sdw)
{
struct sdw_cdns *cdns = &sdw->cdns;
struct sdw_bus *bus = &cdns->bus;
/* Write to register only for multi-link */
if (!bus->multi_link)
return 0;
intel_shim_sync_arm(sdw);
return 0;
}
static int intel_post_bank_switch(struct sdw_intel *sdw)
{
struct sdw_cdns *cdns = &sdw->cdns;
struct sdw_bus *bus = &cdns->bus;
void __iomem *shim = sdw->link_res->shim;
int sync_reg, ret;
/* Write to register only for multi-link */
if (!bus->multi_link)
return 0;
mutex_lock(sdw->link_res->shim_lock);
/* Read SYNC register */
sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
/*
* post_bank_switch() ops is called from the bus in loop for
* all the Masters in the steam with the expectation that
* we trigger the bankswitch for the only first Master in the list
* and do nothing for the other Masters
*
* So, set the SYNCGO bit only if CMDSYNC bit is set for any Master.
*/
if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK)) {
ret = 0;
goto unlock;
}
ret = intel_shim_sync_go_unlocked(sdw);
unlock:
mutex_unlock(sdw->link_res->shim_lock);
if (ret < 0)
dev_err(sdw->cdns.dev, "Post bank switch failed: %d\n", ret);
return ret;
}
/*
* DAI routines
*/
......@@ -817,7 +725,6 @@ static int intel_hw_params(struct snd_pcm_substream *substream,
dai_runtime->paused = false;
dai_runtime->suspended = false;
dai_runtime->pdi = pdi;
dai_runtime->hw_params = params;
/* Inform DSP about PDI stream number */
ret = intel_params_stream(sdw, substream->stream, dai, params,
......@@ -870,6 +777,11 @@ static int intel_prepare(struct snd_pcm_substream *substream,
}
if (dai_runtime->suspended) {
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_pcm_hw_params *hw_params;
hw_params = &rtd->dpcm[substream->stream].hw_params;
dai_runtime->suspended = false;
/*
......@@ -881,7 +793,7 @@ static int intel_prepare(struct snd_pcm_substream *substream,
*/
/* configure stream */
ch = params_channels(dai_runtime->hw_params);
ch = params_channels(hw_params);
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
dir = SDW_DATA_DIR_RX;
else
......@@ -893,7 +805,7 @@ static int intel_prepare(struct snd_pcm_substream *substream,
/* Inform DSP about PDI stream number */
ret = intel_params_stream(sdw, substream->stream, dai,
dai_runtime->hw_params,
hw_params,
sdw->instance,
dai_runtime->pdi->intel_alh_id);
}
......@@ -932,7 +844,6 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
return ret;
}
dai_runtime->hw_params = NULL;
dai_runtime->pdi = NULL;
return 0;
......@@ -1088,7 +999,6 @@ static int intel_create_dai(struct sdw_cdns *cdns,
if (num == 0)
return 0;
/* TODO: Read supported rates/formats from hardware */
for (i = off; i < (off + num); i++) {
dais[i].name = devm_kasprintf(cdns->dev, GFP_KERNEL,
"SDW%d Pin%d",
......@@ -1099,15 +1009,11 @@ static int intel_create_dai(struct sdw_cdns *cdns,
if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) {
dais[i].playback.channels_min = 1;
dais[i].playback.channels_max = max_ch;
dais[i].playback.rates = SNDRV_PCM_RATE_48000;
dais[i].playback.formats = SNDRV_PCM_FMTBIT_S16_LE;
}
if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) {
dais[i].capture.channels_min = 1;
dais[i].capture.channels_max = max_ch;
dais[i].capture.rates = SNDRV_PCM_RATE_48000;
dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE;
}
dais[i].ops = &intel_pcm_dai_ops;
......@@ -1131,7 +1037,7 @@ static int intel_register_dai(struct sdw_intel *sdw)
if (ret)
return ret;
intel_pdi_ch_update(sdw);
intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm);
/* DAIs are created based on total number of PDIs supported */
num_dai = cdns->pcm.num_pdi;
......@@ -1171,205 +1077,6 @@ static int intel_register_dai(struct sdw_intel *sdw)
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;
}
const struct sdw_intel_hw_ops sdw_intel_cnl_hw_ops = {
.debugfs_init = intel_debugfs_init,
......@@ -1391,6 +1098,11 @@ const struct sdw_intel_hw_ops sdw_intel_cnl_hw_ops = {
.pre_bank_switch = intel_pre_bank_switch,
.post_bank_switch = intel_post_bank_switch,
.sync_arm = intel_shim_sync_arm,
.sync_go_unlocked = intel_shim_sync_go_unlocked,
.sync_go = intel_shim_sync_go,
.sync_check_cmdsync_unlocked = intel_check_cmdsync_unlocked,
};
EXPORT_SYMBOL_NS(sdw_intel_cnl_hw_ops, SOUNDWIRE_INTEL);
......@@ -50,6 +50,35 @@ struct sdw_intel {
#endif
};
enum intel_pdi_type {
INTEL_PDI_IN = 0,
INTEL_PDI_OUT = 1,
INTEL_PDI_BD = 2,
};
/*
* Read, write helpers for HW registers
*/
static inline int intel_readl(void __iomem *base, int offset)
{
return readl(base + offset);
}
static inline void intel_writel(void __iomem *base, int offset, int value)
{
writel(value, base + offset);
}
static inline u16 intel_readw(void __iomem *base, int offset)
{
return readw(base + offset);
}
static inline void intel_writew(void __iomem *base, int offset, u16 value)
{
writew(value, base + offset);
}
#define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns)
#define INTEL_MASTER_RESET_ITERATIONS 10
......@@ -138,4 +167,42 @@ static inline void sdw_intel_shim_wake(struct sdw_intel *sdw, bool wake_enable)
SDW_INTEL_OPS(sdw, shim_wake)(sdw, wake_enable);
}
static inline void sdw_intel_sync_arm(struct sdw_intel *sdw)
{
if (SDW_INTEL_CHECK_OPS(sdw, sync_arm))
SDW_INTEL_OPS(sdw, sync_arm)(sdw);
}
static inline int sdw_intel_sync_go_unlocked(struct sdw_intel *sdw)
{
if (SDW_INTEL_CHECK_OPS(sdw, sync_go_unlocked))
return SDW_INTEL_OPS(sdw, sync_go_unlocked)(sdw);
return -ENOTSUPP;
}
static inline int sdw_intel_sync_go(struct sdw_intel *sdw)
{
if (SDW_INTEL_CHECK_OPS(sdw, sync_go))
return SDW_INTEL_OPS(sdw, sync_go)(sdw);
return -ENOTSUPP;
}
static inline bool sdw_intel_sync_check_cmdsync_unlocked(struct sdw_intel *sdw)
{
if (SDW_INTEL_CHECK_OPS(sdw, sync_check_cmdsync_unlocked))
return SDW_INTEL_OPS(sdw, sync_check_cmdsync_unlocked)(sdw);
return false;
}
/* common bus management */
int intel_start_bus(struct sdw_intel *sdw);
int intel_start_bus_after_reset(struct sdw_intel *sdw);
void intel_check_clock_stop(struct sdw_intel *sdw);
int intel_start_bus_after_clock_stop(struct sdw_intel *sdw);
int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop);
/* common bank switch routines */
int intel_pre_bank_switch(struct sdw_intel *sdw);
int intel_post_bank_switch(struct sdw_intel *sdw);
#endif /* __SDW_INTEL_LOCAL_H */
......@@ -358,10 +358,12 @@ static int intel_resume_child_device(struct device *dev, void *data)
}
ret = pm_request_resume(dev);
if (ret < 0)
if (ret < 0) {
dev_err(dev, "%s: pm_request_resume failed: %d\n", __func__, ret);
return ret;
}
return ret;
return 0;
}
static int __maybe_unused intel_pm_prepare(struct device *dev)
......
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
// Copyright(c) 2015-2023 Intel Corporation. All rights reserved.
#include <linux/acpi.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_intel.h>
#include "cadence_master.h"
#include "bus.h"
#include "intel.h"
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)
sdw_intel_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 = sdw_intel_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;
}
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)
sdw_intel_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 = sdw_intel_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;
}
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__);
}
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;
}
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 = sdw_intel_link_power_down(sdw);
if (ret) {
dev_err(dev, "%s: Link power down failed: %d\n", __func__, ret);
return ret;
}
sdw_intel_shim_wake(sdw, wake_enable);
return 0;
}
/*
* bank switch routines
*/
int intel_pre_bank_switch(struct sdw_intel *sdw)
{
struct sdw_cdns *cdns = &sdw->cdns;
struct sdw_bus *bus = &cdns->bus;
/* Write to register only for multi-link */
if (!bus->multi_link)
return 0;
sdw_intel_sync_arm(sdw);
return 0;
}
int intel_post_bank_switch(struct sdw_intel *sdw)
{
struct sdw_cdns *cdns = &sdw->cdns;
struct sdw_bus *bus = &cdns->bus;
int ret = 0;
/* Write to register only for multi-link */
if (!bus->multi_link)
return 0;
mutex_lock(sdw->link_res->shim_lock);
/*
* post_bank_switch() ops is called from the bus in loop for
* all the Masters in the steam with the expectation that
* we trigger the bankswitch for the only first Master in the list
* and do nothing for the other Masters
*
* So, set the SYNCGO bit only if CMDSYNC bit is set for any Master.
*/
if (sdw_intel_sync_check_cmdsync_unlocked(sdw))
ret = sdw_intel_sync_go_unlocked(sdw);
mutex_unlock(sdw->link_res->shim_lock);
if (ret < 0)
dev_err(sdw->cdns.dev, "Post bank switch failed: %d\n", ret);
return ret;
}
......@@ -28,6 +28,9 @@
#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_COMP_HW_VERSION 0x00
#define SWRM_COMP_CFG_ADDR 0x04
#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK BIT(1)
......@@ -351,8 +354,7 @@ static int qcom_swrm_cmd_fifo_wr_cmd(struct qcom_swrm_ctrl *swrm, u8 cmd_data,
/* Its assumed that write is okay as we do not get any status back */
swrm->reg_write(swrm, SWRM_CMD_FIFO_WR_CMD, val);
/* version 1.3 or less */
if (swrm->version <= 0x01030000)
if (swrm->version <= SWRM_VERSION_1_3_0)
usleep_range(150, 155);
if (cmd_id == SWR_BROADCAST_CMD_ID) {
......@@ -695,7 +697,7 @@ static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl)
u32p_replace_bits(&val, SWRM_DEF_CMD_NO_PINGS, SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK);
ctrl->reg_write(ctrl, SWRM_MCP_CFG_ADDR, val);
if (ctrl->version >= 0x01070000) {
if (ctrl->version >= SWRM_VERSION_1_7_0) {
ctrl->reg_write(ctrl, SWRM_LINK_MANAGER_EE, SWRM_EE_CPU);
ctrl->reg_write(ctrl, SWRM_MCP_BUS_CTRL,
SWRM_MCP_BUS_CLK_START << SWRM_EE_CPU);
......@@ -704,8 +706,7 @@ static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl)
}
/* Configure number of retries of a read/write cmd */
if (ctrl->version > 0x01050001) {
/* Only for versions >= 1.5.1 */
if (ctrl->version >= SWRM_VERSION_1_5_1) {
ctrl->reg_write(ctrl, SWRM_CMD_FIFO_CFG_ADDR,
SWRM_RD_WR_CMD_RETRIES |
SWRM_CONTINUE_EXEC_ON_CMD_IGNORE);
......@@ -1217,6 +1218,9 @@ static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl)
ctrl->num_dout_ports = val;
nports = ctrl->num_dout_ports + ctrl->num_din_ports;
if (nports > QCOM_SDW_MAX_PORTS)
return -EINVAL;
/* Valid port numbers are from 1-14, so mask out port 0 explicitly */
set_bit(0, &ctrl->dout_port_mask);
set_bit(0, &ctrl->din_port_mask);
......@@ -1239,7 +1243,7 @@ static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl)
ret = of_property_read_u8_array(np, "qcom,ports-block-pack-mode",
bp_mode, nports);
if (ret) {
if (ctrl->version <= 0x01030000)
if (ctrl->version <= SWRM_VERSION_1_3_0)
memset(bp_mode, SWR_INVALID_PARAM, QCOM_SDW_MAX_PORTS);
else
return ret;
......@@ -1442,7 +1446,7 @@ static int qcom_swrm_probe(struct platform_device *pdev)
pm_runtime_enable(dev);
/* Clk stop is not supported on WSA Soundwire masters */
if (ctrl->version <= 0x01030000) {
if (ctrl->version <= SWRM_VERSION_1_3_0) {
ctrl->clock_stop_not_supported = true;
} else {
ctrl->reg_read(ctrl, SWRM_COMP_MASTER_ID, &val);
......@@ -1527,7 +1531,7 @@ static int __maybe_unused swrm_runtime_resume(struct device *dev)
} else {
reset_control_reset(ctrl->audio_cgcr);
if (ctrl->version >= 0x01070000) {
if (ctrl->version >= SWRM_VERSION_1_7_0) {
ctrl->reg_write(ctrl, SWRM_LINK_MANAGER_EE, SWRM_EE_CPU);
ctrl->reg_write(ctrl, SWRM_MCP_BUS_CTRL,
SWRM_MCP_BUS_CLK_START << SWRM_EE_CPU);
......
......@@ -1369,7 +1369,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream,
if (ret < 0) {
dev_err(bus->dev, "Compute params failed: %d\n",
ret);
return ret;
goto restore_params;
}
}
......@@ -1389,7 +1389,7 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream,
ret = do_bank_switch(stream);
if (ret < 0) {
dev_err(bus->dev, "Bank switch failed: %d\n", ret);
pr_err("%s: do_bank_switch failed: %d\n", __func__, ret);
goto restore_params;
}
......@@ -1477,7 +1477,7 @@ static int _sdw_enable_stream(struct sdw_stream_runtime *stream)
/* Program params */
ret = sdw_program_params(bus, false);
if (ret < 0) {
dev_err(bus->dev, "Program params failed: %d\n", ret);
dev_err(bus->dev, "%s: Program params failed: %d\n", __func__, ret);
return ret;
}
......@@ -1497,7 +1497,7 @@ static int _sdw_enable_stream(struct sdw_stream_runtime *stream)
ret = do_bank_switch(stream);
if (ret < 0) {
dev_err(bus->dev, "Bank switch failed: %d\n", ret);
pr_err("%s: do_bank_switch failed: %d\n", __func__, ret);
return ret;
}
......@@ -1567,14 +1567,14 @@ static int _sdw_disable_stream(struct sdw_stream_runtime *stream)
/* Program params */
ret = sdw_program_params(bus, false);
if (ret < 0) {
dev_err(bus->dev, "Program params failed: %d\n", ret);
dev_err(bus->dev, "%s: Program params failed: %d\n", __func__, ret);
return ret;
}
}
ret = do_bank_switch(stream);
if (ret < 0) {
pr_err("Bank switch failed: %d\n", ret);
pr_err("%s: do_bank_switch failed: %d\n", __func__, ret);
return ret;
}
......@@ -1664,7 +1664,7 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream)
/* Program params */
ret = sdw_program_params(bus, false);
if (ret < 0) {
dev_err(bus->dev, "Program params failed: %d\n", ret);
dev_err(bus->dev, "%s: Program params failed: %d\n", __func__, ret);
return ret;
}
}
......@@ -1893,7 +1893,8 @@ int sdw_stream_add_master(struct sdw_bus *bus,
m_rt = sdw_master_rt_alloc(bus, stream);
if (!m_rt) {
dev_err(bus->dev, "Master runtime alloc failed for stream:%s\n", stream->name);
dev_err(bus->dev, "%s: Master runtime alloc failed for stream:%s\n",
__func__, stream->name);
ret = -ENOMEM;
goto unlock;
}
......@@ -2012,7 +2013,8 @@ int sdw_stream_add_slave(struct sdw_slave *slave,
*/
m_rt = sdw_master_rt_alloc(slave->bus, stream);
if (!m_rt) {
dev_err(&slave->dev, "Master runtime alloc failed for stream:%s\n", stream->name);
dev_err(&slave->dev, "%s: Master runtime alloc failed for stream:%s\n",
__func__, stream->name);
ret = -ENOMEM;
goto unlock;
}
......
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
*/
#ifndef __SDW_AMD_H
#define __SDW_AMD_H
#include <linux/soundwire/sdw.h>
/* AMD pm_runtime quirk definitions */
/*
* Force the clock to stop(ClockStopMode0) when suspend callback
* is invoked.
*/
#define AMD_SDW_CLK_STOP_MODE 1
/*
* Stop the bus when runtime suspend/system level suspend callback
* is invoked. If set, a complete bus reset and re-enumeration will
* be performed when the bus restarts. In-band wake interrupts are
* not supported in this mode.
*/
#define AMD_SDW_POWER_OFF_MODE 2
#define ACP_SDW0 0
#define ACP_SDW1 1
struct acp_sdw_pdata {
u16 instance;
/* mutex to protect acp common register access */
struct mutex *acp_sdw_lock;
};
struct sdw_manager_reg_mask {
u32 sw_pad_enable_mask;
u32 sw_pad_pulldown_mask;
u32 acp_sdw_intr_mask;
};
/**
* struct sdw_amd_dai_runtime: AMD sdw dai runtime data
*
* @name: SoundWire stream name
* @stream: stream runtime
* @bus: Bus handle
* @stream_type: Stream type
*/
struct sdw_amd_dai_runtime {
char *name;
struct sdw_stream_runtime *stream;
struct sdw_bus *bus;
enum sdw_stream_type stream_type;
};
/**
* struct amd_sdw_manager - amd manager driver context
* @bus: bus handle
* @dev: linux device
* @mmio: SoundWire registers mmio base
* @acp_mmio: acp registers mmio base
* @reg_mask: register mask structure per manager instance
* @amd_sdw_irq_thread: SoundWire manager irq workqueue
* @amd_sdw_work: peripheral status work queue
* @probe_work: SoundWire manager probe workqueue
* @acp_sdw_lock: mutex to protect acp share register access
* @status: peripheral devices status array
* @num_din_ports: number of input ports
* @num_dout_ports: number of output ports
* @cols_index: Column index in frame shape
* @rows_index: Rows index in frame shape
* @instance: SoundWire manager instance
* @quirks: SoundWire manager quirks
* @wake_en_mask: wake enable mask per SoundWire manager
* @clk_stopped: flag set to true when clock is stopped
* @power_mode_mask: flag interprets amd SoundWire manager power mode
* @dai_runtime_array: dai runtime array
*/
struct amd_sdw_manager {
struct sdw_bus bus;
struct device *dev;
void __iomem *mmio;
void __iomem *acp_mmio;
struct sdw_manager_reg_mask *reg_mask;
struct work_struct amd_sdw_irq_thread;
struct work_struct amd_sdw_work;
struct work_struct probe_work;
/* mutex to protect acp common register access */
struct mutex *acp_sdw_lock;
enum sdw_slave_status status[SDW_MAX_DEVICES + 1];
int num_din_ports;
int num_dout_ports;
int cols_index;
int rows_index;
u32 instance;
u32 quirks;
u32 wake_en_mask;
u32 power_mode_mask;
bool clk_stopped;
struct sdw_amd_dai_runtime **dai_runtime_array;
};
#endif
......@@ -309,6 +309,12 @@ struct sdw_intel;
* @shim_wake: enable/disable in-band wake management
* @pre_bank_switch: helper for bus management
* @post_bank_switch: helper for bus management
* @sync_arm: helper for multi-link synchronization
* @sync_go_unlocked: helper for multi-link synchronization -
* shim_lock is assumed to be locked at higher level
* @sync_go: helper for multi-link synchronization
* @sync_check_cmdsync_unlocked: helper for multi-link synchronization
* and bank switch - shim_lock is assumed to be locked at higher level
*/
struct sdw_intel_hw_ops {
void (*debugfs_init)(struct sdw_intel *sdw);
......@@ -330,6 +336,11 @@ struct sdw_intel_hw_ops {
int (*pre_bank_switch)(struct sdw_intel *sdw);
int (*post_bank_switch)(struct sdw_intel *sdw);
void (*sync_arm)(struct sdw_intel *sdw);
int (*sync_go_unlocked)(struct sdw_intel *sdw);
int (*sync_go)(struct sdw_intel *sdw);
bool (*sync_check_cmdsync_unlocked)(struct sdw_intel *sdw);
};
extern const struct sdw_intel_hw_ops sdw_intel_cnl_hw_ops;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment