mirror of https://gitee.com/openkylin/linux.git
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 ...
This commit is contained in:
commit
4cb1a880e7
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state)
|
|||
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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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, ¶ms[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
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 @@ int sdw_master_device_add(struct sdw_bus *bus, struct device *parent,
|
|||
*/
|
||||
int sdw_master_device_del(struct sdw_bus *bus)
|
||||
{
|
||||
pm_runtime_disable(&bus->md->dev);
|
||||
device_unregister(bus->dev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */},
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue