Commit 8d7ead5c authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'soundwire-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire

Pull soundwire updates from Vinod Koul:
 "This includes DT support thanks to Srini and more work done by Intel
  (Pierre) on improving cadence and intel support.

  Summary:

   - Add DT bindings and DT support in core

   - Add debugfs support for soundwire properties

   - Improvements on streaming handling to core

   - Improved handling of Cadence module

   - More updates and improvements to Intel driver"

* tag 'soundwire-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire: (30 commits)
  soundwire: stream: make stream name a const pointer
  soundwire: Add compute_params callback
  soundwire: core: add device tree support for slave devices
  dt-bindings: soundwire: add slave bindings
  soundwire: bus: set initial value to port_status
  soundwire: intel: handle disabled links
  soundwire: intel: add debugfs register dump
  soundwire: cadence_master: add debugfs register dump
  soundwire: add debugfs support
  soundwire: intel: remove unused variables
  soundwire: intel: move shutdown() callback and don't export symbol
  soundwire: cadence_master: add kernel parameter to override interrupt mask
  soundwire: intel_init: add kernel module parameter to filter out links
  soundwire: cadence_master: fix divider setting in clock register
  soundwire: cadence_master: make use of mclk_freq property
  soundwire: intel: read mclk_freq property from firmware
  soundwire: add new mclk_freq field for properties
  soundwire: stream: remove unnecessary variable initializations
  soundwire: stream: fix disable sequence
  soundwire: include mod_devicetable.h to avoid compiling warnings
  ...
