diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-24 12:16:16 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-24 12:16:16 -0700 |
| commit | ab9b9b51baa9bb964e9219f81682c4f1761ee47d (patch) | |
| tree | f6b3563402abb965d5a7684f47513bcefc820a3b /drivers | |
| parent | 2f43193b88188b184a967c9427602e019f1b8708 (diff) | |
| parent | 5b08a3afd2995e5312c4c47ffea9d21b651e8e13 (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.c | 7 | ||||
| -rw-r--r-- | drivers/soundwire/bus.h | 10 | ||||
| -rw-r--r-- | drivers/soundwire/cadence_master.c | 35 | ||||
| -rw-r--r-- | drivers/soundwire/cadence_master.h | 1 | ||||
| -rw-r--r-- | drivers/soundwire/dmi-quirks.c | 42 | ||||
| -rw-r--r-- | drivers/soundwire/intel_ace2x.c | 20 | ||||
| -rw-r--r-- | drivers/soundwire/intel_auxdevice.c | 1 | ||||
| -rw-r--r-- | drivers/soundwire/mipi_disco.c | 6 | ||||
| -rw-r--r-- | drivers/soundwire/qcom.c | 24 | ||||
| -rw-r--r-- | drivers/soundwire/stream.c | 17 |
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; } |
