soundwire updates for 7.1

- Core: DP prepare polling for avoiding interrupt deadlock
  - AMD clock init and bandwidth refactoring
  - Intel more codecs to wake list, clear message on before signaling waiting thread
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmnh3twACgkQfBQHDyUj
 g0eZwhAApdSFRhk43yNAMWr33Vxb+IFjRylNaJb6h5IWS7+0Z3yfq6zOuM3tW7Yh
 C9804L0RkncHwq6th0T5pi3neAwm/dVi1zmnzXiAe5Kgzl/Eru4ZNMh59VD8aNwQ
 K/emES6AJR6ulezYHNDE5Y74kjQHzMQUWiUo1DoAapoqr+WnZvfK+ZYAYh7I/ctY
 FxzBxx13yIHInDHsl3WQ+r1CSinDEe3RiQHW5+5DWdmmw8LfHLGUBBFIJQcVxRN/
 8NlNYr1wvRxUij11cZMPc3TetPfSsIGFpa7eOKezX1P9GxwY7qWvPvPmO0kzgLvH
 AsbFXOHZKYbtPy87agF1QdGq0Seym9x6OhNFucXs1yXIk1t/isv9bOrrOOmnSbR2
 U3ycbMCUmJEUKhd7MWPWRaFUCEM2j/CRugFwpIbeE7AgJ5hZIUXlfWykDdlFxDTM
 e9IQ1jMTSnwyXs705FLLF3hxjIIAWS1kHIgEPWrxuSkAD9/Xx6sbD2fca7Ca6qrm
 W90TlprDAj/KiisEvje0tjEfZ8fTUanLC67d2sbxtrpzwpus4u8GzD62RAgsLlYP
 y9qGeAE1I5FMtJB83dewiYPCIofoez4DizM5wemojI+TtmvZwEWYfeXievD3ibad
 xN3uXtX6fqZ6BWUVIaxjdhSEB2FYdaZjTWPSmcUIwKlApOso0dY=
 =0cvn
 -----END PGP SIGNATURE-----

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

Pull soundwire updates from Vinod Koul:

 - Core: DP prepare polling for avoiding interrupt deadlock

 - AMD clock init and bandwidth refactoring

 - Intel more codecs to wake list, clear message on before signaling
   waiting thread

* tag 'soundwire-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire:
  soundwire: intel_auxdevice: Add cs42l49 to wake_capable_list
  soundwire: cadence: Clear message complete before signaling waiting thread
  soundwire: Intel: test bus.bpt_stream before assigning it
  soundwire: bus: demote UNATTACHED state warnings to dev_dbg()
  soundwire: stream: Poll for DP prepare to avoid interrupt deadlock
  soundwire: amd: refactor bandwidth calculation logic
  soundwire: amd: add clock init control function
  soundwire: intel_auxdevice: Add CS47L47 to wake_capable_list
  soundwire: slave: Don't register devices that are disabled in ACPI
  soundwire: sdw.h: repair names and format of kernel-doc comments
master
Linus Torvalds 2026-04-17 10:16:53 -07:00
commit 65bec0c4ea
10 changed files with 138 additions and 36 deletions

View File

