1
0
Fork 0

soundwire updates for 5.10-rc1

This round of update includes:
  - Generic bandwidth allocation algorithm from Intel folks
  - PM support for Intel chipsets
  - Updates to Intel drivers which makes sdw usable on latest laptops
  - Support for MMIO SDW controllers found in QC chipsets
  - Update to subsystem to use helpers in bitfield.h to manage register
    bits
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAl91bukACgkQfBQHDyUj
 g0dllRAAsbAdqrQiWYocxm8WsC4OehwYdgv+WgAcq5820xxKI4XMRhB6TGxeFt3B
 r2fCxoxOqq/z7WCt1Ts6Ivw9dy3E9W8hPgesXIVpqHdyByYLryEllDGuOInkEruO
 Brcqx2E0ySysVyIaw0Nx5UGCwDtPUsH0nfiTXSJrLHv3eQ7SLxtn/S0immfaAMDl
 Y6Z2sa8D77UkOpLxsKBYfcGn+AS7Muc7NS1Sp+TNNZULTC6jL8+HifzqbEcH9GhK
 htBAolCjXmn/FbKCknA+3+zFEe+XKNkG6Y7KApbjViAEGu/fKh8PfghvtMjgAvzk
 xqvoOijxetlovf19Dz9r1/2l4c+O9im6dHOCZCPRQE04/Rcg5J2Oym/c8cZvMjZS
 EpWH34lDcpPgW37IuIUlGqX1crTcfhf4GW931vsJidkM8gAD8DFI90o/ynx+lkca
 SKVS1ZsHnHfP1NkXGikiTxDKtFZzcIJnjJrUFdKRjVgSBKVhRPpbUX3Wd5yOqnmW
 nrKcj6aBkjqy2rpiaV/gqQ65uVobtewqbPF4AIOl2VtwCQZrj5lLERLWz++UWVFB
 DFZhnV912kouPdeI28+UnYTyfVZZGfsmvplJ/dgiNsxixydIqPOl0bA5T+xvDwXc
 jWWTQnvPDL5IoOhmo2NNgvRlWtPmCQpIA1dOUA9b1S/SVvCfnSY=
 =ALTw
 -----END PGP SIGNATURE-----

Merge tag 'soundwire-5.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire into char-misc-next

Vinod writes:

soundwire updates for 5.10-rc1

This round of update includes:
 - Generic bandwidth allocation algorithm from Intel folks
 - PM support for Intel chipsets
 - Updates to Intel drivers which makes sdw usable on latest laptops
 - Support for MMIO SDW controllers found in QC chipsets
 - Update to subsystem to use helpers in bitfield.h to manage register
   bits

* tag 'soundwire-5.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire: (66 commits)
  soundwire: sysfs: add slave status and device number before probe
  soundwire: bus: add enumerated Slave device to device list
  soundwire: remove an unnecessary NULL check
  soundwire: cadence: add data port test fail interrupt
  soundwire: intel: enable test modes
  soundwire: enable Data Port test modes
  soundwire: intel: use {u32|u16}p_replace_bits
  soundwire: cadence: use u32p_replace_bits
  soundwire: qcom: get max rows and cols info from compatible
  soundwire: qcom: add support to block packing mode
  soundwire: qcom: clear BIT FIELDs before value set.
  soundwire: Add generic bandwidth allocation algorithm
  soundwire: cadence: add parity error injection through debugfs
  soundwire: bus: export broadcast read/write capability for tests
  ASoC: codecs: realtek-soundwire: ignore initial PARITY errors
  soundwire: bus: use quirk to filter out invalid parity errors
  soundwire: slave: add first_interrupt_done status
  soundwire: bus: filter-out unwanted interrupt reports
  ASoC/soundwire: bus: use property to set interrupt masks
  soundwire: qcom: fix SLIBMUS/SLIMBUS typo
  ...
zero-sugar-mainline-defconfig
Greg Kroah-Hartman 2020-10-01 22:59:55 +02:00
commit 4cb1a880e7
29 changed files with 1765 additions and 354 deletions

View File

@ -1,3 +1,21 @@
What: /sys/bus/soundwire/devices/sdw:.../status
/sys/bus/soundwire/devices/sdw:.../device_number
Date: September 2020
Contact: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Bard Liao <yung-chuan.liao@linux.intel.com>
Vinod Koul <vkoul@kernel.org>
Description: SoundWire Slave status
These properties report the Slave status, e.g. if it
is UNATTACHED or not, and in the latter case show the
device_number. This status information is useful to
detect devices exposed by platform firmware but not
physically present on the bus, and conversely devices
not exposed in platform firmware but enumerated.
What: /sys/bus/soundwire/devices/sdw:.../dev-properties/mipi_revision
/sys/bus/soundwire/devices/sdw:.../dev-properties/wake_capable
/sys/bus/soundwire/devices/sdw:.../dev-properties/test_mode_capable

View File

@ -11,6 +11,7 @@ board specific bus parameters.
Example:
"qcom,soundwire-v1.3.0"
"qcom,soundwire-v1.5.0"
"qcom,soundwire-v1.5.1"
"qcom,soundwire-v1.6.0"
- reg:
Usage: required

View File

@ -24,6 +24,7 @@ config SOUNDWIRE_CADENCE
config SOUNDWIRE_INTEL
tristate "Intel SoundWire Master driver"
select SOUNDWIRE_CADENCE
select SOUNDWIRE_GENERIC_ALLOCATION
depends on ACPI && SND_SOC
help
SoundWire Intel Master driver.
@ -33,11 +34,15 @@ config SOUNDWIRE_INTEL
config SOUNDWIRE_QCOM
tristate "Qualcomm SoundWire Master driver"
depends on SLIMBUS
imply SLIMBUS
depends on SND_SOC
help
SoundWire Qualcomm Master driver.
If you have an Qualcomm platform which has a SoundWire Master then
enable this config option to get the SoundWire support for that
device
config SOUNDWIRE_GENERIC_ALLOCATION
tristate
endif

View File

@ -8,6 +8,9 @@ soundwire-bus-y := bus_type.o bus.o master.o slave.o mipi_disco.o stream.o \
sysfs_slave.o sysfs_slave_dpn.o
obj-$(CONFIG_SOUNDWIRE) += soundwire-bus.o
soundwire-generic-allocation-objs := generic_bandwidth_allocation.o
obj-$(CONFIG_SOUNDWIRE_GENERIC_ALLOCATION) += soundwire-generic-allocation.o
ifdef CONFIG_DEBUG_FS
soundwire-bus-y += debugfs.o
endif

View File

