Merge tag 'soundwire-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 22 Sep 2019 17:52:23 +0000 (10:52 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 22 Sep 2019 17:52:23 +0000 (10:52 -0700)
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
  ...

15 files changed:
Documentation/devicetree/bindings/soundwire/soundwire-controller.yaml [new file with mode: 0644]
drivers/soundwire/Makefile
drivers/soundwire/bus.c
drivers/soundwire/bus.h
drivers/soundwire/bus_type.c
drivers/soundwire/cadence_master.c
drivers/soundwire/cadence_master.h
drivers/soundwire/debugfs.c [new file with mode: 0644]
drivers/soundwire/intel.c
drivers/soundwire/intel_init.c
drivers/soundwire/mipi_disco.c
drivers/soundwire/slave.c
drivers/soundwire/stream.c
include/linux/soundwire/sdw.h
include/linux/soundwire/sdw_intel.h

diff --git a/Documentation/devicetree/bindings/soundwire/soundwire-controller.yaml b/Documentation/devicetree/bindings/soundwire/soundwire-controller.yaml
new file mode 100644 (file)
index 0000000..1b43993
--- /dev/null
@@ -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>;
+        };
+    };
+
+...
index 45b7e50016539aaad4242a14a7292ff5c4d7b3a6..563894e5ecaf5098986f992a2bb748f41ff8f51b 100644 (file)
@@ -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
index fe745830a261a7c975dc524f3d6a0c5a0a9e7062..fc53dbe57f8545bcad26f84a63bfe57c14e564ed 100644 (file)
@@ -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 */
index 3048ca153f222b2bdfef7a3383f06a19cc770601..cb482da914da942a9d3bc0e26a4e96d7e87cac36 100644 (file)
@@ -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
index 2655602f0cfb7492ac53345152ff78e9704a3e24..4a465f55039f4b769463531a67dea7c02e21ac08 100644 (file)
@@ -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);
 }
 
index 60e8bdee5c754b83f2170bad9757d5cc5c63aa9d..502ed4ec8f070a7c398f2b5fa959408608e8ac02 100644 (file)
@@ -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>
 #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
 
 #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
 
 #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
 
@@ -223,6 +231,112 @@ static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value)
        return -EAGAIN;
 }
 
+/*
+ * 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
  */
@@ -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");
index fe2af62958b15b9868701988b9ddb1e70bae0b80..0b72b70947352620a79d31d77e483f82e303a8fb 100644 (file)
@@ -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 (file)
index 0000000..fb1140e
--- /dev/null
@@ -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);
+}
index 317873bc0555deafbf4b2e26a44a1e67a59cfe9c..f1e38a293967052831fceaf440030581ded8d5db 100644 (file)
@@ -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 */
 
 /* 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)
@@ -161,6 +169,118 @@ static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask)
        return -EAGAIN;
 }
 
+/*
+ * 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(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
        return ret;
 }
 
+static void intel_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);
+}
+
 static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai,
                                    void *stream, int direction)
 {
@@ -664,14 +809,14 @@ static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai,
 static const struct snd_soc_dai_ops intel_pcm_dai_ops = {
        .hw_params = intel_hw_params,
        .hw_free = intel_hw_free,
-       .shutdown = sdw_cdns_shutdown,
+       .shutdown = intel_shutdown,
        .set_sdw_stream = intel_pcm_set_sdw_stream,
 };
 
 static const struct snd_soc_dai_ops intel_pdm_dai_ops = {
        .hw_params = intel_hw_params,
        .hw_free = intel_hw_free,
-       .shutdown = sdw_cdns_shutdown,
+       .shutdown = intel_shutdown,
        .set_sdw_stream = intel_pdm_set_sdw_stream,
 };
 
@@ -796,21 +941,44 @@ static int intel_register_dai(struct sdw_intel *sdw)
                                          dais, num_dai);
 }
 
+static int sdw_master_read_intel_prop(struct sdw_bus *bus)
+{
+       struct sdw_master_prop *prop = &bus->prop;
+       struct fwnode_handle *link;
+       char name[32];
+       u32 quirk_mask;
+
+       /* Find master handle */
+       snprintf(name, sizeof(name),
+                "mipi-sdw-link-%d-subproperties", bus->link_id);
+
+       link = device_get_named_child_node(bus->dev, name);
+       if (!link) {
+               dev_err(bus->dev, "Master node %s not found\n", name);
+               return -EIO;
+       }
+
+       fwnode_property_read_u32(link,
+                                "intel-sdw-ip-clock",
+                                &prop->mclk_freq);
+
+       fwnode_property_read_u32(link,
+                                "intel-quirk-mask",
+                                &quirk_mask);
+
+       if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE)
+               prop->hw_disabled = true;
+
+       return 0;
+}
+
 static int intel_prop_read(struct sdw_bus *bus)
 {
        /* Initialize with default handler to read all DisCo properties */
        sdw_master_read_prop(bus);
 
-       /* BIOS is not giving some values correctly. So, lets override them */
-       bus->prop.num_clk_freq = 1;
-       bus->prop.clk_freq = devm_kcalloc(bus->dev, bus->prop.num_clk_freq,
-                                         sizeof(*bus->prop.clk_freq),
-                                         GFP_KERNEL);
-       if (!bus->prop.clk_freq)
-               return -ENOMEM;
-
-       bus->prop.clk_freq[0] = bus->prop.max_clk_freq;
-       bus->prop.err_threshold = 5;
+       /* read Intel-specific properties */
+       sdw_master_read_intel_prop(bus);
 
        return 0;
 }
@@ -861,6 +1029,12 @@ static int intel_probe(struct platform_device *pdev)
                goto err_master_reg;
        }
 
+       if (sdw->cdns.bus.prop.hw_disabled) {
+               dev_info(&pdev->dev, "SoundWire master %d is disabled, ignoring\n",
+                        sdw->cdns.bus.link_id);
+               return 0;
+       }
+
        /* Initialize shim and controller */
        intel_link_power_up(sdw);
        intel_shim_init(sdw);
@@ -896,6 +1070,8 @@ static int intel_probe(struct platform_device *pdev)
                goto err_dai;
        }
 
+       intel_debugfs_init(sdw);
+
        return 0;
 
 err_dai:
@@ -912,8 +1088,11 @@ static int intel_remove(struct platform_device *pdev)
 
        sdw = platform_get_drvdata(pdev);
 
-       free_irq(sdw->res->irq, sdw);
-       snd_soc_unregister_component(sdw->cdns.dev);
+       if (!sdw->cdns.bus.prop.hw_disabled) {
+               intel_debugfs_exit(sdw);
+               free_irq(sdw->res->irq, sdw);
+               snd_soc_unregister_component(sdw->cdns.dev);
+       }
        sdw_delete_bus_master(&sdw->cdns.bus);
 
        return 0;
index 70637a0383d2a1e75f06e82ec4d68ff32ee168b4..b74c2f14496218d8d515caf65cf3333a23170e59 100644 (file)
 #define SDW_LINK_BASE          0x30000
 #define SDW_LINK_SIZE          0x10000
 