@ -27,6 +27,36 @@
#define to_amd_sdw(b) container_of(b, struct amd_sdw_manager, bus)
static int amd_sdw_clk_init_ctrl(struct amd_sdw_manager *amd_manager)
{
struct sdw_bus *bus = &amd_manager->bus;
struct sdw_master_prop *prop = &bus->prop;
u32 divider;
dev_dbg(amd_manager->dev, "mclk %d max %d row %d col %d frame_rate:%d\n",
prop->mclk_freq, prop->max_clk_freq, prop->default_row,
prop->default_col, prop->default_frame_rate);
if (!prop->default_frame_rate || !prop->default_row) {
dev_err(amd_manager->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 / bus->params.curr_dr_freq);
writel(divider, amd_manager->mmio + ACP_SW_CLK_FREQUENCY_CTRL);
/* Set frame shape base on the actual bus frequency. */
prop->default_col = bus->params.curr_dr_freq /
prop->default_frame_rate / prop->default_row;
amd_manager->cols_index = sdw_find_col_index(prop->default_col);
amd_manager->rows_index = sdw_find_row_index(prop->default_row);
bus->params.col = prop->default_col;
bus->params.row = prop->default_row;
return 0;
}
static int amd_init_sdw_manager(struct amd_sdw_manager *amd_manager)
{
u32 val;
@ -437,12 +467,16 @@ static u32 amd_sdw_read_ping_status(struct sdw_bus *bus)
static int amd_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream)
{
struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);
struct sdw_transport_data t_data = {0};
struct sdw_master_runtime *m_rt;
struct sdw_port_runtime *p_rt;
struct sdw_bus_params *b_params = &bus->params;
int port_bo, hstart, hstop, sample_int;
unsigned int rate, bps;
unsigned int rate, bps, channels;
unsigned int stream_slot_size, max_slots;
static unsigned int next_offset[AMD_SDW_MAX_MANAGER_COUNT] = {1};
unsigned int inst_id = amd_manager->instance;
port_bo = 0;
hstart = 1;
@ -453,11 +487,51 @@ static int amd_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime
list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
rate = m_rt->stream->params.rate;
bps = m_rt->stream->params.bps;
channels = m_rt->stream->params.ch_count;
sample_int = (bus->params.curr_dr_freq / rate);
/* Compute slots required for this stream dynamically */
stream_slot_size = bps * channels;
list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
port_bo = (p_rt->num * 64) + 1;
dev_dbg(bus->dev, "p_rt->num=%d hstart=%d hstop=%d port_bo=%d\n",
p_rt->num, hstart, hstop, port_bo);
if (p_rt->num >= amd_manager->max_ports) {
dev_err(bus->dev, "Port %d exceeds max ports %d\n",
p_rt->num, amd_manager->max_ports);
return -EINVAL;
}
if (!amd_manager->port_offset_map[p_rt->num]) {
/*
* port block offset calculation for 6MHz bus clock frequency with
* different frame sizes 50 x 10 and 125 x 2
*/
if (bus->params.curr_dr_freq == 12000000) {
max_slots = bus->params.row * (bus->params.col - 1);
if (next_offset[inst_id] + stream_slot_size <=
(max_slots - 1)) {
amd_manager->port_offset_map[p_rt->num] =
next_offset[inst_id];
next_offset[inst_id] += stream_slot_size;
} else {
dev_err(bus->dev,
"No space for port %d\n", p_rt->num);
return -ENOMEM;
}
} else {
/*
* port block offset calculation for 12MHz bus clock
* frequency
*/
amd_manager->port_offset_map[p_rt->num] =
(p_rt->num * 64) + 1;
}
}
port_bo = amd_manager->port_offset_map[p_rt->num];
dev_dbg(bus->dev,
"Port=%d hstart=%d hstop=%d port_bo=%d slots=%d max_ports=%d\n",
p_rt->num, hstart, hstop, port_bo, stream_slot_size,
amd_manager->max_ports);
sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
false, SDW_BLK_GRP_CNT_1, sample_int,
port_bo, port_bo >> 8, hstart, hstop,
@ -960,6 +1034,9 @@ int amd_sdw_manager_start(struct amd_sdw_manager *amd_manager)
prop = &amd_manager->bus.prop;
if (!prop->hw_disabled) {
ret = amd_sdw_clk_init_ctrl(amd_manager);
if (ret)
return ret;
ret = amd_init_sdw_manager(amd_manager);
if (ret)
return ret;
@ -984,7 +1061,6 @@ static int amd_sdw_manager_probe(struct platform_device *pdev)
struct resource *res;
struct device *dev = &pdev->dev;
struct sdw_master_prop *prop;
struct sdw_bus_params *params;
struct amd_sdw_manager *amd_manager;
int ret;
@ -1047,15 +1123,14 @@ static int amd_sdw_manager_probe(struct platform_device *pdev)
default:
return -EINVAL;
}
amd_manager->max_ports = amd_manager->num_dout_ports + amd_manager->num_din_ports;
amd_manager->port_offset_map = devm_kcalloc(dev, amd_manager->max_ports,
sizeof(int), GFP_KERNEL);
if (!amd_manager->port_offset_map)
return -ENOMEM;
params = &amd_manager->bus.params;
params->col = AMD_SDW_DEFAULT_COLUMNS;
params->row = AMD_SDW_DEFAULT_ROWS;
prop = &amd_manager->bus.prop;
prop->clk_freq = &amd_sdw_freq_tbl[0];
prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ;
prop->max_clk_freq = AMD_SDW_DEFAULT_CLK_FREQ;
ret = sdw_bus_master_add(&amd_manager->bus, dev, dev->fwnode);
if (ret) {
@ -1347,6 +1422,9 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
}
}
sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
ret = amd_sdw_clk_init_ctrl(amd_manager);
if (ret)
return ret;
amd_init_sdw_manager(amd_manager);
amd_enable_sdw_interrupts(amd_manager);
ret = amd_enable_sdw_manager(amd_manager);

