1
0
Fork 0

soundwire updates for v5.4-rc1

This includes DT support thanks to Srini and more work done by Intel
 (Pierre) on improving cadence and intel support.
 
 Details:
  - 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
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJdgFDvAAoJEHwUBw8lI4NHv7wP/Ro1MQA0WQt8GqDENFdl+JJF
 KTbjn0YOD4eyFKncCiUoewQghPkJUVa8598t3rNn+lhEBePRYDPh0TV44ju2pk4E
 FNpWmkrXih4NvfCqSPWfLB/Xap6BDvX0xO5k7BoYJ08ktwlow6hWpB7FWoV+QTmk
 QqTypte5GTzYf+uo3UXRGPtu2bRlORtsoL8SSGcGaRERImPC0/VMlGRdMEvf2STA
 eyGk/ESi9s22Hl4sEKqk2uDmzUfc7uK6Ey6Ln/hhkQD7K+esmb0fFIU0JguiZo1O
 GAmbFsncV0Zk7bwU8oy4ZSo5Rjk2nuXMbEKWC+HLPA73skz09QyZJbqetimluzgl
 N7BFBVHFA0eLzFtUNieeQjcbNmOIW/rEJLO2iVPHvnOq4OLuEpFMPRhDFkMxD+cH
 7COoZNg8CXeXqceK+dgEe1A8W7ujWpXMN8fuV1ugbMDkZKy08pRL55RBGWcTroxw
 PakDXz5fnqthfvMwb6/jmg1WqzDe0qhIRWub+Sga1wmJMN5qm3iSAYZPqZe6SiNl
 is4zX6w9SO8S/qBI2Y64n1vgTsHbHvSrvPRIxDs9J6TfgeAoXtlJCsNpSa3LDqV9
 KxD+IV7D6MqAILUhlPK6PcMmoPBEOrFxONoOwzokd3NRQk4ZwffasXchwjDETvdc
 H3yzDAfUYvXPNHsHxw65
 =UILk
 -----END PGP SIGNATURE-----

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
  ...
alistair/sunxi64-5.4-dsi
Linus Torvalds 2019-09-22 10:52:23 -07:00
commit 8d7ead5c69
15 changed files with 819 additions and 101 deletions

View File

@ -0,0 +1,82 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/soundwire/soundwire-controller.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: SoundWire Controller Generic Binding
maintainers:
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
- Vinod Koul <vkoul@kernel.org>
description: |
SoundWire busses can be described with a node for the SoundWire controller
device and a set of child nodes for each SoundWire slave on the bus.
properties:
$nodename:
pattern: "^soundwire(@.*)?$"
"#address-cells":
const: 2
"#size-cells":
const: 0
patternProperties:
"^.*@[0-9a-f],[0-9a-f]$":
type: object
properties:
compatible:
pattern: "^sdw[0-9a-f]{1}[0-9a-f]{4}[0-9a-f]{4}[0-9a-f]{2}$"
description: Is the textual representation of SoundWire Enumeration
address. compatible string should contain SoundWire Version ID,
Manufacturer ID, Part ID and Class ID in order and shall be in
lower-case hexadecimal with leading zeroes.
Valid sizes of these fields are
Version ID is 1 nibble, number '0x1' represents SoundWire 1.0
and '0x2' represents SoundWire 1.1 and so on.
MFD is 4 nibbles
PID is 4 nibbles
CID is 2 nibbles
More Information on detail of encoding of these fields can be
found in MIPI Alliance DisCo & SoundWire 1.0 Specifications.
reg:
maxItems: 1
description:
Link ID followed by Instance ID of SoundWire Device Address.
required:
- compatible
- reg
required:
- "#address-cells"
- "#size-cells"
examples:
- |
soundwire@c2d0000 {
#address-cells = <2>;
#size-cells = <0>;
reg = <0x0c2d0000 0x2000>;
speaker@0,1 {
compatible = "sdw10217201000";
reg = <0 1>;
powerdown-gpios = <&wcdpinctrl 2 0>;
#thermal-sensor-cells = <0>;
};
speaker@0,2 {
compatible = "sdw10217201000";
reg = <0 2>;
powerdown-gpios = <&wcdpinctrl 2 0>;
#thermal-sensor-cells = <0>;
};
};
...

