summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-09-22 10:52:23 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2019-09-22 10:52:23 -0700
commit8d7ead5c69dfa928322777c532fa0770af319202 (patch)
treef8cec2042575db9e89409cbfc253aa2ec1c8293a
parente0703556644a531e50b5dc61b9f6ea83af5f6604 (diff)
parentdfcff3f8a5f18a0cfa233522b5647c2e6035fcb5 (diff)
Merge tag 'soundwire-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire
Pull soundwire updates from Vinod Koul: "This includes DT support thanks to Srini and more work done by Intel (Pierre) on improving cadence and intel support. Summary: - Add DT bindings and DT support in core - Add debugfs support for soundwire properties - Improvements on streaming handling to core - Improved handling of Cadence module - More updates and improvements to Intel driver" * tag 'soundwire-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire: (30 commits) soundwire: stream: make stream name a const pointer soundwire: Add compute_params callback soundwire: core: add device tree support for slave devices dt-bindings: soundwire: add slave bindings soundwire: bus: set initial value to port_status soundwire: intel: handle disabled links soundwire: intel: add debugfs register dump soundwire: cadence_master: add debugfs register dump soundwire: add debugfs support soundwire: intel: remove unused variables soundwire: intel: move shutdown() callback and don't export symbol soundwire: cadence_master: add kernel parameter to override interrupt mask soundwire: intel_init: add kernel module parameter to filter out links soundwire: cadence_master: fix divider setting in clock register soundwire: cadence_master: make use of mclk_freq property soundwire: intel: read mclk_freq property from firmware soundwire: add new mclk_freq field for properties soundwire: stream: remove unnecessary variable initializations soundwire: stream: fix disable sequence soundwire: include mod_devicetable.h to avoid compiling warnings ...
-rw-r--r--Documentation/devicetree/bindings/soundwire/soundwire-controller.yaml82
-rw-r--r--drivers/soundwire/Makefile4
-rw-r--r--drivers/soundwire/bus.c20
-rw-r--r--drivers/soundwire/bus.h24
-rw-r--r--drivers/soundwire/bus_type.c3
-rw-r--r--drivers/soundwire/cadence_master.c211
-rw-r--r--drivers/soundwire/cadence_master.h6
-rw-r--r--drivers/soundwire/debugfs.c151
-rw-r--r--drivers/soundwire/intel.c211
-rw-r--r--drivers/soundwire/intel_init.c11
-rw-r--r--drivers/soundwire/mipi_disco.c18
-rw-r--r--drivers/soundwire/slave.c53
-rw-r--r--drivers/soundwire/stream.c105
-rw-r--r--include/linux/soundwire/sdw.h20
-rw-r--r--include/linux/soundwire/sdw_intel.h1
15 files changed, 819 insertions, 101 deletions
diff --git a/Documentation/devicetree/bindings/soundwire/soundwire-controller.yaml b/Documentation/devicetree/bindings/soundwire/soundwire-controller.yaml
new file mode 100644
index 000000000000..1b43993bccdb
--- /dev/null
+++ b/Documentation/devicetree/bindings/soundwire/soundwire-controller.yaml
@@ -0,0 +1,82 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soundwire/soundwire-controller.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: SoundWire Controller Generic Binding
+
+maintainers:
+ - Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+ - Vinod Koul <vkoul@kernel.org>
+
+description: |
+ SoundWire busses can be described with a node for the SoundWire controller
+ device and a set of child nodes for each SoundWire slave on the bus.
+
+properties:
+ $nodename:
+ pattern: "^soundwire(@.*)?$"
+
+ "#address-cells":
+ const: 2
+
+ "#size-cells":
+ const: 0
+
+patternProperties:
+ "^.*@[0-9a-f],[0-9a-f]$":
+ type: object
+
+ properties:
+ compatible:
+ pattern: "^sdw[0-9a-f]{1}[0-9a-f]{4}[0-9a-f]{4}[0-9a-f]{2}$"
+ description: Is the textual representation of SoundWire Enumeration
+ address. compatible string should contain SoundWire Version ID,
+ Manufacturer ID, Part ID and Class ID in order and shall be in
+ lower-case hexadecimal with leading zeroes.
+ Valid sizes of these fields are
+ Version ID is 1 nibble, number '0x1' represents SoundWire 1.0
+ and '0x2' represents SoundWire 1.1 and so on.
+ MFD is 4 nibbles
+ PID is 4 nibbles
+ CID is 2 nibbles
+ More Information on detail of encoding of these fields can be
+ found in MIPI Alliance DisCo & SoundWire 1.0 Specifications.
+
+ reg:
+ maxItems: 1
+ description:
+ Link ID followed by Instance ID of SoundWire Device Address.
+
+ required:
+ - compatible
+ - reg
+
+required:
+ - "#address-cells"
+ - "#size-cells"
+
+examples:
+ - |
+ soundwire@c2d0000 {
+ #address-cells = <2>;
+ #size-cells = <0>;
+ reg = <0x0c2d0000 0x2000>;
+
+ speaker@0,1 {
+ compatible = "sdw10217201000";
+ reg = <0 1>;
+ powerdown-gpios = <&wcdpinctrl 2 0>;
+ #thermal-sensor-cells = <0>;
+ };
+
+ speaker@0,2 {
+ compatible = "sdw10217201000";
+ reg = <0 2>;
+ powerdown-gpios = <&wcdpinctrl 2 0>;
+ #thermal-sensor-cells = <0>;
+ };
+ };
+
+...
diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile
index 45b7e5001653..563894e5ecaf 100644
--- a/drivers/soundwire/Makefile
+++ b/drivers/soundwire/Makefile
@@ -7,6 +7,10 @@
soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o stream.o
obj-$(CONFIG_SOUNDWIRE) += soundwire-bus.o
+ifdef CONFIG_DEBUG_FS
+soundwire-bus-objs += debugfs.o
+endif
+
#Cadence Objs
soundwire-cadence-objs := cadence_master.o
obj-$(CONFIG_SOUNDWIRE_CADENCE) += soundwire-cadence.o
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c
index fe745830a261..fc53dbe57f85 100644
--- a/drivers/soundwire/bus.c
+++ b/drivers/soundwire/bus.c
@@ -49,6 +49,8 @@ int sdw_add_bus_master(struct sdw_bus *bus)
}
}
+ sdw_bus_debugfs_init(bus);
+
/*
* Device numbers in SoundWire are 0 through 15. Enumeration device
* number (0), Broadcast device number (15), Group numbers (12 and
@@ -77,6 +79,8 @@ int sdw_add_bus_master(struct sdw_bus *bus)
*/
if (IS_ENABLED(CONFIG_ACPI) && ACPI_HANDLE(bus->dev))
ret = sdw_acpi_find_slaves(bus);
+ else if (IS_ENABLED(CONFIG_OF) && bus->dev->of_node)
+ ret = sdw_of_find_slaves(bus);
else
ret = -ENOTSUPP; /* No ACPI/DT so error out */
@@ -109,6 +113,8 @@ static int sdw_delete_slave(struct device *dev, void *data)
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct sdw_bus *bus = slave->bus;
+ sdw_slave_debugfs_exit(slave);
+
mutex_lock(&bus->bus_lock);
if (slave->dev_num) /* clear dev_num if assigned */
@@ -130,6 +136,8 @@ static int sdw_delete_slave(struct device *dev, void *data)
void sdw_delete_bus_master(struct sdw_bus *bus)
{
device_for_each_child(bus->dev, NULL, sdw_delete_slave);
+
+ sdw_bus_debugfs_exit(bus);
}
EXPORT_SYMBOL(sdw_delete_bus_master);
@@ -470,7 +478,8 @@ static int sdw_assign_device_num(struct sdw_slave *slave)
ret = sdw_write(slave, SDW_SCP_DEVNUMBER, dev_num);
if (ret < 0) {
- dev_err(&slave->dev, "Program device_num failed: %d\n", ret);
+ dev_err(&slave->dev, "Program device_num %d failed: %d\n",
+ dev_num, ret);
return ret;
}
@@ -527,6 +536,7 @@ static int sdw_program_device_num(struct sdw_bus *bus)
do {
ret = sdw_transfer(bus, &msg);
if (ret == -ENODATA) { /* end of device id reads */
+ dev_dbg(bus->dev, "No more devices to enumerate\n");
ret = 0;
break;
}
@@ -803,7 +813,7 @@ static int sdw_handle_port_interrupt(struct sdw_slave *slave,
static int sdw_handle_slave_alerts(struct sdw_slave *slave)
{
struct sdw_slave_intr_status slave_intr;
- u8 clear = 0, bit, port_status[15];
+ u8 clear = 0, bit, port_status[15] = {0};
int port_num, stat, ret, count = 0;
unsigned long port;
bool slave_notify = false;
@@ -969,9 +979,15 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
int i, ret = 0;
if (status[0] == SDW_SLAVE_ATTACHED) {
+ dev_dbg(bus->dev, "Slave attached, programming device number\n");
ret = sdw_program_device_num(bus);
if (ret)
dev_err(bus->dev, "Slave attach failed: %d\n", ret);
+ /*
+ * programming a device number will have side effects,
+ * so we deal with other devices at a later time
+ */
+ return ret;
}
/* Continue to check other slave statuses */
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h
index 3048ca153f22..cb482da914da 100644
--- a/drivers/soundwire/bus.h
+++ b/drivers/soundwire/bus.h
@@ -15,9 +15,26 @@ static inline int sdw_acpi_find_slaves(struct sdw_bus *bus)
}
#endif
+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);
+#ifdef CONFIG_DEBUG_FS
+void sdw_bus_debugfs_init(struct sdw_bus *bus);
+void sdw_bus_debugfs_exit(struct sdw_bus *bus);
+void sdw_slave_debugfs_init(struct sdw_slave *slave);
+void sdw_slave_debugfs_exit(struct sdw_slave *slave);
+void sdw_debugfs_init(void);
+void sdw_debugfs_exit(void);
+#else
+static inline void sdw_bus_debugfs_init(struct sdw_bus *bus) {}
+static inline void sdw_bus_debugfs_exit(struct sdw_bus *bus) {}
+static inline void sdw_slave_debugfs_init(struct sdw_slave *slave) {}
+static inline void sdw_slave_debugfs_exit(struct sdw_slave *slave) {}
+static inline void sdw_debugfs_init(void) {}
+static inline void sdw_debugfs_exit(void) {}
+#endif
+
enum {
SDW_MSG_FLAG_READ = 0,
SDW_MSG_FLAG_WRITE,
@@ -49,8 +66,11 @@ struct sdw_msg {
#define SDW_DOUBLE_RATE_FACTOR 2
-extern int rows[SDW_FRAME_ROWS];
-extern int cols[SDW_FRAME_COLS];
+extern int sdw_rows[SDW_FRAME_ROWS];
+extern int sdw_cols[SDW_FRAME_COLS];
+
+int sdw_find_row_index(int row);
+int sdw_find_col_index(int col);
/**
* sdw_port_runtime: Runtime port parameters for Master or Slave
diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c
index 2655602f0cfb..4a465f55039f 100644
--- a/drivers/soundwire/bus_type.c
+++ b/drivers/soundwire/bus_type.c
@@ -6,6 +6,7 @@
#include <linux/pm_domain.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
+#include "bus.h"
/**
* sdw_get_device_id - find the matching SoundWire device id
@@ -177,11 +178,13 @@ EXPORT_SYMBOL_GPL(sdw_unregister_driver);
static int __init sdw_bus_init(void)
{
+ sdw_debugfs_init();
return bus_register(&sdw_bus_type);
}
static void __exit sdw_bus_exit(void)
{
+ sdw_debugfs_exit();
bus_unregister(&sdw_bus_type);
}
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c
index 60e8bdee5c75..502ed4ec8f07 100644
--- a/drivers/soundwire/cadence_master.c
+++ b/drivers/soundwire/cadence_master.c
@@ -8,6 +8,7 @@
#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/debugfs.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
@@ -19,6 +20,10 @@
#include "bus.h"
#include "cadence_master.h"
+static int interrupt_mask;
+module_param_named(cnds_mcp_int_mask, interrupt_mask, int, 0444);
+MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask");
+
#define CDNS_MCP_CONFIG 0x0
#define CDNS_MCP_CONFIG_MCMD_RETRY GENMASK(27, 24)
@@ -47,6 +52,8 @@
#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_CONFIG_UPDATE 0x18
#define CDNS_MCP_CONFIG_UPDATE_BIT BIT(0)
@@ -56,6 +63,7 @@
#define CDNS_MCP_SSP_CTRL1 0x28
#define CDNS_MCP_CLK_CTRL0 0x30
#define CDNS_MCP_CLK_CTRL1 0x38
+#define CDNS_MCP_CLK_MCLKD_MASK GENMASK(7, 0)
#define CDNS_MCP_STAT 0x40
@@ -75,9 +83,12 @@
#define CDNS_MCP_INT_DPINT BIT(11)
#define CDNS_MCP_INT_CTRL_CLASH BIT(10)
#define CDNS_MCP_INT_DATA_CLASH BIT(9)
+#define CDNS_MCP_INT_PARITY BIT(8)
#define CDNS_MCP_INT_CMD_ERR BIT(7)
+#define CDNS_MCP_INT_RX_NE BIT(3)
#define CDNS_MCP_INT_RX_WL BIT(2)
#define CDNS_MCP_INT_TXE BIT(1)
+#define CDNS_MCP_INT_TXF BIT(0)
#define CDNS_MCP_INTSET 0x4C
@@ -169,9 +180,6 @@
#define CDNS_PDI_CONFIG_PORT GENMASK(4, 0)
/* Driver defaults */
-
-#define CDNS_DEFAULT_CLK_DIVIDER 0
-#define CDNS_DEFAULT_FRAME_SHAPE 0x30
#define CDNS_DEFAULT_SSP_INTERVAL 0x18
#define CDNS_TX_TIMEOUT 2000
@@ -224,6 +232,112 @@ static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value)
}
/*
+ * debugfs
+ */
+#ifdef CONFIG_DEBUG_FS
+
+#define RD_BUF (2 * PAGE_SIZE)
+
+static ssize_t cdns_sprintf(struct sdw_cdns *cdns,
+ char *buf, size_t pos, unsigned int reg)
+{
+ return scnprintf(buf + pos, RD_BUF - pos,
+ "%4x\t%8x\n", reg, cdns_readl(cdns, reg));
+}
+
+static int cdns_reg_show(struct seq_file *s, void *data)
+{
+ struct sdw_cdns *cdns = s->private;
+ char *buf;
+ ssize_t ret;
+ int num_ports;
+ int i, j;
+
+ buf = kzalloc(RD_BUF, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = scnprintf(buf, RD_BUF, "Register Value\n");
+ ret += scnprintf(buf + ret, RD_BUF - ret, "\nMCP Registers\n");
+ /* 8 MCP registers */
+ for (i = CDNS_MCP_CONFIG; i <= CDNS_MCP_PHYCTRL; i += sizeof(u32))
+ ret += cdns_sprintf(cdns, buf, ret, i);
+
+ ret += scnprintf(buf + ret, RD_BUF - ret,
+ "\nStatus & Intr Registers\n");
+ /* 13 Status & Intr registers (offsets 0x70 and 0x74 not defined) */
+ for (i = CDNS_MCP_STAT; i <= CDNS_MCP_FIFOSTAT; i += sizeof(u32))
+ ret += cdns_sprintf(cdns, buf, ret, i);
+
+ ret += scnprintf(buf + ret, RD_BUF - ret,
+ "\nSSP & Clk ctrl Registers\n");
+ ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL0);
+ ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL1);
+ ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL0);
+ ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL1);
+
+ ret += scnprintf(buf + ret, RD_BUF - ret,
+ "\nDPn B0 Registers\n");
+
+ /*
+ * in sdw_cdns_pdi_init() we filter out the Bulk PDIs,
+ * so the indices need to be corrected again
+ */
+ num_ports = cdns->num_ports + CDNS_PCM_PDI_OFFSET;
+
+ for (i = 0; i < num_ports; i++) {
+ ret += scnprintf(buf + ret, RD_BUF - ret,
+ "\nDP-%d\n", i);
+ for (j = CDNS_DPN_B0_CONFIG(i);
+ j < CDNS_DPN_B0_ASYNC_CTRL(i); j += sizeof(u32))
+ ret += cdns_sprintf(cdns, buf, ret, j);
+ }
+
+ ret += scnprintf(buf + ret, RD_BUF - ret,
+ "\nDPn B1 Registers\n");
+ for (i = 0; i < num_ports; i++) {
+ ret += scnprintf(buf + ret, RD_BUF - ret,
+ "\nDP-%d\n", i);
+
+ for (j = CDNS_DPN_B1_CONFIG(i);
+ j < CDNS_DPN_B1_ASYNC_CTRL(i); j += sizeof(u32))
+ ret += cdns_sprintf(cdns, buf, ret, j);
+ }
+
+ ret += scnprintf(buf + ret, RD_BUF - ret,
+ "\nDPn Control Registers\n");
+ for (i = 0; i < num_ports; i++)
+ ret += cdns_sprintf(cdns, buf, ret,
+ CDNS_PORTCTRL + i * CDNS_PORT_OFFSET);
+
+ ret += scnprintf(buf + ret, RD_BUF - ret,
+ "\nPDIn Config Registers\n");
+
+ /* number of PDI and ports is interchangeable */
+ for (i = 0; i < num_ports; i++)
+ ret += cdns_sprintf(cdns, buf, ret, CDNS_PDI_CONFIG(i));
+
+ seq_printf(s, "%s", buf);
+ kfree(buf);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(cdns_reg);
+
+/**
+ * sdw_cdns_debugfs_init() - Cadence debugfs init
+ * @cdns: Cadence instance
+ * @root: debugfs root
+ */
+void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root)
+{
+ debugfs_create_file("cdns-registers", 0400, root, cdns, &cdns_reg_fops);
+}
+EXPORT_SYMBOL_GPL(sdw_cdns_debugfs_init);
+
+#endif /* CONFIG_DEBUG_FS */
+
+/*
* IO Calls
*/
static enum sdw_command_response
@@ -575,10 +689,14 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id)
}
}
+ if (int_status & CDNS_MCP_INT_PARITY) {
+ /* Parity error detected by Master */
+ dev_err_ratelimited(cdns->dev, "Parity error\n");
+ }
+
if (int_status & CDNS_MCP_INT_CTRL_CLASH) {
/* Slave is driving bit slot during control word */
dev_err_ratelimited(cdns->dev, "Bus clash for control word\n");
- int_status |= CDNS_MCP_INT_CTRL_CLASH;
}
if (int_status & CDNS_MCP_INT_DATA_CLASH) {
@@ -587,7 +705,6 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id)
* ownership of data bits or Slave gone bonkers
*/
dev_err_ratelimited(cdns->dev, "Bus clash for data word\n");
- int_status |= CDNS_MCP_INT_DATA_CLASH;
}
if (int_status & CDNS_MCP_INT_SLAVE_MASK) {
@@ -644,10 +761,26 @@ static int _cdns_enable_interrupt(struct sdw_cdns *cdns)
cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1,
CDNS_MCP_SLAVE_INTMASK1_MASK);
- mask = CDNS_MCP_INT_SLAVE_RSVD | CDNS_MCP_INT_SLAVE_ALERT |
- CDNS_MCP_INT_SLAVE_ATTACH | CDNS_MCP_INT_SLAVE_NATTACH |
- CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH |
- CDNS_MCP_INT_RX_WL | CDNS_MCP_INT_IRQ | CDNS_MCP_INT_DPINT;
+ /* enable detection of all slave state changes */
+ mask = CDNS_MCP_INT_SLAVE_MASK;
+
+ /* enable detection of bus issues */
+ mask |= CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH |
+ CDNS_MCP_INT_PARITY;
+
+ /* no detection of port interrupts for now */
+
+ /* enable detection of RX fifo level */
+ mask |= CDNS_MCP_INT_RX_WL;
+
+ /*
+ * CDNS_MCP_INT_IRQ needs to be set otherwise all previous
+ * settings are irrelevant
+ */
+ mask |= CDNS_MCP_INT_IRQ;
+
+ if (interrupt_mask) /* parameter override */
+ mask = interrupt_mask;
cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
@@ -788,13 +921,30 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
}
EXPORT_SYMBOL(sdw_cdns_pdi_init);
+static u32 cdns_set_initial_frame_shape(int n_rows, int n_cols)
+{
+ u32 val;
+ int c;
+ int r;
+
+ r = sdw_find_row_index(n_rows);
+ c = sdw_find_col_index(n_cols) & CDNS_MCP_FRAME_SHAPE_COL_MASK;
+
+ val = (r << CDNS_MCP_FRAME_SHAPE_ROW_OFFSET) | c;
+
+ return val;
+}
+
/**
* sdw_cdns_init() - Cadence initialization
* @cdns: Cadence instance
*/
int sdw_cdns_init(struct sdw_cdns *cdns)
{
+ struct sdw_bus *bus = &cdns->bus;
+ struct sdw_master_prop *prop = &bus->prop;
u32 val;
+ int divider;
int ret;
/* Exit clock stop */
@@ -806,12 +956,20 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
}
/* Set clock divider */
- val = cdns_readl(cdns, CDNS_MCP_CLK_CTRL0);
- val |= CDNS_DEFAULT_CLK_DIVIDER;
- cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val);
+ divider = (prop->mclk_freq / prop->max_clk_freq) - 1;
- /* Set the default frame shape */
- cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, CDNS_DEFAULT_FRAME_SHAPE);
+ cdns_updatel(cdns, CDNS_MCP_CLK_CTRL0,
+ CDNS_MCP_CLK_MCLKD_MASK, divider);
+ cdns_updatel(cdns, CDNS_MCP_CLK_CTRL1,
+ CDNS_MCP_CLK_MCLKD_MASK, divider);
+
+ /*
+ * Frame shape changes after initialization have to be done
+ * with the bank switch mechanism
+ */
+ val = cdns_set_initial_frame_shape(prop->default_row,
+ prop->default_col);
+ cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, val);
/* Set SSP interval to default value */
cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, CDNS_DEFAULT_SSP_INTERVAL);
@@ -851,8 +1009,9 @@ EXPORT_SYMBOL(sdw_cdns_init);
int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params)
{
+ struct sdw_master_prop *prop = &bus->prop;
struct sdw_cdns *cdns = bus_to_cdns(bus);
- int mcp_clkctrl_off, mcp_clkctrl;
+ int mcp_clkctrl_off;
int divider;
if (!params->curr_dr_freq) {
@@ -860,16 +1019,16 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params)
return -EINVAL;
}
- divider = (params->max_dr_freq / params->curr_dr_freq) - 1;
+ divider = prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR /
+ params->curr_dr_freq;
+ divider--; /* divider is 1/(N+1) */
if (params->next_bank)
mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1;
else
mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0;
- mcp_clkctrl = cdns_readl(cdns, mcp_clkctrl_off);
- mcp_clkctrl |= divider;
- cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl);
+ cdns_updatel(cdns, mcp_clkctrl_off, CDNS_MCP_CLK_MCLKD_MASK, divider);
return 0;
}
@@ -1170,19 +1329,5 @@ int sdw_cdns_alloc_stream(struct sdw_cdns *cdns,
}
EXPORT_SYMBOL(sdw_cdns_alloc_stream);
-void sdw_cdns_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct sdw_cdns_dma_data *dma;
-
- dma = snd_soc_dai_get_dma_data(dai, substream);
- if (!dma)
- return;
-
- snd_soc_dai_set_dma_data(dai, substream, NULL);
- kfree(dma);
-}
-EXPORT_SYMBOL(sdw_cdns_shutdown);
-
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("Cadence Soundwire Library");
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h
index fe2af62958b1..0b72b7094735 100644
--- a/drivers/soundwire/cadence_master.h
+++ b/drivers/soundwire/cadence_master.h
@@ -163,6 +163,10 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
struct sdw_cdns_stream_config config);
int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns);
+#ifdef CONFIG_DEBUG_FS
+void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root);
+#endif
+
int sdw_cdns_get_stream(struct sdw_cdns *cdns,
struct sdw_cdns_streams *stream,
u32 ch, u32 dir);
@@ -172,8 +176,6 @@ int sdw_cdns_alloc_stream(struct sdw_cdns *cdns,
void sdw_cdns_config_stream(struct sdw_cdns *cdns, struct sdw_cdns_port *port,
u32 ch, u32 dir, struct sdw_cdns_pdi *pdi);
-void sdw_cdns_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai);
int sdw_cdns_pcm_set_stream(struct snd_soc_dai *dai,
void *stream, int direction);
int sdw_cdns_pdm_set_stream(struct snd_soc_dai *dai,
diff --git a/drivers/soundwire/debugfs.c b/drivers/soundwire/debugfs.c
new file mode 100644
index 000000000000..fb1140e82b86
--- /dev/null
+++ b/drivers/soundwire/debugfs.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright(c) 2017-2019 Intel Corporation.
+
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/mod_devicetable.h>
+#include <linux/slab.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include "bus.h"
+
+static struct dentry *sdw_debugfs_root;
+
+void sdw_bus_debugfs_init(struct sdw_bus *bus)
+{
+ char name[16];
+
+ if (!sdw_debugfs_root)
+ return;
+
+ /* create the debugfs master-N */
+ snprintf(name, sizeof(name), "master-%d", bus->link_id);
+ bus->debugfs = debugfs_create_dir(name, sdw_debugfs_root);
+}
+
+void sdw_bus_debugfs_exit(struct sdw_bus *bus)
+{
+ debugfs_remove_recursive(bus->debugfs);
+}
+
+#define RD_BUF (3 * PAGE_SIZE)
+
+static ssize_t sdw_sprintf(struct sdw_slave *slave,
+ char *buf, size_t pos, unsigned int reg)
+{
+ int value;
+
+ value = sdw_read(slave, reg);
+
+ if (value < 0)
+ return scnprintf(buf + pos, RD_BUF - pos, "%3x\tXX\n", reg);
+ else
+ return scnprintf(buf + pos, RD_BUF - pos,
+ "%3x\t%2x\n", reg, value);
+}
+
+static int sdw_slave_reg_show(struct seq_file *s_file, void *data)
+{
+ struct sdw_slave *slave = s_file->private;
+ char *buf;
+ ssize_t ret;
+ int i, j;
+
+ buf = kzalloc(RD_BUF, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = scnprintf(buf, RD_BUF, "Register Value\n");
+
+ /* DP0 non-banked registers */
+ ret += scnprintf(buf + ret, RD_BUF - ret, "\nDP0\n");
+ for (i = SDW_DP0_INT; i <= SDW_DP0_PREPARECTRL; i++)
+ ret += sdw_sprintf(slave, buf, ret, i);
+
+ /* DP0 Bank 0 registers */
+ ret += scnprintf(buf + ret, RD_BUF - ret, "Bank0\n");
+ ret += sdw_sprintf(slave, buf, ret, SDW_DP0_CHANNELEN);
+ for (i = SDW_DP0_SAMPLECTRL1; i <= SDW_DP0_LANECTRL; i++)
+ ret += sdw_sprintf(slave, buf, ret, i);
+
+ /* DP0 Bank 1 registers */
+ ret += scnprintf(buf + ret, RD_BUF - ret, "Bank1\n");
+ ret += sdw_sprintf(slave, buf, ret,
+ SDW_DP0_CHANNELEN + SDW_BANK1_OFFSET);
+ for (i = SDW_DP0_SAMPLECTRL1 + SDW_BANK1_OFFSET;
+ i <= SDW_DP0_LANECTRL + SDW_BANK1_OFFSET; i++)
+ ret += sdw_sprintf(slave, buf, ret, i);
+
+ /* SCP registers */
+ ret += scnprintf(buf + ret, RD_BUF - ret, "\nSCP\n");
+ for (i = SDW_SCP_INT1; i <= SDW_SCP_BANKDELAY; i++)
+ ret += sdw_sprintf(slave, buf, ret, i);
+ for (i = SDW_SCP_DEVID_0; i <= SDW_SCP_DEVID_5; i++)
+ ret += sdw_sprintf(slave, buf, ret, i);
+
+ /*
+ * SCP Bank 0/1 registers are read-only and cannot be
+ * retrieved from the Slave. The Master typically keeps track
+ * of the current frame size so the information can be found
+ * in other places
+ */
+
+ /* DP1..14 registers */
+ for (i = 1; SDW_VALID_PORT_RANGE(i); i++) {
+
+ /* DPi registers */
+ ret += scnprintf(buf + ret, RD_BUF - ret, "\nDP%d\n", i);
+ for (j = SDW_DPN_INT(i); j <= SDW_DPN_PREPARECTRL(i); j++)
+ ret += sdw_sprintf(slave, buf, ret, j);
+
+ /* DPi Bank0 registers */
+ ret += scnprintf(buf + ret, RD_BUF - ret, "Bank0\n");
+ for (j = SDW_DPN_CHANNELEN_B0(i);
+ j <= SDW_DPN_LANECTRL_B0(i); j++)
+ ret += sdw_sprintf(slave, buf, ret, j);
+
+ /* DPi Bank1 registers */
+ ret += scnprintf(buf + ret, RD_BUF - ret, "Bank1\n");
+ for (j = SDW_DPN_CHANNELEN_B1(i);
+ j <= SDW_DPN_LANECTRL_B1(i); j++)
+ ret += sdw_sprintf(slave, buf, ret, j);
+ }
+
+ seq_printf(s_file, "%s", buf);
+ kfree(buf);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(sdw_slave_reg);
+
+void sdw_slave_debugfs_init(struct sdw_slave *slave)
+{
+ struct dentry *master;
+ struct dentry *d;
+ char name[32];
+
+ master = slave->bus->debugfs;
+
+ /* create the debugfs slave-name */
+ snprintf(name, sizeof(name), "%s", dev_name(&slave->dev));
+ d = debugfs_create_dir(name, master);
+
+ debugfs_create_file("registers", 0400, d, slave, &sdw_slave_reg_fops);
+
+ slave->debugfs = d;
+}
+
+void sdw_slave_debugfs_exit(struct sdw_slave *slave)
+{
+ debugfs_remove_recursive(slave->debugfs);
+}
+
+void sdw_debugfs_init(void)
+{
+ sdw_debugfs_root = debugfs_create_dir("soundwire", NULL);
+}
+
+void sdw_debugfs_exit(void)
+{
+ debugfs_remove_recursive(sdw_debugfs_root);
+}
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c
index 317873bc0555..f1e38a293967 100644
--- a/drivers/soundwire/intel.c
+++ b/drivers/soundwire/intel.c
@@ -6,6 +6,7 @@
*/
#include <linux/acpi.h>
+#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/interrupt.h>
@@ -16,6 +17,7 @@
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_intel.h>
#include "cadence_master.h"
+#include "bus.h"
#include "intel.h"
/* Intel SHIM Registers Definition */
@@ -83,11 +85,14 @@
/* Intel ALH Register definitions */
#define SDW_ALH_STRMZCFG(x) (0x000 + (0x4 * (x)))
+#define SDW_ALH_NUM_STREAMS 64
#define SDW_ALH_STRMZCFG_DMAT_VAL 0x3
#define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0)
#define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16)
+#define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1)
+
enum intel_pdi_type {
INTEL_PDI_IN = 0,
INTEL_PDI_OUT = 1,
@@ -98,6 +103,9 @@ struct sdw_intel {
struct sdw_cdns cdns;
int instance;
struct sdw_intel_link_res *res;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs;
+#endif
};
#define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns)
@@ -162,6 +170,118 @@ static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask)
}
/*
+ * debugfs
+ */
+#ifdef CONFIG_DEBUG_FS
+
+#define RD_BUF (2 * PAGE_SIZE)
+
+static ssize_t intel_sprintf(void __iomem *mem, bool l,
+ char *buf, size_t pos, unsigned int reg)
+{
+ int value;
+
+ if (l)
+ value = intel_readl(mem, reg);
+ else
+ value = intel_readw(mem, reg);
+
+ return scnprintf(buf + pos, RD_BUF - pos, "%4x\t%4x\n", reg, value);
+}
+
+static int intel_reg_show(struct seq_file *s_file, void *data)
+{
+ struct sdw_intel *sdw = s_file->private;
+ void __iomem *s = sdw->res->shim;
+ void __iomem *a = sdw->res->alh;
+ char *buf;
+ ssize_t ret;
+ int i, j;
+ unsigned int links, reg;
+
+ buf = kzalloc(RD_BUF, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ links = intel_readl(s, SDW_SHIM_LCAP) & GENMASK(2, 0);
+
+ ret = scnprintf(buf, RD_BUF, "Register Value\n");
+ ret += scnprintf(buf + ret, RD_BUF - ret, "\nShim\n");
+
+ for (i = 0; i < links; i++) {
+ reg = SDW_SHIM_LCAP + i * 4;
+ ret += intel_sprintf(s, true, buf, ret, reg);
+ }
+
+ for (i = 0; i < links; i++) {
+ ret += scnprintf(buf + ret, RD_BUF - ret, "\nLink%d\n", i);
+ ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLSCAP(i));
+ ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS0CM(i));
+ ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS1CM(i));
+ ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS2CM(i));
+ ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS3CM(i));
+ ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PCMSCAP(i));
+
+ ret += scnprintf(buf + ret, RD_BUF - ret, "\n PCMSyCH registers\n");
+
+ /*
+ * the value 10 is the number of PDIs. We will need a
+ * cleanup to remove hard-coded Intel configurations
+ * from cadence_master.c
+ */
+ for (j = 0; j < 10; j++) {
+ ret += intel_sprintf(s, false, buf, ret,
+ SDW_SHIM_PCMSYCHM(i, j));
+ ret += intel_sprintf(s, false, buf, ret,
+ SDW_SHIM_PCMSYCHC(i, j));
+ }
+ ret += scnprintf(buf + ret, RD_BUF - ret, "\n PDMSCAP, IOCTL, CTMCTL\n");
+
+ ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PDMSCAP(i));
+ ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_IOCTL(i));
+ ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTMCTL(i));
+ }
+
+ ret += scnprintf(buf + ret, RD_BUF - ret, "\nWake registers\n");
+ ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKEEN);
+ ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKESTS);
+
+ ret += scnprintf(buf + ret, RD_BUF - ret, "\nALH STRMzCFG\n");
+ for (i = 0; i < SDW_ALH_NUM_STREAMS; i++)
+ ret += intel_sprintf(a, true, buf, ret, SDW_ALH_STRMZCFG(i));
+
+ seq_printf(s_file, "%s", buf);
+ kfree(buf);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(intel_reg);
+
+static void intel_debugfs_init(struct sdw_intel *sdw)
+{
+ struct dentry *root = sdw->cdns.bus.debugfs;
+
+ if (!root)
+ return;
+
+ sdw->debugfs = debugfs_create_dir("intel-sdw", root);
+
+ debugfs_create_file("intel-registers", 0400, sdw->debugfs, sdw,
+ &intel_reg_fops);
+
+ sdw_cdns_debugfs_init(&sdw->cdns, sdw->debugfs);
+}
+
+static void intel_debugfs_exit(struct sdw_intel *sdw)
+{
+ debugfs_remove_recursive(sdw->debugfs);
+}
+#else
+static void intel_debugfs_init(struct sdw_intel *sdw) {}
+static void intel_debugfs_exit(struct sdw_intel *sdw) {}
+#endif /* CONFIG_DEBUG_FS */
+
+/*
* shim ops
*/
@@ -289,6 +409,16 @@ intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm)
if (pcm) {
count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num));
+
+ /*
+ * WORKAROUND: on all existing Intel controllers, pdi
+ * number 2 reports channel count as 1 even though it
+ * supports 8 channels. Performing hardcoding for pdi
+ * number 2.
+ */
+ if (pdi_num == 2)
+ count = 7;
+
} else {
count = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id));
count = ((count & SDW_SHIM_PDMSCAP_CPSS) >>
@@ -397,8 +527,10 @@ static int intel_config_stream(struct sdw_intel *sdw,
struct snd_soc_dai *dai,
struct snd_pcm_hw_params *hw_params, int link_id)
{
- if (sdw->res->ops && sdw->res->ops->config_stream)
- return sdw->res->ops->config_stream(sdw->res->arg,
+ struct sdw_intel_link_res *res = sdw->res;
+
+ if (res->ops && res->ops->config_stream && res->arg)
+ return res->ops->config_stream(res->arg,
substream, dai, hw_params, link_id);
return -EIO;
@@ -649,6 +781,19 @@ intel_hw_free