View File

@ -203,10 +203,6 @@
#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,
};
struct sdw_manager_dp_reg {
u32 frame_fmt_reg;
u32 sample_int_reg;

View File

@ -1899,7 +1899,7 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
if (status[i] == SDW_SLAVE_UNATTACHED &&
slave->status != SDW_SLAVE_UNATTACHED) {
dev_warn(&slave->dev, "Slave %d state check1: UNATTACHED, status was %d\n",
dev_dbg(&slave->dev, "Slave %d state check1: UNATTACHED, status was %d\n",
i, slave->status);
sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED);
@ -1951,7 +1951,7 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
if (slave->status == SDW_SLAVE_UNATTACHED)
break;
dev_warn(&slave->dev, "Slave %d state check2: UNATTACHED, status was %d\n",
dev_dbg(&slave->dev, "Slave %d state check2: UNATTACHED, status was %d\n",
i, slave->status);
sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED);

View File

@ -933,6 +933,14 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id)
cdns_read_response(cdns);
/*
* Clear interrupt before signalling the completion to avoid
* a race between this thread and the main thread starting
* another TX.
*/
cdns_writel(cdns, CDNS_MCP_INTSTAT, CDNS_MCP_INT_RX_WL);
int_status &= ~CDNS_MCP_INT_RX_WL;
if (defer && defer->msg) {
cdns_fill_msg_resp(cdns, defer->msg,
defer->length, 0);

View File

@ -82,6 +82,11 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave *
int len;
int i;
if (cdns->bus.bpt_stream) {
dev_err(cdns->dev, "%s: BPT stream already exists\n", __func__);
return -EAGAIN;
}
stream = sdw_alloc_stream("BPT", SDW_STREAM_BPT);
if (!stream)
return -ENOMEM;

View File

@ -53,6 +53,8 @@ struct wake_capable_part {
static struct wake_capable_part wake_capable_list[] = {
{0x01fa, 0x4243},
{0x01fa, 0x4245},
{0x01fa, 0x4249},
{0x01fa, 0x4747},
{0x025d, 0x5682},
{0x025d, 0x700},
{0x025d, 0x711},

View File

@ -115,6 +115,9 @@ static bool find_slave(struct sdw_bus *bus,
u64 addr;
int ret;
if (acpi_bus_get_status(adev) || !acpi_dev_ready_for_enumeration(adev))
return false;
ret = acpi_get_local_u64_address(adev->handle, &addr);
if (ret < 0)
return false;

View File

@ -8,6 +8,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/slab.h>
@ -18,6 +19,8 @@
#include <sound/soc.h>
#include "bus.h"
#define SDW_PORT_PREP_POLL_USEC 1000
/*
* Array of supported rows and columns as per MIPI SoundWire Specification 1.1
*
@ -443,7 +446,6 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus,
struct sdw_port_runtime *p_rt,
bool prep)
{
struct completion *port_ready;
struct sdw_dpn_prop *dpn_prop;
struct sdw_prepare_ch prep_ch;
u32 imp_def_interrupts;
@ -518,14 +520,18 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus,
return ret;
}
/* 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(ch_prep_timeout));
/*
* Poll for NOT_PREPARED==0. Cannot use the interrupt because
* this code holds bus_lock which blocks interrupt handling.
*/
ret = read_poll_timeout(sdw_read_no_pm, val,
(val < 0) || ((val & p_rt->ch_mask) == 0),
SDW_PORT_PREP_POLL_USEC, ch_prep_timeout * USEC_PER_MSEC,
false, s_rt->slave, SDW_DPN_PREPARESTATUS(p_rt->num));
if (ret || (val < 0)) {
if (val < 0)
ret = val;
val = sdw_read_no_pm(s_rt->slave, SDW_DPN_PREPARESTATUS(p_rt->num));
if ((val < 0) || (val & p_rt->ch_mask)) {
ret = (val < 0) ? val : -ETIMEDOUT;
dev_err(&s_rt->slave->dev,
"Chn prep failed for port %d: %d\n", prep_ch.num, ret);
return ret;

View File

@ -532,7 +532,7 @@ struct sdw_slave_intr_status {
};
/**
* sdw_reg_bank - SoundWire register banks
* enum sdw_reg_bank - SoundWire register banks
* @SDW_BANK0: Soundwire register bank 0
* @SDW_BANK1: Soundwire register bank 1
*/
@ -751,7 +751,7 @@ struct sdw_port_params {
* struct sdw_transport_params: Data Port Transport Parameters
*
* @blk_grp_ctrl_valid: Port implements block group control
* @num: Port number
* @port_num: Port number
* @blk_grp_ctrl: Block group control value
* @sample_interval: Sample interval
* @offset1: Blockoffset of the payload data
@ -782,7 +782,7 @@ struct sdw_transport_params {
/**
* struct sdw_enable_ch: Enable/disable Data Port channel
*
* @num: Port number
* @port_num: Port number
* @ch_mask: Active channel mask
* @enable: Enable (true) /disable (false) channel
*/
@ -885,7 +885,7 @@ void sdw_bus_master_delete(struct sdw_bus *bus);
void sdw_show_ping_status(struct sdw_bus *bus, bool sync_delay);
/**
* sdw_port_config: Master or Slave Port configuration
* struct sdw_port_config: Master or Slave Port configuration
*
* @num: Port number
* @ch_mask: channels mask for port
@ -896,7 +896,7 @@ struct sdw_port_config {
};
/**
* sdw_stream_config: Master or Slave stream configuration
* struct sdw_stream_config: Master or Slave stream configuration
*
* @frame_rate: Audio frame rate of the stream, in Hz
* @ch_count: Channel count of the stream
@ -913,7 +913,7 @@ struct sdw_stream_config {
};
/**
* sdw_stream_state: Stream states
* enum sdw_stream_state: Stream states
*
* @SDW_STREAM_ALLOCATED: New stream allocated.
* @SDW_STREAM_CONFIGURED: Stream configured
@ -934,7 +934,7 @@ enum sdw_stream_state {
};
/**
* sdw_stream_params: Stream parameters
* struct sdw_stream_params: Stream parameters
*
* @rate: Sampling frequency, in Hz
* @ch_count: Number of channels
@ -947,7 +947,7 @@ struct sdw_stream_params {
};
/**
* sdw_stream_runtime: Runtime stream parameters
* struct sdw_stream_runtime: Runtime stream parameters
*
* @name: SoundWire stream name
* @params: Stream parameters
@ -983,7 +983,7 @@ struct sdw_stream_runtime {
* @defer_msg: Defer message
* @params: Current bus parameters
* @stream_refcount: number of streams currently using this bus
* @btp_stream_refcount: number of BTP streams currently using this bus (should
* @bpt_stream_refcount: number of BTP streams currently using this bus (should
* be zero or one, multiple streams per link is not supported).
* @bpt_stream: pointer stored to handle BTP streams.
* @ops: Master callback ops

View File

@ -66,8 +66,10 @@ struct sdw_amd_dai_runtime {
* @status: peripheral devices status array
* @num_din_ports: number of input ports
* @num_dout_ports: number of output ports
* @max_ports: total number of input ports and output ports
* @cols_index: Column index in frame shape
* @rows_index: Rows index in frame shape
* @port_offset_map: dynamic array to map port block offset
* @instance: SoundWire manager instance
* @quirks: SoundWire manager quirks
* @wake_en_mask: wake enable mask per SoundWire manager
@ -92,10 +94,12 @@ struct amd_sdw_manager {
int num_din_ports;
int num_dout_ports;
int max_ports;
int cols_index;
int rows_index;
int *port_offset_map;
u32 instance;
u32 quirks;
u32 wake_en_mask;