@ -61,6 +61,12 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent,
return -EINVAL;
}
if (!bus->compute_params) {
dev_err(bus->dev,
"Bandwidth allocation not configured, compute_params no set\n");
return -EINVAL;
}
mutex_init(&bus->msg_lock);
mutex_init(&bus->bus_lock);
INIT_LIST_HEAD(&bus->slaves);
@ -255,6 +261,21 @@ static int sdw_reset_page(struct sdw_bus *bus, u16 dev_num)
return ret;
}
static int sdw_transfer_unlocked(struct sdw_bus *bus, struct sdw_msg *msg)
{
int ret;
ret = do_transfer(bus, msg);
if (ret != 0 && ret != -ENODATA)
dev_err(bus->dev, "trf on Slave %d failed:%d\n",
msg->dev_num, ret);
if (msg->page)
sdw_reset_page(bus, msg->dev_num);
return ret;
}
/**
* sdw_transfer() - Synchronous transfer message to a SDW Slave device
* @bus: SDW bus
@ -266,13 +287,7 @@ int sdw_transfer(struct sdw_bus *bus, struct sdw_msg *msg)
mutex_lock(&bus->msg_lock);
ret = do_transfer(bus, msg);
if (ret != 0 && ret != -ENODATA)
dev_err(bus->dev, "trf on Slave %d failed:%d\n",
msg->dev_num, ret);
if (msg->page)
sdw_reset_page(bus, msg->dev_num);
ret = sdw_transfer_unlocked(bus, msg);
mutex_unlock(&bus->msg_lock);
@ -347,8 +362,8 @@ int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave,
return -EINVAL;
}
msg->addr_page1 = (addr >> SDW_REG_SHIFT(SDW_SCP_ADDRPAGE1_MASK));
msg->addr_page2 = (addr >> SDW_REG_SHIFT(SDW_SCP_ADDRPAGE2_MASK));
msg->addr_page1 = FIELD_GET(SDW_SCP_ADDRPAGE1_MASK, addr);
msg->addr_page2 = FIELD_GET(SDW_SCP_ADDRPAGE2_MASK, addr);
msg->addr |= BIT(15);
msg->page = true;
@ -428,6 +443,39 @@ sdw_bwrite_no_pm(struct sdw_bus *bus, u16 dev_num, u32 addr, u8 value)
return sdw_transfer(bus, &msg);
}
int sdw_bread_no_pm_unlocked(struct sdw_bus *bus, u16 dev_num, u32 addr)
{
struct sdw_msg msg;
u8 buf;
int ret;
ret = sdw_fill_msg(&msg, NULL, addr, 1, dev_num,
SDW_MSG_FLAG_READ, &buf);
if (ret)
return ret;
ret = sdw_transfer_unlocked(bus, &msg);
if (ret < 0)
return ret;
return buf;
}
EXPORT_SYMBOL(sdw_bread_no_pm_unlocked);
int sdw_bwrite_no_pm_unlocked(struct sdw_bus *bus, u16 dev_num, u32 addr, u8 value)
{
struct sdw_msg msg;
int ret;
ret = sdw_fill_msg(&msg, NULL, addr, 1, dev_num,
SDW_MSG_FLAG_WRITE, &value);
if (ret)
return ret;
return sdw_transfer_unlocked(bus, &msg);
}
EXPORT_SYMBOL(sdw_bwrite_no_pm_unlocked);
static int
sdw_read_no_pm(struct sdw_slave *slave, u32 addr)
{
@ -699,6 +747,15 @@ static int sdw_program_device_num(struct sdw_bus *bus)
if (!found) {
/* TODO: Park this device in Group 13 */
/*
* add Slave device even if there is no platform
* firmware description. There will be no driver probe
* but the user/integration will be able to see the
* device, enumeration status and device number in sysfs
*/
sdw_slave_add(bus, &id, NULL);
dev_err(bus->dev, "Slave Entry not found\n");
}
@ -1051,6 +1108,12 @@ int sdw_configure_dpn_intr(struct sdw_slave *slave,
int ret;
u8 val = 0;
if (slave->bus->params.s_data_mode != SDW_PORT_DATA_MODE_NORMAL) {
dev_dbg(&slave->dev, "TEST FAIL interrupt %s\n",
enable ? "on" : "off");
mask |= SDW_DPN_INT_TEST_FAIL;
}
addr = SDW_DPN_INTMASK(port);
/* Set/Clear port ready interrupt mask */
@ -1184,13 +1247,13 @@ static int sdw_initialize_slave(struct sdw_slave *slave)
return ret;
/*
* Set bus clash, parity and SCP implementation
* defined interrupt mask
* TODO: Read implementation defined interrupt mask
* from Slave property
* Set SCP_INT1_MASK register, typically bus clash and
* implementation-defined interrupt mask. The Parity detection
* may not always be correct on startup so its use is
* device-dependent, it might e.g. only be enabled in
* steady-state after a couple of frames.
*/
val = SDW_SCP_INT1_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH |
SDW_SCP_INT1_PARITY;
val = slave->prop.scp_int1_mask;
/* Enable SCP interrupts */
ret = sdw_update(slave, SDW_SCP_INTMASK1, val, val);
@ -1362,6 +1425,8 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
unsigned long port;
bool slave_notify = false;
u8 buf, buf2[2], _buf, _buf2[2];
bool parity_check;
bool parity_quirk;
sdw_modify_slave_status(slave, SDW_SLAVE_ALERT);
@ -1394,12 +1459,18 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
* interrupt
*/
if (buf & SDW_SCP_INT1_PARITY) {
dev_err(&slave->dev, "Parity error detected\n");
parity_check = slave->prop.scp_int1_mask & SDW_SCP_INT1_PARITY;
parity_quirk = !slave->first_interrupt_done &&
(slave->prop.quirks & SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY);
if (parity_check && !parity_quirk)
dev_err(&slave->dev, "Parity error detected\n");
clear |= SDW_SCP_INT1_PARITY;
}
if (buf & SDW_SCP_INT1_BUS_CLASH) {
dev_err(&slave->dev, "Bus clash error detected\n");
if (slave->prop.scp_int1_mask & SDW_SCP_INT1_BUS_CLASH)
dev_err(&slave->dev, "Bus clash detected\n");
clear |= SDW_SCP_INT1_BUS_CLASH;
}
@ -1411,16 +1482,18 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
*/
if (buf & SDW_SCP_INT1_IMPL_DEF) {
dev_dbg(&slave->dev, "Slave impl defined interrupt\n");
if (slave->prop.scp_int1_mask & SDW_SCP_INT1_IMPL_DEF) {
dev_dbg(&slave->dev, "Slave impl defined interrupt\n");
slave_notify = true;
}
clear |= SDW_SCP_INT1_IMPL_DEF;
slave_notify = true;
}
/* Check port 0 - 3 interrupts */
port = buf & SDW_SCP_INT1_PORT0_3;
/* To get port number corresponding to bits, shift it */
port = port >> SDW_REG_SHIFT(SDW_SCP_INT1_PORT0_3);
port = FIELD_GET(SDW_SCP_INT1_PORT0_3, port);
for_each_set_bit(bit, &port, 8) {
sdw_handle_port_interrupt(slave, bit,
&port_status[bit]);
@ -1468,6 +1541,9 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
goto io_err;
}
/* at this point all initial interrupt sources were handled */
slave->first_interrupt_done = true;
/*
* Read status again to ensure no new interrupts arrived
* while servicing interrupts.
@ -1670,8 +1746,10 @@ void sdw_clear_slave_status(struct sdw_bus *bus, u32 request)
if (!slave)
continue;
if (slave->status != SDW_SLAVE_UNATTACHED)
if (slave->status != SDW_SLAVE_UNATTACHED) {
sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED);
slave->first_interrupt_done = false;
}
/* keep track of request, used in pm_runtime resume */
slave->unattach_request = request;

View File

@ -19,6 +19,8 @@ static inline int sdw_acpi_find_slaves(struct sdw_bus *bus)
int sdw_of_find_slaves(struct sdw_bus *bus);
void sdw_extract_slave_id(struct sdw_bus *bus,
u64 addr, struct sdw_slave_id *id);
int sdw_slave_add(struct sdw_bus *bus, struct sdw_slave_id *id,
struct fwnode_handle *fwnode);
int sdw_master_device_add(struct sdw_bus *bus, struct device *parent,
struct fwnode_handle *fwnode);
int sdw_master_device_del(struct sdw_bus *bus);
@ -69,6 +71,7 @@ struct sdw_msg {
};
#define SDW_DOUBLE_RATE_FACTOR 2
#define SDW_STRM_RATE_GROUPING 1
extern int sdw_rows[SDW_FRAME_ROWS];
extern int sdw_cols[SDW_FRAME_COLS];
@ -154,9 +157,50 @@ int sdw_transfer_defer(struct sdw_bus *bus, struct sdw_msg *msg,
int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave,
u32 addr, size_t count, u16 dev_num, u8 flags, u8 *buf);
/* Retrieve and return channel count from channel mask */
static inline int sdw_ch_mask_to_ch(int ch_mask)
{
int c = 0;
for (c = 0; ch_mask; ch_mask >>= 1)
c += ch_mask & 1;
return c;
}
/* Fill transport parameter data structure */
static inline void sdw_fill_xport_params(struct sdw_transport_params *params,
int port_num, bool grp_ctrl_valid,
int grp_ctrl, int sample_int,
int off1, int off2,
int hstart, int hstop,
int pack_mode, int lane_ctrl)
{
params->port_num = port_num;
params->blk_grp_ctrl_valid = grp_ctrl_valid;
params->blk_grp_ctrl = grp_ctrl;
params->sample_interval = sample_int;
params->offset1 = off1;
params->offset2 = off2;
params->hstart = hstart;
params->hstop = hstop;
params->blk_pkg_mode = pack_mode;
params->lane_ctrl = lane_ctrl;
}
/* Fill port parameter data structure */
static inline void sdw_fill_port_params(struct sdw_port_params *params,
int port_num, int bps,
int flow_mode, int data_mode)
{
params->num = port_num;
params->bps = bps;
params->flow_mode = flow_mode;
params->data_mode = data_mode;
}
/* Read-Modify-Write Slave register */
static inline int
sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val)
static inline int sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val)
{
int tmp;
@ -168,6 +212,10 @@ sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val)
return sdw_write(slave, addr, tmp);
}
/* broadcast read/write for tests */
int sdw_bread_no_pm_unlocked(struct sdw_bus *bus, u16 dev_num, u32 addr);
int sdw_bwrite_no_pm_unlocked(struct sdw_bus *bus, u16 dev_num, u32 addr, u8 value);
/*
* At the moment we only track Master-initiated hw_reset.
* Additional fields can be added as needed

View File

@ -84,6 +84,15 @@ static int sdw_drv_probe(struct device *dev)
const struct sdw_device_id *id;
int ret;
/*
* fw description is mandatory to bind
*/
if (!dev->fwnode)
return -ENODEV;
if (!IS_ENABLED(CONFIG_ACPI) && !dev->of_node)
return -ENODEV;
id = sdw_get_device_id(slave, drv);
if (!id)
return -ENODEV;

View File

