Commit c20c76ac authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'soundwire-5.6-rc1' of...

Merge tag 'soundwire-5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire into char-misc-next

Vinod writes:

soundwire updates for v5.6-rc1

This round we have bunch of updates to interfaces for ASoC (audio)
subsystem by Intel and a new Qualcomm controller driver

Details
 - Updates for sdw_slave interfaces for ASoC
 - Updates to cadence library and intel driver
 - New Soundwire controller for Qualcomm masters
 - Rework of device number assignment

* tag 'soundwire-5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire: (27 commits)
  dt-bindings: soundwire: fix example
  soundwire: cadence: fix kernel-doc parameter descriptions
  soundwire: intel: report slave_ids for each link to SOF driver
  soundwire: intel: fix factor of two in MCLK handling
  soundwire: bus: fix device number leak on errors
  soundwire: cadence: remove useless variable incrementation
  soundwire: cadence: update kernel-doc parameter descriptions
  soundwire: qcom: add support for SoundWire controller
  dt-bindings: soundwire: add bindings for Qcom controller
  soundwire: bus: check first if Slaves become UNATTACHED
  soundwire: cadence_master: handle multiple status reports per Slave
  soundwire: cadence_master: remove config update for interrupt setting
  soundwire: cadence_master: log more useful information during timeouts
  soundwire: cadence_master: clear interrupt status before enabling interrupt
  soundwire: cadence_master: filter out bad interrupts
  soundwire: stream: remove redundant pr_err traces
  soundwire: intel: add clock stop quirks
  soundwire: intel: add mutex for shared SHIM register access
  soundwire: intel: add prototype for WAKEEN interrupt processing
  soundwire: intel: add link_list to handle interrupts with a single thread
  ...
