summaryrefslogtreecommitdiff
path: root/drivers/soundwire/amd_manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/soundwire/amd_manager.c')
-rw-r--r--drivers/soundwire/amd_manager.c282
1 files changed, 226 insertions, 56 deletions
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index 6bcf8e75273c..7a671a786197 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -6,6 +6,7 @@
*/
#include <linux/completion.h>
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/jiffies.h>
@@ -89,9 +90,8 @@ static void amd_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
u32 val;
mutex_lock(amd_manager->acp_sdw_lock);
- val = readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance));
- val |= sdw_manager_reg_mask_array[amd_manager->instance];
- writel(val, amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance));
+ val = sdw_manager_reg_mask_array[amd_manager->instance];
+ amd_updatel(amd_manager->acp_mmio, ACP_EXTERNAL_INTR_CNTL(amd_manager->instance), val, val);
mutex_unlock(amd_manager->acp_sdw_lock);
writel(AMD_SDW_IRQ_MASK_0TO7, amd_manager->mmio +
@@ -103,12 +103,12 @@ static void amd_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
static void amd_disable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
{
- u32 val;
+ u32 irq_mask;
mutex_lock(amd_manager->acp_sdw_lock);
- val = readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance));
- val &= ~sdw_manager_reg_mask_array[amd_manager->instance];
- writel(val, amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance));
+ irq_mask = sdw_manager_reg_mask_array[amd_manager->instance];
+ amd_updatel(amd_manager->acp_mmio, ACP_EXTERNAL_INTR_CNTL(amd_manager->instance),
+ irq_mask, 0);
mutex_unlock(amd_manager->acp_sdw_lock);
writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_0TO7);
@@ -143,6 +143,57 @@ static void amd_sdw_wake_enable(struct amd_sdw_manager *amd_manager, bool enable
writel(wake_ctrl, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11);
}
+static int amd_sdw_set_device_state(struct amd_sdw_manager *amd_manager, u32 target_device_state)
+{
+ u32 sdw_dev_state;
+
+ sdw_dev_state = readl(amd_manager->acp_mmio + AMD_SDW_DEVICE_STATE);
+ switch (amd_manager->instance) {
+ case ACP_SDW0:
+ u32p_replace_bits(&sdw_dev_state, target_device_state,
+ AMD_SDW0_DEVICE_STATE_MASK);
+ break;
+ case ACP_SDW1:
+ u32p_replace_bits(&sdw_dev_state, target_device_state,
+ AMD_SDW1_DEVICE_STATE_MASK);
+ break;
+ default:
+ return -EINVAL;
+ }
+ writel(sdw_dev_state, amd_manager->acp_mmio + AMD_SDW_DEVICE_STATE);
+ sdw_dev_state = readl(amd_manager->acp_mmio + AMD_SDW_DEVICE_STATE);
+ dev_dbg(amd_manager->dev, "AMD_SDW_DEVICE_STATE:0x%x\n", sdw_dev_state);
+ return 0;
+}
+
+static int amd_sdw_host_wake_enable(struct amd_sdw_manager *amd_manager, bool enable)
+{
+ u32 intr_cntl1;
+ u32 sdw_host_wake_irq_mask;
+
+ if (!amd_manager->wake_en_mask)
+ return 0;
+
+ switch (amd_manager->instance) {
+ case ACP_SDW0:
+ sdw_host_wake_irq_mask = AMD_SDW0_HOST_WAKE_INTR_MASK;
+ break;
+ case ACP_SDW1:
+ sdw_host_wake_irq_mask = AMD_SDW1_HOST_WAKE_INTR_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ intr_cntl1 = readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(ACP_SDW1));
+ if (enable)
+ intr_cntl1 |= sdw_host_wake_irq_mask;
+ else
+ intr_cntl1 &= ~sdw_host_wake_irq_mask;
+ writel(intr_cntl1, amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(ACP_SDW1));
+ return 0;
+}
+
static void amd_sdw_ctl_word_prep(u32 *lower_word, u32 *upper_word, struct sdw_msg *msg,
int cmd_offset)
{
@@ -187,7 +238,7 @@ static u64 amd_sdw_send_cmd_get_resp(struct amd_sdw_manager *amd_manager, u32 lo
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(AMD_SDW_IMM_RES_VALID, amd_manager->mmio + ACP_SW_IMM_CMD_STS);
}
writel(upper_data, amd_manager->mmio + ACP_SW_IMM_CMD_UPPER_WORD);
writel(lower_data, amd_manager->mmio + ACP_SW_IMM_CMD_LOWER_QWORD);
@@ -295,7 +346,7 @@ static enum sdw_command_response amd_sdw_fill_msg_resp(struct amd_sdw_manager *a
msg->dev_num);
return SDW_CMD_FAIL;
}
- dev_err_ratelimited(amd_manager->dev, "command is ignored for Slave %d\n",
+ dev_dbg_ratelimited(amd_manager->dev, "command is ignored for Slave %d\n",
msg->dev_num);
return SDW_CMD_IGNORED;
}
@@ -384,7 +435,7 @@ static u32 amd_sdw_read_ping_status(struct sdw_bus *bus)
return slave_stat;
}
-static int amd_sdw_compute_params(struct sdw_bus *bus)
+static int amd_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream)
{
struct sdw_transport_data t_data = {0};
struct sdw_master_runtime *m_rt;
@@ -410,7 +461,7 @@ static int amd_sdw_compute_params(struct sdw_bus *bus)
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_BLK_PKG_PER_PORT, p_rt->lane);
sdw_fill_port_params(&p_rt->port_params,
p_rt->num, bps,
@@ -433,12 +484,22 @@ static int amd_sdw_port_params(struct sdw_bus *bus, struct sdw_port_params *p_pa
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;
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ switch (amd_manager->instance) {
+ case ACP_SDW0:
+ frame_fmt_reg = acp63_sdw0_dp_reg[p_params->num].frame_fmt_reg;
+ break;
+ case ACP_SDW1:
+ frame_fmt_reg = acp63_sdw1_dp_reg[p_params->num].frame_fmt_reg;
+ break;
+ default:
+ return -EINVAL;
+ }
break;
- case ACP_SDW1:
- frame_fmt_reg = sdw1_manager_dp_reg[p_params->num].frame_fmt_reg;
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ frame_fmt_reg = acp70_sdw_dp_reg[p_params->num].frame_fmt_reg;
break;
default:
return -EINVAL;
@@ -465,20 +526,36 @@ static int amd_sdw_transport_params(struct sdw_bus *bus,
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;
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ switch (amd_manager->instance) {
+ case ACP_SDW0:
+ frame_fmt_reg = acp63_sdw0_dp_reg[params->port_num].frame_fmt_reg;
+ sample_int_reg = acp63_sdw0_dp_reg[params->port_num].sample_int_reg;
+ hctrl_dp0_reg = acp63_sdw0_dp_reg[params->port_num].hctrl_dp0_reg;
+ offset_reg = acp63_sdw0_dp_reg[params->port_num].offset_reg;
+ lane_ctrl_ch_en_reg =
+ acp63_sdw0_dp_reg[params->port_num].lane_ctrl_ch_en_reg;
+ break;
+ case ACP_SDW1:
+ frame_fmt_reg = acp63_sdw1_dp_reg[params->port_num].frame_fmt_reg;
+ sample_int_reg = acp63_sdw1_dp_reg[params->port_num].sample_int_reg;
+ hctrl_dp0_reg = acp63_sdw1_dp_reg[params->port_num].hctrl_dp0_reg;
+ offset_reg = acp63_sdw1_dp_reg[params->port_num].offset_reg;
+ lane_ctrl_ch_en_reg =
+ acp63_sdw1_dp_reg[params->port_num].lane_ctrl_ch_en_reg;
+ break;
+ default:
+ return -EINVAL;
+ }
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;
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ frame_fmt_reg = acp70_sdw_dp_reg[params->port_num].frame_fmt_reg;
+ sample_int_reg = acp70_sdw_dp_reg[params->port_num].sample_int_reg;
+ hctrl_dp0_reg = acp70_sdw_dp_reg[params->port_num].hctrl_dp0_reg;
+ offset_reg = acp70_sdw_dp_reg[params->port_num].offset_reg;
+ lane_ctrl_ch_en_reg = acp70_sdw_dp_reg[params->port_num].lane_ctrl_ch_en_reg;
break;
default:
return -EINVAL;
@@ -520,12 +597,24 @@ static int amd_sdw_port_enable(struct sdw_bus *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;
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ switch (amd_manager->instance) {
+ case ACP_SDW0:
+ lane_ctrl_ch_en_reg =
+ acp63_sdw0_dp_reg[enable_ch->port_num].lane_ctrl_ch_en_reg;
+ break;
+ case ACP_SDW1:
+ lane_ctrl_ch_en_reg =
+ acp63_sdw1_dp_reg[enable_ch->port_num].lane_ctrl_ch_en_reg;
+ break;
+ default:
+ return -EINVAL;
+ }
break;
- case ACP_SDW1:
- lane_ctrl_ch_en_reg = sdw1_manager_dp_reg[enable_ch->port_num].lane_ctrl_ch_en_reg;
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ lane_ctrl_ch_en_reg = acp70_sdw_dp_reg[enable_ch->port_num].lane_ctrl_ch_en_reg;
break;
default:
return -EINVAL;
@@ -572,6 +661,9 @@ static int sdw_master_read_amd_prop(struct sdw_bus *bus)
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;
+
+ fwnode_handle_put(link);
+
return 0;
}
@@ -601,7 +693,6 @@ static int amd_sdw_hw_params(struct snd_pcm_substream *substream,
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;
@@ -624,11 +715,10 @@ static int amd_sdw_hw_params(struct snd_pcm_substream *substream,
sconfig.bps = snd_pcm_format_width(params_format(params));
/* Port configuration */
- pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL);
- if (!pconfig) {
- ret = -ENOMEM;
- goto error;
- }
+ struct sdw_port_config *pconfig __free(kfree) = kzalloc(sizeof(*pconfig),
+ GFP_KERNEL);
+ if (!pconfig)
+ return -ENOMEM;
pconfig->num = dai->id;
pconfig->ch_mask = (1 << ch) - 1;
@@ -637,8 +727,6 @@ static int amd_sdw_hw_params(struct snd_pcm_substream *substream,
if (ret)
dev_err(amd_manager->dev, "add manager to stream failed:%d\n", ret);
- kfree(pconfig);
-error:
return ret;
}
@@ -828,6 +916,7 @@ static void amd_sdw_update_slave_status(u32 status_change_0to7, u32 status_chang
static void amd_sdw_process_wake_event(struct amd_sdw_manager *amd_manager)
{
+ dev_dbg(amd_manager->dev, "SoundWire Wake event reported\n");
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);
@@ -911,6 +1000,7 @@ static int amd_sdw_manager_probe(struct platform_device *pdev)
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->acp_rev = pdata->acp_rev;
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;
@@ -927,15 +1017,26 @@ static int amd_sdw_manager_probe(struct platform_device *pdev)
* information.
*/
amd_manager->bus.controller_id = 0;
-
- 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;
+ dev_dbg(dev, "acp_rev:0x%x\n", amd_manager->acp_rev);
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ switch (amd_manager->instance) {
+ case ACP_SDW0:
+ amd_manager->num_dout_ports = AMD_ACP63_SDW0_MAX_TX_PORTS;
+ amd_manager->num_din_ports = AMD_ACP63_SDW0_MAX_RX_PORTS;
+ break;
+ case ACP_SDW1:
+ amd_manager->num_dout_ports = AMD_ACP63_SDW1_MAX_TX_PORTS;
+ amd_manager->num_din_ports = AMD_ACP63_SDW1_MAX_RX_PORTS;
+ break;
+ default:
+ return -EINVAL;
+ }
break;
- case ACP_SDW1:
- amd_manager->num_dout_ports = AMD_SDW1_MAX_TX_PORTS;
- amd_manager->num_din_ports = AMD_SDW1_MAX_RX_PORTS;
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ amd_manager->num_dout_ports = AMD_ACP70_SDW_MAX_TX_PORTS;
+ amd_manager->num_din_ports = AMD_ACP70_SDW_MAX_RX_PORTS;
break;
default:
return -EINVAL;
@@ -1108,9 +1209,24 @@ static int __maybe_unused amd_suspend(struct device *dev)
}
if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
+ cancel_work_sync(&amd_manager->amd_sdw_work);
amd_sdw_wake_enable(amd_manager, false);
- return amd_sdw_clock_stop(amd_manager);
+ if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
+ ret = amd_sdw_host_wake_enable(amd_manager, false);
+ if (ret)
+ return ret;
+ }
+ ret = amd_sdw_clock_stop(amd_manager);
+ if (ret)
+ return ret;
} else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
+ cancel_work_sync(&amd_manager->amd_sdw_work);
+ amd_sdw_wake_enable(amd_manager, false);
+ if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
+ ret = amd_sdw_host_wake_enable(amd_manager, false);
+ if (ret)
+ return ret;
+ }
/*
* As per hardware programming sequence on AMD platforms,
* clock stop should be invoked first before powering-off
@@ -1118,7 +1234,14 @@ static int __maybe_unused amd_suspend(struct device *dev)
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
- return amd_deinit_sdw_manager(amd_manager);
+ ret = amd_deinit_sdw_manager(amd_manager);
+ if (ret)
+ return ret;
+ }
+ if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
+ ret = amd_sdw_set_device_state(amd_manager, AMD_SDW_DEVICE_STATE_D3);
+ if (ret)
+ return ret;
}
return 0;
}
@@ -1128,6 +1251,7 @@ 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;
+ u32 val;
if (bus->prop.hw_disabled) {
dev_dbg(bus->dev, "SoundWire manager %d is disabled,\n",
@@ -1136,12 +1260,40 @@ static int __maybe_unused amd_suspend_runtime(struct device *dev)
}
if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
amd_sdw_wake_enable(amd_manager, true);
- return amd_sdw_clock_stop(amd_manager);
+ if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
+ ret = amd_sdw_host_wake_enable(amd_manager, true);
+ if (ret)
+ return ret;
+ }
+ ret = amd_sdw_clock_stop(amd_manager);
+ if (ret)
+ return ret;
} else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
+ amd_sdw_wake_enable(amd_manager, true);
+ if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
+ ret = amd_sdw_host_wake_enable(amd_manager, true);
+ if (ret)
+ return ret;
+ }
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
- return amd_deinit_sdw_manager(amd_manager);
+ ret = amd_deinit_sdw_manager(amd_manager);
+ if (ret)
+ return ret;
+ }
+ if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
+ ret = amd_sdw_set_device_state(amd_manager, AMD_SDW_DEVICE_STATE_D3);
+ if (ret)
+ return ret;
+ if (amd_manager->wake_en_mask) {
+ val = readl(amd_manager->acp_mmio + ACP_PME_EN);
+ if (!val) {
+ writel(1, amd_manager->acp_mmio + ACP_PME_EN);
+ val = readl(amd_manager->acp_mmio + ACP_PME_EN);
+ dev_dbg(amd_manager->dev, "ACP_PME_EN:0x%x\n", val);
+ }
+ }
}
return 0;
}
@@ -1160,8 +1312,21 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
}
if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
- return amd_sdw_clock_stop_exit(amd_manager);
+ ret = amd_sdw_clock_stop_exit(amd_manager);
+ if (ret)
+ return ret;
+ if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
+ ret = amd_sdw_host_wake_enable(amd_manager, false);
+ if (ret)
+ return ret;
+ }
} else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
+ writel(0x00, amd_manager->acp_mmio + ACP_SW_WAKE_EN(amd_manager->instance));
+ if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
+ ret = amd_sdw_host_wake_enable(amd_manager, false);
+ if (ret)
+ return ret;
+ }
val = readl(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL);
if (val) {
val |= AMD_SDW_CLK_RESUME_REQ;
@@ -1182,6 +1347,11 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
return ret;
amd_sdw_set_frameshape(amd_manager);
}
+ if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
+ ret = amd_sdw_set_device_state(amd_manager, AMD_SDW_DEVICE_STATE_D0);
+ if (ret)
+ return ret;
+ }
return 0;
}
@@ -1193,7 +1363,7 @@ static const struct dev_pm_ops amd_pm = {
static struct platform_driver amd_sdw_driver = {
.probe = &amd_sdw_manager_probe,
- .remove_new = &amd_sdw_manager_remove,
+ .remove = &amd_sdw_manager_remove,
.driver = {
.name = "amd_sdw_manager",
.pm = &amd_pm,