@ -13,6 +13,7 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/pm_runtime.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw.h>
#include <sound/pcm_params.h>
@ -50,11 +51,14 @@ MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask");
#define CDNS_MCP_CONTROL_BLOCK_WAKEUP BIT(0)
#define CDNS_MCP_CMDCTRL 0x8
#define CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR BIT(2)
#define CDNS_MCP_SSPSTAT 0xC
#define CDNS_MCP_FRAME_SHAPE 0x10
#define CDNS_MCP_FRAME_SHAPE_INIT 0x14
#define CDNS_MCP_FRAME_SHAPE_COL_MASK GENMASK(2, 0)
#define CDNS_MCP_FRAME_SHAPE_ROW_OFFSET 3
#define CDNS_MCP_FRAME_SHAPE_ROW_MASK GENMASK(7, 3)
#define CDNS_MCP_CONFIG_UPDATE 0x18
#define CDNS_MCP_CONFIG_UPDATE_BIT BIT(0)
@ -129,8 +133,7 @@ MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask");
#define CDNS_MCP_CMD_SSP_TAG BIT(31)
#define CDNS_MCP_CMD_COMMAND GENMASK(30, 28)
#define CDNS_MCP_CMD_DEV_ADDR GENMASK(27, 24)
#define CDNS_MCP_CMD_REG_ADDR_H GENMASK(23, 16)
#define CDNS_MCP_CMD_REG_ADDR_L GENMASK(15, 8)
#define CDNS_MCP_CMD_REG_ADDR GENMASK(23, 8)
#define CDNS_MCP_CMD_REG_DATA GENMASK(7, 0)
#define CDNS_MCP_CMD_READ 2
@ -172,6 +175,7 @@ MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask");
#define CDNS_DPN_HCTRL_LCTRL GENMASK(10, 8)
#define CDNS_PORTCTRL 0x130
#define CDNS_PORTCTRL_TEST_FAILED BIT(1)
#define CDNS_PORTCTRL_DIRN BIT(7)
#define CDNS_PORTCTRL_BANK_INVERT BIT(8)
@ -367,6 +371,85 @@ static int cdns_hw_reset(void *data, u64 value)
DEFINE_DEBUGFS_ATTRIBUTE(cdns_hw_reset_fops, NULL, cdns_hw_reset, "%llu\n");
static int cdns_parity_error_injection(void *data, u64 value)
{
struct sdw_cdns *cdns = data;
struct sdw_bus *bus;
int ret;
if (value != 1)
return -EINVAL;
bus = &cdns->bus;
/*
* Resume Master device. If this results in a bus reset, the
* Slave devices will re-attach and be re-enumerated.
*/
ret = pm_runtime_get_sync(bus->dev);
if (ret < 0 && ret != -EACCES) {
dev_err_ratelimited(cdns->dev,
"pm_runtime_get_sync failed in %s, ret %d\n",
__func__, ret);
pm_runtime_put_noidle(bus->dev);
return ret;
}
/*
* wait long enough for Slave(s) to be in steady state. This
* does not need to be super precise.
*/
msleep(200);
/*
* Take the bus lock here to make sure that any bus transactions
* will be queued while we inject a parity error on a dummy read
*/
mutex_lock(&bus->bus_lock);
/* program hardware to inject parity error */
cdns_updatel(cdns, CDNS_MCP_CMDCTRL,
CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR,
CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR);
/* commit changes */
cdns_updatel(cdns, CDNS_MCP_CONFIG_UPDATE,
CDNS_MCP_CONFIG_UPDATE_BIT,
CDNS_MCP_CONFIG_UPDATE_BIT);
/* do a broadcast dummy read to avoid bus clashes */
ret = sdw_bread_no_pm_unlocked(&cdns->bus, 0xf, SDW_SCP_DEVID_0);
dev_info(cdns->dev, "parity error injection, read: %d\n", ret);
/* program hardware to disable parity error */
cdns_updatel(cdns, CDNS_MCP_CMDCTRL,
CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR,
0);
/* commit changes */
cdns_updatel(cdns, CDNS_MCP_CONFIG_UPDATE,
CDNS_MCP_CONFIG_UPDATE_BIT,
CDNS_MCP_CONFIG_UPDATE_BIT);
/* Continue bus operation with parity error injection disabled */
mutex_unlock(&bus->bus_lock);
/* Userspace changed the hardware state behind the kernel's back */
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
/*
* allow Master device to enter pm_runtime suspend. This may
* also result in Slave devices suspending.
*/
pm_runtime_mark_last_busy(bus->dev);
pm_runtime_put_autosuspend(bus->dev);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(cdns_parity_error_fops, NULL,
cdns_parity_error_injection, "%llu\n");
/**
* sdw_cdns_debugfs_init() - Cadence debugfs init
* @cdns: Cadence instance
@ -378,6 +461,9 @@ void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root)
debugfs_create_file("cdns-hw-reset", 0200, root, cdns,
&cdns_hw_reset_fops);
debugfs_create_file("cdns-parity-error-injection", 0200, root, cdns,
&cdns_parity_error_fops);
}
EXPORT_SYMBOL_GPL(sdw_cdns_debugfs_init);
@ -417,8 +503,7 @@ cdns_fill_msg_resp(struct sdw_cdns *cdns,
/* fill response */
for (i = 0; i < count; i++)
msg->buf[i + offset] = cdns->response_buf[i] >>
SDW_REG_SHIFT(CDNS_MCP_RESP_RDATA);
msg->buf[i + offset] = FIELD_GET(CDNS_MCP_RESP_RDATA, cdns->response_buf[i]);
return SDW_CMD_OK;
}
@ -441,14 +526,15 @@ _cdns_xfer_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int cmd,
addr = msg->addr;
for (i = 0; i < count; i++) {
data = msg->dev_num << SDW_REG_SHIFT(CDNS_MCP_CMD_DEV_ADDR);
data |= cmd << SDW_REG_SHIFT(CDNS_MCP_CMD_COMMAND);
data |= addr++ << SDW_REG_SHIFT(CDNS_MCP_CMD_REG_ADDR_L);
data = FIELD_PREP(CDNS_MCP_CMD_DEV_ADDR, msg->dev_num);
data |= FIELD_PREP(CDNS_MCP_CMD_COMMAND, cmd);
data |= FIELD_PREP(CDNS_MCP_CMD_REG_ADDR, addr);
addr++;
if (msg->flags == SDW_MSG_FLAG_WRITE)
data |= msg->buf[i + offset];
data |= msg->ssp_sync << SDW_REG_SHIFT(CDNS_MCP_CMD_SSP_TAG);
data |= FIELD_PREP(CDNS_MCP_CMD_SSP_TAG, msg->ssp_sync);
cdns_writel(cdns, base, data);
base += CDNS_MCP_CMD_WORD_LEN;
}
@ -483,12 +569,12 @@ cdns_program_scp_addr(struct sdw_cdns *cdns, struct sdw_msg *msg)
cdns->msg_count = CDNS_SCP_RX_FIFOLEVEL;
}
data[0] = msg->dev_num << SDW_REG_SHIFT(CDNS_MCP_CMD_DEV_ADDR);
data[0] |= 0x3 << SDW_REG_SHIFT(CDNS_MCP_CMD_COMMAND);
data[0] = FIELD_PREP(CDNS_MCP_CMD_DEV_ADDR, msg->dev_num);
data[0] |= FIELD_PREP(CDNS_MCP_CMD_COMMAND, 0x3);
data[1] = data[0];
data[0] |= SDW_SCP_ADDRPAGE1 << SDW_REG_SHIFT(CDNS_MCP_CMD_REG_ADDR_L);
data[1] |= SDW_SCP_ADDRPAGE2 << SDW_REG_SHIFT(CDNS_MCP_CMD_REG_ADDR_L);
data[0] |= FIELD_PREP(CDNS_MCP_CMD_REG_ADDR, SDW_SCP_ADDRPAGE1);
data[1] |= FIELD_PREP(CDNS_MCP_CMD_REG_ADDR, SDW_SCP_ADDRPAGE2);
data[0] |= msg->addr_page1;
data[1] |= msg->addr_page2;
@ -785,13 +871,35 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id)
dev_err_ratelimited(cdns->dev, "Bus clash for data word\n");
}
if (cdns->bus.params.m_data_mode != SDW_PORT_DATA_MODE_NORMAL &&
int_status & CDNS_MCP_INT_DPINT) {
u32 port_intstat;
/* just log which ports report an error */
port_intstat = cdns_readl(cdns, CDNS_MCP_PORT_INTSTAT);
dev_err_ratelimited(cdns->dev, "DP interrupt: PortIntStat %8x\n",
port_intstat);
/* clear status w/ write1 */
cdns_writel(cdns, CDNS_MCP_PORT_INTSTAT, port_intstat);
}
if (int_status & CDNS_MCP_INT_SLAVE_MASK) {
/* Mask the Slave interrupt and wake thread */
cdns_updatel(cdns, CDNS_MCP_INTMASK,
CDNS_MCP_INT_SLAVE_MASK, 0);
int_status &= ~CDNS_MCP_INT_SLAVE_MASK;
schedule_work(&cdns->work);
/*
* Deal with possible race condition between interrupt
* handling and disabling interrupts on suspend.
*
* If the master is in the process of disabling
* interrupts, don't schedule a workqueue
*/
if (cdns->interrupt_enabled)
schedule_work(&cdns->work);
}
cdns_writel(cdns, CDNS_MCP_INTSTAT, int_status);
@ -900,7 +1008,9 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state)
mask |= CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH |
CDNS_MCP_INT_PARITY;
/* no detection of port interrupts for now */
/* port interrupt limited to test modes for now */
if (cdns->bus.params.m_data_mode != SDW_PORT_DATA_MODE_NORMAL)
mask |= CDNS_MCP_INT_DPINT;
/* enable detection of RX fifo level */
mask |= CDNS_MCP_INT_RX_WL;
@ -924,6 +1034,19 @@ update_masks:
slave_state = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT1);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT1, slave_state);
}
cdns->interrupt_enabled = state;
/*
* Complete any on-going status updates before updating masks,
* and cancel queued status updates.
*
* There could be a race with a new interrupt thrown before
* the 3 mask updates below are complete, so in the interrupt
* we use the 'interrupt_enabled' status to prevent new work
* from being queued.
*/
if (!state)
cancel_work_sync(&cdns->work);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, slave_intmask0);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, slave_intmask1);
@ -1041,9 +1164,10 @@ static u32 cdns_set_initial_frame_shape(int n_rows, int n_cols)
int r;
r = sdw_find_row_index(n_rows);
c = sdw_find_col_index(n_cols) & CDNS_MCP_FRAME_SHAPE_COL_MASK;
c = sdw_find_col_index(n_cols);
val = (r << CDNS_MCP_FRAME_SHAPE_ROW_OFFSET) | c;
val = FIELD_PREP(CDNS_MCP_FRAME_SHAPE_ROW_MASK, r);
val |= FIELD_PREP(CDNS_MCP_FRAME_SHAPE_COL_MASK, c);
return val;
}
@ -1170,12 +1294,9 @@ static int cdns_port_params(struct sdw_bus *bus,
dpn_config = cdns_readl(cdns, dpn_config_off);
dpn_config |= ((p_params->bps - 1) <<
SDW_REG_SHIFT(CDNS_DPN_CONFIG_WL));
dpn_config |= (p_params->flow_mode <<
SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_FLOW));
dpn_config |= (p_params->data_mode <<
SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_DAT));
u32p_replace_bits(&dpn_config, (p_params->bps - 1), CDNS_DPN_CONFIG_WL);
u32p_replace_bits(&dpn_config, p_params->flow_mode, CDNS_DPN_CONFIG_PORT_FLOW);
u32p_replace_bits(&dpn_config, p_params->data_mode, CDNS_DPN_CONFIG_PORT_DAT);
cdns_writel(cdns, dpn_config_off, dpn_config);
@ -1211,24 +1332,17 @@ static int cdns_transport_params(struct sdw_bus *bus,
}
dpn_config = cdns_readl(cdns, dpn_config_off);
dpn_config |= (t_params->blk_grp_ctrl <<
SDW_REG_SHIFT(CDNS_DPN_CONFIG_BGC));
dpn_config |= (t_params->blk_pkg_mode <<
SDW_REG_SHIFT(CDNS_DPN_CONFIG_BPM));
u32p_replace_bits(&dpn_config, t_params->blk_grp_ctrl, CDNS_DPN_CONFIG_BGC);
u32p_replace_bits(&dpn_config, t_params->blk_pkg_mode, CDNS_DPN_CONFIG_BPM);
cdns_writel(cdns, dpn_config_off, dpn_config);
dpn_offsetctrl |= (t_params->offset1 <<
SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_1));
dpn_offsetctrl |= (t_params->offset2 <<
SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_2));
u32p_replace_bits(&dpn_offsetctrl, t_params->offset1, CDNS_DPN_OFFSET_CTRL_1);
u32p_replace_bits(&dpn_offsetctrl, t_params->offset2, CDNS_DPN_OFFSET_CTRL_2);
cdns_writel(cdns, dpn_offsetctrl_off, dpn_offsetctrl);
dpn_hctrl |= (t_params->hstart <<
SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTART));
dpn_hctrl |= (t_params->hstop << SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTOP));
dpn_hctrl |= (t_params->lane_ctrl <<
SDW_REG_SHIFT(CDNS_DPN_HCTRL_LCTRL));
u32p_replace_bits(&dpn_hctrl, t_params->hstart, CDNS_DPN_HCTRL_HSTART);
u32p_replace_bits(&dpn_hctrl, t_params->hstop, CDNS_DPN_HCTRL_HSTOP);
u32p_replace_bits(&dpn_hctrl, t_params->lane_ctrl, CDNS_DPN_HCTRL_LCTRL);
cdns_writel(cdns, dpn_hctrl_off, dpn_hctrl);
cdns_writel(cdns, dpn_samplectrl_off, (t_params->sample_interval - 1));
@ -1526,15 +1640,20 @@ void sdw_cdns_config_stream(struct sdw_cdns *cdns,
{
u32 offset, val = 0;
if (dir == SDW_DATA_DIR_RX)
if (dir == SDW_DATA_DIR_RX) {
val = CDNS_PORTCTRL_DIRN;
if (cdns->bus.params.m_data_mode != SDW_PORT_DATA_MODE_NORMAL)
val |= CDNS_PORTCTRL_TEST_FAILED;
}
offset = CDNS_PORTCTRL + pdi->num * CDNS_PORT_OFFSET;
cdns_updatel(cdns, offset, CDNS_PORTCTRL_DIRN, val);
cdns_updatel(cdns, offset,
CDNS_PORTCTRL_DIRN | CDNS_PORTCTRL_TEST_FAILED,
val);
val = pdi->num;
val |= CDNS_PDI_CONFIG_SOFT_RESET;
val |= ((1 << ch) - 1) << SDW_REG_SHIFT(CDNS_PDI_CONFIG_CHANNEL);
val |= FIELD_PREP(CDNS_PDI_CONFIG_CHANNEL, (1 << ch) - 1);
cdns_writel(cdns, CDNS_PDI_CONFIG(pdi->num), val);
}
EXPORT_SYMBOL(sdw_cdns_config_stream);

View File

@ -84,6 +84,8 @@ struct sdw_cdns_stream_config {
* @bus: Bus handle
* @stream_type: Stream type
* @link_id: Master link id
* @hw_params: hw_params to be applied in .prepare step
* @suspended: status set when suspended, to be used in .prepare
*/
struct sdw_cdns_dma_data {
char *name;
@ -92,6 +94,8 @@ struct sdw_cdns_dma_data {
struct sdw_bus *bus;
enum sdw_stream_type stream_type;
int link_id;
struct snd_pcm_hw_params *hw_params;
bool suspended;
};
/**
@ -129,6 +133,7 @@ struct sdw_cdns {
bool link_up;
unsigned int msg_count;
bool interrupt_enabled;
struct work_struct work;

View File

@ -0,0 +1,425 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
// Copyright(c) 2015-2020 Intel Corporation.
/*
* Bandwidth management algorithm based on 2^n gears
*
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/slab.h>
#include <linux/soundwire/sdw.h>
#include "bus.h"
#define SDW_STRM_RATE_GROUPING 1
struct sdw_group_params {
unsigned int rate;
int full_bw;
int payload_bw;
int hwidth;
};
struct sdw_group {
unsigned int count;
unsigned int max_size;
unsigned int *rates;
};
struct sdw_transport_data {
int hstart;
int hstop;
int block_offset;
int sub_block_offset;
};
static void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt,
struct sdw_transport_data *t_data)
{
struct sdw_slave_runtime *s_rt = NULL;
struct sdw_port_runtime *p_rt;
int port_bo, sample_int;
unsigned int rate, bps, ch = 0;
unsigned int slave_total_ch;
struct sdw_bus_params *b_params = &m_rt->bus->params;
port_bo = t_data->block_offset;
list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
rate = m_rt->stream->params.rate;
bps = m_rt->stream->params.bps;
sample_int = (m_rt->bus->params.curr_dr_freq / rate);
slave_total_ch = 0;
list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
ch = sdw_ch_mask_to_ch(p_rt->ch_mask);
sdw_fill_xport_params(&p_rt->transport_params,
p_rt->num, false,
SDW_BLK_GRP_CNT_1,
sample_int, port_bo, port_bo >> 8,
t_data->hstart,
t_data->hstop,
(SDW_BLK_GRP_CNT_1 * ch), 0x0);
sdw_fill_port_params(&p_rt->port_params,
p_rt->num, bps,
SDW_PORT_FLOW_MODE_ISOCH,
b_params->s_data_mode);
port_bo += bps * ch;
slave_total_ch += ch;
}
if (m_rt->direction == SDW_DATA_DIR_TX &&
m_rt->ch_count == slave_total_ch) {
/*
* Slave devices were configured to access all channels
* of the stream, which indicates that they operate in
* 'mirror mode'. Make sure we reset the port offset for
* the next device in the list
*/
port_bo = t_data->block_offset;
}
}
}
static void sdw_compute_master_ports(struct sdw_master_runtime *m_rt,
struct sdw_group_params *params,
int port_bo, int hstop)
{
struct sdw_transport_data t_data = {0};
struct sdw_port_runtime *p_rt;
struct sdw_bus *bus = m_rt->bus;
struct sdw_bus_params *b_params = &bus->params;
int sample_int, hstart = 0;
unsigned int rate, bps, ch, no_ch;
rate = m_rt->stream->params.rate;
bps = m_rt->stream->params.bps;
ch = m_rt->ch_count;
sample_int = (bus->params.curr_dr_freq / rate);
if (rate != params->rate)
return;
t_data.hstop = hstop;
hstart = hstop - params->hwidth + 1;
t_data.hstart = hstart;
list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
no_ch = sdw_ch_mask_to_ch(p_rt->ch_mask);
sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
false, SDW_BLK_GRP_CNT_1, sample_int,
port_bo, port_bo >> 8, hstart, hstop,
(SDW_BLK_GRP_CNT_1 * no_ch), 0x0);
sdw_fill_port_params(&p_rt->port_params,
p_rt->num, bps,
SDW_PORT_FLOW_MODE_ISOCH,
b_params->m_data_mode);
/* Check for first entry */
if (!(p_rt == list_first_entry(&m_rt->port_list,
struct sdw_port_runtime,
port_node))) {
port_bo += bps * ch;
continue;
}
t_data.hstart = hstart;
t_data.hstop = hstop;
t_data.block_offset = port_bo;
t_data.sub_block_offset = 0;
port_bo += bps * ch;
}
sdw_compute_slave_ports(m_rt, &t_data);
}
static void _sdw_compute_port_params(struct sdw_bus *bus,
struct sdw_group_params *params, int count)
{
struct sdw_master_runtime *m_rt = NULL;
int hstop = bus->params.col - 1;
int block_offset, port_bo, i;
/* Run loop for all groups to compute transport parameters */
for (i = 0; i < count; i++) {
port_bo = 1;
block_offset = 1;
list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
sdw_compute_master_ports(m_rt, &params[i],
port_bo, hstop);
block_offset += m_rt->ch_count *
m_rt->stream->params.bps;
port_bo = block_offset;
}
hstop = hstop - params[i].hwidth;
}
}
static int sdw_compute_group_params(struct sdw_bus *bus,
struct sdw_group_params *params,
int *rates, int count)
{
struct sdw_master_runtime *m_rt = NULL;
int sel_col = bus->params.col;
unsigned int rate, bps, ch;
int i, column_needed = 0;
/* Calculate bandwidth per group */
for (i = 0; i < count; i++) {
params[i].rate = rates[i];
params[i].full_bw = bus->params.curr_dr_freq / params[i].rate;
}
list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
rate = m_rt->stream->params.rate;
bps = m_rt->stream->params.bps;
ch = m_rt->ch_count;
for (i = 0; i < count; i++) {
if (rate == params[i].rate)
params[i].payload_bw += bps * ch;
}
}
for (i = 0; i < count; i++) {
params[i].hwidth = (sel_col *
params[i].payload_bw + params[i].full_bw - 1) /
params[i].full_bw;
column_needed += params[i].hwidth;
}
if (column_needed > sel_col - 1)
return -EINVAL;
return 0;
}
static int sdw_add_element_group_count(struct sdw_group *group,
unsigned int rate)
{
int num = group->count;
int i;
for (i = 0; i <= num; i++) {
if (rate == group->rates[i])
break;
if (i != num)
continue;
if (group->count >= group->max_size) {
unsigned int *rates;
group->max_size += 1;
rates = krealloc(group->rates,
(sizeof(int) * group->max_size),
GFP_KERNEL);
if (!rates)
return -ENOMEM;
group->rates = rates;
}
group->rates[group->count++] = rate;
}
return 0;
}
static int sdw_get_group_count(struct sdw_bus *bus,
struct sdw_group *group)
{
struct sdw_master_runtime *m_rt;
unsigned int rate;
int ret = 0;
group->count = 0;
group->max_size = SDW_STRM_RATE_GROUPING;
group->rates = kcalloc(group->max_size, sizeof(int), GFP_KERNEL);
if (!group->rates)
return -ENOMEM;
list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
rate = m_rt->stream->params.rate;
if (m_rt == list_first_entry(&bus->m_rt_list,
struct sdw_master_runtime,
bus_node)) {
group->rates[group->count++] = rate;
} else {
ret = sdw_add_element_group_count(group, rate);
if (ret < 0) {
kfree(group->rates);
return ret;
}
}
}
return ret;
}
/**
* sdw_compute_port_params: Compute transport and port parameters
*
* @bus: SDW Bus instance
*/
static int sdw_compute_port_params(struct sdw_bus *bus)
{
struct sdw_group_params *params = NULL;
struct sdw_group group;
int ret;
ret = sdw_get_group_count(bus, &group);
if (ret < 0)
return ret;
if (group.count == 0)
goto out;
params = kcalloc(group.count, sizeof(*params), GFP_KERNEL);
if (!params) {
ret = -ENOMEM;
goto out;
}
/* Compute transport parameters for grouped streams */
ret = sdw_compute_group_params(bus, params,
&group.rates[0], group.count);
if (ret < 0)
goto free_params;
_sdw_compute_port_params(bus, params, group.count);
free_params:
kfree(params);
out:
kfree(group.rates);
return ret;
}
static int sdw_select_row_col(struct sdw_bus *bus, int clk_freq)
{
struct sdw_master_prop *prop = &bus->prop;
int frame_int, frame_freq;
int r, c;
for (c = 0; c < SDW_FRAME_COLS; c++) {
for (r = 0; r < SDW_FRAME_ROWS; r++) {
if (sdw_rows[r] != prop->default_row ||
sdw_cols[c] != prop->default_col)
continue;
frame_int = sdw_rows[r] * sdw_cols[c];
frame_freq = clk_freq / frame_int;
if ((clk_freq - (frame_freq * SDW_FRAME_CTRL_BITS)) <
bus->params.bandwidth)
continue;
bus->params.row = sdw_rows[r];
bus->params.col = sdw_cols[c];
return 0;
}
}
return -EINVAL;
}
/**
* sdw_compute_bus_params: Compute bus parameters
*
* @bus: SDW Bus instance
*/
static int sdw_compute_bus_params(struct sdw_bus *bus)
{
unsigned int max_dr_freq, curr_dr_freq = 0;
struct sdw_master_prop *mstr_prop = &bus->prop;
int i, clk_values, ret;
bool is_gear = false;
u32 *clk_buf;
if (mstr_prop->num_clk_gears) {
clk_values = mstr_prop->num_clk_gears;
clk_buf = mstr_prop->clk_gears;
is_gear = true;
} else if (mstr_prop->num_clk_freq) {
clk_values = mstr_prop->num_clk_freq;
clk_buf = mstr_prop->clk_freq;
} else {
clk_values = 1;
clk_buf = NULL;
}
max_dr_freq = mstr_prop->max_clk_freq * SDW_DOUBLE_RATE_FACTOR;
for (i = 0; i < clk_values; i++) {
if (!clk_buf)
curr_dr_freq = max_dr_freq;
else
curr_dr_freq = (is_gear) ?
(max_dr_freq >> clk_buf[i]) :
clk_buf[i] * SDW_DOUBLE_RATE_FACTOR;
if (curr_dr_freq <= bus->params.bandwidth)
continue;
break;
/*
* TODO: Check all the Slave(s) port(s) audio modes and find
* whether given clock rate is supported with glitchless
* transition.
*/
}
if (i == clk_values)
return -EINVAL;
ret = sdw_select_row_col(bus, curr_dr_freq);
if (ret < 0)
return -EINVAL;
bus->params.curr_dr_freq = curr_dr_freq;
return 0;
}
/**
* sdw_compute_params: Compute bus, transport and port parameters
*
* @bus: SDW Bus instance
*/
int sdw_compute_params(struct sdw_bus *bus)
{
int ret;
/* Computes clock frequency, frame shape and frame frequency */
ret = sdw_compute_bus_params(bus);
if (ret < 0) {
dev_err(bus->dev, "Compute bus params failed: %d", ret);
return ret;
}
/* Compute transport and port params */
ret = sdw_compute_port_params(bus);
if (ret < 0) {
dev_err(bus->dev, "Compute transport params failed: %d", ret);
return ret;
}
return 0;
}
EXPORT_SYMBOL(sdw_compute_params);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("SoundWire Generic Bandwidth Allocation");

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,8 @@
* @dev: device implementing hw_params and free callbacks
* @shim_lock: mutex to handle access to shared SHIM registers
* @shim_mask: global pointer to check SHIM register initialization
* @clock_stop_quirks: mask defining requested behavior on pm_suspend
* @link_mask: global mask needed for power-up/down sequences
* @cdns: Cadence master descriptor
* @list: used to walk-through all masters exposed by the same controller
*/
@ -31,6 +33,8 @@ struct sdw_intel_link_res {
struct device *dev;
struct mutex *shim_lock; /* protect shared registers */
u32 *shim_mask;
u32 clock_stop_quirks;
u32 link_mask;
struct sdw_cdns *cdns;
struct list_head list;
};