+static int link_mask;
+module_param_named(sdw_link_mask, link_mask, int, 0444);
+MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)");
+
 struct sdw_link_data {
        struct sdw_intel_link_res res;
        struct platform_device *pdev;
@@ -111,6 +115,13 @@ static struct sdw_intel_ctx
 
        /* Create SDW Master devices */
        for (i = 0; i < count; i++) {
+               if (link_mask && !(link_mask & BIT(i))) {
+                       dev_dbg(&adev->dev,
+                               "Link %d masked, will not be enabled\n", i);
+                       link++;
+                       continue;
+               }
+
                link->res.irq = res->irq;
                link->res.registers = res->mmio_base + SDW_LINK_BASE
                                        + (SDW_LINK_SIZE * i);
index 79fee1b21ab6e62a8a4a47dd21ba0041a4379508..844e6b22974ffbfc1eb970a18fd751b748187d4a 100644 (file)
@@ -60,8 +60,7 @@ int sdw_master_read_prop(struct sdw_bus *bus)
                                 "mipi-sdw-max-clock-frequency",
                                 &prop->max_clk_freq);
 
-       nval = fwnode_property_read_u32_array(link,
-                       "mipi-sdw-clock-frequencies-supported", NULL, 0);
+       nval = fwnode_property_count_u32(link, "mipi-sdw-clock-frequencies-supported");
        if (nval > 0) {
                prop->num_clk_freq = nval;
                prop->clk_freq = devm_kcalloc(bus->dev, prop->num_clk_freq,
@@ -87,8 +86,7 @@ int sdw_master_read_prop(struct sdw_bus *bus)
                }
        }
 
-       nval = fwnode_property_read_u32_array(link,
-                       "mipi-sdw-supported-clock-gears", NULL, 0);
+       nval = fwnode_property_count_u32(link, "mipi-sdw-supported-clock-gears");
        if (nval > 0) {
                prop->num_clk_gears = nval;
                prop->clk_gears = devm_kcalloc(bus->dev, prop->num_clk_gears,
@@ -134,8 +132,7 @@ static int sdw_slave_read_dp0(struct sdw_slave *slave,
        fwnode_property_read_u32(port, "mipi-sdw-port-min-wordlength",
                                 &dp0->min_word);
 
-       nval = fwnode_property_read_u32_array(port,
-                       "mipi-sdw-port-wordlength-configs", NULL, 0);
+       nval = fwnode_property_count_u32(port, "mipi-sdw-port-wordlength-configs");
        if (nval > 0) {
 
                dp0->num_words = nval;
@@ -193,8 +190,7 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave,
                fwnode_property_read_u32(node, "mipi-sdw-port-min-wordlength",
                                         &dpn[i].min_word);
 
-               nval = fwnode_property_read_u32_array(node,
-                               "mipi-sdw-port-wordlength-configs", NULL, 0);
+               nval = fwnode_property_count_u32(node, "mipi-sdw-port-wordlength-configs");
                if (nval > 0) {
                        dpn[i].num_words = nval;
                        dpn[i].words = devm_kcalloc(&slave->dev,
@@ -233,8 +229,7 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave,
                fwnode_property_read_u32(node, "mipi-sdw-max-channel-number",
                                         &dpn[i].max_ch);
 
-               nval = fwnode_property_read_u32_array(node,
-                               "mipi-sdw-channel-number-list", NULL, 0);
+               nval = fwnode_property_count_u32(node, "mipi-sdw-channel-number-list");
                if (nval > 0) {
                        dpn[i].num_ch = nval;
                        dpn[i].ch = devm_kcalloc(&slave->dev, dpn[i].num_ch,
@@ -248,8 +243,7 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave,
                                        dpn[i].ch, dpn[i].num_ch);
                }
 
-               nval = fwnode_property_read_u32_array(node,
-                               "mipi-sdw-channel-combination-list", NULL, 0);
+               nval = fwnode_property_count_u32(node, "mipi-sdw-channel-combination-list");
                if (nval > 0) {
                        dpn[i].num_ch_combinations = nval;
                        dpn[i].ch_combinations = devm_kcalloc(&slave->dev,
index f39a5815e25dcf4b98e8cc270659d7968d20c71f..48a63ca130d2b2db4ccce74193f76e71c27f571c 100644 (file)
@@ -2,6 +2,7 @@
 // Copyright(c) 2015-17 Intel Corporation.
 
 #include <linux/acpi.h>
+#include <linux/of.h>
 #include <linux/soundwire/sdw.h>
 #include <linux/soundwire/sdw_type.h>
 #include "bus.h"
@@ -35,6 +36,7 @@ static int sdw_slave_add(struct sdw_bus *bus,
 
        slave->dev.release = sdw_slave_release;
        slave->dev.bus = &sdw_bus_type;
+       slave->dev.of_node = of_node_get(to_of_node(fwnode));
        slave->bus = bus;
        slave->status = SDW_SLAVE_UNATTACHED;
        slave->dev_num = 0;
@@ -56,6 +58,7 @@ static int sdw_slave_add(struct sdw_bus *bus,
                mutex_unlock(&bus->bus_lock);
                put_device(&slave->dev);
        }
+       sdw_slave_debugfs_init(slave);
 
        return ret;
 }
@@ -112,3 +115,53 @@ int sdw_acpi_find_slaves(struct sdw_bus *bus)
 }
 
 #endif
+
+/*
+ * sdw_of_find_slaves() - Find Slave devices in master device tree node
+ * @bus: SDW bus instance
+ *
+ * Scans Master DT node for SDW child Slave devices and registers it.
+ */
+int sdw_of_find_slaves(struct sdw_bus *bus)
+{
+       struct device *dev = bus->dev;
+       struct device_node *node;
+
+       for_each_child_of_node(bus->dev->of_node, node) {
+               int link_id, sdw_version, ret, len;
+               const char *compat = NULL;
+               struct sdw_slave_id id;
+               const __be32 *addr;
+
+               compat = of_get_property(node, "compatible", NULL);
+               if (!compat)
+                       continue;
+
+               ret = sscanf(compat, "sdw%01x%04hx%04hx%02hhx", &sdw_version,
+                            &id.mfg_id, &id.part_id, &id.class_id);
+
+               if (ret != 4) {
+                       dev_err(dev, "Invalid compatible string found %s\n",
+                               compat);
+                       continue;
+               }
+
+               addr = of_get_property(node, "reg", &len);
+               if (!addr || (len < 2 * sizeof(u32))) {
+                       dev_err(dev, "Invalid Link and Instance ID\n");
+                       continue;
+               }
+
+               link_id = be32_to_cpup(addr++);
+               id.unique_id = be32_to_cpup(addr);
+               id.sdw_version = sdw_version;
+
+               /* Check for link_id match */
+               if (link_id != bus->link_id)
+                       continue;
+
+               sdw_slave_add(bus, &id, of_fwnode_handle(node));
+       }
+
+       return 0;
+}
index a0476755a4598066512201da2cba4d76981e8b89..e69f94a8c3a865f508ae254671bae129c09b3bc1 100644 (file)
  * The rows are arranged as per the array index value programmed
  * in register. The index 15 has dummy value 0 in order to fill hole.
  */
-int rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147,
+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};
 
-int cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16};
+int sdw_cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16};
 
-static int sdw_find_col_index(int col)
+int sdw_find_col_index(int col)
 {
        int i;
 
        for (i = 0; i < SDW_FRAME_COLS; i++) {
-               if (cols[i] == col)
+               if (sdw_cols[i] == col)
                        return i;
        }
 
        pr_warn("Requested column not found, selecting lowest column no: 2\n");
        return 0;
 }
+EXPORT_SYMBOL(sdw_find_col_index);
 
-static int sdw_find_row_index(int row)
+int sdw_find_row_index(int row)
 {
        int i;
 
        for (i = 0; i < SDW_FRAME_ROWS; i++) {
-               if (rows[i] == row)
+               if (sdw_rows[i] == row)
                        return i;
        }
 
        pr_warn("Requested row not found, selecting lowest row no: 48\n");
        return 0;
 }
+EXPORT_SYMBOL(sdw_find_row_index);
 
 static int _sdw_program_slave_port_params(struct sdw_bus *bus,
                                          struct sdw_slave *slave,
@@ -367,7 +369,7 @@ static int sdw_enable_disable_master_ports(struct sdw_master_runtime *m_rt,
 static int sdw_enable_disable_ports(struct sdw_master_runtime *m_rt, bool en)
 {
        struct sdw_port_runtime *s_port, *m_port;
-       struct sdw_slave_runtime *s_rt = NULL;
+       struct sdw_slave_runtime *s_rt;
        int ret = 0;
 
        /* Enable/Disable Slave port(s) */
@@ -415,7 +417,7 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus,
                                       struct sdw_port_runtime *p_rt,
                                       bool prep)
 {
-       struct completion *port_ready = NULL;
+       struct completion *port_ready;
        struct sdw_dpn_prop *dpn_prop;
        struct sdw_prepare_ch prep_ch;
        unsigned int time_left;
@@ -535,7 +537,7 @@ static int sdw_prep_deprep_master_ports(struct sdw_master_runtime *m_rt,
  */
 static int sdw_prep_deprep_ports(struct sdw_master_runtime *m_rt, bool prep)
 {
-       struct sdw_slave_runtime *s_rt = NULL;
+       struct sdw_slave_runtime *s_rt;
        struct sdw_port_runtime *p_rt;
        int ret = 0;
 
@@ -603,7 +605,7 @@ static int sdw_notify_config(struct sdw_master_runtime *m_rt)
  */
 static int sdw_program_params(struct sdw_bus *bus)
 {
-       struct sdw_master_runtime *m_rt = NULL;
+       struct sdw_master_runtime *m_rt;
        int ret = 0;
 
        list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
@@ -640,8 +642,8 @@ static int sdw_bank_switch(struct sdw_bus *bus, int m_rt_count)
        int col_index, row_index;
        bool multi_link;
        struct sdw_msg *wr_msg;
-       u8 *wbuf = NULL;
-       int ret = 0;
+       u8 *wbuf;
+       int ret;
        u16 addr;
 
        wr_msg = kzalloc(sizeof(*wr_msg), GFP_KERNEL);
@@ -739,9 +741,9 @@ static int sdw_ml_sync_bank_switch(struct sdw_bus *bus)
 
 static int do_bank_switch(struct sdw_stream_runtime *stream)
 {
-       struct sdw_master_runtime *m_rt = NULL;
+       struct sdw_master_runtime *m_rt;
        const struct sdw_master_ops *ops;
-       struct sdw_bus *bus = NULL;
+       struct sdw_bus *bus;
        bool multi_link = false;
        int ret = 0;
 
@@ -863,7 +865,7 @@ EXPORT_SYMBOL(sdw_release_stream);
  * sdw_alloc_stream should be called only once per stream. Typically
  * invoked from ALSA/ASoC machine/platform driver.
  */
-struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name)
+struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name)
 {
        struct sdw_stream_runtime *stream;
 
@@ -884,7 +886,7 @@ static struct sdw_master_runtime
 *sdw_find_master_rt(struct sdw_bus *bus,
                    struct sdw_stream_runtime *stream)
 {
-       struct sdw_master_runtime *m_rt = NULL;
+       struct sdw_master_runtime *m_rt;
 
        /* Retrieve Bus handle if already available */
        list_for_each_entry(m_rt, &stream->master_list, stream_node) {
@@ -953,7 +955,7 @@ static struct sdw_slave_runtime
                    struct sdw_stream_config *stream_config,
                    struct sdw_stream_runtime *stream)
 {
-       struct sdw_slave_runtime *s_rt = NULL;
+       struct sdw_slave_runtime *s_rt;
 
        s_rt = kzalloc(sizeof(*s_rt), GFP_KERNEL);
        if (!s_rt)
@@ -1259,7 +1261,7 @@ int sdw_stream_add_master(struct sdw_bus *bus,
                          unsigned int num_ports,
                          struct sdw_stream_runtime *stream)
 {
-       struct sdw_master_runtime *m_rt = NULL;
+       struct sdw_master_runtime *m_rt;
        int ret;
 
        mutex_lock(&bus->bus_lock);
@@ -1426,7 +1428,7 @@ struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave,
  */
 static void sdw_acquire_bus_lock(struct sdw_stream_runtime *stream)
 {
-       struct sdw_master_runtime *m_rt = NULL;
+       struct sdw_master_runtime *m_rt;
        struct sdw_bus *bus = NULL;
 
        /* Iterate for all Master(s) in Master list */
@@ -1460,9 +1462,9 @@ static void sdw_release_bus_lock(struct sdw_stream_runtime *stream)
 
 static int _sdw_prepare_stream(struct sdw_stream_runtime *stream)
 {
-       struct sdw_master_runtime *m_rt = NULL;
+       struct sdw_master_runtime *m_rt;
        struct sdw_bus *bus = NULL;
-       struct sdw_master_prop *prop = NULL;
+       struct sdw_master_prop *prop;
        struct sdw_bus_params params;
        int ret;
 
@@ -1483,6 +1485,16 @@ static int _sdw_prepare_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);
                if (ret < 0) {
@@ -1491,6 +1503,11 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream)
                }
        }
 
+       if (!bus) {
+               pr_err("Configuration error in %s\n", __func__);
+               return -EINVAL;
+       }
+
        ret = do_bank_switch(stream);
        if (ret < 0) {
                dev_err(bus->dev, "Bank switch failed: %d\n", ret);
@@ -1547,7 +1564,7 @@ EXPORT_SYMBOL(sdw_prepare_stream);
 
 static int _sdw_enable_stream(struct sdw_stream_runtime *stream)
 {
-       struct sdw_master_runtime *m_rt = NULL;
+       struct sdw_master_runtime *m_rt;
        struct sdw_bus *bus = NULL;
        int ret;
 
@@ -1571,6 +1588,11 @@ static int _sdw_enable_stream(struct sdw_stream_runtime *stream)
                }
        }
 
+       if (!bus) {
+               pr_err("Configuration error in %s\n", __func__);
+               return -EINVAL;
+       }
+
        ret = do_bank_switch(stream);
        if (ret < 0) {
                dev_err(bus->dev, "Bank switch failed: %d\n", ret);
@@ -1590,7 +1612,7 @@ static int _sdw_enable_stream(struct sdw_stream_runtime *stream)
  */
 int sdw_enable_stream(struct sdw_stream_runtime *stream)
 {
-       int ret = 0;
+       int ret;
 
        if (!stream) {
                pr_err("SoundWire: Handle not found for stream\n");
@@ -1610,12 +1632,12 @@ EXPORT_SYMBOL(sdw_enable_stream);
 
 static int _sdw_disable_stream(struct sdw_stream_runtime *stream)
 {
-       struct sdw_master_runtime *m_rt = NULL;
-       struct sdw_bus *bus = NULL;
+       struct sdw_master_runtime *m_rt;
        int ret;
 
        list_for_each_entry(m_rt, &stream->master_list, stream_node) {
-               bus = m_rt->bus;
+               struct sdw_bus *bus = m_rt->bus;
+
                /* Disable port(s) */
                ret = sdw_enable_disable_ports(m_rt, false);
                if (ret < 0) {
@@ -1626,7 +1648,8 @@ static int _sdw_disable_stream(struct sdw_stream_runtime *stream)
        stream->state = SDW_STREAM_DISABLED;
 
        list_for_each_entry(m_rt, &stream->master_list, stream_node) {
-               bus = m_rt->bus;
+               struct sdw_bus *bus = m_rt->bus;
+
                /* Program params */
                ret = sdw_program_params(bus);
                if (ret < 0) {
@@ -1635,7 +1658,25 @@ static int _sdw_disable_stream(struct sdw_stream_runtime *stream)
                }
        }
 
-       return do_bank_switch(stream);
+       ret = do_bank_switch(stream);
+       if (ret < 0) {
+               pr_err("Bank switch failed: %d\n", ret);
+               return ret;
+       }
+
+       /* make sure alternate bank (previous current) is also disabled */
+       list_for_each_entry(m_rt, &stream->master_list, stream_node) {
+               struct sdw_bus *bus = m_rt->bus;
+
+               /* Disable port(s) */
+               ret = sdw_enable_disable_ports(m_rt, false);
+               if (ret < 0) {
+                       dev_err(bus->dev, "Disable port(s) failed: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       return 0;
 }
 
 /**
@@ -1647,7 +1688,7 @@ static int _sdw_disable_stream(struct sdw_stream_runtime *stream)
  */
 int sdw_disable_stream(struct sdw_stream_runtime *stream)
 {
-       int ret = 0;
+       int ret;
 
        if (!stream) {
                pr_err("SoundWire: Handle not found for stream\n");
@@ -1667,8 +1708,8 @@ EXPORT_SYMBOL(sdw_disable_stream);
 
 static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream)
 {
-       struct sdw_master_runtime *m_rt = NULL;
-       struct sdw_bus *bus = NULL;
+       struct sdw_master_runtime *m_rt;
+       struct sdw_bus *bus;
        int ret = 0;
 
        list_for_each_entry(m_rt, &stream->master_list, stream_node) {
@@ -1706,7 +1747,7 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream)
  */
 int sdw_deprepare_stream(struct sdw_stream_runtime *stream)
 {
-       int ret = 0;
+       int ret;
 
        if (!stream) {
                pr_err("SoundWire: Handle not found for stream\n");
index bea46bd8b6ce9e90e8e1a5e3d11e5f59e06ab144..ea787201c3ac4f049a2ea76f0dcdc42a118a21e5 100644 (file)
@@ -4,6 +4,8 @@
 #ifndef __SOUNDWIRE_H
 #define __SOUNDWIRE_H
 
+#include <linux/mod_devicetable.h>
+
 struct sdw_bus;
 struct sdw_slave;
 
@@ -377,6 +379,8 @@ struct sdw_slave_prop {
  * @dynamic_frame: Dynamic frame shape supported
  * @err_threshold: Number of times that software may retry sending a single
  * command
+ * @mclk_freq: clock reference passed to SoundWire Master, in Hz.
+ * @hw_disabled: if true, the Master is not functional, typically due to pin-mux
  */
 struct sdw_master_prop {
        u32 revision;
@@ -391,6 +395,8 @@ struct sdw_master_prop {
        u32 default_col;
        bool dynamic_frame;
        u32 err_threshold;
+       u32 mclk_freq;
+       bool hw_disabled;
 };
 
 int sdw_master_read_prop(struct sdw_bus *bus);
@@ -538,6 +544,7 @@ struct sdw_slave_ops {
  * @bus: Bus handle
  * @ops: Slave callback ops
  * @prop: Slave properties
+ * @debugfs: Slave debugfs
  * @node: node for bus list
  * @port_ready: Port ready completion flag for each Slave port
  * @dev_num: Device Number assigned by Bus
@@ -549,6 +556,9 @@ struct sdw_slave {
        struct sdw_bus *bus;
        const struct sdw_slave_ops *ops;
        struct sdw_slave_prop prop;
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *debugfs;
+#endif
        struct list_head node;
        struct completion *port_ready;
        u16 dev_num;
@@ -718,6 +728,7 @@ struct sdw_master_ops {
  * Bit set implies used number, bit clear implies unused number.
  * @bus_lock: bus lock
  * @msg_lock: message lock
+ * @compute_params: points to Bus resource management implementation
  * @ops: Master callback ops
  * @port_ops: Master port callback ops
  * @params: Current bus parameters
@@ -725,6 +736,7 @@ struct sdw_master_ops {
  * @m_rt_list: List of Master instance of all stream(s) running on Bus. This
  * is used to compute and program bus bandwidth, clock, frame shape,
  * transport and port parameters
+ * @debugfs: Bus debugfs
  * @defer_msg: Defer message
  * @clk_stop_timeout: Clock stop timeout computed
  * @bank_switch_timeout: Bank switch timeout computed
@@ -739,11 +751,15 @@ struct sdw_bus {
        DECLARE_BITMAP(assigned, SDW_MAX_DEVICES);
        struct mutex bus_lock;
        struct mutex msg_lock;
+       int (*compute_params)(struct sdw_bus *bus);
        const struct sdw_master_ops *ops;
        const struct sdw_master_port_ops *port_ops;
        struct sdw_bus_params params;
        struct sdw_master_prop prop;
        struct list_head m_rt_list;
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *debugfs;
+#endif
        struct sdw_defer defer_msg;
        unsigned int clk_stop_timeout;
        u32 bank_switch_timeout;
@@ -828,7 +844,7 @@ struct sdw_stream_params {
  * @m_rt_count: Count of Master runtime(s) in this stream
  */
 struct sdw_stream_runtime {
-       char *name;
+       const char *name;
        struct sdw_stream_params params;
        enum sdw_stream_state state;
        enum sdw_stream_type type;
@@ -836,7 +852,7 @@ struct sdw_stream_runtime {
        int m_rt_count;
 };
 
-struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name);
+struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name);
 void sdw_release_stream(struct sdw_stream_runtime *stream);
 int sdw_stream_add_master(struct sdw_bus *bus,
                struct sdw_stream_config *stream_config,
index 4d70da45363d69aaac1bd3dc358409159c3cbc23..c9427cb6020b3edcb3f41e6ad482e44c79670c59 100644 (file)
@@ -8,6 +8,7 @@
  * struct sdw_intel_ops: Intel audio driver callback ops
  *
  * @config_stream: configure the stream with the hw_params
+ * the first argument containing the context is mandatory
  */
 struct sdw_intel_ops {
        int (*config_stream)(void *arg, void *substream,