aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2025-04-01 12:43:13 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2025-04-01 12:43:13 -0700
commit4d31167e844bac8be7587781c52af450b6451f01 (patch)
tree53c16596a96fe51544318cf0bae7c1e7d62bc747 /drivers
parentMerge tag 'char-misc-6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc (diff)
parentsoundwire: take in count the bandwidth of a prepared stream (diff)
downloadwireguard-linux-4d31167e844bac8be7587781c52af450b6451f01.tar.xz
wireguard-linux-4d31167e844bac8be7587781c52af450b6451f01.zip
Merge tag 'soundwire-6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire
Pull soundwire updates from Vinod Koul: - Support for SoundWire Bulk Register Access (BRA) protocol in core along with Intel driver support and ASoC bits required - AMD driver updates and support for ACP 7.0 and 7.1 platforms * tag 'soundwire-6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire: (28 commits) soundwire: take in count the bandwidth of a prepared stream ASoC: rt711-sdca: add DP0 support soundwire: debugfs: add interface for BPT/BRA transfers ASoC: SOF: Intel: hda-sdw-bpt: add CHAIN_DMA support soundwire: intel_ace2x: add BPT send_async/wait callbacks soundwire: intel: add BPT context definition ASoC: SOF: Intel: hda-sdw-bpt: add helpers for SoundWire BPT DMA soundwire: intel_auxdevice: add indirection for BPT send_async/wait soundwire: cadence: add BTP/BRA helpers to format data soundwire: bus: add bpt_stream pointer soundwire: bus: add send_async/wait APIs for BPT protocol soundwire: stream: reuse existing code for BPT stream soundwire: stream: special-case the bus compute_params() routine soundwire: stream: extend sdw_alloc_stream() to take 'type' parameter soundwire: extend sdw_stream_type to BPT soundwire: cadence: add BTP support for DP0 Documentation: driver: add SoundWire BRA description soundwire: amd: change the log level for command response log soundwire: slave: fix an OF node reference leak in soundwire slave device soundwire: Use str_enable_disable-like helpers ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/soundwire/Kconfig1
-rw-r--r--drivers/soundwire/amd_manager.c151
-rw-r--r--drivers/soundwire/amd_manager.h26
-rw-r--r--drivers/soundwire/bus.c48
-rw-r--r--drivers/soundwire/bus.h18
-rw-r--r--drivers/soundwire/cadence_master.c668
-rw-r--r--drivers/soundwire/cadence_master.h20
-rw-r--r--drivers/soundwire/debugfs.c87
-rw-r--r--drivers/soundwire/generic_bandwidth_allocation.c53
-rw-r--r--drivers/soundwire/intel.h23
-rw-r--r--drivers/soundwire/intel_ace2x.c312
-rw-r--r--drivers/soundwire/intel_auxdevice.c45
-rw-r--r--drivers/soundwire/slave.c1
-rw-r--r--drivers/soundwire/stream.c143
14 files changed, 1509 insertions, 87 deletions
diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig
index f66f869dff2e..ad56393e4c93 100644
--- a/drivers/soundwire/Kconfig
+++ b/drivers/soundwire/Kconfig
@@ -31,6 +31,7 @@ config SOUNDWIRE_AMD
config SOUNDWIRE_CADENCE
tristate
+ select CRC8
config SOUNDWIRE_INTEL
tristate "Intel SoundWire Master driver"
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index 5a54b10daf77..a12c68b93b1c 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -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)
{
@@ -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;
}
@@ -446,6 +497,10 @@ static int amd_sdw_port_params(struct sdw_bus *bus, struct sdw_port_params *p_pa
return -EINVAL;
}
break;
+ 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;
}
@@ -494,6 +549,14 @@ static int amd_sdw_transport_params(struct sdw_bus *bus,
return -EINVAL;
}
break;
+ 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;
}
@@ -549,6 +612,10 @@ static int amd_sdw_port_enable(struct sdw_bus *bus,
return -EINVAL;
}
break;
+ 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;
}
@@ -849,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);
@@ -965,6 +1033,11 @@ static int amd_sdw_manager_probe(struct platform_device *pdev)
return -EINVAL;
}
break;
+ 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;
}
@@ -1137,8 +1210,21 @@ static int __maybe_unused amd_suspend(struct device *dev)
if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
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) {
+ 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
@@ -1146,7 +1232,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;
}
@@ -1156,6 +1249,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",
@@ -1164,12 +1258,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;
}
@@ -1188,9 +1310,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;
@@ -1211,6 +1345,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;
}
diff --git a/drivers/soundwire/amd_manager.h b/drivers/soundwire/amd_manager.h
index cc2170e4521e..6cc916b0c820 100644
--- a/drivers/soundwire/amd_manager.h
+++ b/drivers/soundwire/amd_manager.h
@@ -159,8 +159,11 @@
#define AMD_ACP63_SDW0_MAX_RX_PORTS 3
#define AMD_ACP63_SDW1_MAX_TX_PORTS 1
#define AMD_ACP63_SDW1_MAX_RX_PORTS 1
+#define AMD_ACP70_SDW_MAX_TX_PORTS 3
+#define AMD_ACP70_SDW_MAX_RX_PORTS 3
#define AMD_ACP63_SDW0_MAX_DAI 6
#define AMD_ACP63_SDW1_MAX_DAI 2
+#define AMD_ACP70_SDW_MAX_DAI 6
#define AMD_SDW_SLAVE_0_ATTACHED 5
#define AMD_SDW_SSP_COUNTER_VAL 3
@@ -191,6 +194,14 @@
#define AMD_SDW_CLK_RESUME_DONE 3
#define AMD_SDW_WAKE_STAT_MASK BIT(16)
#define AMD_SDW_WAKE_INTR_MASK BIT(16)
+#define AMD_SDW0_HOST_WAKE_INTR_MASK BIT(22)
+#define AMD_SDW1_HOST_WAKE_INTR_MASK BIT(23)
+#define AMD_SDW_DEVICE_STATE 0x1430
+#define AMD_SDW0_DEVICE_STATE_MASK GENMASK(1, 0)
+#define AMD_SDW1_DEVICE_STATE_MASK GENMASK(3, 2)
+#define AMD_SDW_DEVICE_STATE_D0 0
+#define AMD_SDW_DEVICE_STATE_D3 3
+#define ACP_PME_EN 0x0001400
static u32 amd_sdw_freq_tbl[AMD_SDW_MAX_FREQ_NUM] = {
AMD_SDW_DEFAULT_CLK_FREQ,
@@ -244,6 +255,21 @@ static struct sdw_manager_dp_reg acp63_sdw1_dp_reg[AMD_ACP63_SDW1_MAX_DAI] = {
ACP_SW_AUDIO1_RX_OFFSET, ACP_SW_AUDIO1_RX_CHANNEL_ENABLE_DP0}
};
+static struct sdw_manager_dp_reg acp70_sdw_dp_reg[AMD_ACP70_SDW_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 u32 sdw_manager_reg_mask_array[AMD_SDW_MAX_MANAGER_COUNT] = {
AMD_SDW0_EXT_INTR_MASK,
AMD_SDW1_EXT_INTR_MASK
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c
index 9b295fc9acd5..6f8a20014e76 100644
--- a/drivers/soundwire/bus.c
+++ b/drivers/soundwire/bus.c
@@ -8,6 +8,7 @@
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
+#include <linux/string_choices.h>
#include "bus.h"
#include "irq.h"
#include "sysfs_local.h"
@@ -277,7 +278,7 @@ static int sdw_transfer_unlocked(struct sdw_bus *bus, struct sdw_msg *msg)
if (ret != 0 && ret != -ENODATA)
dev_err(bus->dev, "trf on Slave %d failed:%d %s addr %x count %d\n",
msg->dev_num, ret,
- (msg->flags & SDW_MSG_FLAG_WRITE) ? "write" : "read",
+ str_write_read(msg->flags & SDW_MSG_FLAG_WRITE),
msg->addr, msg->len);
return ret;
@@ -1263,7 +1264,7 @@ int sdw_configure_dpn_intr(struct sdw_slave *slave,
if (slave->bus->params.s_data_mode != SDW_PORT_DATA_MODE_NORMAL) {
dev_dbg(&slave->dev, "TEST FAIL interrupt %s\n",
- enable ? "on" : "off");
+ str_on_off(enable));
mask |= SDW_DPN_INT_TEST_FAIL;
}
@@ -2038,3 +2039,46 @@ void sdw_clear_slave_status(struct sdw_bus *bus, u32 request)
}
}
EXPORT_SYMBOL(sdw_clear_slave_status);
+
+int sdw_bpt_send_async(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg)
+{
+ if (msg->len > SDW_BPT_MSG_MAX_BYTES) {
+ dev_err(bus->dev, "Invalid BPT message length %d\n", msg->len);
+ return -EINVAL;
+ }
+
+ /* check device is enumerated */
+ if (slave->dev_num == SDW_ENUM_DEV_NUM ||
+ slave->dev_num > SDW_MAX_DEVICES) {
+ dev_err(&slave->dev, "Invalid device number %d\n", slave->dev_num);
+ return -ENODEV;
+ }
+
+ /* make sure all callbacks are defined */
+ if (!bus->ops->bpt_send_async ||
+ !bus->ops->bpt_wait) {
+ dev_err(bus->dev, "BPT callbacks not defined\n");
+ return -EOPNOTSUPP;
+ }
+
+ return bus->ops->bpt_send_async(bus, slave, msg);
+}
+EXPORT_SYMBOL(sdw_bpt_send_async);
+
+int sdw_bpt_wait(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg)
+{
+ return bus->ops->bpt_wait(bus, slave, msg);
+}
+EXPORT_SYMBOL(sdw_bpt_wait);
+
+int sdw_bpt_send_sync(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg)
+{
+ int ret;
+
+ ret = sdw_bpt_send_async(bus, slave, msg);
+ if (ret < 0)
+ return ret;
+
+ return sdw_bpt_wait(bus, slave, msg);
+}
+EXPORT_SYMBOL(sdw_bpt_send_sync);
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h
index fc990171b3f7..02651fbb683a 100644
--- a/drivers/soundwire/bus.h
+++ b/drivers/soundwire/bus.h
@@ -72,6 +72,24 @@ struct sdw_msg {
bool page;
};
+/**
+ * struct sdw_btp_msg - Message structure
+ * @addr: Start Register address accessed in the Slave
+ * @len: number of bytes to transfer. More than 64Kb can be transferred
+ * but a practical limit of SDW_BPT_MSG_MAX_BYTES is enforced.
+ * @dev_num: Slave device number
+ * @flags: transfer flags, indicate if xfer is read or write
+ * @buf: message data buffer (filled by host for write, filled
+ * by Peripheral hardware for reads)
+ */
+struct sdw_bpt_msg {
+ u32 addr;
+ u32 len;
+ u8 dev_num;
+ u8 flags;
+ u8 *buf;
+};
+
#define SDW_DOUBLE_RATE_FACTOR 2
#define SDW_STRM_RATE_GROUPING 1
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c
index f367670ea991..21bb491d026b 100644
--- a/drivers/soundwire/cadence_master.c
+++ b/drivers/soundwire/cadence_master.c
@@ -7,6 +7,7 @@
*/
#include <linux/cleanup.h>
+#include <linux/crc8.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/debugfs.h>
@@ -184,6 +185,7 @@ MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask");
#define CDNS_PORTCTRL_TEST_FAILED BIT(1)
#define CDNS_PORTCTRL_DIRN BIT(7)
#define CDNS_PORTCTRL_BANK_INVERT BIT(8)
+#define CDNS_PORTCTRL_BULK_ENABLE BIT(16)
#define CDNS_PORT_OFFSET 0x80
@@ -1341,7 +1343,7 @@ static u32 cdns_set_initial_frame_shape(int n_rows, int n_cols)
return val;
}
-static void cdns_init_clock_ctrl(struct sdw_cdns *cdns)
+static int cdns_init_clock_ctrl(struct sdw_cdns *cdns)
{
struct sdw_bus *bus = &cdns->bus;
struct sdw_master_prop *prop = &bus->prop;
@@ -1355,14 +1357,25 @@ static void cdns_init_clock_ctrl(struct sdw_cdns *cdns)
prop->default_row,
prop->default_col);
+ if (!prop->default_frame_rate || !prop->default_row) {
+ dev_err(cdns->dev, "Default frame_rate %d or row %d is invalid\n",
+ prop->default_frame_rate, prop->default_row);
+ return -EINVAL;
+ }
+
/* Set clock divider */
- divider = (prop->mclk_freq / prop->max_clk_freq) - 1;
+ divider = (prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR /
+ bus->params.curr_dr_freq) - 1;
cdns_updatel(cdns, CDNS_MCP_CLK_CTRL0,
CDNS_MCP_CLK_MCLKD_MASK, divider);
cdns_updatel(cdns, CDNS_MCP_CLK_CTRL1,
CDNS_MCP_CLK_MCLKD_MASK, divider);
+ /* Set frame shape base on the actual bus frequency. */
+ prop->default_col = bus->params.curr_dr_freq /
+ prop->default_frame_rate / prop->default_row;
+
/*
* Frame shape changes after initialization have to be done
* with the bank switch mechanism
@@ -1375,6 +1388,8 @@ static void cdns_init_clock_ctrl(struct sdw_cdns *cdns)
ssp_interval = prop->default_frame_rate / SDW_CADENCE_GSYNC_HZ;
cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, ssp_interval);
cdns_writel(cdns, CDNS_MCP_SSP_CTRL1, ssp_interval);
+
+ return 0;
}
/**
@@ -1408,9 +1423,12 @@ EXPORT_SYMBOL(sdw_cdns_soft_reset);
*/
int sdw_cdns_init(struct sdw_cdns *cdns)
{
+ int ret;
u32 val;
- cdns_init_clock_ctrl(cdns);
+ ret = cdns_init_clock_ctrl(cdns);
+ if (ret)
+ return ret;
sdw_cdns_check_self_clearing_bits(cdns, __func__, false, 0);
@@ -1901,13 +1919,20 @@ void sdw_cdns_config_stream(struct sdw_cdns *cdns,
if (cdns->bus.params.m_data_mode != SDW_PORT_DATA_MODE_NORMAL)
val |= CDNS_PORTCTRL_TEST_FAILED;
+ } else if (pdi->num == 0 || pdi->num == 1) {
+ val |= CDNS_PORTCTRL_BULK_ENABLE;
}
offset = CDNS_PORTCTRL + pdi->num * CDNS_PORT_OFFSET;
cdns_updatel(cdns, offset,
- CDNS_PORTCTRL_DIRN | CDNS_PORTCTRL_TEST_FAILED,
+ CDNS_PORTCTRL_DIRN | CDNS_PORTCTRL_TEST_FAILED |
+ CDNS_PORTCTRL_BULK_ENABLE,
val);
- val = pdi->num;
+ /* The DataPort0 needs to be mapped to both PDI0 and PDI1 ! */
+ if (pdi->num == 1)
+ val = 0;
+ else
+ val = pdi->num;
val |= CDNS_PDI_CONFIG_SOFT_RESET;
val |= FIELD_PREP(CDNS_PDI_CONFIG_CHANNEL, (1 << ch) - 1);
cdns_writel(cdns, CDNS_PDI_CONFIG(pdi->num), val);
@@ -1952,5 +1977,638 @@ struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns,
}
EXPORT_SYMBOL(sdw_cdns_alloc_pdi);
+/*
+ * the MIPI SoundWire CRC8 polynomial is X^8 + X^6 + X^3 + X^2 + 1, MSB first
+ * The value is (1)01001101 = 0x4D
+ *
+ * the table below was generated with
+ *
+ * u8 crc8_lookup_table[CRC8_TABLE_SIZE];
+ * crc8_populate_msb(crc8_lookup_table, SDW_CRC8_POLY);
+ *
+ */
+#define SDW_CRC8_SEED 0xFF
+#define SDW_CRC8_POLY 0x4D
+
+static const u8 sdw_crc8_lookup_msb[CRC8_TABLE_SIZE] = {
+ 0x00, 0x4d, 0x9a, 0xd7, 0x79, 0x34, 0xe3, 0xae, /* 0 - 7 */
+ 0xf2, 0xbf, 0x68, 0x25, 0x8b, 0xc6, 0x11, 0x5c, /* 8 -15 */
+ 0xa9, 0xe4, 0x33, 0x7e, 0xd0, 0x9d, 0x4a, 0x07, /* 16 - 23 */
+ 0x5b, 0x16, 0xc1, 0x8c, 0x22, 0x6f, 0xb8, 0xf5, /* 24 - 31 */
+ 0x1f, 0x52, 0x85, 0xc8, 0x66, 0x2b, 0xfc, 0xb1, /* 32 - 39 */
+ 0xed, 0xa0, 0x77, 0x3a, 0x94, 0xd9, 0x0e, 0x43, /* 40 - 47 */
+ 0xb6, 0xfb, 0x2c, 0x61, 0xcf, 0x82, 0x55, 0x18, /* 48 - 55 */
+ 0x44, 0x09, 0xde, 0x93, 0x3d, 0x70, 0xa7, 0xea, /* 56 - 63 */
+ 0x3e, 0x73, 0xa4, 0xe9, 0x47, 0x0a, 0xdd, 0x90, /* 64 - 71 */
+ 0xcc, 0x81, 0x56, 0x1b, 0xb5, 0xf8, 0x2f, 0x62, /* 72 - 79 */
+ 0x97, 0xda, 0x0d, 0x40, 0xee, 0xa3, 0x74, 0x39, /* 80 - 87 */
+ 0x65, 0x28, 0xff, 0xb2, 0x1c, 0x51, 0x86, 0xcb, /* 88 - 95 */
+ 0x21, 0x6c, 0xbb, 0xf6, 0x58, 0x15, 0xc2, 0x8f, /* 96 - 103 */
+ 0xd3, 0x9e, 0x49, 0x04, 0xaa, 0xe7, 0x30, 0x7d, /* 104 - 111 */
+ 0x88, 0xc5, 0x12, 0x5f, 0xf1, 0xbc, 0x6b, 0x26, /* 112 - 119 */
+ 0x7a, 0x37, 0xe0, 0xad, 0x03, 0x4e, 0x99, 0xd4, /* 120 - 127 */
+ 0x7c, 0x31, 0xe6, 0xab, 0x05, 0x48, 0x9f, 0xd2, /* 128 - 135 */
+ 0x8e, 0xc3, 0x14, 0x59, 0xf7, 0xba, 0x6d, 0x20, /* 136 - 143 */
+ 0xd5, 0x98, 0x4f, 0x02, 0xac, 0xe1, 0x36, 0x7b, /* 144 - 151 */
+ 0x27, 0x6a, 0xbd, 0xf0, 0x5e, 0x13, 0xc4, 0x89, /* 152 - 159 */
+ 0x63, 0x2e, 0xf9, 0xb4, 0x1a, 0x57, 0x80, 0xcd, /* 160 - 167 */
+ 0x91, 0xdc, 0x0b, 0x46, 0xe8, 0xa5, 0x72, 0x3f, /* 168 - 175 */
+ 0xca, 0x87, 0x50, 0x1d, 0xb3, 0xfe, 0x29, 0x64, /* 176 - 183 */
+ 0x38, 0x75, 0xa2, 0xef, 0x41, 0x0c, 0xdb, 0x96, /* 184 - 191 */
+ 0x42, 0x0f, 0xd8, 0x95, 0x3b, 0x76, 0xa1, 0xec, /* 192 - 199 */
+ 0xb0, 0xfd, 0x2a, 0x67, 0xc9, 0x84, 0x53, 0x1e, /* 200 - 207 */
+ 0xeb, 0xa6, 0x71, 0x3c, 0x92, 0xdf, 0x08, 0x45, /* 208 - 215 */
+ 0x19, 0x54, 0x83, 0xce, 0x60, 0x2d, 0xfa, 0xb7, /* 216 - 223 */
+ 0x5d, 0x10, 0xc7, 0x8a, 0x24, 0x69, 0xbe, 0xf3, /* 224 - 231 */
+ 0xaf, 0xe2, 0x35, 0x78, 0xd6, 0x9b, 0x4c, 0x01, /* 232 - 239 */
+ 0xf4, 0xb9, 0x6e, 0x23, 0x8d, 0xc0, 0x17, 0x5a, /* 240 - 247 */
+ 0x06, 0x4b, 0x9c, 0xd1, 0x7f, 0x32, 0xe5, 0xa8 /* 248 - 255 */
+};
+
+/* BPT/BRA helpers */
+
+#define SDW_CDNS_BRA_HDR 6 /* defined by MIPI */
+#define SDW_CDNS_BRA_HDR_CRC 1 /* defined by MIPI */
+#define SDW_CDNS_BRA_HDR_CRC_PAD 1 /* Cadence only */
+#define SDW_CDNS_BRA_HDR_RESP 1 /* defined by MIPI */
+#define SDW_CDNS_BRA_HDR_RESP_PAD 1 /* Cadence only */
+
+#define SDW_CDNS_BRA_DATA_PAD 1 /* Cadence only */
+#define SDW_CDNS_BRA_DATA_CRC 1 /* defined by MIPI */
+#define SDW_CDNS_BRA_DATA_CRC_PAD 1 /* Cadence only */
+
+#define SDW_CDNS_BRA_FOOTER_RESP 1 /* defined by MIPI */
+#define SDW_CDNS_BRA_FOOTER_RESP_PAD 1 /* Cadence only */
+
+#define SDW_CDNS_WRITE_PDI1_BUFFER_SIZE \
+ ((SDW_CDNS_BRA_HDR_RESP + SDW_CDNS_BRA_HDR_RESP_PAD + \
+ SDW_CDNS_BRA_FOOTER_RESP + SDW_CDNS_BRA_FOOTER_RESP_PAD) * 2)
+
+#define SDW_CDNS_READ_PDI0_BUFFER_SIZE \
+ ((SDW_CDNS_BRA_HDR + SDW_CDNS_BRA_HDR_CRC + SDW_CDNS_BRA_HDR_CRC_PAD) * 2)
+
+static unsigned int sdw_cdns_bra_actual_data_size(unsigned int allocated_bytes_per_frame)
+{
+ unsigned int total;
+
+ if (allocated_bytes_per_frame < (SDW_CDNS_BRA_HDR + SDW_CDNS_BRA_HDR_CRC +
+ SDW_CDNS_BRA_HDR_RESP + SDW_CDNS_BRA_DATA_CRC +
+ SDW_CDNS_BRA_FOOTER_RESP))
+ return 0;
+
+ total = allocated_bytes_per_frame - SDW_CDNS_BRA_HDR - SDW_CDNS_BRA_HDR_CRC -
+ SDW_CDNS_BRA_HDR_RESP - SDW_CDNS_BRA_DATA_CRC - SDW_CDNS_BRA_FOOTER_RESP;
+
+ return total;
+}
+
+static unsigned int sdw_cdns_write_pdi0_buffer_size(unsigned int actual_data_size)
+{
+ unsigned int total;
+
+ total = SDW_CDNS_BRA_HDR + SDW_CDNS_BRA_HDR_CRC + SDW_CDNS_BRA_HDR_CRC_PAD;
+
+ total += actual_data_size;
+ if (actual_data_size & 1)
+ total += SDW_CDNS_BRA_DATA_PAD;
+
+ total += SDW_CDNS_BRA_DATA_CRC + SDW_CDNS_BRA_DATA_CRC_PAD;
+
+ return total * 2;
+}
+
+static unsigned int sdw_cdns_read_pdi1_buffer_size(unsigned int actual_data_size)
+{
+ unsigned int total;
+
+ total = SDW_CDNS_BRA_HDR_RESP + SDW_CDNS_BRA_HDR_RESP_PAD;
+
+ total += actual_data_size;
+ if (actual_data_size & 1)
+ total += SDW_CDNS_BRA_DATA_PAD;
+
+ total += SDW_CDNS_BRA_HDR_CRC + SDW_CDNS_BRA_HDR_CRC_PAD;
+
+ total += SDW_CDNS_BRA_FOOTER_RESP + SDW_CDNS_BRA_FOOTER_RESP_PAD;
+
+ return total * 2;
+}
+
+int sdw_cdns_bpt_find_buffer_sizes(int command, /* 0: write, 1: read */
+ int row, int col, unsigned int data_bytes,
+ unsigned int requested_bytes_per_frame,
+ unsigned int *data_per_frame, unsigned int *pdi0_buffer_size,
+ unsigned int *pdi1_buffer_size, unsigned int *num_frames)
+{
+ unsigned int bpt_bits = row * (col - 1);
+ unsigned int bpt_bytes = bpt_bits >> 3;
+ unsigned int actual_bpt_bytes;
+ unsigned int pdi0_tx_size;
+ unsigned int pdi1_rx_size;
+ unsigned int remainder;
+
+ if (!data_bytes)
+ return -EINVAL;
+
+ actual_bpt_bytes = sdw_cdns_bra_actual_data_size(bpt_bytes);
+ if (!actual_bpt_bytes)
+ return -EINVAL;
+
+ if (data_bytes < actual_bpt_bytes)
+ actual_bpt_bytes = data_bytes;
+
+ /*
+ * the caller may want to set the number of bytes per frame,
+ * allow when possible
+ */
+ if (requested_bytes_per_frame < actual_bpt_bytes)
+ actual_bpt_bytes = requested_bytes_per_frame;
+
+ *data_per_frame = actual_bpt_bytes;
+
+ if (command == 0) {
+ /*
+ * for writes we need to send all the data_bytes per frame,
+ * even for the last frame which may only transport fewer bytes
+ */
+
+ *num_frames = DIV_ROUND_UP(data_bytes, actual_bpt_bytes);
+
+ pdi0_tx_size = sdw_cdns_write_pdi0_buffer_size(actual_bpt_bytes);
+ pdi1_rx_size = SDW_CDNS_WRITE_PDI1_BUFFER_SIZE;
+
+ *pdi0_buffer_size = pdi0_tx_size * *num_frames;
+ *pdi1_buffer_size = pdi1_rx_size * *num_frames;
+ } else {
+ /*
+ * for reads we need to retrieve only what is requested in the BPT
+ * header, so the last frame needs to be special-cased
+ */
+ *num_frames = data_bytes / actual_bpt_bytes;
+
+ pdi0_tx_size = SDW_CDNS_READ_PDI0_BUFFER_SIZE;
+ pdi1_rx_size = sdw_cdns_read_pdi1_buffer_size(actual_bpt_bytes);
+
+ *pdi0_buffer_size = pdi0_tx_size * *num_frames;
+ *pdi1_buffer_size = pdi1_rx_size * *num_frames;
+
+ remainder = data_bytes % actual_bpt_bytes;
+ if (remainder) {
+ pdi0_tx_size = SDW_CDNS_READ_PDI0_BUFFER_SIZE;
+ pdi1_rx_size = sdw_cdns_read_pdi1_buffer_size(remainder);
+
+ *num_frames = *num_frames + 1;
+ *pdi0_buffer_size += pdi0_tx_size;
+ *pdi1_buffer_size += pdi1_rx_size;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(sdw_cdns_bpt_find_buffer_sizes);
+
+static int sdw_cdns_copy_write_data(u8 *data, int data_size, u8 *dma_buffer, int dma_buffer_size)
+{
+ /*
+ * the implementation copies the data one byte at a time. Experiments with
+ * two bytes at a time did not seem to improve the performance
+ */
+ int i, j;
+
+ /* size check to prevent out of bounds access */
+ i = data_size - 1;
+ j = (2 * i) - (i & 1);
+ if (data_size & 1)
+ j++;
+ j += 2;
+ if (j >= dma_buffer_size)
+ return -EINVAL;
+
+ /* copy data */
+ for (i = 0; i < data_size; i++) {
+ j = (2 * i) - (i & 1);
+ dma_buffer[j] = data[i];
+ }
+ /* add required pad */
+ if (data_size & 1)
+ dma_buffer[++j] = 0;
+ /* skip last two bytes */
+ j += 2;
+
+ /* offset and data are off-by-one */
+ return j + 1;
+}
+
+static int sdw_cdns_prepare_write_pd0_buffer(u8 *header, unsigned int header_size,
+ u8 *data, unsigned int data_size,
+ u8 *dma_buffer, unsigned int dma_buffer_size,
+ unsigned int *dma_data_written,
+ unsigned int frame_counter)
+{
+ int data_written;
+ u8 *last_byte;
+ u8 crc;
+
+ *dma_data_written = 0;
+
+ data_written = sdw_cdns_copy_write_data(header, header_size, dma_buffer, dma_buffer_size);
+ if (data_written < 0)
+ return data_written;
+ dma_buffer[3] = BIT(7);
+ dma_buffer[3] |= frame_counter & GENMASK(3, 0);
+
+ dma_buffer += data_written;
+ dma_buffer_size -= data_written;
+ *dma_data_written += data_written;
+
+ crc = SDW_CRC8_SEED;
+ crc = crc8(sdw_crc8_lookup_msb, header, header_size, crc);
+
+ data_written = sdw_cdns_copy_write_data(&crc, 1, dma_buffer, dma_buffer_size);
+ if (data_written < 0)
+ return data_written;
+ dma_buffer += data_written;
+ dma_buffer_size -= data_written;
+ *dma_data_written += data_written;
+
+ data_written = sdw_cdns_copy_write_data(data, data_size, dma_buffer, dma_buffer_size);
+ if (data_written < 0)
+ return data_written;
+ dma_buffer += data_written;
+ dma_buffer_size -= data_written;
+ *dma_data_written += data_written;
+
+ crc = SDW_CRC8_SEED;
+ crc = crc8(sdw_crc8_lookup_msb, data, data_size, crc);
+ data_written = sdw_cdns_copy_write_data(&crc, 1, dma_buffer, dma_buffer_size);
+ if (data_written < 0)
+ return data_written;
+ dma_buffer += data_written;
+ dma_buffer_size -= data_written;
+ *dma_data_written += data_written;
+
+ /* tag last byte */
+ last_byte = dma_buffer - 1;
+ last_byte[0] = BIT(6);
+
+ return 0;
+}
+
+static int sdw_cdns_prepare_read_pd0_buffer(u8 *header, unsigned int header_size,
+ u8 *dma_buffer, unsigned int dma_buffer_size,
+ unsigned int *dma_data_written,
+ unsigned int frame_counter)
+{
+ int data_written;
+ u8 *last_byte;
+ u8 crc;
+
+ *dma_data_written = 0;
+
+ data_written = sdw_cdns_copy_write_data(header, header_size, dma_buffer, dma_buffer_size);
+ if (data_written < 0)
+ return data_written;
+ dma_buffer[3] = BIT(7);
+ dma_buffer[3] |= frame_counter & GENMASK(3, 0);
+
+ dma_buffer += data_written;
+ dma_buffer_size -= data_written;
+ *dma_data_written += data_written;
+
+ crc = SDW_CRC8_SEED;
+ crc = crc8(sdw_crc8_lookup_msb, header, header_size, crc);
+
+ data_written = sdw_cdns_copy_write_data(&crc, 1, dma_buffer, dma_buffer_size);
+ if (data_written < 0)
+ return data_written;
+ dma_buffer += data_written;
+ dma_buffer_size -= data_written;
+ *dma_data_written += data_written;
+
+ /* tag last byte */
+ last_byte = dma_buffer - 1;
+ last_byte[0] = BIT(6);
+
+ return 0;
+}
+
+#define CDNS_BPT_ROLLING_COUNTER_START 1
+
+int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, u32 start_register, u8 *data, int data_size,
+ int data_per_frame, u8 *dma_buffer, int dma_buffer_size,
+ int *dma_buffer_total_bytes)
+{
+ int total_dma_data_written = 0;
+ u8 *p_dma_buffer = dma_buffer;
+ u8 header[SDW_CDNS_BRA_HDR];
+ int dma_data_written;
+ u8 *p_data = data;
+ u8 counter;
+ int ret;
+
+ counter = CDNS_BPT_ROLLING_COUNTER_START;
+
+ header[0] = BIT(1); /* write command: BIT(1) set */
+ header[0] |= GENMASK(7, 6); /* header is active */
+ header[0] |= (dev_num << 2);
+
+ while (data_size >= data_per_frame) {
+ header[1] = data_per_frame;
+ header[2] = start_register >> 24 & 0xFF;
+ header[3] = start_register >> 16 & 0xFF;
+ header[4] = start_register >> 8 & 0xFF;
+ header[5] = start_register >> 0 & 0xFF;
+
+ ret = sdw_cdns_prepare_write_pd0_buffer(header, SDW_CDNS_BRA_HDR,
+ p_data, data_per_frame,
+ p_dma_buffer, dma_buffer_size,
+ &dma_data_written, counter);
+ if (ret < 0)
+ return ret;
+
+ counter++;
+
+ p_data += data_per_frame;
+ data_size -= data_per_frame;
+
+ p_dma_buffer += dma_data_written;
+ dma_buffer_size -= dma_data_written;
+ total_dma_data_written += dma_data_written;
+
+ start_register += data_per_frame;
+ }
+
+ if (data_size) {
+ header[1] = data_size;
+ header[2] = start_register >> 24 & 0xFF;
+ header[3] = start_register >> 16 & 0xFF;
+ header[4] = start_register >> 8 & 0xFF;
+ header[5] = start_register >> 0 & 0xFF;
+
+ ret = sdw_cdns_prepare_write_pd0_buffer(header, SDW_CDNS_BRA_HDR,
+ p_data, data_size,
+ p_dma_buffer, dma_buffer_size,
+ &dma_data_written, counter);
+ if (ret < 0)
+ return ret;
+
+ total_dma_data_written += dma_data_written;
+ }
+
+ *dma_buffer_total_bytes = total_dma_data_written;
+
+ return 0;
+}
+EXPORT_SYMBOL(sdw_cdns_prepare_write_dma_buffer);
+
+int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, u32 start_register, int data_size,
+ int data_per_frame, u8 *dma_buffer, int dma_buffer_size,
+ int *dma_buffer_total_bytes)
+{
+ int total_dma_data_written = 0;
+ u8 *p_dma_buffer = dma_buffer;
+ u8 header[SDW_CDNS_BRA_HDR];
+ int dma_data_written;
+ u8 counter;
+ int ret;
+
+ counter = CDNS_BPT_ROLLING_COUNTER_START;
+
+ header[0] = 0; /* read command: BIT(1) cleared */
+ header[0] |= GENMASK(7, 6); /* header is active */
+ header[0] |= (dev_num << 2);
+
+ while (data_size >= data_per_frame) {
+ header[1] = data_per_frame;
+ header[2] = start_register >> 24 & 0xFF;
+ header[3] = start_register >> 16 & 0xFF;
+ header[4] = start_register >> 8 & 0xFF;
+ header[5] = start_register >> 0 & 0xFF;
+
+ ret = sdw_cdns_prepare_read_pd0_buffer(header, SDW_CDNS_BRA_HDR, p_dma_buffer,
+ dma_buffer_size, &dma_data_written,
+ counter);
+ if (ret < 0)
+ return ret;
+
+ counter++;
+
+ data_size -= data_per_frame;
+
+ p_dma_buffer += dma_data_written;
+ dma_buffer_size -= dma_data_written;
+ total_dma_data_written += dma_data_written;
+
+ start_register += data_per_frame;
+ }
+
+ if (data_size) {
+ header[1] = data_size;
+ header[2] = start_register >> 24 & 0xFF;
+ header[3] = start_register >> 16 & 0xFF;
+ header[4] = start_register >> 8 & 0xFF;
+ header[5] = start_register >> 0 & 0xFF;
+
+ ret = sdw_cdns_prepare_read_pd0_buffer(header, SDW_CDNS_BRA_HDR, p_dma_buffer,
+ dma_buffer_size, &dma_data_written,
+ counter);
+ if (ret < 0)
+ return ret;
+
+ total_dma_data_written += dma_data_written;
+ }
+
+ *dma_buffer_total_bytes = total_dma_data_written;
+
+ return 0;
+}
+EXPORT_SYMBOL(sdw_cdns_prepare_read_dma_buffer);
+
+static int check_counter(u32 val, u8 counter)
+{
+ u8 frame;
+
+ frame = (val >> 24) & GENMASK(3, 0);
+ if (counter != frame)
+ return -EIO;
+ return 0;
+}
+
+static int check_response(u32 val)
+{
+ u8 response;
+
+ response = (val >> 3) & GENMASK(1, 0);
+ if (response == 0) /* Ignored */
+ return -ENODATA;
+ if (response != 1) /* ACK */
+ return -EIO;
+
+ return 0;
+}
+
+static int check_frame_start(u32 header, u8 counter)
+{
+ int ret;
+
+ /* check frame_start marker */
+ if (!(header & BIT(31)))
+ return -EIO;
+
+ ret = check_counter(header, counter);
+ if (ret < 0)
+ return ret;
+
+ return check_response(header);
+}
+
+static int check_frame_end(u32 footer)
+{
+ /* check frame_end marker */
+ if (!(footer & BIT(30)))
+ return -EIO;
+
+ return check_response(footer);
+}
+
+int sdw_cdns_check_write_response(struct device *dev, u8 *dma_buffer,
+ int dma_buffer_size, int num_frames)
+{
+ u32 *p_data;
+ int counter;
+ u32 header;
+ u32 footer;
+ int ret;
+ int i;
+
+ /* paranoia check on buffer size */
+ if (dma_buffer_size != num_frames * 8)
+ return -EINVAL;
+
+ counter = CDNS_BPT_ROLLING_COUNTER_START;
+ p_data = (u32 *)dma_buffer;
+
+ for (i = 0; i < num_frames; i++) {
+ header = *p_data++;
+ footer = *p_data++;
+
+ ret = check_frame_start(header, counter);
+ if (ret < 0) {
+ dev_err(dev, "%s: bad frame %d/%d start header %x\n",
+ __func__, i, num_frames, header);
+ return ret;
+ }
+
+ ret = check_frame_end(footer);
+ if (ret < 0) {
+ dev_err(dev, "%s: bad frame %d/%d end footer %x\n",
+ __func__, i, num_frames, footer);
+ return ret;
+ }
+
+ counter++;
+ counter &= GENMASK(3, 0);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(sdw_cdns_check_write_response);
+
+static u8 extract_read_data(u32 *data, int num_bytes, u8 *buffer)
+{
+ u32 val;
+ int i;
+ u8 crc;
+ u8 b0;
+ u8 b1;
+
+ crc = SDW_CRC8_SEED;
+
+ /* process two bytes at a time */
+ for (i = 0; i < num_bytes / 2; i++) {
+ val = *data++;
+
+ b0 = val & 0xff;
+ b1 = (val >> 8) & 0xff;
+
+ *buffer++ = b0;
+ crc = crc8(sdw_crc8_lookup_msb, &b0, 1, crc);
+
+ *buffer++ = b1;
+ crc = crc8(sdw_crc8_lookup_msb, &b1, 1, crc);
+ }
+ /* handle remaining byte if it exists */
+ if (num_bytes & 1) {
+ val = *data;
+
+ b0 = val & 0xff;
+
+ *buffer++ = b0;
+ crc = crc8(sdw_crc8_lookup_msb, &b0, 1, crc);
+ }
+ return crc;
+}
+
+int sdw_cdns_check_read_response(struct device *dev, u8 *dma_buffer, int dma_buffer_size,
+ u8 *buffer, int buffer_size, int num_frames, int data_per_frame)
+{
+ int total_num_bytes = 0;
+ u32 *p_data;
+ u8 *p_buf;
+ int counter;
+ u32 header;
+ u32 footer;
+ u8 expected_crc;
+ u8 crc;
+ int len;
+ int ret;
+ int i;
+
+ counter = CDNS_BPT_ROLLING_COUNTER_START;
+ p_data = (u32 *)dma_buffer;
+ p_buf = buffer;
+
+ for (i = 0; i < num_frames; i++) {
+ header = *p_data++;
+
+ ret = check_frame_start(header, counter);
+ if (ret < 0) {
+ dev_err(dev, "%s: bad frame %d/%d start header %x\n",
+ __func__, i, num_frames, header);
+ return ret;
+ }
+
+ len = data_per_frame;
+ if (total_num_bytes + data_per_frame > buffer_size)
+ len = buffer_size - total_num_bytes;
+
+ crc = extract_read_data(p_data, len, p_buf);
+
+ p_data += (len + 1) / 2;
+ expected_crc = *p_data++ & 0xff;
+
+ if (crc != expected_crc) {
+ dev_err(dev, "%s: bad frame %d/%d crc %#x expected %#x\n",
+ __func__, i, num_frames, crc, expected_crc);
+ return -EIO;
+ }
+
+ p_buf += len;
+ total_num_bytes += len;
+
+ footer = *p_data++;
+ ret = check_frame_end(footer);
+ if (ret < 0) {
+ dev_err(dev, "%s: bad frame %d/%d end footer %x\n",
+ __func__, i, num_frames, footer);
+ return ret;
+ }
+
+ counter++;
+ counter &= GENMASK(3, 0);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(sdw_cdns_check_read_response);
+
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("Cadence Soundwire Library");
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h
index c34fb050fe4f..9373426c7f63 100644
--- a/drivers/soundwire/cadence_master.h
+++ b/drivers/soundwire/cadence_master.h
@@ -208,4 +208,24 @@ void sdw_cdns_check_self_clearing_bits(struct sdw_cdns *cdns, const char *string
void sdw_cdns_config_update(struct sdw_cdns *cdns);
int sdw_cdns_config_update_set_wait(struct sdw_cdns *cdns);
+/* SoundWire BPT/BRA helpers to format data */
+int sdw_cdns_bpt_find_buffer_sizes(int command, /* 0: write, 1: read */
+ int row, int col, unsigned int data_bytes,
+ unsigned int requested_bytes_per_frame,
+ unsigned int *data_per_frame, unsigned int *pdi0_buffer_size,
+ unsigned int *pdi1_buffer_size, unsigned int *num_frames);
+
+int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, u32 start_register, u8 *data, int data_size,
+ int data_per_frame, u8 *dma_buffer, int dma_buffer_size,
+ int *dma_buffer_total_bytes);
+
+int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, u32 start_register, int data_size,
+ int data_per_frame, u8 *dma_buffer, int dma_buffer_size,
+ int *dma_buffer_total_bytes);
+
+int sdw_cdns_check_write_response(struct device *dev, u8 *dma_buffer,
+ int dma_buffer_size, int num_frames);
+
+int sdw_cdns_check_read_response(struct device *dev, u8 *dma_buffer, int dma_buffer_size,
+ u8 *buffer, int buffer_size, int num_frames, int data_per_frame);
#endif /* __SDW_CADENCE_H */
diff --git a/drivers/soundwire/debugfs.c b/drivers/soundwire/debugfs.c
index c30f571934ee..3099ea074f10 100644
--- a/drivers/soundwire/debugfs.c
+++ b/drivers/soundwire/debugfs.c
@@ -10,6 +10,7 @@
#include <linux/slab.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_registers.h>
+#include <linux/string_choices.h>
#include "bus.h"
static struct dentry *sdw_debugfs_root;
@@ -135,9 +136,10 @@ static int sdw_slave_reg_show(struct seq_file *s_file, void *data)
}
DEFINE_SHOW_ATTRIBUTE(sdw_slave_reg);
-#define MAX_CMD_BYTES 256
+#define MAX_CMD_BYTES (1024 * 1024)
static int cmd;
+static int cmd_type;
static u32 start_addr;
static size_t num_bytes;
static u8 read_buffer[MAX_CMD_BYTES];
@@ -153,7 +155,7 @@ static int set_command(void *data, u64 value)
/* Userspace changed the hardware state behind the kernel's back */
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
- dev_dbg(&slave->dev, "command: %s\n", value ? "read" : "write");
+ dev_dbg(&slave->dev, "command: %s\n", str_read_write(value));
cmd = value;
return 0;
@@ -161,6 +163,25 @@ static int set_command(void *data, u64 value)
DEFINE_DEBUGFS_ATTRIBUTE(set_command_fops, NULL,
set_command, "%llu\n");
+static int set_command_type(void *data, u64 value)
+{
+ struct sdw_slave *slave = data;
+
+ if (value > 1)
+ return -EINVAL;
+
+ /* Userspace changed the hardware state behind the kernel's back */
+ add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+
+ dev_dbg(&slave->dev, "command type: %s\n", value ? "BRA" : "Column0");
+
+ cmd_type = (int)value;
+
+ return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(set_command_type_fops, NULL,
+ set_command_type, "%llu\n");
+
static int set_start_address(void *data, u64 value)
{
struct sdw_slave *slave = data;
@@ -196,9 +217,28 @@ static int set_num_bytes(void *data, u64 value)
DEFINE_DEBUGFS_ATTRIBUTE(set_num_bytes_fops, NULL,
set_num_bytes, "%llu\n");
+static int do_bpt_sequence(struct sdw_slave *slave, bool write, u8 *buffer)
+{
+ struct sdw_bpt_msg msg = {0};
+
+ msg.addr = start_addr;
+ msg.len = num_bytes;
+ msg.dev_num = slave->dev_num;
+ if (write)
+ msg.flags = SDW_MSG_FLAG_WRITE;
+ else
+ msg.flags = SDW_MSG_FLAG_READ;
+ msg.buf = buffer;
+
+ return sdw_bpt_send_sync(slave->bus, slave, &msg);
+}
+
static int cmd_go(void *data, u64 value)
{
+ const struct firmware *fw = NULL;
struct sdw_slave *slave = data;
+ ktime_t start_t;
+ ktime_t finish_t;
int ret;
if (value != 1)
@@ -215,40 +255,52 @@ static int cmd_go(void *data, u64 value)
return ret;
}
- /* Userspace changed the hardware state behind the kernel's back */
- add_taint(TAINT_USER, LOCKDEP_STILL_OK);
-
- dev_dbg(&slave->dev, "starting command\n");
-
if (cmd == 0) {
- const struct firmware *fw;
-
ret = request_firmware(&fw, firmware_file, &slave->dev);
if (ret < 0) {
dev_err(&slave->dev, "firmware %s not found\n", firmware_file);
goto out;
}
-
- if (fw->size != num_bytes) {
+ if (fw->size < num_bytes) {
dev_err(&slave->dev,
- "firmware %s: unexpected size %zd, desired %zd\n",
+ "firmware %s: firmware size %zd, desired %zd\n",
firmware_file, fw->size, num_bytes);
- release_firmware(fw);
goto out;
}
+ }
- ret = sdw_nwrite_no_pm(slave, start_addr, num_bytes, fw->data);
- release_firmware(fw);
+ /* Userspace changed the hardware state behind the kernel's back */
+ add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+
+ dev_dbg(&slave->dev, "starting command\n");
+ start_t = ktime_get();
+
+ if (cmd == 0) {
+ if (cmd_type)
+ ret = do_bpt_sequence(slave, true, (u8 *)fw->data);
+ else
+ ret = sdw_nwrite_no_pm(slave, start_addr, num_bytes, fw->data);
} else {
- ret = sdw_nread_no_pm(slave, start_addr, num_bytes, read_buffer);
+ memset(read_buffer, 0, sizeof(read_buffer));
+
+ if (cmd_type)
+ ret = do_bpt_sequence(slave, false, read_buffer);
+ else
+ ret = sdw_nread_no_pm(slave, start_addr, num_bytes, read_buffer);
}
- dev_dbg(&slave->dev, "command completed %d\n", ret);
+ finish_t = ktime_get();
out:
+ if (fw)
+ release_firmware(fw);
+
pm_runtime_mark_last_busy(&slave->dev);
pm_runtime_put(&slave->dev);
+ dev_dbg(&slave->dev, "command completed, num_byte %zu status %d, time %lld ms\n",
+ num_bytes, ret, div_u64(finish_t - start_t, NSEC_PER_MSEC));
+
return ret;
}
DEFINE_DEBUGFS_ATTRIBUTE(cmd_go_fops, NULL,
@@ -290,6 +342,7 @@ void sdw_slave_debugfs_init(struct sdw_slave *slave)
/* interface to send arbitrary commands */
debugfs_create_file("command", 0200, d, slave, &set_command_fops);
+ debugfs_create_file("command_type", 0200, d, slave, &set_command_type_fops);
debugfs_create_file("start_address", 0200, d, slave, &set_start_address_fops);
debugfs_create_file("num_bytes", 0200, d, slave, &set_num_bytes_fops);
debugfs_create_file("go", 0200, d, slave, &cmd_go_fops);
diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c
index 59965f43c2fb..1cfaccf43eac 100644
--- a/drivers/soundwire/generic_bandwidth_allocation.c
+++ b/drivers/soundwire/generic_bandwidth_allocation.c
@@ -86,6 +86,49 @@ void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt,
}
EXPORT_SYMBOL(sdw_compute_slave_ports);
+static void sdw_compute_dp0_slave_ports(struct sdw_master_runtime *m_rt)
+{
+ struct sdw_bus *bus = m_rt->bus;
+ struct sdw_slave_runtime *s_rt;
+ struct sdw_port_runtime *p_rt;
+
+ list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
+ list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
+ sdw_fill_xport_params(&p_rt->transport_params, p_rt->num, false,
+ SDW_BLK_GRP_CNT_1, bus->params.col, 0, 0, 1,
+ bus->params.col - 1, SDW_BLK_PKG_PER_PORT, 0x0);
+
+ sdw_fill_port_params(&p_rt->port_params, p_rt->num, bus->params.col - 1,
+ SDW_PORT_FLOW_MODE_ISOCH, SDW_PORT_DATA_MODE_NORMAL);
+ }
+ }
+}
+
+static void sdw_compute_dp0_master_ports(struct sdw_master_runtime *m_rt)
+{
+ struct sdw_port_runtime *p_rt;
+ struct sdw_bus *bus = m_rt->bus;
+
+ list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
+ sdw_fill_xport_params(&p_rt->transport_params, p_rt->num, false,
+ SDW_BLK_GRP_CNT_1, bus->params.col, 0, 0, 1,
+ bus->params.col - 1, SDW_BLK_PKG_PER_PORT, 0x0);
+
+ sdw_fill_port_params(&p_rt->port_params, p_rt->num, bus->params.col - 1,
+ SDW_PORT_FLOW_MODE_ISOCH, SDW_PORT_DATA_MODE_NORMAL);
+ }
+}
+
+static void sdw_compute_dp0_port_params(struct sdw_bus *bus)
+{
+ struct sdw_master_runtime *m_rt;
+
+ list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
+ sdw_compute_dp0_master_ports(m_rt);
+ sdw_compute_dp0_slave_ports(m_rt);
+ }
+}
+
static void sdw_compute_master_ports(struct sdw_master_runtime *m_rt,
struct sdw_group_params *params,
int *port_bo, int hstop)
@@ -194,10 +237,11 @@ static int sdw_compute_group_params(struct sdw_bus *bus,
continue;
} else {
/*
- * Include runtimes with running (ENABLED state) and paused (DISABLED state)
- * streams
+ * Include runtimes with running (ENABLED/PREPARED state) and
+ * paused (DISABLED state) streams
*/
if (m_rt->stream->state != SDW_STREAM_ENABLED &&
+ m_rt->stream->state != SDW_STREAM_PREPARED &&
m_rt->stream->state != SDW_STREAM_DISABLED)
continue;
}
@@ -618,6 +662,11 @@ int sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream)
if (ret < 0)
return ret;
+ if (stream->type == SDW_STREAM_BPT) {
+ sdw_compute_dp0_port_params(bus);
+ return 0;
+ }
+
/* Compute transport and port params */
ret = sdw_compute_port_params(bus, stream);
if (ret < 0) {
diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h
index dddd29381441..d44e70d3c4e3 100644
--- a/drivers/soundwire/intel.h
+++ b/drivers/soundwire/intel.h
@@ -48,11 +48,34 @@ struct sdw_intel_link_res {
struct hdac_bus *hbus;
};
+/**
+ * struct sdw_intel_bpt - SoundWire Intel BPT context
+ * @bpt_tx_stream: BPT TX stream
+ * @dmab_tx_bdl: BPT TX buffer descriptor list
+ * @bpt_rx_stream: BPT RX stream
+ * @dmab_rx_bdl: BPT RX buffer descriptor list
+ * @pdi0_buffer_size: PDI0 buffer size
+ * @pdi1_buffer_size: PDI1 buffer size
+ * @num_frames: number of frames
+ * @data_per_frame: data per frame
+ */
+struct sdw_intel_bpt {
+ struct hdac_ext_stream *bpt_tx_stream;
+ struct snd_dma_buffer dmab_tx_bdl;
+ struct hdac_ext_stream *bpt_rx_stream;
+ struct snd_dma_buffer dmab_rx_bdl;
+ unsigned int pdi0_buffer_size;
+ unsigned int pdi1_buffer_size;
+ unsigned int num_frames;
+ unsigned int data_per_frame;
+};
+
struct sdw_intel {
struct sdw_cdns cdns;
int instance;
struct sdw_intel_link_res *link_res;
bool startup_done;
+ struct sdw_intel_bpt bpt_ctx;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
#endif
diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c
index e305c6258ca9..5b31e1f69591 100644
--- a/drivers/soundwire/intel_ace2x.c
+++ b/drivers/soundwire/intel_ace2x.c
@@ -13,12 +13,320 @@
#include <linux/soundwire/sdw_intel.h>
#include <sound/hdaudio.h>
#include <sound/hda-mlink.h>
+#include <sound/hda-sdw-bpt.h>
#include <sound/hda_register.h>
#include <sound/pcm_params.h>
#include "cadence_master.h"
#include "bus.h"
#include "intel.h"
+static int sdw_slave_bpt_stream_add(struct sdw_slave *slave, struct sdw_stream_runtime *stream)
+{
+ struct sdw_stream_config sconfig = {0};
+ struct sdw_port_config pconfig = {0};
+ int ret;
+
+ /* arbitrary configuration */
+ sconfig.frame_rate = 16000;
+ sconfig.ch_count = 1;
+ sconfig.bps = 32; /* this is required for BPT/BRA */
+ sconfig.direction = SDW_DATA_DIR_RX;
+ sconfig.type = SDW_STREAM_BPT;
+
+ pconfig.num = 0;
+ pconfig.ch_mask = BIT(0);
+
+ ret = sdw_stream_add_slave(slave, &sconfig, &pconfig, 1, stream);
+ if (ret)
+ dev_err(&slave->dev, "%s: failed: %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave *slave,
+ struct sdw_bpt_msg *msg)
+{
+ struct sdw_cdns *cdns = &sdw->cdns;
+ struct sdw_bus *bus = &cdns->bus;
+ struct sdw_master_prop *prop = &bus->prop;
+ struct sdw_stream_runtime *stream;
+ struct sdw_stream_config sconfig;
+ struct sdw_port_config *pconfig;
+ unsigned int pdi0_buffer_size;
+ unsigned int tx_dma_bandwidth;
+ unsigned int pdi1_buffer_size;
+ unsigned int rx_dma_bandwidth;
+ unsigned int data_per_frame;
+ unsigned int tx_total_bytes;
+ struct sdw_cdns_pdi *pdi0;
+ struct sdw_cdns_pdi *pdi1;
+ unsigned int num_frames;
+ int command;
+ int ret1;
+ int ret;
+ int dir;
+ int i;
+
+ stream = sdw_alloc_stream("BPT", SDW_STREAM_BPT);
+ if (!stream)
+ return -ENOMEM;
+
+ cdns->bus.bpt_stream = stream;
+
+ ret = sdw_slave_bpt_stream_add(slave, stream);
+ if (ret < 0)
+ goto release_stream;
+
+ /* handle PDI0 first */
+ dir = SDW_DATA_DIR_TX;
+
+ pdi0 = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, 1, dir, 0);
+ if (!pdi0) {
+ dev_err(cdns->dev, "%s: sdw_cdns_alloc_pdi0 failed\n", __func__);
+ ret = -EINVAL;
+ goto remove_slave;
+ }
+
+ sdw_cdns_config_stream(cdns, 1, dir, pdi0);
+
+ /* handle PDI1 */
+ dir = SDW_DATA_DIR_RX;
+
+ pdi1 = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, 1, dir, 1);
+ if (!pdi1) {
+ dev_err(cdns->dev, "%s: sdw_cdns_alloc_pdi1 failed\n", __func__);
+ ret = -EINVAL;
+ goto remove_slave;
+ }
+
+ sdw_cdns_config_stream(cdns, 1, dir, pdi1);
+
+ /*
+ * the port config direction, number of channels and frame
+ * rate is totally arbitrary
+ */
+ sconfig.direction = dir;
+ sconfig.ch_count = 1;
+ sconfig.frame_rate = 16000;
+ sconfig.type = SDW_STREAM_BPT;
+ sconfig.bps = 32; /* this is required for BPT/BRA */
+
+ /* Port configuration */
+ pconfig = kcalloc(2, sizeof(*pconfig), GFP_KERNEL);
+ if (!pconfig) {
+ ret = -ENOMEM;
+ goto remove_slave;
+ }
+
+ for (i = 0; i < 2 /* num_pdi */; i++) {
+ pconfig[i].num = i;
+ pconfig[i].ch_mask = 1;
+ }
+
+ ret = sdw_stream_add_master(&cdns->bus, &sconfig, pconfig, 2, stream);
+ kfree(pconfig);
+
+ if (ret < 0) {
+ dev_err(cdns->dev, "add master to stream failed:%d\n", ret);
+ goto remove_slave;
+ }
+
+ ret = sdw_prepare_stream(cdns->bus.bpt_stream);
+ if (ret < 0)
+ goto remove_master;
+
+ command = (msg->flags & SDW_MSG_FLAG_WRITE) ? 0 : 1;
+
+ ret = sdw_cdns_bpt_find_buffer_sizes(command, cdns->bus.params.row, cdns->bus.params.col,
+ msg->len, SDW_BPT_MSG_MAX_BYTES, &data_per_frame,
+ &pdi0_buffer_size, &pdi1_buffer_size, &num_frames);
+ if (ret < 0)
+ goto deprepare_stream;
+
+ sdw->bpt_ctx.pdi0_buffer_size = pdi0_buffer_size;
+ sdw->bpt_ctx.pdi1_buffer_size = pdi1_buffer_size;
+ sdw->bpt_ctx.num_frames = num_frames;
+ sdw->bpt_ctx.data_per_frame = data_per_frame;
+ tx_dma_bandwidth = div_u64((u64)pdi0_buffer_size * 8 * (u64)prop->default_frame_rate,
+ num_frames);
+ rx_dma_bandwidth = div_u64((u64)pdi1_buffer_size * 8 * (u64)prop->default_frame_rate,
+ num_frames);
+
+ dev_dbg(cdns->dev, "Message len %d transferred in %d frames (%d per frame)\n",
+ msg->len, num_frames, data_per_frame);
+ dev_dbg(cdns->dev, "sizes pdi0 %d pdi1 %d tx_bandwidth %d rx_bandwidth %d\n",
+ pdi0_buffer_size, pdi1_buffer_size, tx_dma_bandwidth, rx_dma_bandwidth);
+
+ ret = hda_sdw_bpt_open(cdns->dev->parent, /* PCI device */
+ sdw->instance, &sdw->bpt_ctx.bpt_tx_stream,
+ &sdw->bpt_ctx.dmab_tx_bdl, pdi0_buffer_size, tx_dma_bandwidth,
+ &sdw->bpt_ctx.bpt_rx_stream, &sdw->bpt_ctx.dmab_rx_bdl,
+ pdi1_buffer_size, rx_dma_bandwidth);
+ if (ret < 0) {
+ dev_err(cdns->dev, "%s: hda_sdw_bpt_open failed %d\n", __func__, ret);
+ goto deprepare_stream;
+ }
+
+ if (!command) {
+ ret = sdw_cdns_prepare_write_dma_buffer(msg->dev_num, msg->addr, msg->buf,
+ msg->len, data_per_frame,
+ sdw->bpt_ctx.dmab_tx_bdl.area,
+ pdi0_buffer_size, &tx_total_bytes);
+ } else {
+ ret = sdw_cdns_prepare_read_dma_buffer(msg->dev_num, msg->addr, msg->len,
+ data_per_frame,
+ sdw->bpt_ctx.dmab_tx_bdl.area,
+ pdi0_buffer_size, &tx_total_bytes);
+ }
+
+ if (!ret)
+ return 0;
+
+ dev_err(cdns->dev, "%s: sdw_prepare_%s_dma_buffer failed %d\n",
+ __func__, command ? "read" : "write", ret);
+
+ ret1 = hda_sdw_bpt_close(cdns->dev->parent, /* PCI device */
+ sdw->bpt_ctx.bpt_tx_stream, &sdw->bpt_ctx.dmab_tx_bdl,
+ sdw->bpt_ctx.bpt_rx_stream, &sdw->bpt_ctx.dmab_rx_bdl);
+ if (ret1 < 0)
+ dev_err(cdns->dev, "%s: hda_sdw_bpt_close failed: ret %d\n",
+ __func__, ret1);
+
+deprepare_stream:
+ sdw_deprepare_stream(cdns->bus.bpt_stream);
+
+remove_master:
+ ret1 = sdw_stream_remove_master(&cdns->bus, cdns->bus.bpt_stream);
+ if (ret1 < 0)
+ dev_err(cdns->dev, "%s: remove master failed: %d\n",
+ __func__, ret1);
+
+remove_slave:
+ ret1 = sdw_stream_remove_slave(slave, cdns->bus.bpt_stream);
+ if (ret1 < 0)
+ dev_err(cdns->dev, "%s: remove slave failed: %d\n",
+ __func__, ret1);
+
+release_stream:
+ sdw_release_stream(cdns->bus.bpt_stream);
+ cdns->bus.bpt_stream = NULL;
+
+ return ret;
+}
+
+static void intel_ace2x_bpt_close_stream(struct sdw_intel *sdw, struct sdw_slave *slave,
+ struct sdw_bpt_msg *msg)
+{
+ struct sdw_cdns *cdns = &sdw->cdns;
+ int ret;
+
+ ret = hda_sdw_bpt_close(cdns->dev->parent /* PCI device */, sdw->bpt_ctx.bpt_tx_stream,
+ &sdw->bpt_ctx.dmab_tx_bdl, sdw->bpt_ctx.bpt_rx_stream,
+ &sdw->bpt_ctx.dmab_rx_bdl);
+ if (ret < 0)
+ dev_err(cdns->dev, "%s: hda_sdw_bpt_close failed: ret %d\n",
+ __func__, ret);
+
+ ret = sdw_deprepare_stream(cdns->bus.bpt_stream);
+ if (ret < 0)
+ dev_err(cdns->dev, "%s: sdw_deprepare_stream failed: ret %d\n",
+ __func__, ret);
+
+ ret = sdw_stream_remove_master(&cdns->bus, cdns->bus.bpt_stream);
+ if (ret < 0)
+ dev_err(cdns->dev, "%s: remove master failed: %d\n",
+ __func__, ret);
+
+ ret = sdw_stream_remove_slave(slave, cdns->bus.bpt_stream);
+ if (ret < 0)
+ dev_err(cdns->dev, "%s: remove slave failed: %d\n",
+ __func__, ret);
+
+ cdns->bus.bpt_stream = NULL;
+}
+
+#define INTEL_BPT_MSG_BYTE_ALIGNMENT 32
+
+static int intel_ace2x_bpt_send_async(struct sdw_intel *sdw, struct sdw_slave *slave,
+ struct sdw_bpt_msg *msg)
+{
+ struct sdw_cdns *cdns = &sdw->cdns;
+ int ret;
+
+ if (msg->len % INTEL_BPT_MSG_BYTE_ALIGNMENT) {
+ dev_err(cdns->dev, "BPT message length %d is not a multiple of %d bytes\n",
+ msg->len, INTEL_BPT_MSG_BYTE_ALIGNMENT);
+ return -EINVAL;
+ }
+
+ dev_dbg(cdns->dev, "BPT Transfer start\n");
+
+ ret = intel_ace2x_bpt_open_stream(sdw, slave, msg);
+ if (ret < 0)
+ return ret;
+
+ ret = hda_sdw_bpt_send_async(cdns->dev->parent, /* PCI device */
+ sdw->bpt_ctx.bpt_tx_stream, sdw->bpt_ctx.bpt_rx_stream);
+ if (ret < 0) {
+ dev_err(cdns->dev, "%s: hda_sdw_bpt_send_async failed: %d\n",
+ __func__, ret);
+
+ intel_ace2x_bpt_close_stream(sdw, slave, msg);
+
+ return ret;
+ }
+
+ ret = sdw_enable_stream(cdns->bus.bpt_stream);
+ if (ret < 0) {
+ dev_err(cdns->dev, "%s: sdw_stream_enable failed: %d\n",
+ __func__, ret);
+ intel_ace2x_bpt_close_stream(sdw, slave, msg);
+ }
+
+ return ret;
+}
+
+static int intel_ace2x_bpt_wait(struct sdw_intel *sdw, struct sdw_slave *slave,
+ struct sdw_bpt_msg *msg)
+{
+ struct sdw_cdns *cdns = &sdw->cdns;
+ int ret;
+
+ dev_dbg(cdns->dev, "BPT Transfer wait\n");
+
+ ret = hda_sdw_bpt_wait(cdns->dev->parent, /* PCI device */
+ sdw->bpt_ctx.bpt_tx_stream, sdw->bpt_ctx.bpt_rx_stream);
+ if (ret < 0)
+ dev_err(cdns->dev, "%s: hda_sdw_bpt_wait failed: %d\n", __func__, ret);
+
+ ret = sdw_disable_stream(cdns->bus.bpt_stream);
+ if (ret < 0) {
+ dev_err(cdns->dev, "%s: sdw_stream_enable failed: %d\n",
+ __func__, ret);
+ goto err;
+ }
+
+ if (msg->flags & SDW_MSG_FLAG_WRITE) {
+ ret = sdw_cdns_check_write_response(cdns->dev, sdw->bpt_ctx.dmab_rx_bdl.area,
+ sdw->bpt_ctx.pdi1_buffer_size,
+ sdw->bpt_ctx.num_frames);
+ if (ret < 0)
+ dev_err(cdns->dev, "%s: BPT Write failed %d\n", __func__, ret);
+ } else {
+ ret = sdw_cdns_check_read_response(cdns->dev, sdw->bpt_ctx.dmab_rx_bdl.area,
+ sdw->bpt_ctx.pdi1_buffer_size,
+ msg->buf, msg->len, sdw->bpt_ctx.num_frames,
+ sdw->bpt_ctx.data_per_frame);
+ if (ret < 0)
+ dev_err(cdns->dev, "%s: BPT Read failed %d\n", __func__, ret);
+ }
+
+err:
+ intel_ace2x_bpt_close_stream(sdw, slave, msg);
+
+ return ret;
+}
+
/*
* shim vendor-specific (vs) ops
*/
@@ -753,7 +1061,11 @@ const struct sdw_intel_hw_ops sdw_intel_lnl_hw_ops = {
.sync_check_cmdsync_unlocked = intel_check_cmdsync_unlocked,
.program_sdi = intel_program_sdi,
+
+ .bpt_send_async = intel_ace2x_bpt_send_async,
+ .bpt_wait = intel_ace2x_bpt_wait,
};
EXPORT_SYMBOL_NS(sdw_intel_lnl_hw_ops, "SOUNDWIRE_INTEL");
MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_SDW_BPT");
diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c
index 599954d92752..5ea6399e6c9b 100644
--- a/drivers/soundwire/intel_auxdevice.c
+++ b/drivers/soundwire/intel_auxdevice.c
@@ -79,6 +79,27 @@ static bool is_wake_capable(struct sdw_slave *slave)
return false;
}
+static int generic_bpt_send_async(struct sdw_bus *bus, struct sdw_slave *slave,
+ struct sdw_bpt_msg *msg)
+{
+ struct sdw_cdns *cdns = bus_to_cdns(bus);
+ struct sdw_intel *sdw = cdns_to_intel(cdns);
+
+ if (sdw->link_res->hw_ops->bpt_send_async)
+ return sdw->link_res->hw_ops->bpt_send_async(sdw, slave, msg);
+ return -EOPNOTSUPP;
+}
+
+static int generic_bpt_wait(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg)
+{
+ struct sdw_cdns *cdns = bus_to_cdns(bus);
+ struct sdw_intel *sdw = cdns_to_intel(cdns);
+
+ if (sdw->link_res->hw_ops->bpt_wait)
+ return sdw->link_res->hw_ops->bpt_wait(sdw, slave, msg);
+ return -EOPNOTSUPP;
+}
+
static int generic_pre_bank_switch(struct sdw_bus *bus)
{
struct sdw_cdns *cdns = bus_to_cdns(bus);
@@ -222,30 +243,9 @@ static int sdw_master_read_intel_prop(struct sdw_bus *bus)
static int intel_prop_read(struct sdw_bus *bus)
{
- struct sdw_master_prop *prop;
-
/* Initialize with default handler to read all DisCo properties */
sdw_master_read_prop(bus);
- /*
- * Only one bus frequency is supported so far, filter
- * frequencies reported in the DSDT
- */
- prop = &bus->prop;
- if (prop->clk_freq && prop->num_clk_freq > 1) {
- unsigned int default_bus_frequency;
-
- default_bus_frequency =
- prop->default_frame_rate *
- prop->default_row *
- prop->default_col /
- SDW_DOUBLE_RATE_FACTOR;
-
- prop->num_clk_freq = 1;
- prop->clk_freq[0] = default_bus_frequency;
- prop->max_clk_freq = default_bus_frequency;
- }
-
/* read Intel-specific properties */
sdw_master_read_intel_prop(bus);
@@ -288,6 +288,9 @@ static struct sdw_master_ops sdw_intel_ops = {
.get_device_num = intel_get_device_num_ida,
.put_device_num = intel_put_device_num_ida,
.new_peripheral_assigned = generic_new_peripheral_assigned,
+
+ .bpt_send_async = generic_bpt_send_async,
+ .bpt_wait = generic_bpt_wait,
};
/*
diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c
index 4869b073b11c..d2d99555ec5a 100644
--- a/drivers/soundwire/slave.c
+++ b/drivers/soundwire/slave.c
@@ -13,6 +13,7 @@ static void sdw_slave_release(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ of_node_put(slave->dev.of_node);
mutex_destroy(&slave->sdw_dev_lock);
kfree(slave);
}
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c
index e9df503332bb..a4bea742b5d9 100644
--- a/drivers/soundwire/stream.c
+++ b/drivers/soundwire/stream.c
@@ -14,6 +14,7 @@
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
+#include <linux/string_choices.h>
#include <sound/soc.h>
#include "bus.h"
@@ -87,11 +88,14 @@ static int _sdw_program_slave_port_params(struct sdw_bus *bus,
return ret;
}
- /* Program DPN_BlockCtrl3 register */
- ret = sdw_write_no_pm(slave, addr2, t_params->blk_pkg_mode);
- if (ret < 0) {
- dev_err(bus->dev, "DPN_BlockCtrl3 register write failed\n");
- return ret;
+ /* DP0 does not implement BlockCtrl3 */
+ if (t_params->port_num) {
+ /* Program DPN_BlockCtrl3 register */
+ ret = sdw_write_no_pm(slave, addr2, t_params->blk_pkg_mode);
+ if (ret < 0) {
+ dev_err(bus->dev, "DPN_BlockCtrl3 register write failed\n");
+ return ret;
+ }
}
/*
@@ -130,18 +134,28 @@ static int sdw_program_slave_port_params(struct sdw_bus *bus,
struct sdw_port_params *p_params = &p_rt->port_params;
struct sdw_slave_prop *slave_prop = &s_rt->slave->prop;
u32 addr1, addr2, addr3, addr4, addr5, addr6;
- struct sdw_dpn_prop *dpn_prop;
+ enum sdw_dpn_type port_type;
+ bool read_only_wordlength;
int ret;
u8 wbuf;
if (s_rt->slave->is_mockup_device)
return 0;
- dpn_prop = sdw_get_slave_dpn_prop(s_rt->slave,
- s_rt->direction,
- t_params->port_num);
- if (!dpn_prop)
- return -EINVAL;
+ if (t_params->port_num) {
+ struct sdw_dpn_prop *dpn_prop;
+
+ dpn_prop = sdw_get_slave_dpn_prop(s_rt->slave, s_rt->direction,
+ t_params->port_num);
+ if (!dpn_prop)
+ return -EINVAL;
+
+ read_only_wordlength = dpn_prop->read_only_wordlength;
+ port_type = dpn_prop->type;
+ } else {
+ read_only_wordlength = false;
+ port_type = SDW_DPN_FULL;
+ }
addr1 = SDW_DPN_PORTCTRL(t_params->port_num);
addr2 = SDW_DPN_BLOCKCTRL1(t_params->port_num);
@@ -171,7 +185,7 @@ static int sdw_program_slave_port_params(struct sdw_bus *bus,
return ret;
}
- if (!dpn_prop->read_only_wordlength) {
+ if (!read_only_wordlength) {
/* Program DPN_BlockCtrl1 register */
ret = sdw_write_no_pm(s_rt->slave, addr2, (p_params->bps - 1));
if (ret < 0) {
@@ -223,9 +237,9 @@ static int sdw_program_slave_port_params(struct sdw_bus *bus,
}
}
- if (dpn_prop->type != SDW_DPN_SIMPLE) {
+ if (port_type != SDW_DPN_SIMPLE) {
ret = _sdw_program_slave_port_params(bus, s_rt->slave,
- t_params, dpn_prop->type);
+ t_params, port_type);
if (ret < 0)
dev_err(&s_rt->slave->dev,
"Transport reg write failed for port: %d\n",
@@ -358,7 +372,7 @@ static int sdw_enable_disable_master_ports(struct sdw_master_runtime *m_rt,
} else {
dev_err(bus->dev,
"dpn_port_enable_ch not supported, %s failed\n",
- en ? "enable" : "disable");
+ str_enable_disable(en));
return -EINVAL;
}
@@ -432,6 +446,9 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus,
struct completion *port_ready;
struct sdw_dpn_prop *dpn_prop;
struct sdw_prepare_ch prep_ch;
+ u32 imp_def_interrupts;
+ bool simple_ch_prep_sm;
+ u32 ch_prep_timeout;
bool intr = false;
int ret = 0, val;
u32 addr;
@@ -439,20 +456,35 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus,
prep_ch.num = p_rt->num;
prep_ch.ch_mask = p_rt->ch_mask;
- dpn_prop = sdw_get_slave_dpn_prop(s_rt->slave,
- s_rt->direction,
- prep_ch.num);
- if (!dpn_prop) {
- dev_err(bus->dev,
- "Slave Port:%d properties not found\n", prep_ch.num);
- return -EINVAL;
+ if (p_rt->num) {
+ dpn_prop = sdw_get_slave_dpn_prop(s_rt->slave, s_rt->direction, prep_ch.num);
+ if (!dpn_prop) {
+ dev_err(bus->dev,
+ "Slave Port:%d properties not found\n", prep_ch.num);
+ return -EINVAL;
+ }
+
+ imp_def_interrupts = dpn_prop->imp_def_interrupts;
+ simple_ch_prep_sm = dpn_prop->simple_ch_prep_sm;
+ ch_prep_timeout = dpn_prop->ch_prep_timeout;
+ } else {
+ struct sdw_dp0_prop *dp0_prop = s_rt->slave->prop.dp0_prop;
+
+ if (!dp0_prop) {
+ dev_err(bus->dev,
+ "Slave DP0 properties not found\n");
+ return -EINVAL;
+ }
+ imp_def_interrupts = dp0_prop->imp_def_interrupts;
+ simple_ch_prep_sm = dp0_prop->simple_ch_prep_sm;
+ ch_prep_timeout = dp0_prop->ch_prep_timeout;
}
prep_ch.prepare = prep;
prep_ch.bank = bus->params.next_bank;
- if (dpn_prop->imp_def_interrupts || !dpn_prop->simple_ch_prep_sm ||
+ if (imp_def_interrupts || !simple_ch_prep_sm ||
bus->params.s_data_mode != SDW_PORT_DATA_MODE_NORMAL)
intr = true;
@@ -463,7 +495,7 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus,
*/
if (prep && intr) {
ret = sdw_configure_dpn_intr(s_rt->slave, p_rt->num, prep,
- dpn_prop->imp_def_interrupts);
+ imp_def_interrupts);
if (ret < 0)
return ret;
}
@@ -472,7 +504,7 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus,
sdw_do_port_prep(s_rt, prep_ch, prep ? SDW_OPS_PORT_PRE_PREP : SDW_OPS_PORT_PRE_DEPREP);
/* Prepare Slave port implementing CP_SM */
- if (!dpn_prop->simple_ch_prep_sm) {
+ if (!simple_ch_prep_sm) {
addr = SDW_DPN_PREPARECTRL(p_rt->num);
if (prep)
@@ -489,7 +521,7 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus,
/* Wait for completion on port ready */
port_ready = &s_rt->slave->port_ready[prep_ch.num];
wait_for_completion_timeout(port_ready,
- msecs_to_jiffies(dpn_prop->ch_prep_timeout));
+ msecs_to_jiffies(ch_prep_timeout));
val = sdw_read_no_pm(s_rt->slave, SDW_DPN_PREPARESTATUS(p_rt->num));
if ((val < 0) || (val & p_rt->ch_mask)) {
@@ -506,7 +538,7 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus,
/* Disable interrupt after Port de-prepare */
if (!prep && intr)
ret = sdw_configure_dpn_intr(s_rt->slave, p_rt->num, prep,
- dpn_prop->imp_def_interrupts);
+ imp_def_interrupts);
return ret;
}
@@ -1007,7 +1039,8 @@ static int sdw_slave_port_is_valid_range(struct device *dev, int num)
static int sdw_slave_port_config(struct sdw_slave *slave,
struct sdw_slave_runtime *s_rt,
- const struct sdw_port_config *port_config)
+ const struct sdw_port_config *port_config,
+ bool is_bpt_stream)
{
struct sdw_port_runtime *p_rt;
int ret;
@@ -1019,9 +1052,13 @@ static int sdw_slave_port_config(struct sdw_slave *slave,
* TODO: Check valid port range as defined by DisCo/
* slave
*/
- ret = sdw_slave_port_is_valid_range(&slave->dev, port_config[i].num);
- if (ret < 0)
- return ret;
+ if (!is_bpt_stream) {
+ ret = sdw_slave_port_is_valid_range(&slave->dev, port_config[i].num);
+ if (ret < 0)
+ return ret;
+ } else if (port_config[i].num) {
+ return -EINVAL;
+ }
ret = sdw_port_config(p_rt, port_config, i);
if (ret < 0)
@@ -1190,6 +1227,20 @@ static struct sdw_master_runtime
struct sdw_master_runtime *m_rt, *walk_m_rt;
struct list_head *insert_after;
+ if (stream->type == SDW_STREAM_BPT) {
+ if (bus->stream_refcount > 0 || bus->bpt_stream_refcount > 0) {
+ dev_err(bus->dev, "%s: %d/%d audio/BPT stream already allocated\n",
+ __func__, bus->stream_refcount, bus->bpt_stream_refcount);
+ return ERR_PTR(-EBUSY);
+ }
+ } else {
+ if (bus->bpt_stream_refcount > 0) {
+ dev_err(bus->dev, "%s: BPT stream already allocated\n",
+ __func__);
+ return ERR_PTR(-EAGAIN);
+ }
+ }
+
m_rt = kzalloc(sizeof(*m_rt), GFP_KERNEL);
if (!m_rt)
return NULL;
@@ -1218,6 +1269,8 @@ static struct sdw_master_runtime
m_rt->stream = stream;
bus->stream_refcount++;
+ if (stream->type == SDW_STREAM_BPT)
+ bus->bpt_stream_refcount++;
return m_rt;
}
@@ -1266,6 +1319,8 @@ static void sdw_master_rt_free(struct sdw_master_runtime *m_rt,
list_del(&m_rt->bus_node);
kfree(m_rt);
+ if (stream->type == SDW_STREAM_BPT)
+ bus->bpt_stream_refcount--;
bus->stream_refcount--;
}
@@ -1330,6 +1385,11 @@ struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave,
u8 num_ports;
int i;
+ if (!port_num) {
+ dev_err(&slave->dev, "%s: port_num is zero\n", __func__);
+ return NULL;
+ }
+
if (direction == SDW_DATA_DIR_TX) {
num_ports = hweight32(slave->prop.source_ports);
dpn_prop = slave->prop.src_dpn_prop;
@@ -1805,12 +1865,13 @@ static int set_stream(struct snd_pcm_substream *substream,
* sdw_alloc_stream() - Allocate and return stream runtime
*
* @stream_name: SoundWire stream name
+ * @type: stream type (could be PCM ,PDM or BPT)
*
* Allocates a SoundWire stream runtime instance.
* sdw_alloc_stream should be called only once per stream. Typically
* invoked from ALSA/ASoC machine/platform driver.
*/
-struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name)
+struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name, enum sdw_stream_type type)
{
struct sdw_stream_runtime *stream;
@@ -1822,6 +1883,7 @@ struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name)
INIT_LIST_HEAD(&stream->master_list);
stream->state = SDW_STREAM_ALLOCATED;
stream->m_rt_count = 0;
+ stream->type = type;
return stream;
}
@@ -1850,7 +1912,7 @@ int sdw_startup_stream(void *sdw_substream)
if (!name)
return -ENOMEM;
- sdw_stream = sdw_alloc_stream(name);
+ sdw_stream = sdw_alloc_stream(name, SDW_STREAM_PCM);
if (!sdw_stream) {
dev_err(rtd->dev, "alloc stream failed for substream DAI %s\n", substream->name);
ret = -ENOMEM;
@@ -1957,6 +2019,12 @@ int sdw_stream_add_master(struct sdw_bus *bus,
m_rt = sdw_master_rt_find(bus, stream);
if (!m_rt) {
m_rt = sdw_master_rt_alloc(bus, stream);
+ if (IS_ERR(m_rt)) {
+ ret = PTR_ERR(m_rt);
+ dev_err(bus->dev, "%s: Master runtime alloc failed for stream:%s: %d\n",
+ __func__, stream->name, ret);
+ goto unlock;
+ }
if (!m_rt) {
dev_err(bus->dev, "%s: Master runtime alloc failed for stream:%s\n",
__func__, stream->name);
@@ -2072,6 +2140,12 @@ int sdw_stream_add_slave(struct sdw_slave *slave,
* So, allocate m_rt and add Slave to it.
*/
m_rt = sdw_master_rt_alloc(slave->bus, stream);
+ if (IS_ERR(m_rt)) {
+ ret = PTR_ERR(m_rt);
+ dev_err(&slave->dev, "%s: Master runtime alloc failed for stream:%s: %d\n",
+ __func__, stream->name, ret);
+ goto unlock;
+ }
if (!m_rt) {
dev_err(&slave->dev, "%s: Master runtime alloc failed for stream:%s\n",
__func__, stream->name);
@@ -2113,7 +2187,8 @@ int sdw_stream_add_slave(struct sdw_slave *slave,
if (ret)
goto unlock;
- ret = sdw_slave_port_config(slave, s_rt, port_config);
+ ret = sdw_slave_port_config(slave, s_rt, port_config,
+ stream->type == SDW_STREAM_BPT);
if (ret)
goto unlock;