View File

@ -13,6 +13,7 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/soundwire/sdw_intel.h>
#include "cadence_master.h"
#include "intel.h"
@ -68,8 +69,13 @@ static int sdw_intel_cleanup(struct sdw_intel_ctx *ctx)
if (!(link_mask & BIT(i)))
continue;
if (link->pdev)
if (link->pdev) {
pm_runtime_disable(&link->pdev->dev);
platform_device_unregister(link->pdev);
}
if (!link->clock_stop_quirks)
pm_runtime_put_noidle(link->dev);
}
return 0;
@ -246,8 +252,10 @@ static struct sdw_intel_ctx
link->ops = res->ops;
link->dev = res->dev;
link->clock_stop_quirks = res->clock_stop_quirks;
link->shim_lock = &ctx->shim_lock;
link->shim_mask = &ctx->shim_mask;
link->link_mask = link_mask;
memset(&pdevinfo, 0, sizeof(pdevinfo));
@ -334,6 +342,16 @@ sdw_intel_startup_controller(struct sdw_intel_ctx *ctx)
continue;
intel_master_startup(link->pdev);
if (!link->clock_stop_quirks) {
/*
* we need to prevent the parent PCI device
* from entering pm_runtime suspend, so that
* power rails to the SoundWire IP are not
* turned off.
*/
pm_runtime_get_noresume(link->dev);
}
}
return 0;
@ -365,7 +383,7 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
* Name(_ADR, 0x40000000), with bits 31..28 representing the
* SoundWire link so filter accordingly
*/
if ((adr & GENMASK(31, 28)) >> 28 != SDW_LINK_TYPE)
if (FIELD_GET(GENMASK(31, 28), adr) != SDW_LINK_TYPE)
return AE_OK; /* keep going */
/* device found, stop namespace walk */

View File

@ -154,6 +154,7 @@ int sdw_master_device_add(struct sdw_bus *bus, struct device *parent,
bus->dev = &md->dev;
bus->md = md;
pm_runtime_enable(&bus->md->dev);
device_register_err:
return ret;
}
@ -166,6 +167,7 @@ device_register_err:
*/
int sdw_master_device_del(struct sdw_bus *bus)
{
pm_runtime_disable(&bus->md->dev);
device_unregister(bus->dev);
return 0;

View File

@ -289,7 +289,7 @@ int sdw_slave_read_prop(struct sdw_slave *slave)
struct sdw_slave_prop *prop = &slave->prop;
struct device *dev = &slave->dev;
struct fwnode_handle *port;
int num_of_ports, nval, i, dp0 = 0;
int nval;
device_property_read_u32(dev, "mipi-sdw-sw-interface-revision",
&prop->mipi_revision);
@ -352,7 +352,6 @@ int sdw_slave_read_prop(struct sdw_slave *slave)
return -ENOMEM;
sdw_slave_read_dp0(slave, port, prop->dp0_prop);
dp0 = 1;
}
/*
@ -383,21 +382,6 @@ int sdw_slave_read_prop(struct sdw_slave *slave)
sdw_slave_read_dpn(slave, prop->sink_dpn_prop, nval,
prop->sink_ports, "sink");
/* some ports are bidirectional so check total ports by ORing */
nval = prop->source_ports | prop->sink_ports;
num_of_ports = hweight32(nval) + dp0; /* add DP0 */
/* Allocate port_ready based on num_of_ports */
slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports,
sizeof(*slave->port_ready),
GFP_KERNEL);
if (!slave->port_ready)
return -ENOMEM;
/* Initialize completion */
for (i = 0; i < num_of_ports; i++)
init_completion(&slave->port_ready[i]);
return 0;
}
EXPORT_SYMBOL(sdw_slave_read_prop);

View File

@ -34,6 +34,7 @@
#define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED BIT(10)
#define SWRM_INTERRUPT_MASK_ADDR 0x204
#define SWRM_INTERRUPT_CLEAR 0x208
#define SWRM_INTERRUPT_CPU_EN 0x210
#define SWRM_CMD_FIFO_WR_CMD 0x300
#define SWRM_CMD_FIFO_RD_CMD 0x304
#define SWRM_CMD_FIFO_CMD 0x308
@ -43,19 +44,17 @@
#define SWRM_CMD_FIFO_RD_FIFO_ADDR 0x318
#define SWRM_ENUMERATOR_CFG_ADDR 0x500
#define SWRM_MCP_FRAME_CTRL_BANK_ADDR(m) (0x101C + 0x40 * (m))
#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT 3
#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK GENMASK(2, 0)
#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK GENMASK(7, 3)
#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT 0
#define SWRM_MCP_CFG_ADDR 0x1048
#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK GENMASK(21, 17)
#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT 0x11
#define SWRM_DEF_CMD_NO_PINGS 0x1f
#define SWRM_MCP_STATUS 0x104C
#define SWRM_MCP_STATUS_BANK_NUM_MASK BIT(0)
#define SWRM_MCP_SLV_STATUS 0x1090
#define SWRM_MCP_SLV_STATUS_MASK GENMASK(1, 0)
#define SWRM_DP_PORT_CTRL_BANK(n, m) (0x1124 + 0x100 * (n - 1) + 0x40 * m)
#define SWRM_DP_BLOCK_CTRL3_BANK(n, m) (0x1138 + 0x100 * (n - 1) + 0x40 * m)
#define SWRM_DP_PORT_CTRL_EN_CHAN_SHFT 0x18
#define SWRM_DP_PORT_CTRL_OFFSET2_SHFT 0x10
#define SWRM_DP_PORT_CTRL_OFFSET1_SHFT 0x08
@ -67,11 +66,6 @@
#define SWRM_REG_VAL_PACK(data, dev, id, reg) \
((reg) | ((id) << 16) | ((dev) << 20) | ((data) << 24))
#define SWRM_MAX_ROW_VAL 0 /* Rows = 48 */
#define SWRM_DEFAULT_ROWS 48
#define SWRM_MIN_COL_VAL 0 /* Cols = 2 */
#define SWRM_DEFAULT_COL 16
#define SWRM_MAX_COL_VAL 7
#define SWRM_SPECIAL_CMD_ID 0xF
#define MAX_FREQ_NUM 1
#define TIMEOUT_MS (2 * HZ)
@ -84,12 +78,14 @@ struct qcom_swrm_port_config {
u8 si;
u8 off1;
u8 off2;
u8 bp_mode;
};
struct qcom_swrm_ctrl {
struct sdw_bus bus;
struct device *dev;
struct regmap *regmap;
void __iomem *mmio;
struct completion *comp;
struct work_struct slave_work;
/* read/write lock */
@ -103,6 +99,8 @@ struct qcom_swrm_ctrl {
unsigned int version;
int num_din_ports;
int num_dout_ports;
int cols_index;
int rows_index;
unsigned long dout_port_mask;
unsigned long din_port_mask;
struct qcom_swrm_port_config pconfig[QCOM_SDW_MAX_PORTS];
@ -112,9 +110,24 @@ struct qcom_swrm_ctrl {
int (*reg_write)(struct qcom_swrm_ctrl *ctrl, int reg, int val);
};
struct qcom_swrm_data {
u32 default_cols;
u32 default_rows;
};
static struct qcom_swrm_data swrm_v1_3_data = {
.default_rows = 48,
.default_cols = 16,
};
static struct qcom_swrm_data swrm_v1_5_data = {
.default_rows = 50,
.default_cols = 16,
};
#define to_qcom_sdw(b) container_of(b, struct qcom_swrm_ctrl, bus)
static int qcom_swrm_abh_reg_read(struct qcom_swrm_ctrl *ctrl, int reg,
static int qcom_swrm_ahb_reg_read(struct qcom_swrm_ctrl *ctrl, int reg,
u32 *val)
{
struct regmap *wcd_regmap = ctrl->regmap;
@ -154,6 +167,20 @@ static int qcom_swrm_ahb_reg_write(struct qcom_swrm_ctrl *ctrl,
return SDW_CMD_OK;
}
static int qcom_swrm_cpu_reg_read(struct qcom_swrm_ctrl *ctrl, int reg,
u32 *val)
{
*val = readl(ctrl->mmio + reg);
return SDW_CMD_OK;
}
static int qcom_swrm_cpu_reg_write(struct qcom_swrm_ctrl *ctrl, int reg,
int val)
{
writel(val, ctrl->mmio + reg);
return SDW_CMD_OK;
}
static int qcom_swrm_cmd_fifo_wr_cmd(struct qcom_swrm_ctrl *ctrl, u8 cmd_data,
u8 dev_addr, u16 reg_addr)
{
@ -284,8 +311,8 @@ static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl)
u32 val;
/* Clear Rows and Cols */
val = (SWRM_MAX_ROW_VAL << SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT |
SWRM_MIN_COL_VAL << SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT);
val = FIELD_PREP(SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK, ctrl->rows_index);
val |= FIELD_PREP(SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK, ctrl->cols_index);
ctrl->reg_write(ctrl, SWRM_MCP_FRAME_CTRL_BANK_ADDR(0), val);
@ -298,9 +325,7 @@ static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl)
/* Configure No pings */
ctrl->reg_read(ctrl, SWRM_MCP_CFG_ADDR, &val);
val &= ~SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK;
val |= (SWRM_DEF_CMD_NO_PINGS <<
SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT);
u32p_replace_bits(&val, SWRM_DEF_CMD_NO_PINGS, SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK);
ctrl->reg_write(ctrl, SWRM_MCP_CFG_ADDR, val);
/* Configure number of retries of a read/write cmd */
@ -310,6 +335,12 @@ static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl)
ctrl->reg_write(ctrl, SWRM_COMP_CFG_ADDR,
SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK |
SWRM_COMP_CFG_ENABLE_MSK);
/* enable CPU IRQs */
if (ctrl->mmio) {
ctrl->reg_write(ctrl, SWRM_INTERRUPT_CPU_EN,
SWRM_INTERRUPT_STATUS_RMSK);
}
return 0;
}
@ -355,11 +386,8 @@ static int qcom_swrm_pre_bank_switch(struct sdw_bus *bus)
ctrl->reg_read(ctrl, reg, &val);
val &= ~SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK;
val &= ~SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK;
val |= (SWRM_MAX_ROW_VAL << SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT |
SWRM_MAX_COL_VAL << SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT);
u32p_replace_bits(&val, ctrl->cols_index, SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK);
u32p_replace_bits(&val, ctrl->rows_index, SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK);
return ctrl->reg_write(ctrl, reg, val);
}
@ -378,14 +406,22 @@ static int qcom_swrm_transport_params(struct sdw_bus *bus,
{
struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus);
u32 value;
int reg = SWRM_DP_PORT_CTRL_BANK((params->port_num), bank);
int ret;
value = params->offset1 << SWRM_DP_PORT_CTRL_OFFSET1_SHFT;
value |= params->offset2 << SWRM_DP_PORT_CTRL_OFFSET2_SHFT;
value |= params->sample_interval - 1;
return ctrl->reg_write(ctrl,
SWRM_DP_PORT_CTRL_BANK((params->port_num), bank),
value);
ret = ctrl->reg_write(ctrl, reg, value);
if (!ret && params->blk_pkg_mode) {
reg = SWRM_DP_BLOCK_CTRL3_BANK(params->port_num, bank);
ret = ctrl->reg_write(ctrl, reg, 1);
}
return ret;
}
static int qcom_swrm_port_enable(struct sdw_bus *bus,
@ -433,6 +469,7 @@ static int qcom_swrm_compute_params(struct sdw_bus *bus)
p_rt->transport_params.sample_interval = pcfg->si + 1;
p_rt->transport_params.offset1 = pcfg->off1;
p_rt->transport_params.offset2 = pcfg->off2;
p_rt->transport_params.blk_pkg_mode = pcfg->bp_mode;
}
list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
@ -443,6 +480,7 @@ static int qcom_swrm_compute_params(struct sdw_bus *bus)
pcfg->si + 1;
p_rt->transport_params.offset1 = pcfg->off1;
p_rt->transport_params.offset2 = pcfg->off2;
p_rt->transport_params.blk_pkg_mode = pcfg->bp_mode;
i++;
}
}
@ -689,12 +727,13 @@ static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl)
u8 off1[QCOM_SDW_MAX_PORTS];
u8 off2[QCOM_SDW_MAX_PORTS];
u8 si[QCOM_SDW_MAX_PORTS];
u8 bp_mode[QCOM_SDW_MAX_PORTS] = { 0, };
int i, ret, nports, val;
ctrl->reg_read(ctrl, SWRM_COMP_PARAMS, &val);
ctrl->num_dout_ports = val & SWRM_COMP_PARAMS_DOUT_PORTS_MASK;
ctrl->num_din_ports = (val & SWRM_COMP_PARAMS_DIN_PORTS_MASK) >> 5;
ctrl->num_dout_ports = FIELD_GET(SWRM_COMP_PARAMS_DOUT_PORTS_MASK, val);
ctrl->num_din_ports = FIELD_GET(SWRM_COMP_PARAMS_DIN_PORTS_MASK, val);
ret = of_property_read_u32(np, "qcom,din-ports", &val);
if (ret)
@ -731,10 +770,13 @@ static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl)
if (ret)
return ret;
ret = of_property_read_u8_array(np, "qcom,ports-block-pack-mode",
bp_mode, nports);
for (i = 0; i < nports; i++) {
ctrl->pconfig[i].si = si[i];
ctrl->pconfig[i].off1 = off1[i];
ctrl->pconfig[i].off2 = off2[i];
ctrl->pconfig[i].bp_mode = bp_mode[i];
}
return 0;
@ -746,6 +788,7 @@ static int qcom_swrm_probe(struct platform_device *pdev)
struct sdw_master_prop *prop;
struct sdw_bus_params *params;
struct qcom_swrm_ctrl *ctrl;
const struct qcom_swrm_data *data;
int ret;
u32 val;
@ -753,15 +796,25 @@ static int qcom_swrm_probe(struct platform_device *pdev)
if (!ctrl)
return -ENOMEM;
data = of_device_get_match_data(dev);
ctrl->rows_index = sdw_find_row_index(data->default_rows);
ctrl->cols_index = sdw_find_col_index(data->default_cols);
#if IS_ENABLED(CONFIG_SLIMBUS)
if (dev->parent->bus == &slimbus_bus) {
ctrl->reg_read = qcom_swrm_abh_reg_read;
#else
if (false) {
#endif
ctrl->reg_read = qcom_swrm_ahb_reg_read;
ctrl->reg_write = qcom_swrm_ahb_reg_write;
ctrl->regmap = dev_get_regmap(dev->parent, NULL);
if (!ctrl->regmap)
return -EINVAL;
} else {
/* Only WCD based SoundWire controller is supported */
return -ENOTSUPP;
ctrl->reg_read = qcom_swrm_cpu_reg_read;
ctrl->reg_write = qcom_swrm_cpu_reg_write;
ctrl->mmio = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ctrl->mmio))
return PTR_ERR(ctrl->mmio);
}
ctrl->irq = of_irq_get(dev->of_node, 0);
@ -795,8 +848,8 @@ static int qcom_swrm_probe(struct platform_device *pdev)
params = &ctrl->bus.params;
params->max_dr_freq = DEFAULT_CLK_FREQ;
params->curr_dr_freq = DEFAULT_CLK_FREQ;
params->col = SWRM_DEFAULT_COL;
params->row = SWRM_DEFAULT_ROWS;
params->col = data->default_cols;
params->row = data->default_rows;
ctrl->reg_read(ctrl, SWRM_MCP_STATUS, &val);
params->curr_bank = val & SWRM_MCP_STATUS_BANK_NUM_MASK;
params->next_bank = !params->curr_bank;
@ -806,8 +859,8 @@ static int qcom_swrm_probe(struct platform_device *pdev)
prop->num_clk_gears = 0;
prop->num_clk_freq = MAX_FREQ_NUM;
prop->clk_freq = &qcom_swrm_freq_tbl[0];
prop->default_col = SWRM_DEFAULT_COL;
prop->default_row = SWRM_DEFAULT_ROWS;
prop->default_col = data->default_cols;
prop->default_row = data->default_rows;
ctrl->reg_read(ctrl, SWRM_COMP_HW_VERSION, &ctrl->version);
@ -858,7 +911,8 @@ static int qcom_swrm_remove(struct platform_device *pdev)
}
static const struct of_device_id qcom_swrm_of_match[] = {
{ .compatible = "qcom,soundwire-v1.3.0", },
{ .compatible = "qcom,soundwire-v1.3.0", .data = &swrm_v1_3_data },
{ .compatible = "qcom,soundwire-v1.5.1", .data = &swrm_v1_5_data },
{/* sentinel */},
};

View File

@ -6,6 +6,7 @@
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include "bus.h"
#include "sysfs_local.h"
static void sdw_slave_release(struct device *dev)
{
@ -20,11 +21,12 @@ struct device_type sdw_slave_type = {
.uevent = sdw_slave_uevent,
};
static int sdw_slave_add(struct sdw_bus *bus,
struct sdw_slave_id *id, struct fwnode_handle *fwnode)
int sdw_slave_add(struct sdw_bus *bus,
struct sdw_slave_id *id, struct fwnode_handle *fwnode)
{
struct sdw_slave *slave;
int ret;
int i;
slave = kzalloc(sizeof(*slave), GFP_KERNEL);
if (!slave)
@ -50,6 +52,7 @@ static int sdw_slave_add(struct sdw_bus *bus,
slave->dev.bus = &sdw_bus_type;
slave->dev.of_node = of_node_get(to_of_node(fwnode));
slave->dev.type = &sdw_slave_type;
slave->dev.groups = sdw_slave_status_attr_groups;
slave->bus = bus;
slave->status = SDW_SLAVE_UNATTACHED;
init_completion(&slave->enumeration_complete);
@ -57,6 +60,10 @@ static int sdw_slave_add(struct sdw_bus *bus,
slave->dev_num = 0;
init_completion(&slave->probe_complete);
slave->probed = false;
slave->first_interrupt_done = false;
for (i = 0; i < SDW_MAX_PORTS; i++)
init_completion(&slave->port_ready[i]);
mutex_lock(&bus->bus_lock);
list_add_tail(&slave->node, &bus->slaves);
@ -102,7 +109,7 @@ static bool find_slave(struct sdw_bus *bus,
}
/* Extract link id from ADR, Bit 51 to 48 (included) */
link_id = (addr >> 48) & GENMASK(3, 0);
link_id = SDW_DISCO_LINK_ID(addr);
/* Check for link_id match */
if (link_id != bus->link_id)

View File

@ -25,8 +25,10 @@
int sdw_rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147,
96, 100, 120, 128, 150, 160, 250, 0,
192, 200, 240, 256, 72, 144, 90, 180};
EXPORT_SYMBOL(sdw_rows);
int sdw_cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16};
EXPORT_SYMBOL(sdw_cols);
int sdw_find_col_index(int col)
{
@ -100,9 +102,7 @@ static int _sdw_program_slave_port_params(struct sdw_bus *bus,
return ret;
/* Program DPN_SampleCtrl2 register */
wbuf = (t_params->sample_interval - 1);
wbuf &= SDW_DPN_SAMPLECTRL_HIGH;
wbuf >>= SDW_REG_SHIFT(SDW_DPN_SAMPLECTRL_HIGH);
wbuf = FIELD_GET(SDW_DPN_SAMPLECTRL_HIGH, t_params->sample_interval - 1);
ret = sdw_write(slave, addr3, wbuf);
if (ret < 0) {
@ -111,9 +111,8 @@ static int _sdw_program_slave_port_params(struct sdw_bus *bus,
}
/* Program DPN_HCtrl register */
wbuf = t_params->hstart;
wbuf <<= SDW_REG_SHIFT(SDW_DPN_HCTRL_HSTART);
wbuf |= t_params->hstop;
wbuf = FIELD_PREP(SDW_DPN_HCTRL_HSTART, t_params->hstart);
wbuf |= FIELD_PREP(SDW_DPN_HCTRL_HSTOP, t_params->hstop);
ret = sdw_write(slave, addr4, wbuf);
if (ret < 0)
@ -157,8 +156,8 @@ static int sdw_program_slave_port_params(struct sdw_bus *bus,
}
/* Program DPN_PortCtrl register */
wbuf = p_params->data_mode << SDW_REG_SHIFT(SDW_DPN_PORTCTRL_DATAMODE);
wbuf |= p_params->flow_mode;
wbuf = FIELD_PREP(SDW_DPN_PORTCTRL_DATAMODE, p_params->data_mode);
wbuf |= FIELD_PREP(SDW_DPN_PORTCTRL_FLOWMODE, p_params->flow_mode);
ret = sdw_update(s_rt->slave, addr1, 0xF, wbuf);
if (ret < 0) {
@ -444,7 +443,8 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus,
prep_ch.bank = bus->params.next_bank;
if (dpn_prop->imp_def_interrupts || !dpn_prop->simple_ch_prep_sm)
if (dpn_prop->imp_def_interrupts || !dpn_prop->simple_ch_prep_sm ||
bus->params.s_data_mode != SDW_PORT_DATA_MODE_NORMAL)
intr = true;
/*
@ -689,9 +689,9 @@ static int sdw_bank_switch(struct sdw_bus *bus, int m_rt_count)
/*
* Set the multi_link flag only when both the hardware supports
* and there is a stream handled by multiple masters
* and hardware-based sync is required
*/
multi_link = bus->multi_link && (m_rt_count > 1);
multi_link = bus->multi_link && (m_rt_count >= bus->hw_sync_min_links);
if (multi_link)
ret = sdw_transfer_defer(bus, wr_msg, &bus->defer_msg);
@ -761,13 +761,16 @@ static int do_bank_switch(struct sdw_stream_runtime *stream)
const struct sdw_master_ops *ops;
struct sdw_bus *bus;
bool multi_link = false;
int m_rt_count;
int ret = 0;
m_rt_count = stream->m_rt_count;
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
bus = m_rt->bus;
ops = bus->ops;
if (bus->multi_link) {
if (bus->multi_link && m_rt_count >= bus->hw_sync_min_links) {
multi_link = true;
mutex_lock(&bus->msg_lock);
}
@ -788,7 +791,7 @@ static int do_bank_switch(struct sdw_stream_runtime *stream)
* synchronized across all Masters and happens later as a
* part of post_bank_switch ops.
*/
ret = sdw_bank_switch(bus, stream->m_rt_count);
ret = sdw_bank_switch(bus, m_rt_count);
if (ret < 0) {
dev_err(bus->dev, "Bank switch failed: %d\n", ret);
goto error;
@ -814,7 +817,7 @@ static int do_bank_switch(struct sdw_stream_runtime *stream)
ret);
goto error;
}
} else if (bus->multi_link && stream->m_rt_count > 1) {
} else if (multi_link) {
dev_err(bus->dev,
"Post bank switch ops not implemented\n");
goto error;
@ -832,7 +835,7 @@ static int do_bank_switch(struct sdw_stream_runtime *stream)
goto error;
}
if (bus->multi_link)
if (multi_link)
mutex_unlock(&bus->msg_lock);
}
@ -1784,6 +1787,16 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream)
bus->params.bandwidth -= m_rt->stream->params.rate *
m_rt->ch_count * m_rt->stream->params.bps;
/* Compute params */
if (bus->compute_params) {
ret = bus->compute_params(bus);
if (ret < 0) {
dev_err(bus->dev, "Compute params failed: %d",
ret);
return ret;
}
}
/* Program params */
ret = sdw_program_params(bus, false);
if (ret < 0) {
@ -1913,7 +1926,7 @@ void sdw_shutdown_stream(void *sdw_substream)
sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream);
if (!sdw_stream) {
if (IS_ERR(sdw_stream)) {
dev_err(rtd->dev, "no stream found for DAI %s", dai->name);
return;
}

View File

@ -8,6 +8,10 @@
* SDW sysfs APIs -
*/
/* basic attributes to report status of Slave (attachment, dev_num) */
extern const struct attribute_group *sdw_slave_status_attr_groups[];
/* additional device-managed properties reported after driver probe */
int sdw_slave_sysfs_init(struct sdw_slave *slave);
int sdw_slave_sysfs_dpn_init(struct sdw_slave *slave);

View File

@ -16,9 +16,13 @@
/*
* The sysfs for Slave reflects the MIPI description as given
* in the MIPI DisCo spec
* in the MIPI DisCo spec.
* status and device_number come directly from the MIPI SoundWire
* 1.x specification.
*
* Base file is device
* |---- status
* |---- device_number
* |---- modalias
* |---- dev-properties
* |---- mipi_revision
@ -212,3 +216,55 @@ int sdw_slave_sysfs_init(struct sdw_slave *slave)
return 0;
}
/*
* the status is shown in capital letters for UNATTACHED and RESERVED
* on purpose, to highligh users to the fact that these status values
* are not expected.
*/
static const char *const slave_status[] = {
[SDW_SLAVE_UNATTACHED] = "UNATTACHED",
[SDW_SLAVE_ATTACHED] = "Attached",
[SDW_SLAVE_ALERT] = "Alert",
[SDW_SLAVE_RESERVED] = "RESERVED",
};
static ssize_t status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
return sprintf(buf, "%s\n", slave_status[slave->status]);
}
static DEVICE_ATTR_RO(status);
static ssize_t device_number_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
if (slave->status == SDW_SLAVE_UNATTACHED)
return sprintf(buf, "%s", "N/A");
else
return sprintf(buf, "%d", slave->dev_num);
}
static DEVICE_ATTR_RO(device_number);
static struct attribute *slave_status_attrs[] = {
&dev_attr_status.attr,
&dev_attr_device_number.attr,
NULL,
};
/*
* we don't use ATTRIBUTES_GROUP here since the group is used in a
* separate file and can't be handled as a static.
*/
static const struct attribute_group sdw_slave_status_attr_group = {
.attrs = slave_status_attrs,
};
const struct attribute_group *sdw_slave_status_attr_groups[] = {
&sdw_slave_status_attr_group,
NULL
};

View File

@ -5,6 +5,7 @@
#define __SOUNDWIRE_H
#include <linux/mod_devicetable.h>
#include <linux/bitfield.h>
struct sdw_bus;
struct sdw_slave;
@ -38,7 +39,8 @@ struct sdw_slave;
#define SDW_FRAME_CTRL_BITS 48
#define SDW_MAX_DEVICES 11
#define SDW_VALID_PORT_RANGE(n) ((n) <= 14 && (n) >= 1)
#define SDW_MAX_PORTS 15
#define SDW_VALID_PORT_RANGE(n) ((n) < SDW_MAX_PORTS && (n) >= 1)
enum {
SDW_PORT_DIRN_SINK = 0,
@ -355,6 +357,8 @@ struct sdw_dpn_prop {
* @dp0_prop: Data Port 0 properties
* @src_dpn_prop: Source Data Port N properties
* @sink_dpn_prop: Sink Data Port N properties
* @scp_int1_mask: SCP_INT1_MASK desired settings
* @quirks: bitmask identifying deltas from the MIPI specification
*/
struct sdw_slave_prop {
u32 mipi_revision;
@ -376,8 +380,12 @@ struct sdw_slave_prop {
struct sdw_dp0_prop *dp0_prop;
struct sdw_dpn_prop *src_dpn_prop;
struct sdw_dpn_prop *sink_dpn_prop;
u8 scp_int1_mask;
u32 quirks;
};
#define SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY BIT(0)
/**
* struct sdw_master_prop - Master properties
* @revision: MIPI spec version of the implementation
@ -455,13 +463,19 @@ struct sdw_slave_id {
*
* The MIPI DisCo for SoundWire defines in addition the link_id as bits 51:48
*/
#define SDW_DISCO_LINK_ID_MASK GENMASK_ULL(51, 48)
#define SDW_VERSION_MASK GENMASK_ULL(47, 44)
#define SDW_UNIQUE_ID_MASK GENMASK_ULL(43, 40)
#define SDW_MFG_ID_MASK GENMASK_ULL(39, 24)
#define SDW_PART_ID_MASK GENMASK_ULL(23, 8)
#define SDW_CLASS_ID_MASK GENMASK_ULL(7, 0)
#define SDW_DISCO_LINK_ID(adr) (((adr) >> 48) & GENMASK(3, 0))
#define SDW_VERSION(adr) (((adr) >> 44) & GENMASK(3, 0))
#define SDW_UNIQUE_ID(adr) (((adr) >> 40) & GENMASK(3, 0))
#define SDW_MFG_ID(adr) (((adr) >> 24) & GENMASK(15, 0))
#define SDW_PART_ID(adr) (((adr) >> 8) & GENMASK(15, 0))
#define SDW_CLASS_ID(adr) ((adr) & GENMASK(7, 0))
#define SDW_DISCO_LINK_ID(addr) FIELD_GET(SDW_DISCO_LINK_ID_MASK, addr)
#define SDW_VERSION(addr) FIELD_GET(SDW_VERSION_MASK, addr)
#define SDW_UNIQUE_ID(addr) FIELD_GET(SDW_UNIQUE_ID_MASK, addr)
#define SDW_MFG_ID(addr) FIELD_GET(SDW_MFG_ID_MASK, addr)
#define SDW_PART_ID(addr) FIELD_GET(SDW_PART_ID_MASK, addr)
#define SDW_CLASS_ID(addr) FIELD_GET(SDW_CLASS_ID_MASK, addr)
/**
* struct sdw_slave_intr_status - Slave interrupt status
@ -540,6 +554,10 @@ enum sdw_port_prep_ops {
* @bandwidth: Current bandwidth
* @col: Active columns
* @row: Active rows
* @s_data_mode: NORMAL, STATIC or PRBS mode for all Slave ports
* @m_data_mode: NORMAL, STATIC or PRBS mode for all Master ports. The value
* should be the same to detect transmission issues, but can be different to
* test the interrupt reports
*/
struct sdw_bus_params {
enum sdw_reg_bank curr_bank;
@ -549,6 +567,8 @@ struct sdw_bus_params {
unsigned int bandwidth;
unsigned int col;
unsigned int row;
int s_data_mode;
int m_data_mode;
};
/**
@ -606,6 +626,8 @@ struct sdw_slave_ops {
* between the Master suspending and the codec resuming, and make sure that
* when the Master triggered a reset the Slave is properly enumerated and
* initialized
* @first_interrupt_done: status flag tracking if the interrupt handling
* for a Slave happens for the first time after enumeration
*/
struct sdw_slave {
struct sdw_slave_id id;
@ -618,7 +640,7 @@ struct sdw_slave {
struct dentry *debugfs;
#endif
struct list_head node;
struct completion *port_ready;
struct completion port_ready[SDW_MAX_PORTS];
enum sdw_clk_stop_mode curr_clk_stop_mode;
u16 dev_num;
u16 dev_num_sticky;
@ -627,6 +649,7 @@ struct sdw_slave {
struct completion enumeration_complete;
struct completion initialization_complete;
u32 unattach_request;
bool first_interrupt_done;
};
#define dev_to_sdw_dev(_dev) container_of(_dev, struct sdw_slave, dev)
@ -827,6 +850,11 @@ struct sdw_master_ops {
* @multi_link: Store bus property that indicates if multi links
* are supported. This flag is populated by drivers after reading
* appropriate firmware (ACPI/DT).
* @hw_sync_min_links: Number of links used by a stream above which
* hardware-based synchronization is required. This value is only
* meaningful if multi_link is set. If set to 1, hardware-based
* synchronization will be used even if a stream only uses a single
* SoundWire segment.
*/
struct sdw_bus {
struct device *dev;
@ -850,6 +878,7 @@ struct sdw_bus {
unsigned int clk_stop_timeout;
u32 bank_switch_timeout;
bool multi_link;
int hw_sync_min_links;
};
int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent,
@ -941,6 +970,9 @@ struct sdw_stream_runtime {
struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name);
void sdw_release_stream(struct sdw_stream_runtime *stream);
int sdw_compute_params(struct sdw_bus *bus);
int sdw_stream_add_master(struct sdw_bus *bus,
struct sdw_stream_config *stream_config,
struct sdw_port_config *port_config,

View File

@ -4,13 +4,6 @@
#ifndef __SDW_REGISTERS_H
#define __SDW_REGISTERS_H
/*
* typically we define register and shifts but if one observes carefully,
* the shift can be generated from MASKS using few bit primitaives like ffs
* etc, so we use that and avoid defining shifts
*/
#define SDW_REG_SHIFT(n) (ffs(n) - 1)
/*
* SDW registers as defined by MIPI 1.2 Spec
*/

View File

@ -15,6 +15,7 @@
#include <linux/of.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <linux/soundwire/sdw_registers.h>
#include "max98373.h"
#include "max98373-sdw.h"
@ -282,11 +283,13 @@ static const struct dev_pm_ops max98373_pm = {
static int max98373_read_prop(struct sdw_slave *slave)
{
struct sdw_slave_prop *prop = &slave->prop;
int nval, i, num_of_ports;
int nval, i;
u32 bit;
unsigned long addr;
struct sdw_dpn_prop *dpn;
prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
/* BITMAP: 00001000 Dataport 3 is active */
prop->source_ports = BIT(3);
/* BITMAP: 00000010 Dataport 1 is active */
@ -295,7 +298,6 @@ static int max98373_read_prop(struct sdw_slave *slave)
prop->clk_stop_timeout = 20;
nval = hweight32(prop->source_ports);
num_of_ports = nval;
prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
sizeof(*prop->src_dpn_prop),
GFP_KERNEL);
@ -315,7 +317,6 @@ static int max98373_read_prop(struct sdw_slave *slave)
/* do this again for sink now */
nval = hweight32(prop->sink_ports);
num_of_ports += nval;
prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
sizeof(*prop->sink_dpn_prop),
GFP_KERNEL);
@ -333,17 +334,6 @@ static int max98373_read_prop(struct sdw_slave *slave)
i++;
}
/* Allocate port_ready based on num_of_ports */
slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports,
sizeof(*slave->port_ready),
GFP_KERNEL);
if (!slave->port_ready)
return -ENOMEM;
/* Initialize completion */
for (i = 0; i < num_of_ports; i++)
init_completion(&slave->port_ready[i]);
/* set the timeout values */
prop->clk_stop_timeout = 20;

View File

@ -118,11 +118,14 @@ static int rt1308_clock_config(struct device *dev)
static int rt1308_read_prop(struct sdw_slave *slave)
{
struct sdw_slave_prop *prop = &slave->prop;
int nval, i, num_of_ports = 1;
int nval, i;
u32 bit;
unsigned long addr;
struct sdw_dpn_prop *dpn;
prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
prop->paging_support = true;
/* first we need to allocate memory for set bits in port lists */
@ -131,7 +134,6 @@ static int rt1308_read_prop(struct sdw_slave *slave)
/* for sink */
nval = hweight32(prop->sink_ports);
num_of_ports += nval;
prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
sizeof(*prop->sink_dpn_prop),
GFP_KERNEL);
@ -149,17 +151,6 @@ static int rt1308_read_prop(struct sdw_slave *slave)
i++;
}
/* Allocate port_ready based on num_of_ports */
slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports,
sizeof(*slave->port_ready),
GFP_KERNEL);
if (!slave->port_ready)
return -ENOMEM;
/* Initialize completion */
for (i = 0; i < num_of_ports; i++)
init_completion(&slave->port_ready[i]);
/* set the timeout values */
prop->clk_stop_timeout = 20;

View File

@ -19,6 +19,7 @@
#include <linux/mutex.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <linux/soundwire/sdw_registers.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@ -537,11 +538,15 @@ static int rt5682_update_status(struct sdw_slave *slave,
static int rt5682_read_prop(struct sdw_slave *slave)
{
struct sdw_slave_prop *prop = &slave->prop;
int nval, i, num_of_ports = 1;
int nval, i;
u32 bit;
unsigned long addr;
struct sdw_dpn_prop *dpn;
prop->scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH |
SDW_SCP_INT1_PARITY;
prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
prop->paging_support = false;
/* first we need to allocate memory for set bits in port lists */
@ -549,7 +554,6 @@ static int rt5682_read_prop(struct sdw_slave *slave)
prop->sink_ports = 0x2; /* BITMAP: 00000010 */
nval = hweight32(prop->source_ports);
num_of_ports += nval;
prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
sizeof(*prop->src_dpn_prop),
GFP_KERNEL);
@ -569,7 +573,6 @@ static int rt5682_read_prop(struct sdw_slave *slave)
/* do this again for sink now */
nval = hweight32(prop->sink_ports);
num_of_ports += nval;
prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
sizeof(*prop->sink_dpn_prop),
GFP_KERNEL);
@ -587,17 +590,6 @@ static int rt5682_read_prop(struct sdw_slave *slave)
i++;
}
/* Allocate port_ready based on num_of_ports */
slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports,
sizeof(*slave->port_ready),
GFP_KERNEL);
if (!slave->port_ready)
return -ENOMEM;
/* Initialize completion */
for (i = 0; i < num_of_ports; i++)
init_completion(&slave->port_ready[i]);
/* set the timeout values */
prop->clk_stop_timeout = 20;

View File

@ -11,6 +11,7 @@
#include <linux/mod_devicetable.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
@ -333,11 +334,15 @@ static int rt700_update_status(struct sdw_slave *slave,
static int rt700_read_prop(struct sdw_slave *slave)
{
struct sdw_slave_prop *prop = &slave->prop;
int nval, i, num_of_ports = 1;
int nval, i;
u32 bit;
unsigned long addr;
struct sdw_dpn_prop *dpn;
prop->scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH |
SDW_SCP_INT1_PARITY;
prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
prop->paging_support = false;
/* first we need to allocate memory for set bits in port lists */
@ -345,7 +350,6 @@ static int rt700_read_prop(struct sdw_slave *slave)
prop->sink_ports = 0xA; /* BITMAP: 00001010 */
nval = hweight32(prop->source_ports);
num_of_ports += nval;
prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
sizeof(*prop->src_dpn_prop),
GFP_KERNEL);
@ -365,7 +369,6 @@ static int rt700_read_prop(struct sdw_slave *slave)
/* do this again for sink now */
nval = hweight32(prop->sink_ports);
num_of_ports += nval;
prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
sizeof(*prop->sink_dpn_prop),
GFP_KERNEL);
@ -383,17 +386,6 @@ static int rt700_read_prop(struct sdw_slave *slave)
i++;
}
/* Allocate port_ready based on num_of_ports */
slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports,
sizeof(*slave->port_ready),
GFP_KERNEL);
if (!slave->port_ready)
return -ENOMEM;
/* Initialize completion */
for (i = 0; i < num_of_ports; i++)
init_completion(&slave->port_ready[i]);
/* set the timeout values */
prop->clk_stop_timeout = 20;

View File

@ -11,6 +11,7 @@
#include <linux/mod_devicetable.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
@ -337,11 +338,15 @@ static int rt711_update_status(struct sdw_slave *slave,
static int rt711_read_prop(struct sdw_slave *slave)
{
struct sdw_slave_prop *prop = &slave->prop;
int nval, i, num_of_ports = 1;
int nval, i;
u32 bit;
unsigned long addr;
struct sdw_dpn_prop *dpn;
prop->scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH |
SDW_SCP_INT1_PARITY;
prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
prop->paging_support = false;
/* first we need to allocate memory for set bits in port lists */
@ -349,7 +354,6 @@ static int rt711_read_prop(struct sdw_slave *slave)
prop->sink_ports = 0x8; /* BITMAP: 00001000 */
nval = hweight32(prop->source_ports);
num_of_ports += nval;
prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
sizeof(*prop->src_dpn_prop),
GFP_KERNEL);
@ -369,7 +373,6 @@ static int rt711_read_prop(struct sdw_slave *slave)
/* do this again for sink now */
nval = hweight32(prop->sink_ports);
num_of_ports += nval;
prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
sizeof(*prop->sink_dpn_prop),
GFP_KERNEL);
@ -387,17 +390,6 @@ static int rt711_read_prop(struct sdw_slave *slave)
i++;
}
/* Allocate port_ready based on num_of_ports */
slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports,
sizeof(*slave->port_ready),
GFP_KERNEL);
if (!slave->port_ready)
return -ENOMEM;
/* Initialize completion */
for (i = 0; i < num_of_ports; i++)
init_completion(&slave->port_ready[i]);
/* set the timeout values */
prop->clk_stop_timeout = 20;

View File

@ -12,6 +12,7 @@
#include <linux/mod_devicetable.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>
@ -431,11 +432,15 @@ static int rt715_update_status(struct sdw_slave *slave,
static int rt715_read_prop(struct sdw_slave *slave)
{
struct sdw_slave_prop *prop = &slave->prop;
int nval, i, num_of_ports = 1;
int nval, i;
u32 bit;
unsigned long addr;
struct sdw_dpn_prop *dpn;
prop->scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH |
SDW_SCP_INT1_PARITY;
prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
prop->paging_support = false;
/* first we need to allocate memory for set bits in port lists */
@ -443,7 +448,6 @@ static int rt715_read_prop(struct sdw_slave *slave)
prop->sink_ports = 0x0; /* BITMAP: 00000000 */
nval = hweight32(prop->source_ports);
num_of_ports += nval;
prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
sizeof(*prop->src_dpn_prop),
GFP_KERNEL);
@ -460,36 +464,6 @@ static int rt715_read_prop(struct sdw_slave *slave)
i++;
}
/* do this again for sink now */
nval = hweight32(prop->sink_ports);
num_of_ports += nval;
prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
sizeof(*prop->sink_dpn_prop),
GFP_KERNEL);
if (!prop->sink_dpn_prop)
return -ENOMEM;
dpn = prop->sink_dpn_prop;
i = 0;
addr = prop->sink_ports;
for_each_set_bit(bit, &addr, 32) {
dpn[i].num = bit;
dpn[i].simple_ch_prep_sm = true;
dpn[i].ch_prep_timeout = 10;
i++;
}
/* Allocate port_ready based on num_of_ports */
slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports,
sizeof(*slave->port_ready),
GFP_KERNEL);
if (!slave->port_ready)
return -ENOMEM;
/* Initialize completion */
for (i = 0; i < num_of_ports; i++)
init_completion(&slave->port_ready[i]);
/* set the timeout values */
prop->clk_stop_timeout = 20;

View File

@ -1112,6 +1112,7 @@ static int wsa881x_probe(struct sdw_slave *pdev,
wsa881x->sconfig.type = SDW_STREAM_PDM;
pdev->prop.sink_ports = GENMASK(WSA881X_MAX_SWR_PORTS, 0);
pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop;
pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
gpiod_direction_output(wsa881x->sd_n, 1);
wsa881x->regmap = devm_regmap_init_sdw(pdev, &wsa881x_regmap_config);