parents 1660557b 5098cae1
Qualcomm SoundWire Controller Bindings
This binding describes the Qualcomm SoundWire Controller along with its
board specific bus parameters.
- compatible:
Usage: required
Value type: <stringlist>
Definition: must be "qcom,soundwire-v<MAJOR>.<MINOR>.<STEP>",
Example:
"qcom,soundwire-v1.3.0"
"qcom,soundwire-v1.5.0"
"qcom,soundwire-v1.6.0"
- reg:
Usage: required
Value type: <prop-encoded-array>
Definition: the base address and size of SoundWire controller
address space.
- interrupts:
Usage: required
Value type: <prop-encoded-array>
Definition: should specify the SoundWire Controller IRQ
- clock-names:
Usage: required
Value type: <stringlist>
Definition: should be "iface" for SoundWire Controller interface clock
- clocks:
Usage: required
Value type: <prop-encoded-array>
Definition: should specify the SoundWire Controller interface clock
- #sound-dai-cells:
Usage: required
Value type: <u32>
Definition: must be 1 for digital audio interfaces on the controller.
- qcom,dout-ports:
Usage: required
Value type: <u32>
Definition: must be count of data out ports
- qcom,din-ports:
Usage: required
Value type: <u32>
Definition: must be count of data in ports
- qcom,ports-offset1:
Usage: required
Value type: <prop-encoded-array>
Definition: should specify payload transport window offset1 of each
data port. Out ports followed by In ports.
More info in MIPI Alliance SoundWire 1.0 Specifications.
- qcom,ports-offset2:
Usage: required
Value type: <prop-encoded-array>
Definition: should specify payload transport window offset2 of each
data port. Out ports followed by In ports.
More info in MIPI Alliance SoundWire 1.0 Specifications.
- qcom,ports-sinterval-low:
Usage: required
Value type: <prop-encoded-array>
Definition: should be sample interval low of each data port.
Out ports followed by In ports. Used for Sample Interval
calculation.
More info in MIPI Alliance SoundWire 1.0 Specifications.
- qcom,ports-word-length:
Usage: optional
Value type: <prop-encoded-array>
Definition: should be size of payload channel sample.
More info in MIPI Alliance SoundWire 1.0 Specifications.
- qcom,ports-block-pack-mode:
Usage: optional
Value type: <prop-encoded-array>
Definition: should be 0 or 1 to indicate the block packing mode.
0 to indicate Blocks are per Channel
1 to indicate Blocks are per Port.
Out ports followed by In ports.
More info in MIPI Alliance SoundWire 1.0 Specifications.
- qcom,ports-block-group-count:
Usage: optional
Value type: <prop-encoded-array>
Definition: should be in range 1 to 4 to indicate how many sample
intervals are combined into a payload.
Out ports followed by In ports.
More info in MIPI Alliance SoundWire 1.0 Specifications.
- qcom,ports-lane-control:
Usage: optional
Value type: <prop-encoded-array>
Definition: should be in range 0 to 7 to identify which data lane
the data port uses.
Out ports followed by In ports.
More info in MIPI Alliance SoundWire 1.0 Specifications.
- qcom,ports-hstart:
Usage: optional
Value type: <prop-encoded-array>
Definition: should be number identifying lowerst numbered coloum in
SoundWire Frame, i.e. left edge of the Transport sub-frame
for each port. Values between 0 and 15 are valid.
Out ports followed by In ports.
More info in MIPI Alliance SoundWire 1.0 Specifications.
- qcom,ports-hstop:
Usage: optional
Value type: <prop-encoded-array>
Definition: should be number identifying highest numbered coloum in
SoundWire Frame, i.e. the right edge of the Transport
sub-frame for each port. Values between 0 and 15 are valid.
Out ports followed by In ports.
More info in MIPI Alliance SoundWire 1.0 Specifications.
- qcom,dports-type:
Usage: optional
Value type: <prop-encoded-array>
Definition: should be one of the following types
0 for reduced port
1 for simple ports
2 for full port
Out ports followed by In ports.
More info in MIPI Alliance SoundWire 1.0 Specifications.
Note:
More Information on detail of encoding of these fields can be
found in MIPI Alliance SoundWire 1.0 Specifications.
= SoundWire devices
Each subnode of the bus represents SoundWire device attached to it.
The properties of these nodes are defined by the individual bindings.
= EXAMPLE
The following example represents a SoundWire controller on DB845c board
which has controller integrated inside WCD934x codec on SDM845 SoC.
soundwire: soundwire@c85 {
compatible = "qcom,soundwire-v1.3.0";
reg = <0xc85 0x20>;
interrupts = <20 IRQ_TYPE_EDGE_RISING>;
clocks = <&wcc>;
clock-names = "iface";
#sound-dai-cells = <1>;
qcom,dports-type = <0>;
qcom,dout-ports = <6>;
qcom,din-ports = <2>;
qcom,ports-sinterval-low = /bits/ 8 <0x07 0x1F 0x3F 0x7 0x1F 0x3F 0x0F 0x0F>;
qcom,ports-offset1 = /bits/ 8 <0x01 0x02 0x0C 0x6 0x12 0x0D 0x07 0x0A >;
qcom,ports-offset2 = /bits/ 8 <0x00 0x00 0x1F 0x00 0x00 0x1F 0x00 0x00>;
/* Left Speaker */
left{
....
};
/* Right Speaker */
right{
....
};
};
...@@ -69,6 +69,7 @@ examples: ...@@ -69,6 +69,7 @@ examples:
reg = <0 1>; reg = <0 1>;
powerdown-gpios = <&wcdpinctrl 2 0>; powerdown-gpios = <&wcdpinctrl 2 0>;
#thermal-sensor-cells = <0>; #thermal-sensor-cells = <0>;
#sound-dai-cells = <0>;
}; };
speaker@0,2 { speaker@0,2 {
...@@ -76,6 +77,7 @@ examples: ...@@ -76,6 +77,7 @@ examples:
reg = <0 2>; reg = <0 2>;
powerdown-gpios = <&wcdpinctrl 2 0>; powerdown-gpios = <&wcdpinctrl 2 0>;
#thermal-sensor-cells = <0>; #thermal-sensor-cells = <0>;
#sound-dai-cells = <0>;
}; };
}; };
......
...@@ -31,4 +31,13 @@ config SOUNDWIRE_INTEL ...@@ -31,4 +31,13 @@ config SOUNDWIRE_INTEL
enable this config option to get the SoundWire support for that enable this config option to get the SoundWire support for that
device. device.
config SOUNDWIRE_QCOM
tristate "Qualcomm SoundWire Master driver"
depends on SLIMBUS
depends on SND_SOC
help
SoundWire Qualcomm Master driver.
If you have an Qualcomm platform which has a SoundWire Master then
enable this config option to get the SoundWire support for that
device
endif endif
...@@ -21,3 +21,7 @@ obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel.o ...@@ -21,3 +21,7 @@ obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel.o
soundwire-intel-init-objs := intel_init.o soundwire-intel-init-objs := intel_init.o
obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel-init.o obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel-init.o
#Qualcomm driver
soundwire-qcom-objs := qcom.o
obj-$(CONFIG_SOUNDWIRE_QCOM) += soundwire-qcom.o
...@@ -456,26 +456,35 @@ static int sdw_get_device_num(struct sdw_slave *slave) ...@@ -456,26 +456,35 @@ static int sdw_get_device_num(struct sdw_slave *slave)
static int sdw_assign_device_num(struct sdw_slave *slave) static int sdw_assign_device_num(struct sdw_slave *slave)
{ {
int ret, dev_num; int ret, dev_num;
bool new_device = false;
/* check first if device number is assigned, if so reuse that */ /* check first if device number is assigned, if so reuse that */
if (!slave->dev_num) { if (!slave->dev_num) {
mutex_lock(&slave->bus->bus_lock); if (!slave->dev_num_sticky) {
dev_num = sdw_get_device_num(slave); mutex_lock(&slave->bus->bus_lock);
mutex_unlock(&slave->bus->bus_lock); dev_num = sdw_get_device_num(slave);
if (dev_num < 0) { mutex_unlock(&slave->bus->bus_lock);
dev_err(slave->bus->dev, "Get dev_num failed: %d\n", if (dev_num < 0) {
dev_num); dev_err(slave->bus->dev, "Get dev_num failed: %d\n",
return dev_num; dev_num);
return dev_num;
}
slave->dev_num = dev_num;
slave->dev_num_sticky = dev_num;
new_device = true;
} else {
slave->dev_num = slave->dev_num_sticky;
} }
} else { }
if (!new_device)
dev_info(slave->bus->dev, dev_info(slave->bus->dev,
"Slave already registered dev_num:%d\n", "Slave already registered, reusing dev_num:%d\n",
slave->dev_num); slave->dev_num);
/* Clear the slave->dev_num to transfer message on device 0 */ /* Clear the slave->dev_num to transfer message on device 0 */
dev_num = slave->dev_num; dev_num = slave->dev_num;
slave->dev_num = 0; slave->dev_num = 0;
}
ret = sdw_write(slave, SDW_SCP_DEVNUMBER, dev_num); ret = sdw_write(slave, SDW_SCP_DEVNUMBER, dev_num);
if (ret < 0) { if (ret < 0) {
...@@ -485,7 +494,7 @@ static int sdw_assign_device_num(struct sdw_slave *slave) ...@@ -485,7 +494,7 @@ static int sdw_assign_device_num(struct sdw_slave *slave)
} }
/* After xfer of msg, restore dev_num */ /* After xfer of msg, restore dev_num */
slave->dev_num = dev_num; slave->dev_num = slave->dev_num_sticky;
return 0; return 0;
} }
...@@ -979,6 +988,24 @@ int sdw_handle_slave_status(struct sdw_bus *bus, ...@@ -979,6 +988,24 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
struct sdw_slave *slave; struct sdw_slave *slave;
int i, ret = 0; int i, ret = 0;
/* first check if any Slaves fell off the bus */
for (i = 1; i <= SDW_MAX_DEVICES; i++) {
mutex_lock(&bus->bus_lock);
if (test_bit(i, bus->assigned) == false) {
mutex_unlock(&bus->bus_lock);
continue;
}
mutex_unlock(&bus->bus_lock);
slave = sdw_get_slave(bus, i);
if (!slave)
continue;
if (status[i] == SDW_SLAVE_UNATTACHED &&
slave->status != SDW_SLAVE_UNATTACHED)
sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED);
}
if (status[0] == SDW_SLAVE_ATTACHED) { if (status[0] == SDW_SLAVE_ATTACHED) {
dev_dbg(bus->dev, "Slave attached, programming device number\n"); dev_dbg(bus->dev, "Slave attached, programming device number\n");
ret = sdw_program_device_num(bus); ret = sdw_program_device_num(bus);
......
...@@ -74,6 +74,7 @@ MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask"); ...@@ -74,6 +74,7 @@ MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask");
#define CDNS_MCP_INTMASK 0x48 #define CDNS_MCP_INTMASK 0x48
#define CDNS_MCP_INT_IRQ BIT(31) #define CDNS_MCP_INT_IRQ BIT(31)
#define CDNS_MCP_INT_RESERVED1 GENMASK(30, 17)
#define CDNS_MCP_INT_WAKEUP BIT(16) #define CDNS_MCP_INT_WAKEUP BIT(16)
#define CDNS_MCP_INT_SLAVE_RSVD BIT(15) #define CDNS_MCP_INT_SLAVE_RSVD BIT(15)
#define CDNS_MCP_INT_SLAVE_ALERT BIT(14) #define CDNS_MCP_INT_SLAVE_ALERT BIT(14)
...@@ -85,10 +86,12 @@ MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask"); ...@@ -85,10 +86,12 @@ MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask");
#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_PARITY BIT(8)
#define CDNS_MCP_INT_CMD_ERR BIT(7) #define CDNS_MCP_INT_CMD_ERR BIT(7)
#define CDNS_MCP_INT_RESERVED2 GENMASK(6, 4)
#define CDNS_MCP_INT_RX_NE BIT(3) #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_INT_TXF BIT(0)
#define CDNS_MCP_INT_RESERVED (CDNS_MCP_INT_RESERVED1 | CDNS_MCP_INT_RESERVED2)
#define CDNS_MCP_INTSET 0x4C #define CDNS_MCP_INTSET 0x4C
...@@ -444,7 +447,8 @@ _cdns_xfer_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int cmd, ...@@ -444,7 +447,8 @@ _cdns_xfer_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int cmd,
time = wait_for_completion_timeout(&cdns->tx_complete, time = wait_for_completion_timeout(&cdns->tx_complete,
msecs_to_jiffies(CDNS_TX_TIMEOUT)); msecs_to_jiffies(CDNS_TX_TIMEOUT));
if (!time) { if (!time) {
dev_err(cdns->dev, "IO transfer timed out\n"); dev_err(cdns->dev, "IO transfer timed out, cmd %d device %d addr %x len %d\n",
cmd, msg->dev_num, msg->addr, msg->len);
msg->len = 0; msg->len = 0;
return SDW_CMD_TIMEOUT; return SDW_CMD_TIMEOUT;
} }
...@@ -672,13 +676,36 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns, ...@@ -672,13 +676,36 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns,
/* first check if Slave reported multiple status */ /* first check if Slave reported multiple status */
if (set_status > 1) { if (set_status > 1) {
u32 val;
dev_warn_ratelimited(cdns->dev,
"Slave %d reported multiple Status: %d\n",
i, mask);
/* check latest status extracted from PING commands */
val = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT);
val >>= (i * 2);
switch (val & 0x3) {
case 0:
status[i] = SDW_SLAVE_UNATTACHED;
break;
case 1:
status[i] = SDW_SLAVE_ATTACHED;
break;
case 2:
status[i] = SDW_SLAVE_ALERT;
break;
case 3:
default:
status[i] = SDW_SLAVE_RESERVED;
break;
}
dev_warn_ratelimited(cdns->dev, dev_warn_ratelimited(cdns->dev,
"Slave reported multiple Status: %d\n", "Slave %d status updated to %d\n",
mask); i, status[i]);
/*
* TODO: we need to reread the status here by
* issuing a PING cmd
*/
} }
} }
...@@ -705,6 +732,10 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id) ...@@ -705,6 +732,10 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id)
int_status = cdns_readl(cdns, CDNS_MCP_INTSTAT); int_status = cdns_readl(cdns, CDNS_MCP_INTSTAT);
/* check for reserved values read as zero */
if (int_status & CDNS_MCP_INT_RESERVED)
return IRQ_NONE;
if (!(int_status & CDNS_MCP_INT_IRQ)) if (!(int_status & CDNS_MCP_INT_IRQ))
return IRQ_NONE; return IRQ_NONE;
...@@ -812,8 +843,9 @@ int sdw_cdns_exit_reset(struct sdw_cdns *cdns) ...@@ -812,8 +843,9 @@ int sdw_cdns_exit_reset(struct sdw_cdns *cdns)
EXPORT_SYMBOL(sdw_cdns_exit_reset); EXPORT_SYMBOL(sdw_cdns_exit_reset);
/** /**
* sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config * sdw_cdns_enable_interrupt() - Enable SDW interrupts
* @cdns: Cadence instance * @cdns: Cadence instance
* @state: True if we are trying to enable interrupt.
*/ */
int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state) int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state)
{ {
...@@ -849,12 +881,21 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state) ...@@ -849,12 +881,21 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state)
mask = interrupt_mask; mask = interrupt_mask;
update_masks: update_masks:
/* clear slave interrupt status before enabling interrupt */
if (state) {
u32 slave_state;
slave_state = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT0);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT0, slave_state);
slave_state = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT1);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT1, slave_state);
}
cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, slave_intmask0); cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, slave_intmask0);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, slave_intmask1); cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, slave_intmask1);
cdns_writel(cdns, CDNS_MCP_INTMASK, mask); cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
/* commit changes */ return 0;
return cdns_update_config(cdns);
} }
EXPORT_SYMBOL(sdw_cdns_enable_interrupt); EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
...@@ -948,8 +989,6 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, ...@@ -948,8 +989,6 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
ret = cdns_allocate_pdi(cdns, &stream->out, ret = cdns_allocate_pdi(cdns, &stream->out,
stream->num_out, offset); stream->num_out, offset);
offset += stream->num_out;
if (ret) if (ret)
return ret; return ret;
...@@ -1224,8 +1263,10 @@ EXPORT_SYMBOL(cdns_set_sdw_stream); ...@@ -1224,8 +1263,10 @@ EXPORT_SYMBOL(cdns_set_sdw_stream);
* cdns_find_pdi() - Find a free PDI * cdns_find_pdi() - Find a free PDI
* *
* @cdns: Cadence instance * @cdns: Cadence instance
* @offset: Starting offset
* @num: Number of PDIs * @num: Number of PDIs
* @pdi: PDI instances * @pdi: PDI instances
* @dai_id: DAI id
* *
* Find a PDI for a given PDI array. The PDI num and dai_id are * Find a PDI for a given PDI array. The PDI num and dai_id are
* expected to match, return NULL otherwise. * expected to match, return NULL otherwise.
...@@ -1277,6 +1318,7 @@ EXPORT_SYMBOL(sdw_cdns_config_stream); ...@@ -1277,6 +1318,7 @@ EXPORT_SYMBOL(sdw_cdns_config_stream);
* @stream: Stream to be allocated * @stream: Stream to be allocated
* @ch: Channel count * @ch: Channel count
* @dir: Data direction * @dir: Data direction
* @dai_id: DAI id
*/ */
struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns, struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns,
struct sdw_cdns_streams *stream, struct sdw_cdns_streams *stream,
......
...@@ -529,17 +529,24 @@ intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) ...@@ -529,17 +529,24 @@ intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf); intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf);
} }
static int intel_config_stream(struct sdw_intel *sdw, static int intel_params_stream(struct sdw_intel *sdw,
struct snd_pcm_substream *substream, struct snd_pcm_substream *substream,
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, int alh_stream_id)
{ {
struct sdw_intel_link_res *res = sdw->res; struct sdw_intel_link_res *res = sdw->res;
struct sdw_intel_stream_params_data params_data;
if (res->ops && res->ops->config_stream && res->arg) params_data.substream = substream;
return res->ops->config_stream(res->arg, params_data.dai = dai;
substream, dai, hw_params, link_id); params_data.hw_params = hw_params;
params_data.link_id = link_id;
params_data.alh_stream_id = alh_stream_id;
if (res->ops && res->ops->params_stream && res->dev)
return res->ops->params_stream(res->dev,
&params_data);
return -EIO; return -EIO;
} }
...@@ -654,7 +661,8 @@ static int intel_hw_params(struct snd_pcm_substream *substream, ...@@ -654,7 +661,8 @@ static int intel_hw_params(struct snd_pcm_substream *substream,
/* Inform DSP about PDI stream number */ /* Inform DSP about PDI stream number */
ret = intel_config_stream(sdw, substream, dai, params, ret = intel_params_stream(sdw, substream, dai, params,
sdw->instance,
pdi->intel_alh_id); pdi->intel_alh_id);
if (ret) if (ret)
goto error; goto error;
...@@ -872,6 +880,9 @@ static int sdw_master_read_intel_prop(struct sdw_bus *bus) ...@@ -872,6 +880,9 @@ static int sdw_master_read_intel_prop(struct sdw_bus *bus)
"intel-sdw-ip-clock", "intel-sdw-ip-clock",
&prop->mclk_freq); &prop->mclk_freq);
/* the values reported by BIOS are the 2x clock, not the bus clock */
prop->mclk_freq /= 2;
fwnode_property_read_u32(link, fwnode_property_read_u32(link,
"intel-quirk-mask", "intel-quirk-mask",
&quirk_mask); &quirk_mask);
......
...@@ -5,23 +5,26 @@ ...@@ -5,23 +5,26 @@
#define __SDW_INTEL_LOCAL_H #define __SDW_INTEL_LOCAL_H
/** /**
* struct sdw_intel_link_res - Soundwire link resources * struct sdw_intel_link_res - Soundwire Intel link resource structure,
* typically populated by the controller driver.
* @pdev: platform_device
* @mmio_base: mmio base of SoundWire registers
* @registers: Link IO registers base * @registers: Link IO registers base
* @shim: Audio shim pointer * @shim: Audio shim pointer
* @alh: ALH (Audio Link Hub) pointer * @alh: ALH (Audio Link Hub) pointer
* @irq: Interrupt line * @irq: Interrupt line
* @ops: Shim callback ops * @ops: Shim callback ops
* @arg: Shim callback ops argument * @dev: device implementing hw_params and free callbacks
*
* This is set as pdata for each link instance.
*/ */
struct sdw_intel_link_res { struct sdw_intel_link_res {
struct platform_device *pdev;
void __iomem *mmio_base; /* not strictly needed, useful for debug */
void __iomem *registers; void __iomem *registers;
void __iomem *shim; void __iomem *shim;
void __iomem *alh; void __iomem *alh;
int irq; int irq;
const struct sdw_intel_ops *ops; const struct sdw_intel_ops *ops;
void *arg; struct device *dev;
}; };
#endif /* __SDW_INTEL_LOCAL_H */ #endif /* __SDW_INTEL_LOCAL_H */
...@@ -27,19 +27,9 @@ static int link_mask; ...@@ -27,19 +27,9 @@ static int link_mask;
module_param_named(sdw_link_mask, link_mask, int, 0444); module_param_named(sdw_link_mask, link_mask, int, 0444);
MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)"); 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;
};
struct sdw_intel_ctx {
int count;
struct sdw_link_data *links;
};
static int sdw_intel_cleanup_pdev(struct sdw_intel_ctx *ctx) static int sdw_intel_cleanup_pdev(struct sdw_intel_ctx *ctx)
{ {
struct sdw_link_data *link = ctx->links; struct sdw_intel_link_res *link = ctx->links;
int i; int i;
if (!link) if (!link)
...@@ -62,7 +52,7 @@ static struct sdw_intel_ctx ...@@ -62,7 +52,7 @@ static struct sdw_intel_ctx
{ {
struct platform_device_info pdevinfo; struct platform_device_info pdevinfo;
struct platform_device *pdev; struct platform_device *pdev;
struct sdw_link_data *link; struct sdw_intel_link_res *link;
struct sdw_intel_ctx *ctx; struct sdw_intel_ctx *ctx;
struct acpi_device *adev; struct acpi_device *adev;
int ret, i; int ret, i;
...@@ -123,14 +113,13 @@ static struct sdw_intel_ctx ...@@ -123,14 +113,13 @@ static struct sdw_intel_ctx
continue; continue;
} }
link->res.irq = res->irq; link->registers = res->mmio_base + SDW_LINK_BASE
link->res.registers = res->mmio_base + SDW_LINK_BASE
+ (SDW_LINK_SIZE * i); + (SDW_LINK_SIZE * i);
link->res.shim = res->mmio_base + SDW_SHIM_BASE; link->shim = res->mmio_base + SDW_SHIM_BASE;
link->res.alh = res->mmio_base + SDW_ALH_BASE; link->alh = res->mmio_base + SDW_ALH_BASE;
link->res.ops = res->ops; link->ops = res->ops;
link->res.arg = res->arg; link->dev = res->dev;
memset(&pdevinfo, 0, sizeof(pdevinfo)); memset(&pdevinfo, 0, sizeof(pdevinfo));
...@@ -138,8 +127,6 @@ static struct sdw_intel_ctx ...@@ -138,8 +127,6 @@ static struct sdw_intel_ctx
pdevinfo.name = "int-sdw"; pdevinfo.name = "int-sdw";
pdevinfo.id = i; pdevinfo.id = i;
pdevinfo.fwnode = acpi_fwnode_handle(adev); pdevinfo.fwnode = acpi_fwnode_handle(adev);
pdevinfo.data = &link->res;
pdevinfo.size_data = sizeof(link->res);
pdev = platform_device_register_full(&pdevinfo); pdev = platform_device_register_full(&pdevinfo);
if (IS_ERR(pdev)) { if (IS_ERR(pdev)) {
...@@ -216,7 +203,6 @@ void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res) ...@@ -216,7 +203,6 @@ void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res)
return sdw_intel_add_controller(res); return sdw_intel_add_controller(res);
} }
EXPORT_SYMBOL(sdw_intel_init);
/** /**
* sdw_intel_exit() - SoundWire Intel exit * sdw_intel_exit() - SoundWire Intel exit
...@@ -224,10 +210,8 @@ EXPORT_SYMBOL(sdw_intel_init); ...@@ -224,10 +210,8 @@ EXPORT_SYMBOL(sdw_intel_init);
* *
* Delete the controller instances created and cleanup * Delete the controller instances created and cleanup
*/ */
void sdw_intel_exit(void *arg) void sdw_intel_exit(struct sdw_intel_ctx *ctx)
{ {
struct sdw_intel_ctx *ctx = arg;
sdw_intel_cleanup_pdev(ctx); sdw_intel_cleanup_pdev(ctx);
kfree(ctx); kfree(ctx);
} }
......
This diff is collapsed.
...@@ -1554,8 +1554,6 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream) ...@@ -1554,8 +1554,6 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream)
sdw_acquire_bus_lock(stream); sdw_acquire_bus_lock(stream);
ret = _sdw_prepare_stream(stream); ret = _sdw_prepare_stream(stream);
if (ret < 0)
pr_err("Prepare for stream:%s failed: %d\n", stream->name, ret);
sdw_release_bus_lock(stream); sdw_release_bus_lock(stream);
return ret; return ret;
...@@ -1622,8 +1620,6 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream) ...@@ -1622,8 +1620,6 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream)
sdw_acquire_bus_lock(stream); sdw_acquire_bus_lock(stream);
ret = _sdw_enable_stream(stream); ret = _sdw_enable_stream(stream);
if (ret < 0)
pr_err("Enable for stream:%s failed: %d\n", stream->name, ret);
sdw_release_bus_lock(stream); sdw_release_bus_lock(stream);
return ret; return ret;
...@@ -1698,8 +1694,6 @@ int sdw_disable_stream(struct sdw_stream_runtime *stream) ...@@ -1698,8 +1694,6 @@ int sdw_disable_stream(struct sdw_stream_runtime *stream)
sdw_acquire_bus_lock(stream); sdw_acquire_bus_lock(stream);
ret = _sdw_disable_stream(stream); ret = _sdw_disable_stream(stream);
if (ret < 0)
pr_err("Disable for stream:%s failed: %d\n", stream->name, ret);
sdw_release_bus_lock(stream); sdw_release_bus_lock(stream);
return ret; return ret;
...@@ -1756,8 +1750,6 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream) ...@@ -1756,8 +1750,6 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream)
sdw_acquire_bus_lock(stream); sdw_acquire_bus_lock(stream);
ret = _sdw_deprepare_stream(stream); ret = _sdw_deprepare_stream(stream);
if (ret < 0)
pr_err("De-prepare for stream:%d failed: %d\n", ret, ret);
sdw_release_bus_lock(stream); sdw_release_bus_lock(stream);
return ret; return ret;
......
...@@ -546,7 +546,22 @@ struct sdw_slave_ops { ...@@ -546,7 +546,22 @@ struct sdw_slave_ops {
* @debugfs: Slave debugfs * @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: Current Device Number, values can be 0 or dev_num_sticky
* @dev_num_sticky: one-time static Device Number assigned by Bus
* @probed: boolean tracking driver state
* @probe_complete: completion utility to control potential races
* on startup between driver probe/initialization and SoundWire
* Slave state changes/implementation-defined interrupts
* @enumeration_complete: completion utility to control potential races
* on startup between device enumeration and read/write access to the
* Slave device
* @initialization_complete: completion utility to control potential races
* on startup between device enumeration and settings being restored
* @unattach_request: mask field to keep track why the Slave re-attached and
* was re-initialized. This is useful to deal with potential race conditions
* between the Master suspending and the codec resuming, and make sure that
* when the Master triggered a reset the Slave is properly enumerated and
* initialized
*/ */
struct sdw_slave { struct sdw_slave {
struct sdw_slave_id id; struct sdw_slave_id id;
...@@ -561,6 +576,12 @@ struct sdw_slave { ...@@ -561,6 +576,12 @@ struct sdw_slave {
struct list_head node; struct list_head node;
struct completion *port_ready; struct completion *port_ready;
u16 dev_num; u16 dev_num;
u16 dev_num_sticky;
bool probed;
struct completion probe_complete;
struct completion enumeration_complete;
struct completion initialization_complete;
u32 unattach_request;
}; };
#define dev_to_sdw_dev(_dev) container_of(_dev, struct sdw_slave, dev) #define dev_to_sdw_dev(_dev) container_of(_dev, struct sdw_slave, dev)
......
...@@ -4,36 +4,185 @@ ...@@ -4,36 +4,185 @@
#ifndef __SDW_INTEL_H #ifndef __SDW_INTEL_H
#define __SDW_INTEL_H #define __SDW_INTEL_H
#include <linux/irqreturn.h>
#include <linux/soundwire/sdw.h>
/**
* struct sdw_intel_stream_params_data: configuration passed during
* the @params_stream callback, e.g. for interaction with DSP
* firmware.
*/
struct sdw_intel_stream_params_data {
struct snd_pcm_substream *substream;
struct snd_soc_dai *dai;
struct snd_pcm_hw_params *hw_params;
int link_id;
int alh_stream_id;
};
/**
* struct sdw_intel_stream_free_data: configuration passed during
* the @free_stream callback, e.g. for interaction with DSP
* firmware.
*/
struct sdw_intel_stream_free_data {
struct snd_pcm_substream *substream;
struct snd_soc_dai *dai;
int link_id;
};
/** /**
* 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
* the first argument containing the context is mandatory
*/ */
struct sdw_intel_ops { struct sdw_intel_ops {
int (*config_stream)(void *arg, void *substream, int (*params_stream)(struct device *dev,
void *dai, void *hw_params, int stream_num); struct sdw_intel_stream_params_data *params_data);
int (*free_stream)(struct device *dev,
struct sdw_intel_stream_free_data *free_data);
};
/**
* struct sdw_intel_acpi_info - Soundwire Intel information found in ACPI tables
* @handle: ACPI controller handle
* @count: link count found with "sdw-master-count" property
* @link_mask: bit-wise mask listing links enabled by BIOS menu
*
* this structure could be expanded to e.g. provide all the _ADR
* information in case the link_mask is not sufficient to identify
* platform capabilities.
*/
struct sdw_intel_acpi_info {
acpi_handle handle;
int count;
u32 link_mask;
};
struct sdw_intel_link_res;
/* Intel clock-stop/pm_runtime quirk definitions */
/*
* Force the clock to remain on during pm_runtime suspend. This might
* be needed if Slave devices do not have an alternate clock source or
* if the latency requirements are very strict.
*/
#define SDW_INTEL_CLK_STOP_NOT_ALLOWED BIT(0)
/*
* Stop the bus during pm_runtime suspend. If set, a complete bus
* reset and re-enumeration will be performed when the bus
* restarts. This mode shall not be used if Slave devices can generate
* in-band wakes.
*/
#define SDW_INTEL_CLK_STOP_TEARDOWN BIT(1)
/*
* Stop the bus during pm_suspend if Slaves are not wake capable
* (e.g. speaker amplifiers). The clock-stop mode is typically
* slightly higher power than when the IP is completely powered-off.
*/
#define SDW_INTEL_CLK_STOP_WAKE_CAPABLE_ONLY BIT(2)
/*
* Require a bus reset (and complete re-enumeration) when exiting
* clock stop modes. This may be needed if the controller power was
* turned off and all context lost. This quirk shall not be used if a
* Slave device needs to remain enumerated and keep its context,
* e.g. to provide the reasons for the wake, report acoustic events or
* pass a history buffer.
*/
#define SDW_INTEL_CLK_STOP_BUS_RESET BIT(3)
struct sdw_intel_slave_id {
int link_id;
struct sdw_slave_id id;
}; };
/** /**
* struct sdw_intel_res - Soundwire Intel resource structure * struct sdw_intel_ctx - context allocated by the controller
* driver probe
* @count: link count
* @mmio_base: mmio base of SoundWire registers, only used to check
* hardware capabilities after all power dependencies are settled.
* @link_mask: bit-wise mask listing SoundWire links reported by the
* Controller
* @num_slaves: total number of devices exposed across all enabled links
* @handle: ACPI parent handle
* @links: information for each link (controller-specific and kept
* opaque here)
* @ids: array of slave_id, representing Slaves exposed across all enabled
* links
* @link_list: list to handle interrupts across all links
* @shim_lock: mutex to handle concurrent rmw access to shared SHIM registers.
*/
struct sdw_intel_ctx {
int count;
void __iomem *mmio_base;
u32 link_mask;
int num_slaves;
acpi_handle handle;
struct sdw_intel_link_res *links;
struct sdw_intel_slave_id *ids;
struct list_head link_list;
struct mutex shim_lock; /* lock for access to shared SHIM registers */
};
/**
* struct sdw_intel_res - Soundwire Intel global resource structure,
* typically populated by the DSP driver
*
* @count: link count
* @mmio_base: mmio base of SoundWire registers * @mmio_base: mmio base of SoundWire registers
* @irq: interrupt number * @irq: interrupt number
* @handle: ACPI parent handle * @handle: ACPI parent handle
* @parent: parent device * @parent: parent device
* @ops: callback ops * @ops: callback ops
* @arg: callback arg * @dev: device implementing hwparams and free callbacks
* @link_mask: bit-wise mask listing links selected by the DSP driver
* This mask may be a subset of the one reported by the controller since
* machine-specific quirks are handled in the DSP driver.
* @clock_stop_quirks: mask array of possible behaviors requested by the
* DSP driver. The quirks are common for all links for now.
*/ */
struct sdw_intel_res { struct sdw_intel_res {
int count;
void __iomem *mmio_base; void __iomem *mmio_base;
int irq; int irq;
acpi_handle handle; acpi_handle handle;
struct device *parent; struct device *parent;
const struct sdw_intel_ops *ops; const struct sdw_intel_ops *ops;
void *arg; struct device *dev;
u32 link_mask;
u32 clock_stop_quirks;
}; };
void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res); /*
void sdw_intel_exit(void *arg); * On Intel platforms, the SoundWire IP has dependencies on power
* rails shared with the DSP, and the initialization steps are split
* in three. First an ACPI scan to check what the firmware describes
* in DSDT tables, then an allocation step (with no hardware
* configuration but with all the relevant devices created) and last
* the actual hardware configuration. The final stage is a global
* interrupt enable which is controlled by the DSP driver. Splitting
* these phases helps simplify the boot flow and make early decisions
* on e.g. which machine driver to select (I2S mode, HDaudio or
* SoundWire).
*/
int sdw_intel_acpi_scan(acpi_handle *parent_handle,
struct sdw_intel_acpi_info *info);
void sdw_intel_process_wakeen_event(struct sdw_intel_ctx *ctx);
struct sdw_intel_ctx *
sdw_intel_probe(struct sdw_intel_res *res);
int sdw_intel_startup(struct sdw_intel_ctx *ctx);
void sdw_intel_exit(struct sdw_intel_ctx *ctx);
void sdw_intel_enable_irq(void __iomem *mmio_base, bool enable);
irqreturn_t sdw_intel_thread(int irq, void *dev_id);
#endif #endif
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