View File

@ -7,6 +7,10 @@
soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o stream.o
obj-$(CONFIG_SOUNDWIRE) += soundwire-bus.o
ifdef CONFIG_DEBUG_FS
soundwire-bus-objs += debugfs.o
endif
#Cadence Objs
soundwire-cadence-objs := cadence_master.o
obj-$(CONFIG_SOUNDWIRE_CADENCE) += soundwire-cadence.o

View File

@ -49,6 +49,8 @@ int sdw_add_bus_master(struct sdw_bus *bus)
}
}
sdw_bus_debugfs_init(bus);
/*
* Device numbers in SoundWire are 0 through 15. Enumeration device
* number (0), Broadcast device number (15), Group numbers (12 and
@ -77,6 +79,8 @@ int sdw_add_bus_master(struct sdw_bus *bus)
*/
if (IS_ENABLED(CONFIG_ACPI) && ACPI_HANDLE(bus->dev))
ret = sdw_acpi_find_slaves(bus);
else if (IS_ENABLED(CONFIG_OF) && bus->dev->of_node)
ret = sdw_of_find_slaves(bus);
else
ret = -ENOTSUPP; /* No ACPI/DT so error out */
@ -109,6 +113,8 @@ static int sdw_delete_slave(struct device *dev, void *data)
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct sdw_bus *bus = slave->bus;
sdw_slave_debugfs_exit(slave);
mutex_lock(&bus->bus_lock);
if (slave->dev_num) /* clear dev_num if assigned */
@ -130,6 +136,8 @@ static int sdw_delete_slave(struct device *dev, void *data)
void sdw_delete_bus_master(struct sdw_bus *bus)
{
device_for_each_child(bus->dev, NULL, sdw_delete_slave);
sdw_bus_debugfs_exit(bus);
}
EXPORT_SYMBOL(sdw_delete_bus_master);
@ -470,7 +478,8 @@ static int sdw_assign_device_num(struct sdw_slave *slave)
ret = sdw_write(slave, SDW_SCP_DEVNUMBER, dev_num);
if (ret < 0) {
dev_err(&slave->dev, "Program device_num failed: %d\n", ret);
dev_err(&slave->dev, "Program device_num %d failed: %d\n",
dev_num, ret);
return ret;
}
@ -527,6 +536,7 @@ static int sdw_program_device_num(struct sdw_bus *bus)
do {
ret = sdw_transfer(bus, &msg);
if (ret == -ENODATA) { /* end of device id reads */
dev_dbg(bus->dev, "No more devices to enumerate\n");
ret = 0;
break;
}
@ -803,7 +813,7 @@ static int sdw_handle_port_interrupt(struct sdw_slave *slave,
static int sdw_handle_slave_alerts(struct sdw_slave *slave)
{
struct sdw_slave_intr_status slave_intr;
u8 clear = 0, bit, port_status[15];
u8 clear = 0, bit, port_status[15] = {0};
int port_num, stat, ret, count = 0;
unsigned long port;
bool slave_notify = false;
@ -969,9 +979,15 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
int i, ret = 0;
if (status[0] == SDW_SLAVE_ATTACHED) {
dev_dbg(bus->dev, "Slave attached, programming device number\n");
ret = sdw_program_device_num(bus);
if (ret)
dev_err(bus->dev, "Slave attach failed: %d\n", ret);
/*
* programming a device number will have side effects,
* so we deal with other devices at a later time
*/
return ret;
}
/* Continue to check other slave statuses */

View File

@ -15,9 +15,26 @@ static inline int sdw_acpi_find_slaves(struct sdw_bus *bus)
}
#endif
int sdw_of_find_slaves(struct sdw_bus *bus);
void sdw_extract_slave_id(struct sdw_bus *bus,
u64 addr, struct sdw_slave_id *id);
#ifdef CONFIG_DEBUG_FS
void sdw_bus_debugfs_init(struct sdw_bus *bus);
void sdw_bus_debugfs_exit(struct sdw_bus *bus);
void sdw_slave_debugfs_init(struct sdw_slave *slave);
void sdw_slave_debugfs_exit(struct sdw_slave *slave);
void sdw_debugfs_init(void);
void sdw_debugfs_exit(void);
#else
static inline void sdw_bus_debugfs_init(struct sdw_bus *bus) {}
static inline void sdw_bus_debugfs_exit(struct sdw_bus *bus) {}
static inline void sdw_slave_debugfs_init(struct sdw_slave *slave) {}
static inline void sdw_slave_debugfs_exit(struct sdw_slave *slave) {}
static inline void sdw_debugfs_init(void) {}
static inline void sdw_debugfs_exit(void) {}
#endif
enum {
SDW_MSG_FLAG_READ = 0,
SDW_MSG_FLAG_WRITE,
@ -49,8 +66,11 @@ struct sdw_msg {
#define SDW_DOUBLE_RATE_FACTOR 2
extern int rows[SDW_FRAME_ROWS];
extern int cols[SDW_FRAME_COLS];
extern int sdw_rows[SDW_FRAME_ROWS];
extern int sdw_cols[SDW_FRAME_COLS];
int sdw_find_row_index(int row);
int sdw_find_col_index(int col);
/**
* sdw_port_runtime: Runtime port parameters for Master or Slave

View File

@ -6,6 +6,7 @@
#include <linux/pm_domain.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include "bus.h"
/**
* sdw_get_device_id - find the matching SoundWire device id
@ -177,11 +178,13 @@ EXPORT_SYMBOL_GPL(sdw_unregister_driver);
static int __init sdw_bus_init(void)
{
sdw_debugfs_init();
return bus_register(&sdw_bus_type);
}
static void __exit sdw_bus_exit(void)
{
sdw_debugfs_exit();
bus_unregister(&sdw_bus_type);
}

View File

@ -8,6 +8,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
@ -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");

View File

@ -163,6 +163,10 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
struct sdw_cdns_stream_config config);
int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns);
#ifdef CONFIG_DEBUG_FS
void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root);
#endif
int sdw_cdns_get_stream(struct sdw_cdns *cdns,
struct sdw_cdns_streams *stream,
u32 ch, u32 dir);
@ -172,8 +176,6 @@ int sdw_cdns_alloc_stream(struct sdw_cdns *cdns,
void sdw_cdns_config_stream(struct sdw_cdns *cdns, struct sdw_cdns_port *port,
u32 ch, u32 dir, struct sdw_cdns_pdi *pdi);
void sdw_cdns_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai);
int sdw_cdns_pcm_set_stream(struct snd_soc_dai *dai,
void *stream, int direction);
int sdw_cdns_pdm_set_stream(struct snd_soc_dai *dai,

View File

@ -0,0 +1,151 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright(c) 2017-2019 Intel Corporation.
#include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/mod_devicetable.h>
#include <linux/slab.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_registers.h>
#include "bus.h"
static struct dentry *sdw_debugfs_root;
void sdw_bus_debugfs_init(struct sdw_bus *bus)
{
char name[16];
if (!sdw_debugfs_root)
return;
/* create the debugfs master-N */
snprintf(name, sizeof(name), "master-%d", bus->link_id);
bus->debugfs = debugfs_create_dir(name, sdw_debugfs_root);
}
void sdw_bus_debugfs_exit(struct sdw_bus *bus)
{
debugfs_remove_recursive(bus->debugfs);
}
#define RD_BUF (3 * PAGE_SIZE)
static ssize_t sdw_sprintf(struct sdw_slave *slave,
char *buf, size_t pos, unsigned int reg)
{
int value;
value = sdw_read(slave, reg);
if (value < 0)
return scnprintf(buf + pos, RD_BUF - pos, "%3x\tXX\n", reg);
else
return scnprintf(buf + pos, RD_BUF - pos,
"%3x\t%2x\n", reg, value);
}
static int sdw_slave_reg_show(struct seq_file *s_file, void *data)
{
struct sdw_slave *slave = s_file->private;
char *buf;
ssize_t ret;
int i, j;
buf = kzalloc(RD_BUF, GFP_KERNEL);
if (!buf)
return -ENOMEM;
ret = scnprintf(buf, RD_BUF, "Register Value\n");
/* DP0 non-banked registers */
ret += scnprintf(buf + ret, RD_BUF - ret, "\nDP0\n");
for (i = SDW_DP0_INT; i <= SDW_DP0_PREPARECTRL; i++)
ret += sdw_sprintf(slave, buf, ret, i);
/* DP0 Bank 0 registers */
ret += scnprintf(buf + ret, RD_BUF - ret, "Bank0\n");
ret += sdw_sprintf(slave, buf, ret, SDW_DP0_CHANNELEN);
for (i = SDW_DP0_SAMPLECTRL1; i <= SDW_DP0_LANECTRL; i++)
ret += sdw_sprintf(slave, buf, ret, i);
/* DP0 Bank 1 registers */
ret += scnprintf(buf + ret, RD_BUF - ret, "Bank1\n");
ret += sdw_sprintf(slave, buf, ret,
SDW_DP0_CHANNELEN + SDW_BANK1_OFFSET);
for (i = SDW_DP0_SAMPLECTRL1 + SDW_BANK1_OFFSET;
i <= SDW_DP0_LANECTRL + SDW_BANK1_OFFSET; i++)
ret += sdw_sprintf(slave, buf, ret, i);
/* SCP registers */
ret += scnprintf(buf + ret, RD_BUF - ret, "\nSCP\n");
for (i = SDW_SCP_INT1; i <= SDW_SCP_BANKDELAY; i++)
ret += sdw_sprintf(slave, buf, ret, i);
for (i = SDW_SCP_DEVID_0; i <= SDW_SCP_DEVID_5; i++)
ret += sdw_sprintf(slave, buf, ret, i);
/*
* SCP Bank 0/1 registers are read-only and cannot be
* retrieved from the Slave. The Master typically keeps track
* of the current frame size so the information can be found
* in other places
*/
/* DP1..14 registers */
for (i = 1; SDW_VALID_PORT_RANGE(i); i++) {
/* DPi registers */
ret += scnprintf(buf + ret, RD_BUF - ret, "\nDP%d\n", i);
for (j = SDW_DPN_INT(i); j <= SDW_DPN_PREPARECTRL(i); j++)
ret += sdw_sprintf(slave, buf, ret, j);
/* DPi Bank0 registers */
ret += scnprintf(buf + ret, RD_BUF - ret, "Bank0\n");
for (j = SDW_DPN_CHANNELEN_B0(i);
j <= SDW_DPN_LANECTRL_B0(i); j++)
ret += sdw_sprintf(slave, buf, ret, j);
/* DPi Bank1 registers */
ret += scnprintf(buf + ret, RD_BUF - ret, "Bank1\n");
for (j = SDW_DPN_CHANNELEN_B1(i);
j <= SDW_DPN_LANECTRL_B1(i); j++)
ret += sdw_sprintf(slave, buf, ret, j);
}
seq_printf(s_file, "%s", buf);
kfree(buf);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(sdw_slave_reg);
void sdw_slave_debugfs_init(struct sdw_slave *slave)
{
struct dentry *master;
struct dentry *d;
char name[32];
master = slave->bus->debugfs;
/* create the debugfs slave-name */
snprintf(name, sizeof(name), "%s", dev_name(&slave->dev));
d = debugfs_create_dir(name, master);
debugfs_create_file("registers", 0400, d, slave, &sdw_slave_reg_fops);
slave->debugfs = d;
}
void sdw_slave_debugfs_exit(struct sdw_slave *slave)
{
debugfs_remove_recursive(slave->debugfs);
}
void sdw_debugfs_init(void)
{
sdw_debugfs_root = debugfs_create_dir("soundwire", NULL);
}
void sdw_debugfs_exit(void)
{
debugfs_remove_recursive(sdw_debugfs_root);
}

View File

@ -6,6 +6,7 @@
*/
#include <linux/acpi.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/interrupt.h>
@ -16,6 +17,7 @@
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_intel.h>
#include "cadence_master.h"
#include "bus.h"
#include "intel.h"
/* Intel SHIM Registers Definition */
@ -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;

View File

@ -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);

View File

@ -60,8 +60,7 @@ int sdw_master_read_prop(struct sdw_bus *bus)
"mipi-sdw-max-clock-frequency",
&prop->max_clk_freq);
nval = fwnode_property_read_u32_array(link,
"mipi-sdw-clock-frequencies-supported", NULL, 0);
nval = fwnode_property_count_u32(link, "mipi-sdw-clock-frequencies-supported");
if (nval > 0) {
prop->num_clk_freq = nval;
prop->clk_freq = devm_kcalloc(bus->dev, prop->num_clk_freq,
@ -87,8 +86,7 @@ int sdw_master_read_prop(struct sdw_bus *bus)
}
}
nval = fwnode_property_read_u32_array(link,
"mipi-sdw-supported-clock-gears", NULL, 0);
nval = fwnode_property_count_u32(link, "mipi-sdw-supported-clock-gears");
if (nval > 0) {
prop->num_clk_gears = nval;
prop->clk_gears = devm_kcalloc(bus->dev, prop->num_clk_gears,
@ -134,8 +132,7 @@ static int sdw_slave_read_dp0(struct sdw_slave *slave,
fwnode_property_read_u32(port, "mipi-sdw-port-min-wordlength",
&dp0->min_word);
nval = fwnode_property_read_u32_array(port,
"mipi-sdw-port-wordlength-configs", NULL, 0);
nval = fwnode_property_count_u32(port, "mipi-sdw-port-wordlength-configs");
if (nval > 0) {
dp0->num_words = nval;
@ -193,8 +190,7 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave,
fwnode_property_read_u32(node, "mipi-sdw-port-min-wordlength",
&dpn[i].min_word);
nval = fwnode_property_read_u32_array(node,
"mipi-sdw-port-wordlength-configs", NULL, 0);
nval = fwnode_property_count_u32(node, "mipi-sdw-port-wordlength-configs");
if (nval > 0) {
dpn[i].num_words = nval;
dpn[i].words = devm_kcalloc(&slave->dev,
@ -233,8 +229,7 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave,
fwnode_property_read_u32(node, "mipi-sdw-max-channel-number",
&dpn[i].max_ch);
nval = fwnode_property_read_u32_array(node,
"mipi-sdw-channel-number-list", NULL, 0);
nval = fwnode_property_count_u32(node, "mipi-sdw-channel-number-list");
if (nval > 0) {
dpn[i].num_ch = nval;
dpn[i].ch = devm_kcalloc(&slave->dev, dpn[i].num_ch,
@ -248,8 +243,7 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave,
dpn[i].ch, dpn[i].num_ch);
}
nval = fwnode_property_read_u32_array(node,
"mipi-sdw-channel-combination-list", NULL, 0);
nval = fwnode_property_count_u32(node, "mipi-sdw-channel-combination-list");
if (nval > 0) {
dpn[i].num_ch_combinations = nval;
dpn[i].ch_combinations = devm_kcalloc(&slave->dev,

View File

@ -2,6 +2,7 @@
// Copyright(c) 2015-17 Intel Corporation.
#include <linux/acpi.h>
#include <linux/of.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include "bus.h"
@ -35,6 +36,7 @@ static int sdw_slave_add(struct sdw_bus *bus,
slave->dev.release = sdw_slave_release;
slave->dev.bus = &sdw_bus_type;
slave->dev.of_node = of_node_get(to_of_node(fwnode));
slave->bus = bus;
slave->status = SDW_SLAVE_UNATTACHED;
slave->dev_num = 0;
@ -56,6 +58,7 @@ static int sdw_slave_add(struct sdw_bus *bus,
mutex_unlock(&bus->bus_lock);
put_device(&slave->dev);
}
sdw_slave_debugfs_init(slave);
return ret;
}
@ -112,3 +115,53 @@ int sdw_acpi_find_slaves(struct sdw_bus *bus)
}
#endif
/*
* sdw_of_find_slaves() - Find Slave devices in master device tree node
* @bus: SDW bus instance
*
* Scans Master DT node for SDW child Slave devices and registers it.
*/
int sdw_of_find_slaves(struct sdw_bus *bus)
{
struct device *dev = bus->dev;
struct device_node *node;
for_each_child_of_node(bus->dev->of_node, node) {
int link_id, sdw_version, ret, len;
const char *compat = NULL;
struct sdw_slave_id id;
const __be32 *addr;
compat = of_get_property(node, "compatible", NULL);
if (!compat)
continue;
ret = sscanf(compat, "sdw%01x%04hx%04hx%02hhx", &sdw_version,
&id.mfg_id, &id.part_id, &id.class_id);
if (ret != 4) {
dev_err(dev, "Invalid compatible string found %s\n",
compat);
continue;
}
addr = of_get_property(node, "reg", &len);
if (!addr || (len < 2 * sizeof(u32))) {
dev_err(dev, "Invalid Link and Instance ID\n");
continue;
}
link_id = be32_to_cpup(addr++);
id.unique_id = be32_to_cpup(addr);
id.sdw_version = sdw_version;
/* Check for link_id match */
if (link_id != bus->link_id)
continue;
sdw_slave_add(bus, &id, of_fwnode_handle(node));
}
return 0;
}

View File

@ -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");

View File

@ -4,6 +4,8 @@
#ifndef __SOUNDWIRE_H
#define __SOUNDWIRE_H
#include <linux/mod_devicetable.h>
struct sdw_bus;
struct sdw_slave;
@ -377,6 +379,8 @@ struct sdw_slave_prop {
* @dynamic_frame: Dynamic frame shape supported
* @err_threshold: Number of times that software may retry sending a single
* command
* @mclk_freq: clock reference passed to SoundWire Master, in Hz.
* @hw_disabled: if true, the Master is not functional, typically due to pin-mux
*/
struct sdw_master_prop {
u32 revision;
@ -391,6 +395,8 @@ struct sdw_master_prop {
u32 default_col;
bool dynamic_frame;
u32 err_threshold;
u32 mclk_freq;
bool hw_disabled;
};
int sdw_master_read_prop(struct sdw_bus *bus);
@ -538,6 +544,7 @@ struct sdw_slave_ops {
* @bus: Bus handle
* @ops: Slave callback ops
* @prop: Slave properties
* @debugfs: Slave debugfs
* @node: node for bus list
* @port_ready: Port ready completion flag for each Slave port
* @dev_num: Device Number assigned by Bus
@ -549,6 +556,9 @@ struct sdw_slave {
struct sdw_bus *bus;
const struct sdw_slave_ops *ops;
struct sdw_slave_prop prop;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
#endif
struct list_head node;
struct completion *port_ready;
u16 dev_num;
@ -718,6 +728,7 @@ struct sdw_master_ops {
* Bit set implies used number, bit clear implies unused number.
* @bus_lock: bus lock
* @msg_lock: message lock
* @compute_params: points to Bus resource management implementation
* @ops: Master callback ops
* @port_ops: Master port callback ops
* @params: Current bus parameters
@ -725,6 +736,7 @@ struct sdw_master_ops {
* @m_rt_list: List of Master instance of all stream(s) running on Bus. This
* is used to compute and program bus bandwidth, clock, frame shape,
* transport and port parameters
* @debugfs: Bus debugfs
* @defer_msg: Defer message
* @clk_stop_timeout: Clock stop timeout computed
* @bank_switch_timeout: Bank switch timeout computed
@ -739,11 +751,15 @@ struct sdw_bus {
DECLARE_BITMAP(assigned, SDW_MAX_DEVICES);
struct mutex bus_lock;
struct mutex msg_lock;
int (*compute_params)(struct sdw_bus *bus);
const struct sdw_master_ops *ops;
const struct sdw_master_port_ops *port_ops;
struct sdw_bus_params params;
struct sdw_master_prop prop;
struct list_head m_rt_list;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
#endif
struct sdw_defer defer_msg;
unsigned int clk_stop_timeout;
u32 bank_switch_timeout;
@ -828,7 +844,7 @@ struct sdw_stream_params {
* @m_rt_count: Count of Master runtime(s) in this stream
*/
struct sdw_stream_runtime {
char *name;
const char *name;
struct sdw_stream_params params;
enum sdw_stream_state state;
enum sdw_stream_type type;
@ -836,7 +852,7 @@ struct sdw_stream_runtime {
int m_rt_count;
};
struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name);
struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name);
void sdw_release_stream(struct sdw_stream_runtime *stream);
int sdw_stream_add_master(struct sdw_bus *bus,
struct sdw_stream_config *stream_config,

View File

@ -8,6 +8,7 @@
* struct sdw_intel_ops: Intel audio driver callback ops
*
* @config_stream: configure the stream with the hw_params
* the first argument containing the context is mandatory
*/
struct sdw_intel_ops {
int (*config_stream)(void *arg, void *substream,