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 + - Vinod Koul + +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 #include #include +#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 #include +#include #include #include #include @@ -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 @@ -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"); 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 +#include +#include +#include +#include +#include +#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 +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #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) @@ -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; diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 70637a0383d2..b74c2f144962 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -22,6 +22,10 @@ #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); diff --git a/drivers/soundwire/mipi_disco.c b/drivers/soundwire/mipi_disco.c index 79fee1b21ab6..844e6b22974f 100644 --- a/drivers/soundwire/mipi_disco.c +++ b/drivers/soundwire/mipi_disco.c @@ -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, diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index f39a5815e25d..48a63ca130d2 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -2,6 +2,7 @@ // Copyright(c) 2015-17 Intel Corporation. #include +#include #include #include #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; +} diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index a0476755a459..e69f94a8c3a8 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -21,37 +21,39 @@ * 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"); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index bea46bd8b6ce..ea787201c3ac 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -4,6 +4,8 @@ #ifndef __SOUNDWIRE_H #define __SOUNDWIRE_H +#include + 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, diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h index 4d70da45363d..c9427cb6020b 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -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,