summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-24 12:16:16 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-24 12:16:16 -0700
commitab9b9b51baa9bb964e9219f81682c4f1761ee47d (patch)
treef6b3563402abb965d5a7684f47513bcefc820a3b /drivers
parent2f43193b88188b184a967c9427602e019f1b8708 (diff)
parent5b08a3afd2995e5312c4c47ffea9d21b651e8e13 (diff)
Merge tag 'soundwire-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire
Pull soundwire updates from Vinod Koul: - Intel dmi quirks ghost list handling for Asus Zenbook Duo, Asus ROG Zephyrus Duo and Asus Expertbook. Intel Peripheral bra_block_alignment handling - Cadence library BRA_NumBytes[8] support - Qualcomm SCP address paging, bus mclk_freq support. Increase of data ports to 17 and driver improvements * tag 'soundwire-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire: soundwire: dmi-quirks: Disable ghost Realtek on Asus ROG Zephyrus Duo soundwire: stream: validate slave port properties soundwire: honor clock_reg_supported in the clock scaling check soundwire: qcom: set the bus mclk_freq property soundwire: dmi-quirks: Disable ghost Realtek on Asus Zenbook Duo soundwire: intel_ace2x: handle the max_data_per_frame property soundwire: get mipi-sdw-bra-mode-max-data-per-frame property soundwire: intel: handle Peripheral bra_block_alignment soundwire: Add bra_block_alignment property support soundwire: cadence_master: add BRA_NumBytes[8] support soundwire: bus.h: repair kernel-doc comments soundwire: intel_auxdevice: Add cs42l44 to wake_capable_list soundwire: qcom: add SCP address paging support soundwire: dmi-quirks: add a global ghost list soundwire: dmi-quirks: Disable ghost Realtek on Asus Expertbook soundwire: qcom: Allocate sruntime array dynamically soundwire: qcom: Fix port exhaustion check in stream_alloc_ports dt-bindings: soundwire: qcom: Increase max data ports to 17
Diffstat (limited to 'drivers')
-rw-r--r--drivers/soundwire/bus.c7
-rw-r--r--drivers/soundwire/bus.h10
-rw-r--r--drivers/soundwire/cadence_master.c35
-rw-r--r--drivers/soundwire/cadence_master.h1
-rw-r--r--drivers/soundwire/dmi-quirks.c42
-rw-r--r--drivers/soundwire/intel_ace2x.c20
-rw-r--r--drivers/soundwire/intel_auxdevice.c1
-rw-r--r--drivers/soundwire/mipi_disco.c6
-rw-r--r--drivers/soundwire/qcom.c24
-rw-r--r--drivers/soundwire/stream.c17
10 files changed, 140 insertions, 23 deletions
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c
index a1e8f87a9399..12087cbd87b7 100644
--- a/drivers/soundwire/bus.c
+++ b/drivers/soundwire/bus.c
@@ -816,8 +816,11 @@ bool is_clock_scaling_supported_by_slave(struct sdw_slave *slave)
/*
* Dynamic scaling is a defined by SDCA. However, some devices expose the class ID but
* can't support dynamic scaling. We might need a quirk to handle such devices.
+ * The clock base and scale registers themselves are SoundWire 1.2, so a device
+ * may implement them without setting the class field; the driver says so with
+ * clock_reg_supported.
*/
- return slave->id.class_id;
+ return slave->id.class_id || slave->prop.clock_reg_supported;
}
EXPORT_SYMBOL(is_clock_scaling_supported_by_slave);
@@ -1384,7 +1387,7 @@ static int sdw_slave_set_frequency(struct sdw_slave *slave)
* DisCo property to discover support for the scaling registers
* from platform firmware.
*/
- if (!slave->id.class_id && !slave->prop.clock_reg_supported)
+ if (!is_clock_scaling_supported_by_slave(slave))
return 0;
scale_index = sdw_slave_get_scale_index(slave, &base);
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h
index 8115c64dd48e..44e4f5193917 100644
--- a/drivers/soundwire/bus.h
+++ b/drivers/soundwire/bus.h
@@ -73,7 +73,7 @@ struct sdw_msg {
};
/**
- * struct sdw_btp_section - Message section structure
+ * struct sdw_bpt_section - Message section 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.
@@ -87,7 +87,7 @@ struct sdw_bpt_section {
};
/**
- * struct sdw_btp_msg - Message structure
+ * struct sdw_bpt_msg - Message structure
* @sec: Pointer to array of sections
* @sections: Number of sections in the array
* @dev_num: Slave device number
@@ -110,7 +110,7 @@ int sdw_find_row_index(int row);
int sdw_find_col_index(int col);
/**
- * sdw_port_runtime: Runtime port parameters for Master or Slave
+ * struct sdw_port_runtime - Runtime port parameters for Master or Slave
*
* @num: Port number. For audio streams, valid port number ranges from
* [1,14]
@@ -133,7 +133,7 @@ struct sdw_port_runtime {
};
/**
- * sdw_slave_runtime: Runtime Stream parameters for Slave
+ * struct sdw_slave_runtime - Runtime Stream parameters for Slave
*
* @slave: Slave handle
* @direction: Data direction for Slave
@@ -151,7 +151,7 @@ struct sdw_slave_runtime {
};
/**
- * sdw_master_runtime: Runtime stream parameters for Master
+ * struct sdw_master_runtime - Runtime stream parameters for Master
*
* @bus: Bus handle
* @stream: Stream runtime handle
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c
index eb66345a6a42..e690237fe981 100644
--- a/drivers/soundwire/cadence_master.c
+++ b/drivers/soundwire/cadence_master.c
@@ -2134,6 +2134,7 @@ EXPORT_SYMBOL(sdw_cdns_bpt_find_bandwidth);
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 bra_block_alignment,
unsigned int *data_per_frame, unsigned int *pdi0_buffer_size,
unsigned int *pdi1_buffer_size, unsigned int *num_frames)
{
@@ -2158,6 +2159,16 @@ int sdw_cdns_bpt_find_buffer_sizes(int command, /* 0: write, 1: read */
if (requested_bytes_per_frame < actual_bpt_bytes)
actual_bpt_bytes = requested_bytes_per_frame;
+ if (bra_block_alignment) {
+ /* align to a multiple of bra_block_alignment */
+ if (actual_bpt_bytes < bra_block_alignment) {
+ pr_err("effective bytes per frame %u is smaller than block alignment %u\n",
+ actual_bpt_bytes, bra_block_alignment);
+ return -EINVAL;
+ }
+ actual_bpt_bytes -= (actual_bpt_bytes % bra_block_alignment);
+ }
+
*data_per_frame = actual_bpt_bytes;
if (data_bytes < actual_bpt_bytes)
@@ -2358,7 +2369,9 @@ int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, i
p_data = sec[i].buf;
while (section_size >= data_per_frame) {
- header[1] = data_per_frame;
+ header[0] &= ~BIT(0);
+ header[0] |= (data_per_frame >> 8) & BIT(0);
+ header[1] = data_per_frame & 0xFF;
header[2] = start_register >> 24 & 0xFF;
header[3] = start_register >> 16 & 0xFF;
header[4] = start_register >> 8 & 0xFF;
@@ -2384,7 +2397,9 @@ int sdw_cdns_prepare_write_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, i
}
if (section_size) {
- header[1] = section_size;
+ header[0] &= ~BIT(0);
+ header[0] |= (section_size >> 8) & BIT(0);
+ header[1] = section_size & 0xFF;
header[2] = start_register >> 24 & 0xFF;
header[3] = start_register >> 16 & 0xFF;
header[4] = start_register >> 8 & 0xFF;
@@ -2435,7 +2450,9 @@ int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, in
start_register = sec[i].addr;
data_size = sec[i].len;
while (data_size >= data_per_frame) {
- header[1] = data_per_frame;
+ header[0] &= ~BIT(0);
+ header[0] |= (data_per_frame >> 8) & BIT(0);
+ header[1] = data_per_frame & 0xFF;
header[2] = start_register >> 24 & 0xFF;
header[3] = start_register >> 16 & 0xFF;
header[4] = start_register >> 8 & 0xFF;
@@ -2459,7 +2476,9 @@ int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, in
}
if (data_size) {
- header[1] = data_size;
+ header[0] &= ~BIT(0);
+ header[0] |= (data_size >> 8) & BIT(0);
+ header[1] = data_size & 0xFF;
header[2] = start_register >> 24 & 0xFF;
header[3] = start_register >> 16 & 0xFF;
header[4] = start_register >> 8 & 0xFF;
@@ -2482,7 +2501,9 @@ int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, in
/* Add fake frame */
header[0] &= ~GENMASK(7, 6); /* Set inactive flag in BPT/BRA frame heade */
while (fake_size >= data_per_frame) {
- header[1] = data_per_frame;
+ header[0] &= ~BIT(0);
+ header[0] |= (data_per_frame >> 8) & BIT(0);
+ header[1] = data_per_frame & 0xFF;
ret = sdw_cdns_prepare_read_pd0_buffer(header, SDW_CDNS_BRA_HDR, p_dma_buffer,
dma_buffer_size, &dma_data_written,
counter);
@@ -2498,7 +2519,9 @@ int sdw_cdns_prepare_read_dma_buffer(u8 dev_num, struct sdw_bpt_section *sec, in
}
if (fake_size) {
- header[1] = fake_size;
+ header[0] &= ~BIT(0);
+ header[0] |= (fake_size >> 8) & BIT(0);
+ header[1] = fake_size & 0xFF;
ret = sdw_cdns_prepare_read_pd0_buffer(header, SDW_CDNS_BRA_HDR, p_dma_buffer,
dma_buffer_size, &dma_data_written,
counter);
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h
index 668f807cff4b..f4e41a9ab165 100644
--- a/drivers/soundwire/cadence_master.h
+++ b/drivers/soundwire/cadence_master.h
@@ -218,6 +218,7 @@ int sdw_cdns_bpt_find_bandwidth(int command, /* 0: write, 1: read */
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 bra_block_alignment,
unsigned int *data_per_frame, unsigned int *pdi0_buffer_size,
unsigned int *pdi1_buffer_size, unsigned int *num_frames);
diff --git a/drivers/soundwire/dmi-quirks.c b/drivers/soundwire/dmi-quirks.c
index 32a46a2d90f7..768255dd12db 100644
--- a/drivers/soundwire/dmi-quirks.c
+++ b/drivers/soundwire/dmi-quirks.c
@@ -15,6 +15,14 @@ struct adr_remap {
u64 remapped_adr;
};
+static const struct adr_remap global_ghost_adr[] = {
+ {
+ 0x000000D010010500ull,
+ 0x0000000000000000ull
+ },
+ {}
+};
+
/*
* Some TigerLake devices based on an initial Intel BIOS do not expose
* the correct _ADR in the DSDT.
@@ -181,12 +189,33 @@ static const struct dmi_system_id adr_remap_quirk_table[] = {
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUS"),
+ DMI_MATCH(DMI_BOARD_NAME, "B9406CAA"),
+ },
+ .driver_data = (void *)ghost_realtek,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUS"),
+ DMI_MATCH(DMI_BOARD_NAME, "GX651AR"),
+ },
+ .driver_data = (void *)ghost_realtek,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUS"),
DMI_MATCH(DMI_BOARD_NAME, "UX5406AA"),
},
.driver_data = (void *)ghost_realtek,
},
{
.matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUS"),
+ DMI_MATCH(DMI_BOARD_NAME, "UX8407AA"),
+ },
+ .driver_data = (void *)ghost_realtek,
+ },
+ {
+ .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "83QK"),
},
@@ -205,6 +234,7 @@ static const struct dmi_system_id adr_remap_quirk_table[] = {
u64 sdw_dmi_override_adr(struct sdw_bus *bus, u64 addr)
{
const struct dmi_system_id *dmi_id;
+ int i;
/* check if any address remap quirk applies */
dmi_id = dmi_first_match(adr_remap_quirk_table);
@@ -216,10 +246,20 @@ u64 sdw_dmi_override_adr(struct sdw_bus *bus, u64 addr)
dev_dbg(bus->dev, "remapped _ADR 0x%llx as 0x%llx\n",
addr, map->remapped_adr);
addr = map->remapped_adr;
- break;
+ goto out;
}
}
}
+ /* remap the ghost ADRs */
+ for (i = 0; i < ARRAY_SIZE(global_ghost_adr); i++) {
+ if (global_ghost_adr[i].adr == addr) {
+ dev_dbg(bus->dev, "remapped _ADR 0x%llx as 0x%llx\n",
+ addr, global_ghost_adr[i].remapped_adr);
+ addr = global_ghost_adr[i].remapped_adr;
+ break;
+ }
+ }
+out:
return addr;
}
diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c
index 63f131d5682b..642b33ab5526 100644
--- a/drivers/soundwire/intel_ace2x.c
+++ b/drivers/soundwire/intel_ace2x.c
@@ -57,6 +57,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave *
struct sdw_port_config *pconfig;
unsigned int pdi0_buf_size_pre_frame;
unsigned int pdi1_buf_size_pre_frame;
+ unsigned int max_data_per_frame;
unsigned int pdi0_buffer_size_;
unsigned int pdi1_buffer_size_;
unsigned int pdi0_buffer_size;
@@ -168,11 +169,25 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave *
pdi0_buffer_size = 0;
pdi1_buffer_size = 0;
num_frames = 0;
+
+ if (slave->prop.bra_max_data_per_frame) {
+ max_data_per_frame = slave->prop.bra_max_data_per_frame;
+ if (max_data_per_frame > SDW_BRA_MAX_BYTES_PER_FRAME) {
+ dev_warn(&slave->dev,
+ "BRA max_data_per_frame %u exceeds limit %u, clamping\n",
+ max_data_per_frame, SDW_BRA_MAX_BYTES_PER_FRAME);
+ max_data_per_frame = SDW_BRA_MAX_BYTES_PER_FRAME;
+ }
+ } else {
+ max_data_per_frame = SDW_BRA_MAX_BYTES_PER_FRAME;
+ }
+
/* Add up pdi buffer size and frame numbers of each BPT sections */
for (i = 0; i < msg->sections; i++) {
ret = sdw_cdns_bpt_find_buffer_sizes(command, cdns->bus.params.row,
cdns->bus.params.col,
- msg->sec[i].len, SDW_BPT_MSG_MAX_BYTES,
+ msg->sec[i].len, max_data_per_frame,
+ slave->prop.bra_block_alignment,
&data_per_frame, &pdi0_buffer_size_,
&pdi1_buffer_size_, &num_frames_);
if (ret < 0)
@@ -196,7 +211,8 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave *
/* Get buffer size of a full frame */
ret = sdw_cdns_bpt_find_buffer_sizes(command, cdns->bus.params.row,
cdns->bus.params.col,
- data_per_frame, SDW_BPT_MSG_MAX_BYTES,
+ data_per_frame, max_data_per_frame,
+ slave->prop.bra_block_alignment,
&data_per_frame, &pdi0_buf_size_pre_frame,
&pdi1_buf_size_pre_frame, &fake_num_frames);
if (ret < 0)
diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c
index 0b8107bec9ab..a8407560bf4f 100644
--- a/drivers/soundwire/intel_auxdevice.c
+++ b/drivers/soundwire/intel_auxdevice.c
@@ -54,6 +54,7 @@ static struct wake_capable_part wake_capable_list[] = {
{0x01fa, 0x2A30},
{0x01fa, 0x2A3B},
{0x01fa, 0x4243},
+ {0x01fa, 0x4244},
{0x01fa, 0x4245},
{0x01fa, 0x4249},
{0x01fa, 0x4747},
diff --git a/drivers/soundwire/mipi_disco.c b/drivers/soundwire/mipi_disco.c
index fdbf51f2bb77..259ad98b5b7a 100644
--- a/drivers/soundwire/mipi_disco.c
+++ b/drivers/soundwire/mipi_disco.c
@@ -470,6 +470,12 @@ int sdw_slave_read_prop(struct sdw_slave *slave)
device_property_read_u32(dev, "mipi-sdw-sdca-interrupt-register-list",
&prop->sdca_interrupt_register_list);
+ device_property_read_u32(dev, "mipi-sdw-bra-mode-block-alignment",
+ &prop->bra_block_alignment);
+
+ device_property_read_u32(dev, "mipi-sdw-bra-mode-max-data-per-frame",
+ &prop->bra_max_data_per_frame);
+
prop->commit_register_supported = mipi_device_property_read_bool(dev,
"mipi-sdw-commit-register-supported");
diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c
index 3d8f5a81eff1..55678a30cd4a 100644
--- a/drivers/soundwire/qcom.c
+++ b/drivers/soundwire/qcom.c
@@ -134,7 +134,6 @@
#define TIMEOUT_MS 100
#define QCOM_SWRM_MAX_RD_LEN 0x1
#define DEFAULT_CLK_FREQ 9600000
-#define SWRM_MAX_DAIS 0xF
#define SWR_INVALID_PARAM 0xFF
#define SWR_HSTOP_MAX_VAL 0xF
#define SWR_HSTART_MIN_VAL 0x0
@@ -215,7 +214,7 @@ struct qcom_swrm_ctrl {
u8 wcmd_id;
/* Port numbers are 1 - 14 */
struct qcom_swrm_port_config *pconfig;
- struct sdw_stream_runtime *sruntime[SWRM_MAX_DAIS];
+ struct sdw_stream_runtime **sruntime;
enum sdw_slave_status status[SDW_MAX_DEVICES + 1];
int (*reg_read)(struct qcom_swrm_ctrl *ctrl, int reg, u32 *val);
int (*reg_write)(struct qcom_swrm_ctrl *ctrl, int reg, int val);
@@ -976,6 +975,20 @@ static enum sdw_command_response qcom_swrm_xfer_msg(struct sdw_bus *bus,
struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus);
int ret, i, len;
+ if (msg->page) {
+ ret = qcom_swrm_cmd_fifo_wr_cmd(ctrl, msg->addr_page1,
+ msg->dev_num,
+ SDW_SCP_ADDRPAGE1);
+ if (ret)
+ return ret;
+
+ ret = qcom_swrm_cmd_fifo_wr_cmd(ctrl, msg->addr_page2,
+ msg->dev_num,
+ SDW_SCP_ADDRPAGE2);
+ if (ret)
+ return ret;
+ }
+
if (msg->flags == SDW_MSG_FLAG_READ) {
for (i = 0; i < msg->len;) {
len = min(msg->len - i, QCOM_SWRM_MAX_RD_LEN);
@@ -1271,7 +1284,7 @@ static int qcom_swrm_stream_alloc_ports(struct qcom_swrm_ctrl *ctrl,
else
pn = find_first_zero_bit(port_mask, maxport);
- if (pn > maxport) {
+ if (pn >= maxport) {
dev_err(ctrl->dev, "All ports busy\n");
return -EBUSY;
}
@@ -1384,6 +1397,10 @@ static int qcom_swrm_register_dais(struct qcom_swrm_ctrl *ctrl)
struct device *dev = ctrl->dev;
int i;
+ ctrl->sruntime = devm_kcalloc(dev, num_dais, sizeof(*ctrl->sruntime), GFP_KERNEL);
+ if (!ctrl->sruntime)
+ return -ENOMEM;
+
/* PDM dais are only tested for now */
dais = devm_kcalloc(dev, num_dais, sizeof(*dais), GFP_KERNEL);
if (!dais)
@@ -1616,6 +1633,7 @@ static int qcom_swrm_probe(struct platform_device *pdev)
prop = &ctrl->bus.prop;
prop->max_clk_freq = DEFAULT_CLK_FREQ;
+ prop->mclk_freq = DEFAULT_CLK_FREQ;
prop->num_clk_gears = 0;
prop->num_clk_freq = MAX_FREQ_NUM;
prop->clk_freq = &qcom_swrm_freq_tbl[0];
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c
index 5d20e95a1e23..f106a877d7b2 100644
--- a/drivers/soundwire/stream.c
+++ b/drivers/soundwire/stream.c
@@ -1060,14 +1060,23 @@ static int sdw_slave_port_config(struct sdw_slave *slave,
i = 0;
list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
- /*
- * TODO: Check valid port range as defined by DisCo/
- * slave
- */
if (!is_bpt_stream) {
ret = sdw_slave_port_is_valid_range(&slave->dev, port_config[i].num);
if (ret < 0)
return ret;
+
+ /*
+ * A port in the generic valid range may still be unsupported by
+ * the Slave or unavailable for the requested stream direction.
+ */
+ if (!sdw_get_slave_dpn_prop(slave, s_rt->direction,
+ port_config[i].num)) {
+ dev_err(&slave->dev,
+ "port %u not supported for %s\n",
+ port_config[i].num,
+ s_rt->direction == SDW_DATA_DIR_TX ? "TX" : "RX");
+ return -EINVAL;
+ }
} else if (port_config[i].num) {
return -EINVAL;
}