parents e0703556 dfcff3f8
# 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>;
};
};
...
...@@ -7,6 +7,10 @@ ...@@ -7,6 +7,10 @@
soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o stream.o soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o stream.o
obj-$(CONFIG_SOUNDWIRE) += soundwire-bus.o obj-$(CONFIG_SOUNDWIRE) += soundwire-bus.o
ifdef CONFIG_DEBUG_FS
soundwire-bus-objs += debugfs.o
endif
#Cadence Objs #Cadence Objs
soundwire-cadence-objs := cadence_master.o soundwire-cadence-objs := cadence_master.o
obj-$(CONFIG_SOUNDWIRE_CADENCE) += soundwire-cadence.o obj-$(CONFIG_SOUNDWIRE_CADENCE) += soundwire-cadence.o
......
...@@ -49,6 +49,8 @@ int sdw_add_bus_master(struct sdw_bus *bus) ...@@ -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 * Device numbers in SoundWire are 0 through 15. Enumeration device
* number (0), Broadcast device number (15), Group numbers (12 and * number (0), Broadcast device number (15), Group numbers (12 and
...@@ -77,6 +79,8 @@ int sdw_add_bus_master(struct sdw_bus *bus) ...@@ -77,6 +79,8 @@ int sdw_add_bus_master(struct sdw_bus *bus)
*/ */
if (IS_ENABLED(CONFIG_ACPI) && ACPI_HANDLE(bus->dev)) if (IS_ENABLED(CONFIG_ACPI) && ACPI_HANDLE(bus->dev))
ret = sdw_acpi_find_slaves(bus); ret = sdw_acpi_find_slaves(bus);
else if (IS_ENABLED(CONFIG_OF) && bus->dev->of_node)
ret = sdw_of_find_slaves(bus);
else else
ret = -ENOTSUPP; /* No ACPI/DT so error out */ ret = -ENOTSUPP; /* No ACPI/DT so error out */
...@@ -109,6 +113,8 @@ static int sdw_delete_slave(struct device *dev, void *data) ...@@ -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_slave *slave = dev_to_sdw_dev(dev);
struct sdw_bus *bus = slave->bus; struct sdw_bus *bus = slave->bus;
sdw_slave_debugfs_exit(slave);
mutex_lock(&bus->bus_lock); mutex_lock(&bus->bus_lock);
if (slave->dev_num) /* clear dev_num if assigned */ if (slave->dev_num) /* clear dev_num if assigned */
...@@ -130,6 +136,8 @@ static int sdw_delete_slave(struct device *dev, void *data) ...@@ -130,6 +136,8 @@ static int sdw_delete_slave(struct device *dev, void *data)
void sdw_delete_bus_master(struct sdw_bus *bus) void sdw_delete_bus_master(struct sdw_bus *bus)
{ {
device_for_each_child(bus->dev, NULL, sdw_delete_slave); device_for_each_child(bus->dev, NULL, sdw_delete_slave);
sdw_bus_debugfs_exit(bus);
} }
EXPORT_SYMBOL(sdw_delete_bus_master); EXPORT_SYMBOL(sdw_delete_bus_master);
...@@ -470,7 +478,8 @@ static int sdw_assign_device_num(struct sdw_slave *slave) ...@@ -470,7 +478,8 @@ static int sdw_assign_device_num(struct sdw_slave *slave)
ret = sdw_write(slave, SDW_SCP_DEVNUMBER, dev_num); ret = sdw_write(slave, SDW_SCP_DEVNUMBER, dev_num);
if (ret < 0) { 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; return ret;
} }
...@@ -527,6 +536,7 @@ static int sdw_program_device_num(struct sdw_bus *bus) ...@@ -527,6 +536,7 @@ static int sdw_program_device_num(struct sdw_bus *bus)
do { do {
ret = sdw_transfer(bus, &msg); ret = sdw_transfer(bus, &msg);
if (ret == -ENODATA) { /* end of device id reads */ if (ret == -ENODATA) { /* end of device id reads */
dev_dbg(bus->dev, "No more devices to enumerate\n");
ret = 0; ret = 0;
break; break;
} }
...@@ -803,7 +813,7 @@ static int sdw_handle_port_interrupt(struct sdw_slave *slave, ...@@ -803,7 +813,7 @@ static int sdw_handle_port_interrupt(struct sdw_slave *slave,
static int sdw_handle_slave_alerts(struct sdw_slave *slave) static int sdw_handle_slave_alerts(struct sdw_slave *slave)
{ {
struct sdw_slave_intr_status slave_intr; 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; int port_num, stat, ret, count = 0;
unsigned long port; unsigned long port;
bool slave_notify = false; bool slave_notify = false;
...@@ -969,9 +979,15 @@ int sdw_handle_slave_status(struct sdw_bus *bus, ...@@ -969,9 +979,15 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
int i, ret = 0; int i, ret = 0;
if (status[0] == SDW_SLAVE_ATTACHED) { if (status[0] == SDW_SLAVE_ATTACHED) {
dev_dbg(bus->dev, "Slave attached, programming device number\n");
ret = sdw_program_device_num(bus); ret = sdw_program_device_num(bus);
if (ret) if (ret)
dev_err(bus->dev, "Slave attach failed: %d\n", 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 */ /* Continue to check other slave statuses */
......
...@@ -15,9 +15,26 @@ static inline int sdw_acpi_find_slaves(struct sdw_bus *bus) ...@@ -15,9 +15,26 @@ static inline int sdw_acpi_find_slaves(struct sdw_bus *bus)
} }
#endif #endif
int sdw_of_find_slaves(struct sdw_bus *bus);
void sdw_extract_slave_id(struct sdw_bus *bus, void sdw_extract_slave_id(struct sdw_bus *bus,
u64 addr, struct sdw_slave_id *id); 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 { enum {
SDW_MSG_FLAG_READ = 0, SDW_MSG_FLAG_READ = 0,
SDW_MSG_FLAG_WRITE, SDW_MSG_FLAG_WRITE,
...@@ -49,8 +66,11 @@ struct sdw_msg { ...@@ -49,8 +66,11 @@ struct sdw_msg {
#define SDW_DOUBLE_RATE_FACTOR 2 #define SDW_DOUBLE_RATE_FACTOR 2
extern int rows[SDW_FRAME_ROWS]; extern int sdw_rows[SDW_FRAME_ROWS];
extern int cols[SDW_FRAME_COLS]; 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 * sdw_port_runtime: Runtime port parameters for Master or Slave
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <linux/pm_domain.h> #include <linux/pm_domain.h>
#include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h> #include <linux/soundwire/sdw_type.h>
#include "bus.h"
/** /**
* sdw_get_device_id - find the matching SoundWire device id * sdw_get_device_id - find the matching SoundWire device id
...@@ -177,11 +178,13 @@ EXPORT_SYMBOL_GPL(sdw_unregister_driver); ...@@ -177,11 +178,13 @@ EXPORT_SYMBOL_GPL(sdw_unregister_driver);
static int __init sdw_bus_init(void) static int __init sdw_bus_init(void)
{ {
sdw_debugfs_init();
return bus_register(&sdw_bus_type); return bus_register(&sdw_bus_type);
} }
static void __exit sdw_bus_exit(void) static void __exit sdw_bus_exit(void)
{ {
sdw_debugfs_exit();
bus_unregister(&sdw_bus_type); bus_unregister(&sdw_bus_type);
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -19,6 +20,10 @@ ...@@ -19,6 +20,10 @@
#include "bus.h" #include "bus.h"
#include "cadence_master.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 0x0
#define CDNS_MCP_CONFIG_MCMD_RETRY GENMASK(27, 24) #define CDNS_MCP_CONFIG_MCMD_RETRY GENMASK(27, 24)
...@@ -47,6 +52,8 @@ ...@@ -47,6 +52,8 @@
#define CDNS_MCP_SSPSTAT 0xC #define CDNS_MCP_SSPSTAT 0xC
#define CDNS_MCP_FRAME_SHAPE 0x10 #define CDNS_MCP_FRAME_SHAPE 0x10
#define CDNS_MCP_FRAME_SHAPE_INIT 0x14 #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 0x18
#define CDNS_MCP_CONFIG_UPDATE_BIT BIT(0) #define CDNS_MCP_CONFIG_UPDATE_BIT BIT(0)
...@@ -56,6 +63,7 @@ ...@@ -56,6 +63,7 @@
#define CDNS_MCP_SSP_CTRL1 0x28 #define CDNS_MCP_SSP_CTRL1 0x28
#define CDNS_MCP_CLK_CTRL0 0x30 #define CDNS_MCP_CLK_CTRL0 0x30
#define CDNS_MCP_CLK_CTRL1 0x38 #define CDNS_MCP_CLK_CTRL1 0x38
#define CDNS_MCP_CLK_MCLKD_MASK GENMASK(7, 0)
#define CDNS_MCP_STAT 0x40 #define CDNS_MCP_STAT 0x40
...@@ -75,9 +83,12 @@ ...@@ -75,9 +83,12 @@
#define CDNS_MCP_INT_DPINT BIT(11) #define CDNS_MCP_INT_DPINT BIT(11)
#define CDNS_MCP_INT_CTRL_CLASH BIT(10) #define CDNS_MCP_INT_CTRL_CLASH BIT(10)
#define CDNS_MCP_INT_DATA_CLASH BIT(9) #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_CMD_ERR BIT(7)
#define CDNS_MCP_INT_RX_NE BIT(3)
#define CDNS_MCP_INT_RX_WL BIT(2) #define CDNS_MCP_INT_RX_WL BIT(2)
#define CDNS_MCP_INT_TXE BIT(1) #define CDNS_MCP_INT_TXE BIT(1)
#define CDNS_MCP_INT_TXF BIT(0)
#define CDNS_MCP_INTSET 0x4C #define CDNS_MCP_INTSET 0x4C
...@@ -169,9 +180,6 @@ ...@@ -169,9 +180,6 @@
#define CDNS_PDI_CONFIG_PORT GENMASK(4, 0) #define CDNS_PDI_CONFIG_PORT GENMASK(4, 0)
/* Driver defaults */ /* Driver defaults */
#define CDNS_DEFAULT_CLK_DIVIDER 0
#define CDNS_DEFAULT_FRAME_SHAPE 0x30
#define CDNS_DEFAULT_SSP_INTERVAL 0x18 #define CDNS_DEFAULT_SSP_INTERVAL 0x18
#define CDNS_TX_TIMEOUT 2000 #define CDNS_TX_TIMEOUT 2000
...@@ -223,6 +231,112 @@ static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value) ...@@ -223,6 +231,112 @@ static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value)
return -EAGAIN; 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 * IO Calls
*/ */
...@@ -575,10 +689,14 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id) ...@@ -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) { if (int_status & CDNS_MCP_INT_CTRL_CLASH) {
/* Slave is driving bit slot during control word */ /* Slave is driving bit slot during control word */
dev_err_ratelimited(cdns->dev, "Bus clash for control word\n"); 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) { if (int_status & CDNS_MCP_INT_DATA_CLASH) {
...@@ -587,7 +705,6 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id) ...@@ -587,7 +705,6 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id)
* ownership of data bits or Slave gone bonkers * ownership of data bits or Slave gone bonkers
*/ */
dev_err_ratelimited(cdns->dev, "Bus clash for data word\n"); 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) { if (int_status & CDNS_MCP_INT_SLAVE_MASK) {
...@@ -644,10 +761,26 @@ static int _cdns_enable_interrupt(struct sdw_cdns *cdns) ...@@ -644,10 +761,26 @@ static int _cdns_enable_interrupt(struct sdw_cdns *cdns)
cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1,
CDNS_MCP_SLAVE_INTMASK1_MASK); CDNS_MCP_SLAVE_INTMASK1_MASK);
mask = CDNS_MCP_INT_SLAVE_RSVD | CDNS_MCP_INT_SLAVE_ALERT | /* enable detection of all slave state changes */
CDNS_MCP_INT_SLAVE_ATTACH | CDNS_MCP_INT_SLAVE_NATTACH | mask = CDNS_MCP_INT_SLAVE_MASK;
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 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); cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
...@@ -788,13 +921,30 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, ...@@ -788,13 +921,30 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
} }
EXPORT_SYMBOL(sdw_cdns_pdi_init); 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 * sdw_cdns_init() - Cadence initialization
* @cdns: Cadence instance * @cdns: Cadence instance
*/ */
int sdw_cdns_init(struct sdw_cdns *cdns) int sdw_cdns_init(struct sdw_cdns *cdns)
{ {
struct sdw_bus *bus = &cdns->bus;
struct sdw_master_prop *prop = &bus->prop;
u32 val; u32 val;
int divider;
int ret; int ret;
/* Exit clock stop */ /* Exit clock stop */
...@@ -806,12 +956,20 @@ int sdw_cdns_init(struct sdw_cdns *cdns) ...@@ -806,12 +956,20 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
} }
/* Set clock divider */ /* Set clock divider */
val = cdns_readl(cdns, CDNS_MCP_CLK_CTRL0); divider = (prop->mclk_freq / prop->max_clk_freq) - 1;
val |= CDNS_DEFAULT_CLK_DIVIDER;
cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val);
/* Set the default frame shape */ cdns_updatel(cdns, CDNS_MCP_CLK_CTRL0,
cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, CDNS_DEFAULT_FRAME_SHAPE); 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 */ /* Set SSP interval to default value */
cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, CDNS_DEFAULT_SSP_INTERVAL); cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, CDNS_DEFAULT_SSP_INTERVAL);
...@@ -851,8 +1009,9 @@ EXPORT_SYMBOL(sdw_cdns_init); ...@@ -851,8 +1009,9 @@ EXPORT_SYMBOL(sdw_cdns_init);
int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) 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); struct sdw_cdns *cdns = bus_to_cdns(bus);
int mcp_clkctrl_off, mcp_clkctrl; int mcp_clkctrl_off;
int divider; int divider;
if (!params->curr_dr_freq) { if (!params->curr_dr_freq) {
...@@ -860,16 +1019,16 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) ...@@ -860,16 +1019,16 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params)
return -EINVAL; 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) if (params->next_bank)
mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1; mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1;
else else
mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0; mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0;
mcp_clkctrl = cdns_readl(cdns, mcp_clkctrl_off); cdns_updatel(cdns, mcp_clkctrl_off, CDNS_MCP_CLK_MCLKD_MASK, divider);
mcp_clkctrl |= divider;
cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl);
return 0; return 0;
} }
...@@ -1170,19 +1329,5 @@ int sdw_cdns_alloc_stream(struct sdw_cdns *cdns, ...@@ -1170,19 +1329,5 @@ int sdw_cdns_alloc_stream(struct sdw_cdns *cdns,
} }
EXPORT_SYMBOL(sdw_cdns_alloc_stream); 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_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("Cadence Soundwire Library"); MODULE_DESCRIPTION("Cadence Soundwire Library");
...@@ -163,6 +163,10 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, ...@@ -163,6 +163,10 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
struct sdw_cdns_stream_config config); struct sdw_cdns_stream_config config);
int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns); 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, int sdw_cdns_get_stream(struct sdw_cdns *cdns,
struct sdw_cdns_streams *stream, struct sdw_cdns_streams *stream,
u32 ch, u32 dir); u32 ch, u32 dir);
...@@ -172,8 +176,6 @@ int sdw_cdns_alloc_stream(struct sdw_cdns *cdns, ...@@ -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, void sdw_cdns_config_stream(struct sdw_cdns *cdns, struct sdw_cdns_port *port,
u32 ch, u32 dir, struct sdw_cdns_pdi *pdi); 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, int sdw_cdns_pcm_set_stream(struct snd_soc_dai *dai,
void *stream, int direction); void *stream, int direction);
int sdw_cdns_pdm_set_stream(struct snd_soc_dai *dai, int sdw_cdns_pdm_set_stream(struct snd_soc_dai *dai,
......
// 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);
}
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
*/ */
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/debugfs.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
...@@ -16,6 +17,7 @@ ...@@ -16,6 +17,7 @@
#include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_intel.h> #include <linux/soundwire/sdw_intel.h>
#include "cadence_master.h" #include "cadence_master.h"
#include "bus.h"
#include "intel.h" #include "intel.h"
/* Intel SHIM Registers Definition */ /* Intel SHIM Registers Definition */
...@@ -83,11 +85,14 @@ ...@@ -83,11 +85,14 @@
/* Intel ALH Register definitions */ /* Intel ALH Register definitions */
#define SDW_ALH_STRMZCFG(x) (0x000 + (0x4 * (x))) #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_VAL 0x3
#define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0) #define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0)
#define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16) #define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16)
#define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1)
enum intel_pdi_type { enum intel_pdi_type {
INTEL_PDI_IN = 0, INTEL_PDI_IN = 0,
INTEL_PDI_OUT = 1, INTEL_PDI_OUT = 1,
...@@ -98,6 +103,9 @@ struct sdw_intel { ...@@ -98,6 +103,9 @@ struct sdw_intel {
struct sdw_cdns cdns; struct sdw_cdns cdns;
int instance; int instance;
struct sdw_intel_link_res *res; 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) #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) ...@@ -161,6 +169,118 @@ static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask)
return -EAGAIN; 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 * shim ops
*/ */
...@@ -289,6 +409,16 @@ intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm) ...@@ -289,6 +409,16 @@ intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm)
if (pcm) { if (pcm) {
count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num)); 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 { } else {
count = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id)); count = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id));
count = ((count & SDW_SHIM_PDMSCAP_CPSS) >> count = ((count & SDW_SHIM_PDMSCAP_CPSS) >>
...@@ -397,8 +527,10 @@ static int intel_config_stream(struct sdw_intel *sdw, ...@@ -397,8 +527,10 @@ static int intel_config_stream(struct sdw_intel *sdw,
struct snd_soc_dai *dai, struct snd_soc_dai *dai,
struct snd_pcm_hw_params *hw_params, int link_id) struct snd_pcm_hw_params *hw_params, int link_id)
{ {
if (sdw->res->ops && sdw->res->ops->config_stream) struct sdw_intel_link_res *res = sdw->res;
return sdw->res->ops->config_stream(sdw->res->arg,
if (res->ops && res->ops->config_stream && res->arg)
return res->ops->config_stream(res->arg,
substream, dai, hw_params, link_id); substream, dai, hw_params, link_id);
return -EIO; return -EIO;
...@@ -649,6 +781,19 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) ...@@ -649,6 +781,19 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
return ret; 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, static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai,
void *stream, int direction) void *stream, int direction)
{ {
...@@ -664,14 +809,14 @@ static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, ...@@ -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 = { static const struct snd_soc_dai_ops intel_pcm_dai_ops = {
.hw_params = intel_hw_params, .hw_params = intel_hw_params,
.hw_free = intel_hw_free, .hw_free = intel_hw_free,
.shutdown = sdw_cdns_shutdown, .shutdown = intel_shutdown,
.set_sdw_stream = intel_pcm_set_sdw_stream, .set_sdw_stream = intel_pcm_set_sdw_stream,
}; };
static const struct snd_soc_dai_ops intel_pdm_dai_ops = { static const struct snd_soc_dai_ops intel_pdm_dai_ops = {
.hw_params = intel_hw_params, .hw_params = intel_hw_params,
.hw_free = intel_hw_free, .hw_free = intel_hw_free,
.shutdown = sdw_cdns_shutdown, .shutdown = intel_shutdown,
.set_sdw_stream = intel_pdm_set_sdw_stream, .set_sdw_stream = intel_pdm_set_sdw_stream,
}; };
...@@ -796,21 +941,44 @@ static int intel_register_dai(struct sdw_intel *sdw) ...@@ -796,21 +941,44 @@ static int intel_register_dai(struct sdw_intel *sdw)
dais, num_dai); 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) static int intel_prop_read(struct sdw_bus *bus)
{ {
/* Initialize with default handler to read all DisCo properties */ /* Initialize with default handler to read all DisCo properties */
sdw_master_read_prop(bus); sdw_master_read_prop(bus);
/* BIOS is not giving some values correctly. So, lets override them */ /* read Intel-specific properties */
bus->prop.num_clk_freq = 1; sdw_master_read_intel_prop(bus);
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;
return 0; return 0;
} }
...@@ -861,6 +1029,12 @@ static int intel_probe(struct platform_device *pdev) ...@@ -861,6 +1029,12 @@ static int intel_probe(struct platform_device *pdev)
goto err_master_reg; 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 */ /* Initialize shim and controller */
intel_link_power_up(sdw); intel_link_power_up(sdw);
intel_shim_init(sdw); intel_shim_init(sdw);
...@@ -896,6 +1070,8 @@ static int intel_probe(struct platform_device *pdev) ...@@ -896,6 +1070,8 @@ static int intel_probe(struct platform_device *pdev)
goto err_dai; goto err_dai;
} }
intel_debugfs_init(sdw);
return 0; return 0;
err_dai: err_dai:
...@@ -912,8 +1088,11 @@ static int intel_remove(struct platform_device *pdev) ...@@ -912,8 +1088,11 @@ static int intel_remove(struct platform_device *pdev)
sdw = platform_get_drvdata(pdev); sdw = platform_get_drvdata(pdev);
if (!sdw->cdns.bus.prop.hw_disabled) {
intel_debugfs_exit(sdw);
free_irq(sdw->res->irq, sdw); free_irq(sdw->res->irq, sdw);
snd_soc_unregister_component(sdw->cdns.dev); snd_soc_unregister_component(sdw->cdns.dev);
}
sdw_delete_bus_master(&sdw->cdns.bus); sdw_delete_bus_master(&sdw->cdns.bus);
return 0; return 0;
......
...@@ -22,6 +22,10 @@ ...@@ -22,6 +22,10 @@
#define SDW_LINK_BASE 0x30000 #define SDW_LINK_BASE 0x30000
#define SDW_LINK_SIZE 0x10000 #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_link_data {
struct sdw_intel_link_res res; struct sdw_intel_link_res res;
struct platform_device *pdev; struct platform_device *pdev;
...@@ -111,6 +115,13 @@ static struct sdw_intel_ctx ...@@ -111,6 +115,13 @@ static struct sdw_intel_ctx
/* Create SDW Master devices */ /* Create SDW Master devices */
for (i = 0; i < count; i++) { 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.irq = res->irq;
link->res.registers = res->mmio_base + SDW_LINK_BASE link->res.registers = res->mmio_base + SDW_LINK_BASE
+ (SDW_LINK_SIZE * i); + (SDW_LINK_SIZE * i);
......
...@@ -60,8 +60,7 @@ int sdw_master_read_prop(struct sdw_bus *bus) ...@@ -60,8 +60,7 @@ int sdw_master_read_prop(struct sdw_bus *bus)
"mipi-sdw-max-clock-frequency", "mipi-sdw-max-clock-frequency",
&prop->max_clk_freq); &prop->max_clk_freq);
nval = fwnode_property_read_u32_array(link, nval = fwnode_property_count_u32(link, "mipi-sdw-clock-frequencies-supported");
"mipi-sdw-clock-frequencies-supported", NULL, 0);
if (nval > 0) { if (nval > 0) {
prop->num_clk_freq = nval; prop->num_clk_freq = nval;
prop->clk_freq = devm_kcalloc(bus->dev, prop->num_clk_freq, prop->clk_freq = devm_kcalloc(bus->dev, prop->num_clk_freq,
...@@ -87,8 +86,7 @@ int sdw_master_read_prop(struct sdw_bus *bus) ...@@ -87,8 +86,7 @@ int sdw_master_read_prop(struct sdw_bus *bus)
} }
} }
nval = fwnode_property_read_u32_array(link, nval = fwnode_property_count_u32(link, "mipi-sdw-supported-clock-gears");
"mipi-sdw-supported-clock-gears", NULL, 0);
if (nval > 0) { if (nval > 0) {
prop->num_clk_gears = nval; prop->num_clk_gears = nval;
prop->clk_gears = devm_kcalloc(bus->dev, prop->num_clk_gears, 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, ...@@ -134,8 +132,7 @@ static int sdw_slave_read_dp0(struct sdw_slave *slave,
fwnode_property_read_u32(port, "mipi-sdw-port-min-wordlength", fwnode_property_read_u32(port, "mipi-sdw-port-min-wordlength",
&dp0->min_word); &dp0->min_word);
nval = fwnode_property_read_u32_array(port, nval = fwnode_property_count_u32(port, "mipi-sdw-port-wordlength-configs");
"mipi-sdw-port-wordlength-configs", NULL, 0);
if (nval > 0) { if (nval > 0) {
dp0->num_words = nval; dp0->num_words = nval;
...@@ -193,8 +190,7 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave, ...@@ -193,8 +190,7 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave,
fwnode_property_read_u32(node, "mipi-sdw-port-min-wordlength", fwnode_property_read_u32(node, "mipi-sdw-port-min-wordlength",
&dpn[i].min_word); &dpn[i].min_word);
nval = fwnode_property_read_u32_array(node, nval = fwnode_property_count_u32(node, "mipi-sdw-port-wordlength-configs");
"mipi-sdw-port-wordlength-configs", NULL, 0);
if (nval > 0) { if (nval > 0) {
dpn[i].num_words = nval; dpn[i].num_words = nval;
dpn[i].words = devm_kcalloc(&slave->dev, dpn[i].words = devm_kcalloc(&slave->dev,
...@@ -233,8 +229,7 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave, ...@@ -233,8 +229,7 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave,
fwnode_property_read_u32(node, "mipi-sdw-max-channel-number", fwnode_property_read_u32(node, "mipi-sdw-max-channel-number",
&dpn[i].max_ch); &dpn[i].max_ch);
nval = fwnode_property_read_u32_array(node, nval = fwnode_property_count_u32(node, "mipi-sdw-channel-number-list");
"mipi-sdw-channel-number-list", NULL, 0);
if (nval > 0) { if (nval > 0) {
dpn[i].num_ch = nval; dpn[i].num_ch = nval;
dpn[i].ch = devm_kcalloc(&slave->dev, dpn[i].num_ch, 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, ...@@ -248,8 +243,7 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave,
dpn[i].ch, dpn[i].num_ch); dpn[i].ch, dpn[i].num_ch);
} }
nval = fwnode_property_read_u32_array(node, nval = fwnode_property_count_u32(node, "mipi-sdw-channel-combination-list");
"mipi-sdw-channel-combination-list", NULL, 0);
if (nval > 0) { if (nval > 0) {
dpn[i].num_ch_combinations = nval; dpn[i].num_ch_combinations = nval;
dpn[i].ch_combinations = devm_kcalloc(&slave->dev, dpn[i].ch_combinations = devm_kcalloc(&slave->dev,
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Copyright(c) 2015-17 Intel Corporation. // Copyright(c) 2015-17 Intel Corporation.
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/of.h>
#include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h> #include <linux/soundwire/sdw_type.h>
#include "bus.h" #include "bus.h"
...@@ -35,6 +36,7 @@ static int sdw_slave_add(struct sdw_bus *bus, ...@@ -35,6 +36,7 @@ static int sdw_slave_add(struct sdw_bus *bus,
slave->dev.release = sdw_slave_release; slave->dev.release = sdw_slave_release;
slave->dev.bus = &sdw_bus_type; slave->dev.bus = &sdw_bus_type;
slave->dev.of_node = of_node_get(to_of_node(fwnode));
slave->bus = bus; slave->bus = bus;
slave->status = SDW_SLAVE_UNATTACHED; slave->status = SDW_SLAVE_UNATTACHED;
slave->dev_num = 0; slave->dev_num = 0;
...@@ -56,6 +58,7 @@ static int sdw_slave_add(struct sdw_bus *bus, ...@@ -56,6 +58,7 @@ static int sdw_slave_add(struct sdw_bus *bus,
mutex_unlock(&bus->bus_lock); mutex_unlock(&bus->bus_lock);
put_device(&slave->dev); put_device(&slave->dev);
} }
sdw_slave_debugfs_init(slave);
return ret; return ret;
} }
...@@ -112,3 +115,53 @@ int sdw_acpi_find_slaves(struct sdw_bus *bus) ...@@ -112,3 +115,53 @@ int sdw_acpi_find_slaves(struct sdw_bus *bus)
} }
#endif #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;
}
...@@ -21,37 +21,39 @@ ...@@ -21,37 +21,39 @@
* The rows are arranged as per the array index value programmed * 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. * 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, 96, 100, 120, 128, 150, 160, 250, 0,
192, 200, 240, 256, 72, 144, 90, 180}; 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; int i;
for (i = 0; i < SDW_FRAME_COLS; i++) { for (i = 0; i < SDW_FRAME_COLS; i++) {
if (cols[i] == col) if (sdw_cols[i] == col)
return i; return i;
} }
pr_warn("Requested column not found, selecting lowest column no: 2\n"); pr_warn("Requested column not found, selecting lowest column no: 2\n");
return 0; 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; int i;
for (i = 0; i < SDW_FRAME_ROWS; i++) { for (i = 0; i < SDW_FRAME_ROWS; i++) {
if (rows[i] == row) if (sdw_rows[i] == row)
return i; return i;
} }
pr_warn("Requested row not found, selecting lowest row no: 48\n"); pr_warn("Requested row not found, selecting lowest row no: 48\n");
return 0; return 0;
} }
EXPORT_SYMBOL(sdw_find_row_index);
static int _sdw_program_slave_port_params(struct sdw_bus *bus, static int _sdw_program_slave_port_params(struct sdw_bus *bus,
struct sdw_slave *slave, struct sdw_slave *slave,
...@@ -367,7 +369,7 @@ static int sdw_enable_disable_master_ports(struct sdw_master_runtime *m_rt, ...@@ -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) static int sdw_enable_disable_ports(struct sdw_master_runtime *m_rt, bool en)
{ {
struct sdw_port_runtime *s_port, *m_port; struct sdw_port_runtime *s_port, *m_port;
struct sdw_slave_runtime *s_rt = NULL; struct sdw_slave_runtime *s_rt;
int ret = 0; int ret = 0;
/* Enable/Disable Slave port(s) */ /* Enable/Disable Slave port(s) */
...@@ -415,7 +417,7 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus, ...@@ -415,7 +417,7 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus,
struct sdw_port_runtime *p_rt, struct sdw_port_runtime *p_rt,
bool prep) bool prep)
{ {
struct completion *port_ready = NULL; struct completion *port_ready;
struct sdw_dpn_prop *dpn_prop; struct sdw_dpn_prop *dpn_prop;
struct sdw_prepare_ch prep_ch; struct sdw_prepare_ch prep_ch;
unsigned int time_left; unsigned int time_left;
...@@ -535,7 +537,7 @@ static int sdw_prep_deprep_master_ports(struct sdw_master_runtime *m_rt, ...@@ -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) 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; struct sdw_port_runtime *p_rt;
int ret = 0; int ret = 0;
...@@ -603,7 +605,7 @@ static int sdw_notify_config(struct sdw_master_runtime *m_rt) ...@@ -603,7 +605,7 @@ static int sdw_notify_config(struct sdw_master_runtime *m_rt)
*/ */
static int sdw_program_params(struct sdw_bus *bus) 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; int ret = 0;
list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { 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) ...@@ -640,8 +642,8 @@ static int sdw_bank_switch(struct sdw_bus *bus, int m_rt_count)
int col_index, row_index; int col_index, row_index;
bool multi_link; bool multi_link;
struct sdw_msg *wr_msg; struct sdw_msg *wr_msg;
u8 *wbuf = NULL; u8 *wbuf;
int ret = 0; int ret;
u16 addr; u16 addr;
wr_msg = kzalloc(sizeof(*wr_msg), GFP_KERNEL); wr_msg = kzalloc(sizeof(*wr_msg), GFP_KERNEL);
...@@ -739,9 +741,9 @@ static int sdw_ml_sync_bank_switch(struct sdw_bus *bus) ...@@ -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) 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; const struct sdw_master_ops *ops;
struct sdw_bus *bus = NULL; struct sdw_bus *bus;
bool multi_link = false; bool multi_link = false;
int ret = 0; int ret = 0;
...@@ -863,7 +865,7 @@ EXPORT_SYMBOL(sdw_release_stream); ...@@ -863,7 +865,7 @@ EXPORT_SYMBOL(sdw_release_stream);
* sdw_alloc_stream should be called only once per stream. Typically * sdw_alloc_stream should be called only once per stream. Typically
* invoked from ALSA/ASoC machine/platform driver. * 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; struct sdw_stream_runtime *stream;
...@@ -884,7 +886,7 @@ static struct sdw_master_runtime ...@@ -884,7 +886,7 @@ static struct sdw_master_runtime
*sdw_find_master_rt(struct sdw_bus *bus, *sdw_find_master_rt(struct sdw_bus *bus,
struct sdw_stream_runtime *stream) struct sdw_stream_runtime *stream)
{ {
struct sdw_master_runtime *m_rt = NULL; struct sdw_master_runtime *m_rt;
/* Retrieve Bus handle if already available */ /* Retrieve Bus handle if already available */
list_for_each_entry(m_rt, &stream->master_list, stream_node) { list_for_each_entry(m_rt, &stream->master_list, stream_node) {
...@@ -953,7 +955,7 @@ static struct sdw_slave_runtime ...@@ -953,7 +955,7 @@ static struct sdw_slave_runtime
struct sdw_stream_config *stream_config, struct sdw_stream_config *stream_config,
struct sdw_stream_runtime *stream) 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); s_rt = kzalloc(sizeof(*s_rt), GFP_KERNEL);
if (!s_rt) if (!s_rt)
...@@ -1259,7 +1261,7 @@ int sdw_stream_add_master(struct sdw_bus *bus, ...@@ -1259,7 +1261,7 @@ int sdw_stream_add_master(struct sdw_bus *bus,
unsigned int num_ports, unsigned int num_ports,
struct sdw_stream_runtime *stream) struct sdw_stream_runtime *stream)
{ {
struct sdw_master_runtime *m_rt = NULL; struct sdw_master_runtime *m_rt;
int ret; int ret;
mutex_lock(&bus->bus_lock); mutex_lock(&bus->bus_lock);
...@@ -1426,7 +1428,7 @@ struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave, ...@@ -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) 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; struct sdw_bus *bus = NULL;
/* Iterate for all Master(s) in Master list */ /* Iterate for all Master(s) in Master list */
...@@ -1460,9 +1462,9 @@ static void sdw_release_bus_lock(struct sdw_stream_runtime *stream) ...@@ -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) 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_bus *bus = NULL;
struct sdw_master_prop *prop = NULL; struct sdw_master_prop *prop;
struct sdw_bus_params params; struct sdw_bus_params params;
int ret; int ret;
...@@ -1483,6 +1485,16 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) ...@@ -1483,6 +1485,16 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream)
bus->params.bandwidth += m_rt->stream->params.rate * bus->params.bandwidth += m_rt->stream->params.rate *
m_rt->ch_count * m_rt->stream->params.bps; 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 */ /* Program params */
ret = sdw_program_params(bus); ret = sdw_program_params(bus);
if (ret < 0) { if (ret < 0) {
...@@ -1491,6 +1503,11 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) ...@@ -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); ret = do_bank_switch(stream);
if (ret < 0) { if (ret < 0) {
dev_err(bus->dev, "Bank switch failed: %d\n", ret); dev_err(bus->dev, "Bank switch failed: %d\n", ret);
...@@ -1547,7 +1564,7 @@ EXPORT_SYMBOL(sdw_prepare_stream); ...@@ -1547,7 +1564,7 @@ EXPORT_SYMBOL(sdw_prepare_stream);
static int _sdw_enable_stream(struct sdw_stream_runtime *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; struct sdw_bus *bus = NULL;
int ret; int ret;
...@@ -1571,6 +1588,11 @@ static int _sdw_enable_stream(struct sdw_stream_runtime *stream) ...@@ -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); ret = do_bank_switch(stream);
if (ret < 0) { if (ret < 0) {
dev_err(bus->dev, "Bank switch failed: %d\n", ret); dev_err(bus->dev, "Bank switch failed: %d\n", ret);
...@@ -1590,7 +1612,7 @@ static int _sdw_enable_stream(struct sdw_stream_runtime *stream) ...@@ -1590,7 +1612,7 @@ static int _sdw_enable_stream(struct sdw_stream_runtime *stream)
*/ */
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) { if (!stream) {
pr_err("SoundWire: Handle not found for stream\n"); pr_err("SoundWire: Handle not found for stream\n");
...@@ -1610,12 +1632,12 @@ EXPORT_SYMBOL(sdw_enable_stream); ...@@ -1610,12 +1632,12 @@ EXPORT_SYMBOL(sdw_enable_stream);
static int _sdw_disable_stream(struct sdw_stream_runtime *stream) static int _sdw_disable_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; int ret;
list_for_each_entry(m_rt, &stream->master_list, stream_node) { 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) */ /* Disable port(s) */
ret = sdw_enable_disable_ports(m_rt, false); ret = sdw_enable_disable_ports(m_rt, false);
if (ret < 0) { if (ret < 0) {
...@@ -1626,7 +1648,8 @@ static int _sdw_disable_stream(struct sdw_stream_runtime *stream) ...@@ -1626,7 +1648,8 @@ static int _sdw_disable_stream(struct sdw_stream_runtime *stream)
stream->state = SDW_STREAM_DISABLED; stream->state = SDW_STREAM_DISABLED;
list_for_each_entry(m_rt, &stream->master_list, stream_node) { list_for_each_entry(m_rt, &stream->master_list, stream_node) {
bus = m_rt->bus; struct sdw_bus *bus = m_rt->bus;
/* Program params */ /* Program params */
ret = sdw_program_params(bus); ret = sdw_program_params(bus);
if (ret < 0) { if (ret < 0) {
...@@ -1635,7 +1658,25 @@ static int _sdw_disable_stream(struct sdw_stream_runtime *stream) ...@@ -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) ...@@ -1647,7 +1688,7 @@ static int _sdw_disable_stream(struct sdw_stream_runtime *stream)
*/ */
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) { if (!stream) {
pr_err("SoundWire: Handle not found for stream\n"); pr_err("SoundWire: Handle not found for stream\n");
...@@ -1667,8 +1708,8 @@ EXPORT_SYMBOL(sdw_disable_stream); ...@@ -1667,8 +1708,8 @@ EXPORT_SYMBOL(sdw_disable_stream);
static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) static int _sdw_deprepare_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_bus *bus;
int ret = 0; int ret = 0;
list_for_each_entry(m_rt, &stream->master_list, stream_node) { 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) ...@@ -1706,7 +1747,7 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream)
*/ */
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) { if (!stream) {
pr_err("SoundWire: Handle not found for stream\n"); pr_err("SoundWire: Handle not found for stream\n");
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#ifndef __SOUNDWIRE_H #ifndef __SOUNDWIRE_H
#define __SOUNDWIRE_H #define __SOUNDWIRE_H
#include <linux/mod_devicetable.h>
struct sdw_bus; struct sdw_bus;
struct sdw_slave; struct sdw_slave;
...@@ -377,6 +379,8 @@ struct sdw_slave_prop { ...@@ -377,6 +379,8 @@ struct sdw_slave_prop {
* @dynamic_frame: Dynamic frame shape supported * @dynamic_frame: Dynamic frame shape supported
* @err_threshold: Number of times that software may retry sending a single * @err_threshold: Number of times that software may retry sending a single
* command * 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 { struct sdw_master_prop {
u32 revision; u32 revision;
...@@ -391,6 +395,8 @@ struct sdw_master_prop { ...@@ -391,6 +395,8 @@ struct sdw_master_prop {
u32 default_col; u32 default_col;
bool dynamic_frame; bool dynamic_frame;
u32 err_threshold; u32 err_threshold;
u32 mclk_freq;
bool hw_disabled;
}; };
int sdw_master_read_prop(struct sdw_bus *bus); int sdw_master_read_prop(struct sdw_bus *bus);
...@@ -538,6 +544,7 @@ struct sdw_slave_ops { ...@@ -538,6 +544,7 @@ struct sdw_slave_ops {
* @bus: Bus handle * @bus: Bus handle
* @ops: Slave callback ops * @ops: Slave callback ops
* @prop: Slave properties * @prop: Slave properties
* @debugfs: Slave debugfs
* @node: node for bus list * @node: node for bus list
* @port_ready: Port ready completion flag for each Slave port * @port_ready: Port ready completion flag for each Slave port
* @dev_num: Device Number assigned by Bus * @dev_num: Device Number assigned by Bus
...@@ -549,6 +556,9 @@ struct sdw_slave { ...@@ -549,6 +556,9 @@ struct sdw_slave {
struct sdw_bus *bus; struct sdw_bus *bus;
const struct sdw_slave_ops *ops; const struct sdw_slave_ops *ops;
struct sdw_slave_prop prop; struct sdw_slave_prop prop;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
#endif
struct list_head node; struct list_head node;
struct completion *port_ready; struct completion *port_ready;
u16 dev_num; u16 dev_num;
...@@ -718,6 +728,7 @@ struct sdw_master_ops { ...@@ -718,6 +728,7 @@ struct sdw_master_ops {
* Bit set implies used number, bit clear implies unused number. * Bit set implies used number, bit clear implies unused number.
* @bus_lock: bus lock * @bus_lock: bus lock
* @msg_lock: message lock * @msg_lock: message lock
* @compute_params: points to Bus resource management implementation
* @ops: Master callback ops * @ops: Master callback ops
* @port_ops: Master port callback ops * @port_ops: Master port callback ops
* @params: Current bus parameters * @params: Current bus parameters
...@@ -725,6 +736,7 @@ struct sdw_master_ops { ...@@ -725,6 +736,7 @@ struct sdw_master_ops {
* @m_rt_list: List of Master instance of all stream(s) running on Bus. This * @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, * is used to compute and program bus bandwidth, clock, frame shape,
* transport and port parameters * transport and port parameters
* @debugfs: Bus debugfs
* @defer_msg: Defer message * @defer_msg: Defer message
* @clk_stop_timeout: Clock stop timeout computed * @clk_stop_timeout: Clock stop timeout computed
* @bank_switch_timeout: Bank switch timeout computed * @bank_switch_timeout: Bank switch timeout computed
...@@ -739,11 +751,15 @@ struct sdw_bus { ...@@ -739,11 +751,15 @@ struct sdw_bus {
DECLARE_BITMAP(assigned, SDW_MAX_DEVICES); DECLARE_BITMAP(assigned, SDW_MAX_DEVICES);
struct mutex bus_lock; struct mutex bus_lock;
struct mutex msg_lock; struct mutex msg_lock;
int (*compute_params)(struct sdw_bus *bus);
const struct sdw_master_ops *ops; const struct sdw_master_ops *ops;
const struct sdw_master_port_ops *port_ops; const struct sdw_master_port_ops *port_ops;
struct sdw_bus_params params; struct sdw_bus_params params;
struct sdw_master_prop prop; struct sdw_master_prop prop;
struct list_head m_rt_list; struct list_head m_rt_list;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
#endif
struct sdw_defer defer_msg; struct sdw_defer defer_msg;
unsigned int clk_stop_timeout; unsigned int clk_stop_timeout;
u32 bank_switch_timeout; u32 bank_switch_timeout;
...@@ -828,7 +844,7 @@ struct sdw_stream_params { ...@@ -828,7 +844,7 @@ struct sdw_stream_params {
* @m_rt_count: Count of Master runtime(s) in this stream * @m_rt_count: Count of Master runtime(s) in this stream
*/ */
struct sdw_stream_runtime { struct sdw_stream_runtime {
char *name; const char *name;
struct sdw_stream_params params; struct sdw_stream_params params;
enum sdw_stream_state state; enum sdw_stream_state state;
enum sdw_stream_type type; enum sdw_stream_type type;
...@@ -836,7 +852,7 @@ struct sdw_stream_runtime { ...@@ -836,7 +852,7 @@ struct sdw_stream_runtime {
int m_rt_count; 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); void sdw_release_stream(struct sdw_stream_runtime *stream);
int sdw_stream_add_master(struct sdw_bus *bus, int sdw_stream_add_master(struct sdw_bus *bus,
struct sdw_stream_config *stream_config, struct sdw_stream_config *stream_config,
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
* struct sdw_intel_ops: Intel audio driver callback ops * struct sdw_intel_ops: Intel audio driver callback ops
* *
* @config_stream: configure the stream with the hw_params * @config_stream: configure the stream with the hw_params
* the first argument containing the context is mandatory
*/ */
struct sdw_intel_ops { struct sdw_intel_ops {
int (*config_stream)(void *arg, void *substream, int (*config_stream)(void *arg, void *substream,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment