All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/13] soundwire: Add stream support
@ 2018-03-28  9:38 Vinod Koul
  2018-03-28  9:38 ` [PATCH 01/13] soundwire: Add more documentation Vinod Koul
                   ` (12 more replies)
  0 siblings, 13 replies; 25+ messages in thread
From: Vinod Koul @ 2018-03-28  9:38 UTC (permalink / raw)
  To: Greg KH
  Cc: ALSA, tiwai, Pierre-Louis Bossart, liam.r.girdwood,
	patches.audio, broonie, Vinod Koul


This series adds support in SoundWire subsystem for:
 - Documentation for stream support
 - stream management
 - data port management
 - DAI ops in cadence and Intel drivers
 - ASoC API to propagate SDW stream

Sanyog Kale (5):
  soundwire: Add more documentation
  soundwire: Add support for SoundWire stream management
  soundwire: Add Master and Slave port programming
  soundwire: Add helpers for ports operations
  soundwire: Add bank switch routine

Shreyas NC (2):
  ASoC: Add SoundWire stream programming interface
  soundwire: Remove cdns_master_ops

Vinod Koul (6):
  soundwire: Add support for port management
  soundwire: Add stream configuration APIs
  soundwire: cdns: Add port routines
  soundwire: cdns: Add stream routines
  soundwire: intel: Add stream initialization
  soundwire: intel: Add audio DAI ops

 .../driver-api/soundwire/error_handling.rst        |  65 ++
 Documentation/driver-api/soundwire/index.rst       |   3 +
 Documentation/driver-api/soundwire/locking.rst     | 106 +++
 Documentation/driver-api/soundwire/stream.rst      | 369 +++++++++
 drivers/soundwire/Kconfig                          |   2 +-
 drivers/soundwire/Makefile                         |   3 +-
 drivers/soundwire/bus.c                            |  44 +
 drivers/soundwire/bus.h                            |  81 ++
 drivers/soundwire/cadence_master.c                 | 449 +++++++++-
 drivers/soundwire/cadence_master.h                 | 151 ++++
 drivers/soundwire/intel.c                          | 589 ++++++++++++-
 drivers/soundwire/intel.h                          |   4 +
 drivers/soundwire/intel_init.c                     |   3 +
 drivers/soundwire/runtime.c                        | 907 +++++++++++++++++++++
 drivers/soundwire/stream.c                         | 678 +++++++++++++++
 include/linux/soundwire/sdw.h                      | 337 +++++++-
 include/linux/soundwire/sdw_intel.h                |  14 +
 include/sound/soc-dai.h                            |   5 +
 sound/soc/soc-core.c                               |  20 +
 19 files changed, 3816 insertions(+), 14 deletions(-)
 create mode 100644 Documentation/driver-api/soundwire/error_handling.rst
 create mode 100644 Documentation/driver-api/soundwire/locking.rst
 create mode 100644 Documentation/driver-api/soundwire/stream.rst
 create mode 100644 drivers/soundwire/runtime.c
 create mode 100644 drivers/soundwire/stream.c

-- 
2.7.4

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH 01/13] soundwire: Add more documentation
  2018-03-28  9:38 [PATCH 00/13] soundwire: Add stream support Vinod Koul
@ 2018-03-28  9:38 ` Vinod Koul
  2018-03-30  1:47   ` Pierre-Louis Bossart
  2018-03-28  9:38 ` [PATCH 02/13] soundwire: Add support for SoundWire stream management Vinod Koul
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 25+ messages in thread
From: Vinod Koul @ 2018-03-28  9:38 UTC (permalink / raw)
  To: Greg KH
  Cc: ALSA, tiwai, Pierre-Louis Bossart, liam.r.girdwood,
	patches.audio, broonie, Sanyog Kale, Vinod Koul

From: Sanyog Kale <sanyog.r.kale@intel.com>

This adds documentation for error handling, locking and streams.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 .../driver-api/soundwire/error_handling.rst        |  65 ++++
 Documentation/driver-api/soundwire/index.rst       |   3 +
 Documentation/driver-api/soundwire/locking.rst     | 106 ++++++
 Documentation/driver-api/soundwire/stream.rst      | 369 +++++++++++++++++++++
 4 files changed, 543 insertions(+)
 create mode 100644 Documentation/driver-api/soundwire/error_handling.rst
 create mode 100644 Documentation/driver-api/soundwire/locking.rst
 create mode 100644 Documentation/driver-api/soundwire/stream.rst

diff --git a/Documentation/driver-api/soundwire/error_handling.rst b/Documentation/driver-api/soundwire/error_handling.rst
new file mode 100644
index 000000000000..aa3a0a23a066
--- /dev/null
+++ b/Documentation/driver-api/soundwire/error_handling.rst
@@ -0,0 +1,65 @@
+========================
+SoundWire Error Handling
+========================
+
+The SoundWire PHY was designed with care and errors on the bus are going to
+be very unlikely, and if they happen it should be limited to single bit
+errors. Examples of this design can be found in the synchronization
+mechanism (sync loss after two errors) and short CRCs used for the Bulk
+Register Access.
+
+The errors can be detected with multiple mechanisms:
+
+1. Bus clash or parity errors: This mechanism relies on low-level detectors
+   that are independent of the payload and usages, and they cover both control
+   and audio data. The current implementation only logs such errors.
+   Improvements could be invalidating an entire programming sequence and
+   restarting from a known position. In the case of such errors outside of a
+   control/command sequence, there is no concealment or recovery for audio
+   data enabled by the SoundWire protocol, the location of the error will also
+   impact its audibility (most-significant bits will be more impacted in PCM),
+   and after a number of such errors are detected the bus might be reset. Note
+   that bus clashes due to programming errors (two streams using the same bit
+   slots) or electrical issues during the transmit/receive transition cannot
+   be distinguished, although a recurring bus clash when audio is enabled is a
+   indication of a bus allocation issue. The interrupt mechanism can also help
+   identify Slaves which detected a Bus Clash or a Parity Error, but they may
+   not be responsible for the errors so resetting them individually is not a
+   viable recovery strategy.
+
+2. Command status: Each command is associated with a status, which only
+   covers transmission of the data between devices. The ACK status indicates
+   that the command was received and will be executed by the end of the
+   current frame. A NAK indicates that the command was in error and will not
+   be applied. In case of a bad programming (command sent to non-existent
+   Slave or to a non-implemented register) or electrical issue, no response
+   signals the command was ignored. Some Master implementations allow for a
+   command to be retransmitted several times.  If the retransmission fails,
+   backtracking and restarting the entire programming sequence might be a
+   solution. Alternatively some implementations might directly issue a bus
+   reset and re-enumerate all devices.
+
+3. Timeouts: In a number of cases such as ChannelPrepare or
+   ClockStopPrepare, the bus driver is supposed to poll a register field until
+   it transitions to a NotFinished value of zero. The MIPI SoundWire spec 1.1
+   does not define timeouts but the MIPI SoundWire DisCo document adds
+   recommendation on timeouts. If such configurations do not complete, the
+   driver will return a -ETIMEOUT. Such timeouts are symptoms of a faulty
+   Slave device and are likely impossible to recover from.
+
+Errors during global reconfiguration sequences are extremely difficult to
+handle:
+
+1. BankSwitch: An error during the last command issuing a BankSwitch is
+   difficult to backtrack from. Retransmitting the Bank Switch command may be
+   possible in a single segment setup, but this can lead to synchronization
+   problems when enabling multiple bus segments (a command with side effects
+   such as frame reconfiguration would be handled at different times). A global
+   hard-reset might be the best solution.
+
+Note that SoundWire does not provide a mechanism to detect illegal values
+written in valid registers. In a number of cases the standard even mentions
+that the Slave might behave in implementation-defined ways. The bus
+implementation does not provide a recovery mechanism for such errors, Slave
+or Master driver implementers are responsible for writing valid values in
+valid registers and implement additional range checking if needed.
diff --git a/Documentation/driver-api/soundwire/index.rst b/Documentation/driver-api/soundwire/index.rst
index 647e94654752..6db026028f27 100644
--- a/Documentation/driver-api/soundwire/index.rst
+++ b/Documentation/driver-api/soundwire/index.rst
@@ -6,6 +6,9 @@ SoundWire Documentation
    :maxdepth: 1
 
    summary
+   stream
+   error_handling
+   locking
 
 .. only::  subproject
 
diff --git a/Documentation/driver-api/soundwire/locking.rst b/Documentation/driver-api/soundwire/locking.rst
new file mode 100644
index 000000000000..06c7000160ee
--- /dev/null
+++ b/Documentation/driver-api/soundwire/locking.rst
@@ -0,0 +1,106 @@
+=================
+SoundWire Locking
+=================
+
+This document explains locking mechanism of the SoundWire Bus. Bus uses
+following locks in order to avoid race conditions in Bus operations on
+shared resources.
+
+  - Bus lock
+
+  - Message lock
+
+Bus lock
+========
+
+SoundWire Bus lock is a mutex and is part of Bus data structure
+(sdw_bus) which is used for every Bus instance. This lock is used to
+serialize each of the following operations(s) within SoundWire Bus instance.
+
+  - Addition and removal of Slave(s), changing Slave status.
+
+  - Prepare, Enable, Disable and De-prepare stream operations.
+
+  - Access of Stream data structure.
+
+Message lock
+============
+
+SoundWire message transfer lock. This mutex is part of
+Bus data structure (sdw_bus). This lock is used to serialize the message
+transfers (read/write) within a SoundWire Bus instance.
+
+Below examples show how locks are acquired.
+
+Example 1
+---------
+
+Message transfer.
+
+  1. For every message transfer
+
+     a. Acquire Message lock.
+
+     b. Transfer message (Read/Write) to Slave1 or broadcast message on
+        Bus in case of bank switch.
+
+     c. Release Message lock ::
+
+	+----------+                    +---------+
+	|          |                    |         |
+	|   Bus    |                    | Master  |
+	|          |                    | Driver  |
+	|          |                    |         |
+	+----+-----+                    +----+----+
+	     |                               |
+	     |     bus->ops->xfer_msg()      |
+	     +------------------------------->   a. Acquire Message lock
+	     |                               |   b. Transfer message
+	     |                               |
+	     <-------------------------------+   c. Release Message lock
+	     |    return success/error       |   d. Return success/error
+	     |                               |
+	     +                               +
+
+Example 2
+---------
+
+Prepare operation.
+
+  1. Acquire lock for Bus instance associated with Master 1.
+
+  2. For every message transfer in Prepare operation
+
+     a. Acquire Message lock.
+
+     b. Transfer message (Read/Write) to Slave1 or broadcast message on
+        Bus in case of bank switch.
+
+     c. Release Message lock.
+
+  3. Release lock for Bus instance associated with Master 1 ::
+
+	+----------+                    +---------+
+	|          |                    |         |
+	|   Bus    |                    | Master  |
+	|          |                    | Driver  |
+	|          |                    |         |
+	+----+-----+                    +----+----+
+	     |                               |
+	     |    sdw_prepare_stream()       |
+	     +------------------------------->   1. Acquire bus lock
+	     |                               |   2. Perform stream prepare
+	     |                               |
+	     |                               |
+	     |     bus->ops->xfer_msg()      |
+	     +------------------------------->   a. Acquire Message lock
+	     |                               |   b. Transfer message
+	     |                               |
+	     <-------------------------------+   c. Release Message lock
+	     |    return success/error       |   d. Return success/error
+	     |                               |
+	     |                               |
+	     |    return success/error       |   3. Release bus lock
+	     <-------------------------------+   4. Return success/error
+	     |                               |
+	     +                               +
diff --git a/Documentation/driver-api/soundwire/stream.rst b/Documentation/driver-api/soundwire/stream.rst
new file mode 100644
index 000000000000..94fc0a31df77
--- /dev/null
+++ b/Documentation/driver-api/soundwire/stream.rst
@@ -0,0 +1,369 @@
+=========================
+Audio Stream in SoundWire
+=========================
+
+An audio stream is a logical or virtual connection created between
+
+  (1) System memory buffer(s) and Codec(s)
+
+  (2) DSP memory buffer(s) and Codec(s)
+
+  (3) FIFO(s) and Codec(s)
+
+  (4) Codec(s) and Codec(s)
+
+which is typically driven by a DMA(s) channel through the data link. An
+audio stream contains one or more channels of data. All channels within
+stream must have same sample rate and same sample size.
+
+Assume a stream with two channels (Left & Right) is opened using SoundWire
+interface. Below are some ways a stream can be represented in SoundWire.
+
+Stream Sample in memory (System memory, DSP memory or FIFOs) ::
+
+	-------------------------
+	| L | R | L | R | L | R |
+	-------------------------
+
+Example 1: Stereo Stream with L and R channels is rendered from Master to
+Slave. Both Master and Slave is using single port. ::
+
+	+---------------+                    Clock Signal  +---------------+
+	|    Master     +----------------------------------+     Slave     |
+	|   Interface   |                                  |   Interface   |
+	|               |                                  |       1       |
+	|               |                     Data Signal  |               |
+	|    L  +  R    +----------------------------------+    L  +  R    |
+	|     (Data)    |     Data Direction               |     (Data)    |
+	+---------------+  +----------------------->       +---------------+
+
+
+Example 2: Stereo Stream with L and R channels is captured from Slave to
+Master. Both Master and Slave is using single port. ::
+
+
+	+---------------+                    Clock Signal  +---------------+
+	|    Master     +----------------------------------+     Slave     |
+	|   Interface   |                                  |   Interface   |
+	|               |                                  |       1       |
+	|               |                     Data Signal  |               |
+	|    L  +  R    +----------------------------------+    L  +  R    |
+	|     (Data)    |     Data Direction               |     (Data)    |
+	+---------------+  <-----------------------+       +---------------+
+
+
+Example 3: Stereo Stream with L and R channels is rendered by Master. Each
+of the L and R channel is received by two different Slaves. Master and both
+Slaves are using single port. ::
+
+	+---------------+                    Clock Signal  +---------------+
+	|    Master     +---------+------------------------+     Slave     |
+	|   Interface   |         |                        |   Interface   |
+	|               |         |                        |       1       |
+	|               |         |           Data Signal  |               |
+	|    L  +  R    +---+------------------------------+       L       |
+	|     (Data)    |   |     |    Data Direction      |     (Data)    |
+	+---------------+   |     |   +------------->      +---------------+
+	                    |     |
+	                    |     |
+	                    |     |                        +---------------+
+	                    |     +----------------------> |     Slave     |
+	                    |                              |   Interface   |
+	                    |                              |       2       |
+	                    |                              |               |
+	                    +----------------------------> |       R       |
+	                                                   |     (Data)    |
+	                                                   +---------------+
+
+
+Example 4: Stereo Stream with L and R channel is rendered by two different
+Ports of the Master and is received by only single Port of the Slave
+interface. ::
+
+	+--------------------+
+	|                    |
+	|     +--------------+                             +----------------+
+	|     |             ||                             |                |
+	|     |  Data Port  ||  L Channel                  |                |
+	|     |      1      |------------+                 |                |
+	|     |  L Channel  ||           |                 +-----+----+     |
+	|     |   (Data)    ||           |   L + R Channel ||    Data |     |
+	| Master  +----------+           | +---+---------> ||    Port |     |
+	| Interface          |           |                 ||     1   |     |
+	|     +--------------+           |                 ||         |     |
+	|     |             ||           |                 +----------+     |
+	|     |  Data Port  |------------+                 |                |
+	|     |      2      ||  R Channel                  |     Slave      |
+	|     |  R Channel  ||                             |   Interface    |
+	|     |   (Data)    ||                             |       1        |
+	|     +--------------+         Clock Signal        |     L  +  R    |
+	|                    +---------------------------> |      (Data)    |
+	+--------------------+                             |                |
+							   +----------------+
+
+SoundWire Stream Management flow
+================================
+
+Stream definitions
+------------------
+
+  (1) Current stream: This is classified as the stream on which operation has
+      to be performed like prepare, enable, disable, de-prepare etc.
+
+  (2) Active stream: This is classified as the stream which is already active
+      on Bus other than current stream. There can be multiple active streams
+      on the Bus.
+
+SoundWire Bus manages stream operations for each stream getting
+rendered/captured on the SoundWire Bus. This section explains Bus operations
+done for each of the stream allocated/released on Bus.  Following are the
+stream states maintained by the Bus for each of the audio stream.
+
+
+SoundWire stream states
+-----------------------
+
+Below shows the SoundWire stream states and state transition diagram. ::
+
+	+---------+     +----------+     +----------+     +----------+
+	|  ALLOC  +---->|  CONFIG  +---->| PREPARE  +---->|  ENABLE  |
+	|  STATE  |     |  STATE   |     |  STATE   |     |  STATE   |
+	+---------+     +----------+     +----------+     +----+-----+
+	                                                       ^
+	                                                       |
+	                                                       |
+	                                                       v
+	         +----------+           +-----------+     +----+-----+
+	         |  RELEASE |<----------+ DEPREPARE |<----+  DISABLE |
+	         |   STATE  |           |  STATE    |     |  STATE   |
+	         +----------+           +-----------+     +----------+
+
+NOTE: All stream transitions in MIPI Spec are NOT supported by software.
+
+Stream State Operations
+-----------------------
+
+Below section explains the operations done by the Bus on Master(s) and
+Slave(s) as part of stream state transitions.
+
+SDW_STREAM_ALLOC
+~~~~~~~~~~~~~~~~
+
+Allocation state for stream. This is the entry state
+of the stream. Operations performed before entering in this state:
+
+  (1) A stream runtime is allocated for the stream. This stream
+      runtime is used as a reference for all the operations performed
+      on the stream.
+
+  (2) The resources required for holding stream runtime information are
+      allocated and initialized. This holds all stream related information
+      such as stream type (PCM/PDM) and parameters, Master and Slave
+      interface associated with the stream, reference counting, stream
+      state etc.
+
+After all above operations are successful, stream state is set to
+``SDW_STREAM_ALLOC``.
+
+Bus implements below API for allocate a stream which needs to be called once
+per stream. From ASoC DPCM framework, this stream state maybe linked to
+.startup() operation.
+
+  .. code-block:: c
+  int sdw_alloc_stream(char * stream_name);
+
+
+SDW_STREAM_CONFIG
+~~~~~~~~~~~~~~~~~
+
+Configuration state of stream. Operations performed before entering in
+this state:
+
+  (1) The resources allocated for stream information in SDW_STREAM_ALLOC
+      state are updated here. This includes stream parameters, Master(s)
+      and Slave(s) runtime information associated with current stream.
+
+  (2) All the Master(s) and Slave(s) associated with current stream provide
+      the port information to Bus which includes port numbers allocated by
+      Master(s) and Slave(s) for current stream and their channel mask.
+
+After all above operations are successful, stream state is set to
+``SDW_STREAM_CONFIG``.
+
+Bus implements below APIs for CONFIG state which needs to be called by
+the respective Master(s) and Slave(s) associated with stream. These APIs can
+only be invoked once by respective Master(s) and Slave(s). From ASoC DPCM
+framework, this stream state is linked to .hw_params() operation.
+
+  .. code-block:: c
+  int sdw_stream_add_master(struct sdw_bus * bus,
+		struct sdw_stream_config * stream_config,
+		struct sdw_ports_config * ports_config,
+		struct sdw_stream_runtime * stream);
+
+  int sdw_stream_add_slave(struct sdw_slave * slave,
+		struct sdw_stream_config * stream_config,
+		struct sdw_ports_config * ports_config,
+		struct sdw_stream_runtime * stream);
+
+
+SDW_STREAM_PREPARE
+~~~~~~~~~~~~~~~~~~
+
+Prepare state of stream. Operations performed before entering in this state:
+
+  (1) Bus parameters such as bandwidth, frame shape, clock frequency,
+      are computed based on current stream as well as already active
+      stream(s) on Bus. Re-computation is required to accommodate current
+      stream on the Bus.
+
+  (2) Transport and port parameters of all Master(s) and Slave(s) port(s) are
+      computed for the current as well as already active stream based on frame
+      shape and clock frequency computed in step 1.
+
+  (3) Computed Bus and transport parameters are programmed in Master(s) and
+      Slave(s) registers. The banked registers programming is done on the
+      alternate bank (bank currently unused). Port(s) are enabled for the
+      already active stream(s) on the alternate bank (bank currently unused).
+      This is done in order to not disrupt already active stream(s).
+
+  (4) Once all the values are programmed, Bus initiates switch to alternate
+      bank where all new values programmed gets into effect.
+
+  (5) Ports of Master(s) and Slave(s) for current stream are prepared by
+      programming PrepareCtrl register.
+
+After all above operations are successful, stream state is set to
+``SDW_STREAM_PREPARE``.
+
+Bus implements below API for PREPARE state which needs to be called once per
+stream. From ASoC DPCM framework, this stream state is linked to
+.prepare() operation.
+
+  .. code-block:: c
+  int sdw_prepare_stream(struct sdw_stream_runtime * stream);
+
+
+SDW_STREAM_ENABLE
+~~~~~~~~~~~~~~~~~
+
+Enable state of stream. The data port(s) are enabled upon entering this state.
+Operations performed before entering in this state:
+
+  (1) All the values computed in SDW_STREAM_PREPARE state are programmed
+      in alternate bank (bank currently unused). It includes programming of
+      already active stream(s) as well.
+
+  (2) All the Master(s) and Slave(s) port(s) for the current stream are
+      enabled on alternate bank (bank currently unused) by programming
+      ChannelEn register.
+
+  (3) Once all the values are programmed, Bus initiates switch to alternate
+      bank where all new values programmed gets into effect and port(s)
+      associated with current stream are enabled.
+
+After all above operations are successful, stream state is set to
+``SDW_STREAM_ENABLE``.
+
+Bus implements below API for ENABLE state which needs to be called once per
+stream. From ASoC DPCM framework, this stream state is linked to
+.trigger() start operation.
+
+  .. code-block:: c
+  int sdw_enable_stream(struct sdw_stream_runtime * stream);
+
+SDW_STREAM_DISABLE
+~~~~~~~~~~~~~~~~~~
+
+Disable state of stream. The data port(s) are disabled upon exiting this state.
+Operations performed before entering in this state:
+
+  (1) All the Master(s) and Slave(s) port(s) for the current stream are
+      disabled on alternate bank (bank currently unused) by programming
+      ChannelEn register.
+
+  (2) All the current configuration of Bus and active stream(s) are programmed
+      into alternate bank (bank currently unused).
+
+  (3) Once all the values are programmed, Bus initiates switch to alternate
+      bank where all new values programmed gets into effect and port(s) associated
+      with current stream are disabled.
+
+After all above operations are successful, stream state is set to
+``SDW_STREAM_DISABLE``.
+
+Bus implements below API for DISABLE state which needs to be called once
+per stream. From ASoC DPCM framework, this stream state is linked to
+.trigger() stop operation.
+
+  .. code-block:: c
+  int sdw_disable_stream(struct sdw_stream_runtime * stream);
+
+
+SDW_STREAM_DEPREPARE
+~~~~~~~~~~~~~~~~~~~~
+
+De-prepare state of stream. Operations performed before entering in this
+state:
+
+  (1) All the port(s) of Master(s) and Slave(s) for current stream are
+      de-prepared by programming PrepareCtrl register.
+
+  (2) The payload bandwidth of current stream is reduce from the total
+      bandwidth requirement of bus.
+
+After all above operations are successful, stream state is set to
+``SDW_STREAM_DEPREPARE``.
+
+Bus implements below API for DEPREPARE state which needs to be called once
+per stream. From ASoC DPCM framework, this stream state is linked to
+.trigger() stop operation.
+
+  .. code-block:: c
+  int sdw_deprepare_stream(struct sdw_stream_runtime * stream);
+
+
+SDW_STREAM_RELEASE
+~~~~~~~~~~~~~~~~~~
+
+Release state of stream. Operations performed before entering in this state:
+
+  (1) Release port resources for all Master(s) and Slave(s) port(s)
+      associated with current stream.
+
+  (2) Release Master(s) and Slave(s) runtime resources associated with
+      current stream.
+
+  (3) Release stream runtime resources associated with current stream.
+
+After all above operations are successful, stream state is set to
+``SDW_STREAM_RELEASE``.
+
+Bus implements below APIs for RELEASE state which needs to be called by
+all the Master(s) and Slave(s) associated with stream. From ASoC DPCM
+framework, this stream state is linked to .hw_free() operation.
+
+  .. code-block:: c
+  int sdw_stream_remove_master(struct sdw_bus * bus,
+		struct sdw_stream_runtime * stream);
+  int sdw_stream_remove_slave(struct sdw_slave * slave,
+		struct sdw_stream_runtime * stream);
+
+
+The .shutdown() ASoC DPCM operation calls below Bus API to release
+stream assigned as part of ALLOC state. free sdw_stream_runtime
+> data structure.
+
+
+In .shutdown() the data structure maintaining stream state are freed up.
+
+  .. code-block:: c
+  void sdw_release_stream(struct sdw_stream_runtime * stream);
+
+Not Supported
+=============
+
+1. A single port with multiple channels supported cannot be used between two
+streams or across stream. For example a port with 4 channels cannot be used
+to handle 2 independent stereo streams even though it's possible in theory
+in SoundWire.
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 02/13] soundwire: Add support for SoundWire stream management
  2018-03-28  9:38 [PATCH 00/13] soundwire: Add stream support Vinod Koul
  2018-03-28  9:38 ` [PATCH 01/13] soundwire: Add more documentation Vinod Koul
@ 2018-03-28  9:38 ` Vinod Koul
  2018-03-30  1:57   ` Pierre-Louis Bossart
  2018-03-28  9:38 ` [PATCH 03/13] soundwire: Add support for port management Vinod Koul
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 25+ messages in thread
From: Vinod Koul @ 2018-03-28  9:38 UTC (permalink / raw)
  To: Greg KH
  Cc: ALSA, tiwai, Pierre-Louis Bossart, liam.r.girdwood,
	patches.audio, broonie, Sanyog Kale, Vinod Koul

From: Sanyog Kale <sanyog.r.kale@intel.com>

This patch adds APIs and relevant stream data structures
for initialization and release of stream.

Signed-off-by: Hardik T Shah <hardik.t.shah@intel.com>
Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 drivers/soundwire/Makefile    |   2 +-
 drivers/soundwire/bus.c       |   1 +
 drivers/soundwire/bus.h       |  32 ++++
 drivers/soundwire/stream.c    | 337 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/soundwire/sdw.h | 112 ++++++++++++++
 5 files changed, 483 insertions(+), 1 deletion(-)
 create mode 100644 drivers/soundwire/stream.c

diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile
index e1a74c5692aa..5817beaca0e1 100644
--- a/drivers/soundwire/Makefile
+++ b/drivers/soundwire/Makefile
@@ -3,7 +3,7 @@
 #
 
 #Bus Objs
-soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o
+soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o stream.o
 obj-$(CONFIG_SOUNDWIRE_BUS) += soundwire-bus.o
 
 #Cadence Objs
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c
index d6dc8e7a8614..abf046f6b188 100644
--- a/drivers/soundwire/bus.c
+++ b/drivers/soundwire/bus.c
@@ -32,6 +32,7 @@ int sdw_add_bus_master(struct sdw_bus *bus)
 	mutex_init(&bus->msg_lock);
 	mutex_init(&bus->bus_lock);
 	INIT_LIST_HEAD(&bus->slaves);
+	INIT_LIST_HEAD(&bus->m_rt_list);
 
 	if (bus->ops->read_prop) {
 		ret = bus->ops->read_prop(bus);
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h
index 345c34d697e9..0f55a18fa652 100644
--- a/drivers/soundwire/bus.h
+++ b/drivers/soundwire/bus.h
@@ -45,6 +45,38 @@ struct sdw_msg {
 	bool page;
 };
 
+/**
+ * sdw_slave_runtime: Runtime Stream parameters for Slave
+ *
+ * @slave: Slave handle
+ * @direction: Data direction w.r.t Slave
+ * @ch_count: Channel count of the Slave w.r.t stream
+ * @m_rt_node: sdw_master_runtime list node
+ */
+struct sdw_slave_runtime {
+	struct sdw_slave *slave;
+	enum sdw_data_direction direction;
+	unsigned int ch_count;
+	struct list_head m_rt_node;
+};
+
+/**
+ * sdw_master_runtime: Runtime stream parameters for Master
+ *
+ * @bus: Bus handle
+ * @stream: Stream runtime handle
+ * @ch_count: Master channel count
+ * @slave_rt_list: Slave runtime list
+ * @bus_node: sdw_bus m_rt_list node
+ */
+struct sdw_master_runtime {
+	struct sdw_bus *bus;
+	struct sdw_stream_runtime *stream;
+	unsigned int ch_count;
+	struct list_head slave_rt_list;
+	struct list_head bus_node;
+};
+
 int sdw_transfer(struct sdw_bus *bus, struct sdw_msg *msg);
 int sdw_transfer_defer(struct sdw_bus *bus, struct sdw_msg *msg,
 				struct sdw_defer *defer);
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c
new file mode 100644
index 000000000000..00b9fcea4369
--- /dev/null
+++ b/drivers/soundwire/stream.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2015-18 Intel Corporation.
+
+/*
+ *  stream.c - SoundWire Bus stream operations.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/slab.h>
+#include <linux/soundwire/sdw.h>
+#include "bus.h"
+
+/**
+ * sdw_release_stream: Free the assigned stream runtime
+ *
+ * @stream: SoundWire stream runtime
+ *
+ * sdw_release_stream should be called only once per stream
+ */
+void sdw_release_stream(struct sdw_stream_runtime *stream)
+{
+	kfree(stream);
+}
+EXPORT_SYMBOL(sdw_release_stream);
+
+/**
+ * sdw_alloc_stream: Allocate and return stream runtime
+ *
+ * @stream_name: SoundWire stream name
+ *
+ * Allocates a SoundWire stream runtime instance.
+ * sdw_alloc_stream should be called only once per stream
+ */
+struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name)
+{
+	struct sdw_stream_runtime *stream;
+
+	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+	if (!stream)
+		return NULL;
+
+	stream->name = stream_name;
+	stream->state = SDW_STREAM_ALLOC;
+
+	return stream;
+}
+EXPORT_SYMBOL(sdw_alloc_stream);
+
+/**
+ * sdw_alloc_master_rt: Allocates and initialize Master runtime handle
+ *
+ * @bus: SDW bus instance
+ * @stream_config: Stream configuration
+ * @stream: Stream runtime handle.
+ *
+ * This function is to be called with bus_lock held.
+ */
+static struct sdw_master_runtime
+*sdw_alloc_master_rt(struct sdw_bus *bus,
+			struct sdw_stream_config *stream_config,
+			struct sdw_stream_runtime *stream)
+{
+	struct sdw_master_runtime *m_rt;
+
+	m_rt = stream->m_rt;
+	if (m_rt)
+		goto stream_config;
+
+	m_rt = kzalloc(sizeof(*m_rt), GFP_KERNEL);
+	if (!m_rt)
+		return NULL;
+
+	/* Initialization of Master runtime handle */
+	INIT_LIST_HEAD(&m_rt->slave_rt_list);
+	stream->m_rt = m_rt;
+
+	list_add_tail(&m_rt->bus_node, &bus->m_rt_list);
+
+stream_config:
+	m_rt->ch_count = stream_config->ch_count;
+	m_rt->bus = bus;
+	m_rt->stream = stream;
+
+	return m_rt;
+}
+
+/**
+ * sdw_alloc_slave_rt: Allocate and initialize Slave runtime handle.
+ *
+ * @slave: Slave handle
+ * @stream_config: Stream configuration
+ * @stream: Stream runtime handle
+ *
+ * This function is to be called with bus_lock held.
+ */
+static struct sdw_slave_runtime
+*sdw_alloc_slave_rt(struct sdw_slave *slave,
+			struct sdw_stream_config *stream_config,
+			struct sdw_stream_runtime *stream)
+{
+	struct sdw_slave_runtime *s_rt = NULL;
+
+	s_rt = kzalloc(sizeof(*s_rt), GFP_KERNEL);
+	if (!s_rt)
+		return NULL;
+
+
+	s_rt->ch_count = stream_config->ch_count;
+	s_rt->direction = stream_config->direction;
+	s_rt->slave = slave;
+
+	return s_rt;
+}
+
+/**
+ * sdw_release_slave_stream: Free Slave(s) runtime handle
+ *
+ * @slave: Slave handle.
+ * @stream: Stream runtime handle.
+ *
+ * This function is to be called with bus_lock held.
+ */
+static void sdw_release_slave_stream(struct sdw_slave *slave,
+			struct sdw_stream_runtime *stream)
+{
+	struct sdw_slave_runtime *s_rt, *_s_rt;
+	struct sdw_master_runtime *m_rt = stream->m_rt;
+
+	/* Retrieve Slave runtime handle */
+	list_for_each_entry_safe(s_rt, _s_rt,
+			&m_rt->slave_rt_list, m_rt_node) {
+
+		if (s_rt->slave == slave) {
+			list_del(&s_rt->m_rt_node);
+			kfree(s_rt);
+			return;
+		}
+	}
+}
+
+static void sdw_release_master_stream(struct sdw_stream_runtime *stream)
+{
+	struct sdw_master_runtime *m_rt = stream->m_rt;
+	struct sdw_slave_runtime *s_rt, *_s_rt;
+
+	list_for_each_entry_safe(s_rt, _s_rt,
+			&m_rt->slave_rt_list, m_rt_node)
+		sdw_release_slave_stream(s_rt->slave, stream);
+
+	list_del(&m_rt->bus_node);
+}
+
+/**
+ * sdw_stream_remove_master: Remove master from sdw_stream
+ *
+ * @bus: SDW Bus instance
+ * @stream: Soundwire stream
+ *
+ * This removes and frees master_rt from a stream
+ */
+
+int sdw_stream_remove_master(struct sdw_bus *bus,
+		struct sdw_stream_runtime *stream)
+{
+	mutex_lock(&bus->bus_lock);
+
+	sdw_release_master_stream(stream);
+	stream->state = SDW_STREAM_RELEASE;
+	kfree(stream->m_rt);
+	stream->m_rt = NULL;
+
+	mutex_unlock(&bus->bus_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(sdw_stream_remove_master);
+
+/**
+ * sdw_stream_remove_slave: Remove slave from sdw_stream
+ *
+ * @slave: SDW Slave instance
+ * @stream: Soundwire stream
+ *
+ * This removes and frees slave_rt from a stream
+ */
+
+int sdw_stream_remove_slave(struct sdw_slave *slave,
+		struct sdw_stream_runtime *stream)
+{
+	mutex_lock(&slave->bus->bus_lock);
+
+	sdw_release_slave_stream(slave, stream);
+
+	mutex_unlock(&slave->bus->bus_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(sdw_stream_remove_slave);
+
+static int sdw_config_stream(struct device *dev,
+		struct sdw_stream_runtime *stream,
+		struct sdw_stream_config *stream_config)
+{
+
+	/*
+	 * Update the stream rate, channel and bps based on data
+	 * transmitter. For more than one transmitter (multilink),
+	 * match the rate, bps and increment number of channels.
+	 */
+	if ((stream->params.rate) &&
+			(stream->params.rate != stream_config->frame_rate)) {
+		dev_err(dev, "rate for multilink not matching, stream:%s",
+				stream->name);
+		return -EINVAL;
+	}
+
+	if ((stream->params.bps) &&
+			(stream->params.bps != stream_config->bps)) {
+		dev_err(dev, "bps for multilink not matching, stream:%s",
+				stream->name);
+		return -EINVAL;
+	}
+
+	stream->params.rate = stream_config->frame_rate;
+	stream->params.bps = stream_config->bps;
+	stream->params.ch_count += stream_config->ch_count;
+	stream->type = stream_config->type;
+
+	return 0;
+}
+
+/**
+ * sdw_stream_add_master: Allocate and add master runtime to a stream
+ *
+ * @bus: SDW Bus instance
+ * @stream_config: Stream configuration for audio stream
+ * @stream: Soundwire stream
+ */
+int sdw_stream_add_master(struct sdw_bus *bus,
+		struct sdw_stream_config *stream_config,
+		struct sdw_stream_runtime *stream)
+{
+	struct sdw_master_runtime *m_rt = NULL;
+	int ret;
+
+	if (stream->state != SDW_STREAM_ALLOC &&
+			stream->state != SDW_STREAM_CONFIG) {
+		dev_err(bus->dev,
+			"Invalid stream state %d", stream->state);
+		return -EINVAL;
+	}
+
+	mutex_lock(&bus->bus_lock);
+
+	m_rt = sdw_alloc_master_rt(bus, stream_config, stream);
+	if (!m_rt) {
+		dev_err(bus->dev,
+				"Master runtime config failed for stream:%s",
+				stream->name);
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	if (!list_empty(&m_rt->slave_rt_list) &&
+			stream->state == SDW_STREAM_ALLOC)
+		stream->state = SDW_STREAM_CONFIG;
+
+error:
+	mutex_unlock(&bus->bus_lock);
+	return ret;
+
+}
+EXPORT_SYMBOL(sdw_stream_add_master);
+
+/**
+ * sdw_stream_add_slave: Allocate and add master/slave runtime to a stream
+ *
+ * @slave: SDW Slave instance
+ * @stream_config: Stream configuration for audio stream
+ * @stream: Soundwire stream
+ */
+int sdw_stream_add_slave(struct sdw_slave *slave,
+		struct sdw_stream_config *stream_config,
+		struct sdw_stream_runtime *stream)
+{
+	struct sdw_slave_runtime *s_rt;
+	struct sdw_master_runtime *m_rt;
+	int ret;
+
+	if (stream->state != SDW_STREAM_ALLOC &&
+			stream->state != SDW_STREAM_CONFIG) {
+		dev_err(&slave->dev,
+			"Invalid stream state %d", stream->state);
+		return -EINVAL;
+	}
+
+	mutex_lock(&slave->bus->bus_lock);
+
+	/*
+	 * If this API is invoked by slave first then m_rt is not valid.
+	 * So, allocate that and add the slave to it.
+	 */
+	m_rt = sdw_alloc_master_rt(slave->bus, stream_config, stream);
+	if (!m_rt) {
+		dev_err(&slave->dev,
+				"alloc master runtime failed for stream:%s",
+				stream->name);
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	s_rt = sdw_alloc_slave_rt(slave, stream_config, stream);
+	if (!s_rt) {
+		dev_err(&slave->dev,
+				"Slave runtime config failed for stream:%s",
+				stream->name);
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	ret = sdw_config_stream(&slave->dev, stream, stream_config);
+	if (ret)
+		goto error;
+
+	list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list);
+
+	stream->state = SDW_STREAM_CONFIG;
+
+error:
+	mutex_unlock(&slave->bus->bus_lock);
+	return ret;
+}
+EXPORT_SYMBOL(sdw_stream_add_slave);
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h
index e91fdcf41049..cb006cfd1e31 100644
--- a/include/linux/soundwire/sdw.h
+++ b/include/linux/soundwire/sdw.h
@@ -61,6 +61,33 @@ enum sdw_command_response {
 	SDW_CMD_FAIL_OTHER = 4,
 };
 
+/**
+ * enum sdw_stream_type: data stream type
+ *
+ * @SDW_STREAM_PCM: PCM data stream
+ * @SDW_STREAM_PDM: PDM data stream
+ *
+ * spec doesn't define this, but is used in implementation
+ */
+enum sdw_stream_type {
+	SDW_STREAM_PCM = 0,
+	SDW_STREAM_PDM = 1,
+};
+
+/**
+ * enum sdw_data_direction: Data direction
+ *
+ * @SDW_DATA_DIR_IN: Data into Port
+ * @SDW_DATA_DIR_OUT: Data out of Port
+ *
+ * For TX it would refer to SDW_DATA_DIR_OUT. For RX it would be
+ * SDW_DATA_DIR_IN
+ */
+enum sdw_data_direction {
+	SDW_DATA_DIR_IN = 0,
+	SDW_DATA_DIR_OUT = 1,
+};
+
 /*
  * SDW properties, defined in MIPI DisCo spec v1.0
  */
@@ -450,6 +477,9 @@ struct sdw_master_ops {
  * @msg_lock: message lock
  * @ops: Master callback ops
  * @prop: Master properties
+ * @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
  * @defer_msg: Defer message
  * @clk_stop_timeout: Clock stop timeout computed
  */
@@ -462,6 +492,7 @@ struct sdw_bus {
 	struct mutex msg_lock;
 	const struct sdw_master_ops *ops;
 	struct sdw_master_prop prop;
+	struct list_head m_rt_list;
 	struct sdw_defer defer_msg;
 	unsigned int clk_stop_timeout;
 };
@@ -469,6 +500,87 @@ struct sdw_bus {
 int sdw_add_bus_master(struct sdw_bus *bus);
 void sdw_delete_bus_master(struct sdw_bus *bus);
 
+/**
+ * sdw_stream_config: Master or Slave stream configuration
+ *
+ * @frame_rate: Audio frame rate of the stream, in Hz
+ * @ch_count: Channel count of the stream
+ * @bps: Number of bits per audio sample
+ * @direction: Data direction
+ * @type: Stream type PCM or PDM
+ */
+struct sdw_stream_config {
+	unsigned int frame_rate;
+	unsigned int ch_count;
+	unsigned int bps;
+	enum sdw_data_direction direction;
+	enum sdw_stream_type type;
+};
+
+/**
+ * sdw_stream_state: Stream states
+ *
+ * @SDW_STREAM_RELEASE: Stream released
+ * @SDW_STREAM_ALLOC: New stream allocated.
+ * @SDW_STREAM_CONFIG: Stream configured
+ * @SDW_STREAM_PREPARE: Stream prepared
+ * @SDW_STREAM_ENABLE: Stream enabled
+ * @SDW_STREAM_DISABLE: Stream disabled
+ * @SDW_STREAM_DEPREPARE: Stream de-prepared
+ */
+enum sdw_stream_state {
+	SDW_STREAM_RELEASE = 0,
+	SDW_STREAM_ALLOC = 1,
+	SDW_STREAM_CONFIG = 2,
+	SDW_STREAM_PREPARE = 3,
+	SDW_STREAM_ENABLE = 4,
+	SDW_STREAM_DISABLE = 5,
+	SDW_STREAM_DEPREPARE = 6,
+};
+
+/**
+ * sdw_stream_params: Stream parameters
+ *
+ * @rate: Sampling frequency, in Hz
+ * @ch_count: Number of channels
+ * @bps: bits per channel sample
+ */
+struct sdw_stream_params {
+	unsigned int rate;
+	unsigned int ch_count;
+	unsigned int bps;
+};
+
+/**
+ * sdw_stream_runtime: Runtime stream parameters
+ *
+ * @name: SoundWire stream name
+ * @params: Stream parameters
+ * @state: Current state of the stream
+ * @type: Stream type PCM or PDM
+ * @m_rt: Master runtime
+ */
+struct sdw_stream_runtime {
+	char *name;
+	struct sdw_stream_params params;
+	enum sdw_stream_state state;
+	enum sdw_stream_type type;
+	struct sdw_master_runtime *m_rt;
+};
+
+struct sdw_stream_runtime *sdw_alloc_stream(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,
+		struct sdw_stream_runtime *stream);
+int sdw_stream_add_slave(struct sdw_slave *slave,
+		struct sdw_stream_config *stream_config,
+		struct sdw_stream_runtime *stream);
+int sdw_stream_remove_master(struct sdw_bus *bus,
+		struct sdw_stream_runtime *stream);
+int sdw_stream_remove_slave(struct sdw_slave *slave,
+		struct sdw_stream_runtime *stream);
+
 /* messaging and data APIs */
 
 int sdw_read(struct sdw_slave *slave, u32 addr);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 03/13] soundwire: Add support for port management
  2018-03-28  9:38 [PATCH 00/13] soundwire: Add stream support Vinod Koul
  2018-03-28  9:38 ` [PATCH 01/13] soundwire: Add more documentation Vinod Koul
  2018-03-28  9:38 ` [PATCH 02/13] soundwire: Add support for SoundWire stream management Vinod Koul
@ 2018-03-28  9:38 ` Vinod Koul
  2018-03-28  9:38 ` [PATCH 04/13] soundwire: Add Master and Slave port programming Vinod Koul
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Vinod Koul @ 2018-03-28  9:38 UTC (permalink / raw)
  To: Greg KH
  Cc: ALSA, tiwai, Pierre-Louis Bossart, liam.r.girdwood,
	patches.audio, broonie, Vinod Koul

Add Soundwire port data structures and APIS for initialization
and release of ports.

Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 drivers/soundwire/bus.h       |  32 +++++++++
 drivers/soundwire/stream.c    | 163 +++++++++++++++++++++++++++++++++++++++++-
 include/linux/soundwire/sdw.h |  67 +++++++++++++++++
 3 files changed, 259 insertions(+), 3 deletions(-)

diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h
index 0f55a18fa652..b28a0c224323 100644
--- a/drivers/soundwire/bus.h
+++ b/drivers/soundwire/bus.h
@@ -46,18 +46,45 @@ struct sdw_msg {
 };
 
 /**
+ * sdw_port_runtime: Runtime port parameters for Master or Slave
+ *
+ * @num: Port number. For audio streams, valid port number ranges from
+ * [1,14]
+ * @ch_mask: Channel mask
+ * @transport_params: Transport parameters
+ * @port_params: Port parameters
+ * @port_node: List node for Master or Slave port_list
+ *
+ * SoundWire spec has no mention of ports for Master interface but the
+ * concept is logically extended.
+ */
+struct sdw_port_runtime {
+	int num;
+	int ch_mask;
+	struct sdw_transport_params transport_params;
+	struct sdw_port_params port_params;
+	struct list_head port_node;
+};
+
+/**
  * sdw_slave_runtime: Runtime Stream parameters for Slave
  *
  * @slave: Slave handle
  * @direction: Data direction w.r.t Slave
  * @ch_count: Channel count of the Slave w.r.t stream
  * @m_rt_node: sdw_master_runtime list node
+ * @port_list: List of Slave Ports for this Stream. This list is
+ * used for computing and programming transport parameters, port
+ * parameters, prepare, enable, disable and de-prepare of Slave port
+ * maintains list of Slave runtime associated with Master runtime for
+ * this stream
  */
 struct sdw_slave_runtime {
 	struct sdw_slave *slave;
 	enum sdw_data_direction direction;
 	unsigned int ch_count;
 	struct list_head m_rt_node;
+	struct list_head port_list;
 };
 
 /**
@@ -67,6 +94,10 @@ struct sdw_slave_runtime {
  * @stream: Stream runtime handle
  * @ch_count: Master channel count
  * @slave_rt_list: Slave runtime list
+ * @port_list: List of Master Ports for this Stream. This list is
+ * used for computing and programming transport parameters, port
+ * parameters of Master port
+ * @slave_list: Slave runtime list
  * @bus_node: sdw_bus m_rt_list node
  */
 struct sdw_master_runtime {
@@ -74,6 +105,7 @@ struct sdw_master_runtime {
 	struct sdw_stream_runtime *stream;
 	unsigned int ch_count;
 	struct list_head slave_rt_list;
+	struct list_head port_list;
 	struct list_head bus_node;
 };
 
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c
index 00b9fcea4369..95ad714b9c76 100644
--- a/drivers/soundwire/stream.c
+++ b/drivers/soundwire/stream.c
@@ -75,6 +75,7 @@ static struct sdw_master_runtime
 		return NULL;
 
 	/* Initialization of Master runtime handle */
+	INIT_LIST_HEAD(&m_rt->port_list);
 	INIT_LIST_HEAD(&m_rt->slave_rt_list);
 	stream->m_rt = m_rt;
 
@@ -108,6 +109,7 @@ static struct sdw_slave_runtime
 	if (!s_rt)
 		return NULL;
 
+	INIT_LIST_HEAD(&s_rt->port_list);
 
 	s_rt->ch_count = stream_config->ch_count;
 	s_rt->direction = stream_config->direction;
@@ -116,6 +118,41 @@ static struct sdw_slave_runtime
 	return s_rt;
 }
 
+static void sdw_master_port_deconfig(struct sdw_bus *bus,
+			struct sdw_master_runtime *m_rt)
+{
+	struct sdw_port_runtime *p_rt, *_p_rt;
+
+	list_for_each_entry_safe(p_rt, _p_rt,
+			&m_rt->port_list, port_node) {
+
+		list_del(&p_rt->port_node);
+		kfree(p_rt);
+	}
+}
+
+static void sdw_slave_port_deconfig(struct sdw_bus *bus,
+		struct sdw_slave *slave,
+		struct sdw_stream_runtime *stream)
+{
+	struct sdw_port_runtime *p_rt, *_p_rt;
+	struct sdw_master_runtime *m_rt = stream->m_rt;
+	struct sdw_slave_runtime *s_rt;
+
+	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
+
+		if (s_rt->slave != slave)
+			continue;
+
+		list_for_each_entry_safe(p_rt, _p_rt,
+				&s_rt->port_list, port_node) {
+
+			list_del(&p_rt->port_node);
+			kfree(p_rt);
+		}
+	}
+}
+
 /**
  * sdw_release_slave_stream: Free Slave(s) runtime handle
  *
@@ -160,7 +197,7 @@ static void sdw_release_master_stream(struct sdw_stream_runtime *stream)
  * @bus: SDW Bus instance
  * @stream: Soundwire stream
  *
- * This removes and frees master_rt from a stream
+ * This removes and frees port_rt and master_rt from a stream
  */
 
 int sdw_stream_remove_master(struct sdw_bus *bus,
@@ -169,6 +206,7 @@ int sdw_stream_remove_master(struct sdw_bus *bus,
 	mutex_lock(&bus->bus_lock);
 
 	sdw_release_master_stream(stream);
+	sdw_master_port_deconfig(bus, stream->m_rt);
 	stream->state = SDW_STREAM_RELEASE;
 	kfree(stream->m_rt);
 	stream->m_rt = NULL;
@@ -185,7 +223,7 @@ EXPORT_SYMBOL(sdw_stream_remove_master);
  * @slave: SDW Slave instance
  * @stream: Soundwire stream
  *
- * This removes and frees slave_rt from a stream
+ * This removes and frees port_rt and slave_rt from a stream
  */
 
 int sdw_stream_remove_slave(struct sdw_slave *slave,
@@ -193,6 +231,7 @@ int sdw_stream_remove_slave(struct sdw_slave *slave,
 {
 	mutex_lock(&slave->bus->bus_lock);
 
+	sdw_slave_port_deconfig(slave->bus, slave, stream);
 	sdw_release_slave_stream(slave, stream);
 
 	mutex_unlock(&slave->bus->bus_lock);
@@ -233,15 +272,113 @@ static int sdw_config_stream(struct device *dev,
 	return 0;
 }
 
+static int sdw_is_valid_port_range(struct device *dev,
+				struct sdw_port_runtime *p_rt)
+{
+	if (!SDW_VALID_PORT_RANGE(p_rt->num)) {
+		dev_err(dev,
+			"SoundWire: Invalid port number :%d", p_rt->num);
+		return -EINVAL;
+	}
+
+	return 0;
+
+}
+
+static struct sdw_port_runtime *sdw_port_alloc(struct device *dev,
+				struct sdw_port_config *port_config,
+				int port_index)
+{
+	struct sdw_port_runtime *p_rt;
+
+	p_rt = kzalloc(sizeof(*p_rt), GFP_KERNEL);
+	if (!p_rt)
+		return NULL;
+
+	p_rt->ch_mask = port_config[port_index].ch_mask;
+	p_rt->num = port_config[port_index].num;
+
+	return p_rt;
+
+}
+
+static int sdw_master_port_config(struct sdw_bus *bus,
+			struct sdw_master_runtime *m_rt,
+			struct sdw_port_config *port_config,
+			unsigned int num_ports)
+{
+	struct sdw_port_runtime *p_rt;
+	int i, ret;
+
+	/* Iterate for number of ports to perform initialization */
+	for (i = 0; i < num_ports; i++) {
+
+		p_rt = sdw_port_alloc(bus->dev, port_config, i);
+		if (!p_rt)
+			return -ENOMEM;
+
+		ret = sdw_is_valid_port_range(bus->dev, p_rt);
+		if (ret < 0) {
+			kfree(p_rt);
+			return ret;
+		}
+
+		/*
+		 * TODO: Check port capabilities for requested
+		 * configuration (audio mode support)
+		 */
+
+		list_add_tail(&p_rt->port_node, &m_rt->port_list);
+	}
+
+	return 0;
+}
+
+static int sdw_slave_port_config(struct sdw_slave *slave,
+			struct sdw_slave_runtime *s_rt,
+			struct sdw_port_config *port_config,
+			unsigned int num_config)
+{
+	struct sdw_port_runtime *p_rt;
+	int i, ret;
+
+	/* Iterate for number of ports to perform initialization */
+	for (i = 0; i < num_config; i++) {
+
+		p_rt = sdw_port_alloc(&slave->dev, port_config, i);
+		if (!p_rt)
+			return -ENOMEM;
+
+		ret = sdw_is_valid_port_range(&slave->dev, p_rt);
+		if (ret < 0) {
+			kfree(p_rt);
+			return ret;
+		}
+
+		/*
+		 * TODO: Check port capabilities for requested
+		 * configuration (audio mode support)
+		 */
+
+		list_add_tail(&p_rt->port_node, &s_rt->port_list);
+	}
+
+	return 0;
+}
+
 /**
  * sdw_stream_add_master: Allocate and add master runtime to a stream
  *
  * @bus: SDW Bus instance
  * @stream_config: Stream configuration for audio stream
+ * @port_config: Port configuration for audio stream
+ * @num_ports: Number of ports
  * @stream: Soundwire stream
  */
 int sdw_stream_add_master(struct sdw_bus *bus,
 		struct sdw_stream_config *stream_config,
+		struct sdw_port_config *port_config,
+		unsigned int num_ports,
 		struct sdw_stream_runtime *stream)
 {
 	struct sdw_master_runtime *m_rt = NULL;
@@ -265,10 +402,19 @@ int sdw_stream_add_master(struct sdw_bus *bus,
 		goto error;
 	}
 
+	ret = sdw_master_port_config(bus, m_rt, port_config, num_ports);
+	if (ret)
+		goto port_error;
+
 	if (!list_empty(&m_rt->slave_rt_list) &&
 			stream->state == SDW_STREAM_ALLOC)
 		stream->state = SDW_STREAM_CONFIG;
 
+	goto error;
+
+port_error:
+	sdw_release_master_stream(stream);
+
 error:
 	mutex_unlock(&bus->bus_lock);
 	return ret;
@@ -281,10 +427,14 @@ EXPORT_SYMBOL(sdw_stream_add_master);
  *
  * @slave: SDW Slave instance
  * @stream_config: Stream configuration for audio stream
+ * @port_config: Port configuration for audio stream
+ * @num_ports: Number of ports
  * @stream: Soundwire stream
  */
 int sdw_stream_add_slave(struct sdw_slave *slave,
 		struct sdw_stream_config *stream_config,
+		struct sdw_port_config *port_config,
+		unsigned int num_ports,
 		struct sdw_stream_runtime *stream)
 {
 	struct sdw_slave_runtime *s_rt;
@@ -324,12 +474,19 @@ int sdw_stream_add_slave(struct sdw_slave *slave,
 
 	ret = sdw_config_stream(&slave->dev, stream, stream_config);
 	if (ret)
-		goto error;
+		goto port_error;
 
 	list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list);
 
+	ret = sdw_slave_port_config(slave, s_rt, port_config, num_ports);
+	if (ret)
+		goto port_error;
+
 	stream->state = SDW_STREAM_CONFIG;
+	goto error;
 
+port_error:
+	sdw_release_master_stream(stream);
 error:
 	mutex_unlock(&slave->bus->bus_lock);
 	return ret;
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h
index cb006cfd1e31..a6db8e6dccc9 100644
--- a/include/linux/soundwire/sdw.h
+++ b/include/linux/soundwire/sdw.h
@@ -26,6 +26,8 @@ struct sdw_slave;
 
 #define SDW_MAX_DEVICES			11
 
+#define SDW_VALID_PORT_RANGE(n)		(n <= 14 && n >= 1)
+
 /**
  * enum sdw_slave_status - Slave status
  * @SDW_SLAVE_UNATTACHED: Slave is not attached with the bus.
@@ -433,6 +435,56 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
  * SDW master structures and APIs
  */
 
+/**
+ * struct sdw_port_params: Data Port parameters
+ *
+ * @num: Port number
+ * @bps: Word length of the Port
+ * @flow_mode: Port Data flow mode
+ * @data_mode: Test modes or normal mode
+ *
+ * This is used to program the Data Port based on Data Port stream
+ * parameters.
+ */
+struct sdw_port_params {
+	unsigned int num;
+	unsigned int bps;
+	unsigned int flow_mode;
+	unsigned int data_mode;
+};
+
+/**
+ * struct sdw_transport_params: Data Port Transport Parameters
+ *
+ * @blk_grp_ctrl_valid: Port implements block group control
+ * @num: Port number
+ * @blk_grp_ctrl: Block group control value
+ * @sample_interval: Sample interval
+ * @offset1: Blockoffset of the payload data
+ * @offset2: Blockoffset of the payload data
+ * @hstart: Horizontal start of the payload data
+ * @hstop: Horizontal stop of the payload data
+ * @blk_pkg_mode: Block per channel or block per port
+ * @lane_ctrl: Data lane Port uses for Data transfer. Currently only single
+ * data lane is supported in bus
+ *
+ * This is used to program the Data Port based on Data Port transport
+ * parameters. All these parameters are banked and can be modified
+ * during a bank switch without any artifacts in audio stream.
+ */
+struct sdw_transport_params {
+	bool blk_grp_ctrl_valid;
+	unsigned int port_num;
+	unsigned int blk_grp_ctrl;
+	unsigned int sample_interval;
+	unsigned int offset1;
+	unsigned int offset2;
+	unsigned int hstart;
+	unsigned int hstop;
+	unsigned int blk_pkg_mode;
+	unsigned int lane_ctrl;
+};
+
 struct sdw_msg;
 
 /**
@@ -501,6 +553,17 @@ int sdw_add_bus_master(struct sdw_bus *bus);
 void sdw_delete_bus_master(struct sdw_bus *bus);
 
 /**
+ * sdw_port_config: Master or Slave Port configuration
+ *
+ * @num: Port number
+ * @ch_mask: channels mask for port
+ */
+struct sdw_port_config {
+	unsigned int num;
+	unsigned int ch_mask;
+};
+
+/**
  * sdw_stream_config: Master or Slave stream configuration
  *
  * @frame_rate: Audio frame rate of the stream, in Hz
@@ -572,9 +635,13 @@ struct sdw_stream_runtime *sdw_alloc_stream(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,
+		struct sdw_port_config *port_config,
+		unsigned int num_ports,
 		struct sdw_stream_runtime *stream);
 int sdw_stream_add_slave(struct sdw_slave *slave,
 		struct sdw_stream_config *stream_config,
+		struct sdw_port_config *port_config,
+		unsigned int num_ports,
 		struct sdw_stream_runtime *stream);
 int sdw_stream_remove_master(struct sdw_bus *bus,
 		struct sdw_stream_runtime *stream);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 04/13] soundwire: Add Master and Slave port programming
  2018-03-28  9:38 [PATCH 00/13] soundwire: Add stream support Vinod Koul
                   ` (2 preceding siblings ...)
  2018-03-28  9:38 ` [PATCH 03/13] soundwire: Add support for port management Vinod Koul
@ 2018-03-28  9:38 ` Vinod Koul
  2018-03-28  9:38 ` [PATCH 05/13] soundwire: Add helpers for ports operations Vinod Koul
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Vinod Koul @ 2018-03-28  9:38 UTC (permalink / raw)
  To: Greg KH
  Cc: ALSA, tiwai, Pierre-Louis Bossart, liam.r.girdwood,
	patches.audio, broonie, Sanyog Kale, Vinod Koul

From: Sanyog Kale <sanyog.r.kale@intel.com>

Master and Slave port registers need to be programmed for each port
used in a stream. Add the helpers for port register programming.

Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 drivers/soundwire/Makefile    |   3 +-
 drivers/soundwire/bus.h       |   4 +
 drivers/soundwire/runtime.c   | 243 ++++++++++++++++++++++++++++++++++++++++++
 drivers/soundwire/stream.c    |  33 ++++++
 include/linux/soundwire/sdw.h |  47 +++++++-
 5 files changed, 328 insertions(+), 2 deletions(-)
 create mode 100644 drivers/soundwire/runtime.c

diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile
index 5817beaca0e1..64fe4712ed87 100644
--- a/drivers/soundwire/Makefile
+++ b/drivers/soundwire/Makefile
@@ -3,7 +3,8 @@
 #
 
 #Bus Objs
-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 \
+			runtime.o
 obj-$(CONFIG_SOUNDWIRE_BUS) += soundwire-bus.o
 
 #Cadence Objs
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h
index b28a0c224323..8bb150294936 100644
--- a/drivers/soundwire/bus.h
+++ b/drivers/soundwire/bus.h
@@ -109,6 +109,10 @@ struct sdw_master_runtime {
 	struct list_head bus_node;
 };
 
+struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave,
+				enum sdw_data_direction direction,
+				unsigned int port_num);
+
 int sdw_transfer(struct sdw_bus *bus, struct sdw_msg *msg);
 int sdw_transfer_defer(struct sdw_bus *bus, struct sdw_msg *msg,
 				struct sdw_defer *defer);
diff --git a/drivers/soundwire/runtime.c b/drivers/soundwire/runtime.c
new file mode 100644
index 000000000000..6e8826a6ea8e
--- /dev/null
+++ b/drivers/soundwire/runtime.c
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2015-18 Intel Corporation.
+
+/*
+ *  runtime.c - SoundWire Stream runtime operations.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/mod_devicetable.h>
+#include <linux/soundwire/sdw.h>
+#include "bus.h"
+
+static int _sdw_program_slave_port_params(struct sdw_bus *bus,
+				struct sdw_slave *slave,
+				struct sdw_transport_params *t_params,
+				enum sdw_dpn_type type)
+{
+	u32 addr1, addr2, addr3, addr4;
+	int ret;
+	u8 wbuf;
+
+	if (bus->params.next_bank) {
+		addr1 = SDW_DPN_OFFSETCTRL2_B1(t_params->port_num);
+		addr2 = SDW_DPN_BLOCKCTRL3_B1(t_params->port_num);
+		addr3 = SDW_DPN_SAMPLECTRL2_B1(t_params->port_num);
+		addr4 = SDW_DPN_HCTRL_B1(t_params->port_num);
+	} else {
+		addr1 = SDW_DPN_OFFSETCTRL2_B0(t_params->port_num);
+		addr2 = SDW_DPN_BLOCKCTRL3_B0(t_params->port_num);
+		addr3 = SDW_DPN_SAMPLECTRL2_B0(t_params->port_num);
+		addr4 = SDW_DPN_HCTRL_B0(t_params->port_num);
+	}
+
+	/* Program DPN_OffsetCtrl2 registers */
+	ret = sdw_write(slave, addr1, t_params->offset2);
+	if (ret < 0) {
+		dev_err(bus->dev, "DPN_OffsetCtrl2 register write failed");
+		return ret;
+	}
+
+	/* Program DPN_BlockCtrl3 register */
+	ret = sdw_write(slave, addr2, t_params->blk_pkg_mode);
+	if (ret < 0) {
+		dev_err(bus->dev, "DPN_BlockCtrl3 register write failed");
+		return ret;
+	}
+
+	/*
+	 * Data ports are FULL and REDUCED type. Above handles both and
+	 * beyond this point only FULL is handled, so bail out if we are
+	 * not FULL data port type
+	 */
+	if (type != SDW_DPN_FULL)
+		return ret;
+
+	/* Program DPN_SampleCtrl2 register */
+	wbuf = ((t_params->sample_interval - 1) &
+			SDW_DPN_SAMPLECTRL_HIGH) >>
+			SDW_REG_SHIFT(SDW_DPN_SAMPLECTRL_HIGH);
+
+	ret = sdw_write(slave, addr3, wbuf);
+	if (ret < 0) {
+		dev_err(bus->dev, "DPN_SampleCtrl2 register write failed");
+		return ret;
+	}
+
+	/* Program DPN_HCtrl register */
+	wbuf = (t_params->hstop | (t_params->hstart <<
+				SDW_REG_SHIFT(SDW_DPN_HCTRL_HSTART)));
+	ret = sdw_write(slave, addr4, wbuf);
+	if (ret < 0)
+		dev_err(bus->dev, "DPN_HCtrl register write failed");
+
+	return ret;
+}
+
+static int sdw_program_slave_port_params(struct sdw_bus *bus,
+			struct sdw_slave_runtime *s_rt,
+			struct sdw_port_runtime *p_rt)
+{
+	struct sdw_transport_params *t_params = &p_rt->transport_params;
+	struct sdw_port_params *p_params = &p_rt->port_params;
+	struct sdw_slave_prop *slave_prop = &s_rt->slave->prop;
+	u32 addr1, addr2, addr3, addr4, addr5, addr6;
+	struct sdw_dpn_prop *dpn_prop;
+	int ret;
+	u8 wbuf;
+
+	dpn_prop = sdw_get_slave_dpn_prop(s_rt->slave,
+					s_rt->direction,
+					t_params->port_num);
+	if (!dpn_prop)
+		return -EINVAL;
+
+	addr1 = SDW_DPN_PORTCTRL(t_params->port_num);
+	addr2 = SDW_DPN_BLOCKCTRL1(t_params->port_num);
+
+	if (bus->params.next_bank) {
+		addr3 = SDW_DPN_SAMPLECTRL1_B1(t_params->port_num);
+		addr4 = SDW_DPN_OFFSETCTRL1_B1(t_params->port_num);
+		addr5 = SDW_DPN_BLOCKCTRL2_B1(t_params->port_num);
+		addr6 = SDW_DPN_LANECTRL_B1(t_params->port_num);
+
+	} else {
+		addr3 = SDW_DPN_SAMPLECTRL1_B0(t_params->port_num);
+		addr4 = SDW_DPN_OFFSETCTRL1_B0(t_params->port_num);
+		addr5 = SDW_DPN_BLOCKCTRL2_B0(t_params->port_num);
+		addr6 = SDW_DPN_LANECTRL_B0(t_params->port_num);
+	}
+
+	/* Program DPN_PortCtrl register */
+	wbuf = p_params->data_mode << SDW_REG_SHIFT(SDW_DPN_PORTCTRL_DATAMODE);
+	wbuf |= p_params->flow_mode;
+
+	ret = sdw_update(s_rt->slave, addr1, 0xF, wbuf);
+	if (ret < 0) {
+		dev_err(&s_rt->slave->dev,
+			"DPN_PortCtrl register write failed for port %d",
+			t_params->port_num);
+		return ret;
+	}
+
+	/* Program DPN_BlockCtrl1 register */
+	ret = sdw_write(s_rt->slave, addr2, (p_params->bps - 1));
+	if (ret < 0) {
+		dev_err(&s_rt->slave->dev,
+			"DPN_BlockCtrl1 register write failed for port %d",
+			t_params->port_num);
+		return ret;
+	}
+
+	/* Program DPN_SampleCtrl1 register */
+	wbuf = (t_params->sample_interval - 1) & SDW_DPN_SAMPLECTRL_LOW;
+	ret = sdw_write(s_rt->slave, addr3, wbuf);
+	if (ret < 0) {
+		dev_err(&s_rt->slave->dev,
+			"DPN_SampleCtrl1 register write failed for port %d",
+			t_params->port_num);
+		return ret;
+	}
+
+	/* Program DPN_OffsetCtrl1 registers */
+	ret = sdw_write(s_rt->slave, addr4, t_params->offset1);
+	if (ret < 0) {
+		dev_err(&s_rt->slave->dev,
+			"DPN_OffsetCtrl1 register write failed for port %d",
+			t_params->port_num);
+		return ret;
+	}
+
+	/* Program DPN_BlockCtrl2 register*/
+	if (t_params->blk_grp_ctrl_valid) {
+		ret = sdw_write(s_rt->slave, addr5, t_params->blk_grp_ctrl);
+		if (ret < 0) {
+			dev_err(&s_rt->slave->dev,
+				"DPN_BlockCtrl2 reg write failed for port %d",
+				t_params->port_num);
+			return ret;
+		}
+	}
+
+	/* program DPN_LaneCtrl register */
+	if (slave_prop->lane_control_support) {
+		ret = sdw_write(s_rt->slave, addr6, t_params->lane_ctrl);
+		if (ret < 0) {
+			dev_err(&s_rt->slave->dev,
+				"DPN_LaneCtrl register write failed for port %d",
+				t_params->port_num);
+			return ret;
+		}
+	}
+
+	if (dpn_prop->type != SDW_DPN_SIMPLE) {
+		ret = _sdw_program_slave_port_params(bus, s_rt->slave,
+						t_params, dpn_prop->type);
+		if (ret < 0)
+			dev_err(&s_rt->slave->dev,
+				"Transport reg write failed for port: %d",
+				t_params->port_num);
+	}
+
+	return ret;
+}
+
+static int sdw_program_master_port_params(struct sdw_bus *bus,
+		struct sdw_port_runtime *p_rt)
+{
+	int ret;
+
+	/*
+	 * we need to set transport and port parameters for the port.
+	 * Transport parameters refers to the smaple interval, offsets and
+	 * hstart/stop etc of the data. Port parameters refers to word
+	 * length, flow mode etc of the port
+	 */
+	ret = bus->port_ops->dpn_set_port_transport_params(bus,
+					&p_rt->transport_params,
+					bus->params.next_bank);
+	if (ret < 0)
+		return ret;
+
+	return bus->port_ops->dpn_set_port_params(bus,
+				&p_rt->port_params,
+				bus->params.next_bank);
+}
+
+/**
+ * sdw_program_port_params: Programs transport parameters of Master(s)
+ * and Slave(s)
+ *
+ * @m_rt: Master stream runtime
+ */
+static int sdw_program_port_params(struct sdw_master_runtime *m_rt)
+{
+	struct sdw_slave_runtime *s_rt = NULL;
+	struct sdw_bus *bus = m_rt->bus;
+	struct sdw_port_runtime *p_rt;
+	int ret = 0;
+
+	/* Program transport & port parameters for Slave(s) */
+	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
+		list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
+
+			ret = sdw_program_slave_port_params(bus, s_rt, p_rt);
+			if (ret < 0)
+				return ret;
+
+		}
+	}
+
+	/* Program transport & port parameters for Master(s) */
+	list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
+		ret = sdw_program_master_port_params(bus, p_rt);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c
index 95ad714b9c76..a29447e9a2f0 100644
--- a/drivers/soundwire/stream.c
+++ b/drivers/soundwire/stream.c
@@ -492,3 +492,36 @@ int sdw_stream_add_slave(struct sdw_slave *slave,
 	return ret;
 }
 EXPORT_SYMBOL(sdw_stream_add_slave);
+
+/**
+ * sdw_get_slave_dpn_prop: Get Slave port capabilities
+ *
+ * @slave: Slave handle
+ * @direction: Data direction.
+ * @port_num: Port number
+ */
+struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave,
+				enum sdw_data_direction direction,
+				unsigned int port_num)
+{
+	struct sdw_dpn_prop *dpn_prop;
+	u8 num_ports;
+	int i;
+
+	if (direction == SDW_DATA_DIR_OUT) {
+		num_ports = hweight32(slave->prop.source_ports);
+		dpn_prop = slave->prop.src_dpn_prop;
+	} else {
+		num_ports = hweight32(slave->prop.sink_ports);
+		dpn_prop = slave->prop.sink_dpn_prop;
+	}
+
+	for (i = 0; i < num_ports; i++) {
+		dpn_prop = &dpn_prop[i];
+
+		if (dpn_prop->num == port_num)
+			return &dpn_prop[i];
+	}
+
+	return NULL;
+}
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h
index a6db8e6dccc9..57aba663a4e3 100644
--- a/include/linux/soundwire/sdw.h
+++ b/include/linux/soundwire/sdw.h
@@ -370,7 +370,30 @@ struct sdw_slave_intr_status {
 };
 
 /**
- * struct sdw_slave_ops - Slave driver callback ops
+ * sdw_reg_bank - SoundWire register banks
+ * @SDW_BANK0: Soundwire register bank 0
+ * @SDW_BANK1: Soundwire register bank 1
+ */
+enum sdw_reg_bank {
+	SDW_BANK0,
+	SDW_BANK1,
+};
+
+/**
+ * struct sdw_bus_params: Structure holding bus configuration
+ *
+ * @curr_bank: Current bank in use (BANK0/BANK1)
+ * @next_bank: Next bank to use (BANK0/BANK1). next_bank will always be
+ * set to !curr_bank
+ */
+struct sdw_bus_params {
+	enum sdw_reg_bank curr_bank;
+	enum sdw_reg_bank next_bank;
+};
+
+/**
+ * struct sdw_slave_ops: Slave driver callback ops
+ *
  * @read_prop: Read Slave properties
  * @interrupt_callback: Device interrupt notification (invoked in thread
  * context)
@@ -485,6 +508,24 @@ struct sdw_transport_params {
 	unsigned int lane_ctrl;
 };
 
+/**
+ * struct sdw_master_port_ops: Callback functions from bus to Master
+ * driver to set Master Data ports.
+ *
+ * @dpn_set_port_params: Set the Port parameters for the Master Port.
+ * Mandatory callback
+ * @dpn_set_port_transport_params: Set transport parameters for the Master
+ * Port. Mandatory callback
+ */
+struct sdw_master_port_ops {
+	int (*dpn_set_port_params)(struct sdw_bus *bus,
+			struct sdw_port_params *port_params,
+			unsigned int bank);
+	int (*dpn_set_port_transport_params)(struct sdw_bus *bus,
+			struct sdw_transport_params *transport_params,
+			enum sdw_reg_bank bank);
+};
+
 struct sdw_msg;
 
 /**
@@ -528,6 +569,8 @@ struct sdw_master_ops {
  * @bus_lock: bus lock
  * @msg_lock: message lock
  * @ops: Master callback ops
+ * @port_ops: Master port callback ops
+ * @params: Current bus parameters
  * @prop: Master properties
  * @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,
@@ -543,6 +586,8 @@ struct sdw_bus {
 	struct mutex bus_lock;
 	struct mutex msg_lock;
 	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;
 	struct sdw_defer defer_msg;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 05/13] soundwire: Add helpers for ports operations
  2018-03-28  9:38 [PATCH 00/13] soundwire: Add stream support Vinod Koul
                   ` (3 preceding siblings ...)
  2018-03-28  9:38 ` [PATCH 04/13] soundwire: Add Master and Slave port programming Vinod Koul
@ 2018-03-28  9:38 ` Vinod Koul
  2018-03-28  9:38 ` [PATCH 06/13] soundwire: Add bank switch routine Vinod Koul
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Vinod Koul @ 2018-03-28  9:38 UTC (permalink / raw)
  To: Greg KH
  Cc: ALSA, tiwai, Pierre-Louis Bossart, liam.r.girdwood,
	patches.audio, broonie, Sanyog Kale, Vinod Koul

From: Sanyog Kale <sanyog.r.kale@intel.com>

Add helpers to configure, prepare, enable, disable and
de-prepare ports.

Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 drivers/soundwire/bus.c       |  26 +++++
 drivers/soundwire/bus.h       |   2 +
 drivers/soundwire/runtime.c   | 259 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/soundwire/sdw.h |  54 +++++++++
 4 files changed, 341 insertions(+)

diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c
index abf046f6b188..b8c93f0ac0a0 100644
--- a/drivers/soundwire/bus.c
+++ b/drivers/soundwire/bus.c
@@ -577,6 +577,32 @@ static void sdw_modify_slave_status(struct sdw_slave *slave,
 	mutex_unlock(&slave->bus->bus_lock);
 }
 
+int sdw_configure_dpn_intr(struct sdw_slave *slave,
+			int port, bool enable, int mask)
+{
+	u32 addr;
+	int ret;
+	u8 val = 0;
+
+	addr = SDW_DPN_INTMASK(port);
+
+	/* Set/Clear port ready interrupt mask */
+	if (enable) {
+		val |= mask;
+		val |= SDW_DPN_INT_PORT_READY;
+	} else {
+		val &= ~(mask);
+		val &= ~SDW_DPN_INT_PORT_READY;
+	}
+
+	ret = sdw_update(slave, addr, (mask | SDW_DPN_INT_PORT_READY), val);
+	if (ret < 0)
+		dev_err(slave->bus->dev,
+				"SDW_DPN_INTMASK write failed:%d", val);
+
+	return ret;
+}
+
 static int sdw_initialize_slave(struct sdw_slave *slave)
 {
 	struct sdw_slave_prop *prop = &slave->prop;
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h
index 8bb150294936..95764f1b9339 100644
--- a/drivers/soundwire/bus.h
+++ b/drivers/soundwire/bus.h
@@ -112,6 +112,8 @@ struct sdw_master_runtime {
 struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave,
 				enum sdw_data_direction direction,
 				unsigned int port_num);
+int sdw_configure_dpn_intr(struct sdw_slave *slave, int port,
+					bool enable, int mask);
 
 int sdw_transfer(struct sdw_bus *bus, struct sdw_msg *msg);
 int sdw_transfer_defer(struct sdw_bus *bus, struct sdw_msg *msg,
diff --git a/drivers/soundwire/runtime.c b/drivers/soundwire/runtime.c
index 6e8826a6ea8e..5ceea1052b92 100644
--- a/drivers/soundwire/runtime.c
+++ b/drivers/soundwire/runtime.c
@@ -241,3 +241,262 @@ static int sdw_program_port_params(struct sdw_master_runtime *m_rt)
 
 	return 0;
 }
+
+static int sdw_enable_disable_slave_ports(struct sdw_bus *bus,
+				struct sdw_slave_runtime *s_rt,
+				struct sdw_port_runtime *p_rt, bool en)
+{
+	struct sdw_transport_params *t_params = &p_rt->transport_params;
+	u32 addr;
+	int ret;
+
+	if (bus->params.next_bank)
+		addr = SDW_DPN_CHANNELEN_B1(p_rt->num);
+	else
+		addr = SDW_DPN_CHANNELEN_B0(p_rt->num);
+
+	/*
+	 * Since we do not support sharing a port across two streams,
+	 * it is safe to reset this register
+	 */
+	if (en)
+		ret = sdw_update(s_rt->slave, addr, 0xFF, p_rt->ch_mask);
+	else
+		ret = sdw_update(s_rt->slave, addr, 0xFF, 0x0);
+
+	if (ret < 0)
+		dev_err(&s_rt->slave->dev,
+			"Slave chn_en reg write failed:%d port:%d",
+			ret, t_params->port_num);
+
+	return ret;
+}
+
+static int sdw_enable_disable_mstr_ports(struct sdw_master_runtime *m_rt,
+			struct sdw_port_runtime *p_rt, bool en)
+{
+	struct sdw_transport_params *t_params = &p_rt->transport_params;
+	struct sdw_bus *bus = m_rt->bus;
+	struct sdw_enable_ch enable_ch;
+	int ret = 0;
+
+	enable_ch.num = p_rt->num;
+	enable_ch.ch_mask = p_rt->ch_mask;
+	enable_ch.enable = en;
+
+	/* Perform Master port channel(s) enable/disable */
+	if (bus->port_ops->dpn_port_enable_ch) {
+		ret = bus->port_ops->dpn_port_enable_ch(bus,
+				&enable_ch, bus->params.next_bank);
+		if (ret < 0) {
+			dev_err(bus->dev,
+				"Master chn_en write failed:%d port:%d",
+				ret, t_params->port_num);
+			return ret;
+		}
+	} else {
+		dev_err(bus->dev,
+			"enable/disable Master port channel unsuuported");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * sdw_enable_disable_ports: Enable/disable port(s) for Master and
+ * Slave(s)
+ *
+ * @m_rt: Master stream runtime
+ * @en: mode (enable/disable)
+ */
+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;
+	int ret = 0;
+
+	/* Enable/Disable Slave port(s) */
+	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
+		list_for_each_entry(s_port, &s_rt->port_list, port_node) {
+			ret = sdw_enable_disable_slave_ports(m_rt->bus, s_rt,
+							s_port, en);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	/* Enable/Disable Master port(s) */
+	list_for_each_entry(m_port, &m_rt->port_list, port_node) {
+		ret = sdw_enable_disable_mstr_ports(m_rt, m_port, en);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int sdw_do_port_prep(struct sdw_slave_runtime *s_rt,
+		struct sdw_prepare_ch prep_ch, enum sdw_port_prep_ops cmd)
+{
+	const struct sdw_slave_ops *ops = s_rt->slave->ops;
+	int ret;
+
+	if (ops->port_prep) {
+		ret = ops->port_prep(s_rt->slave, &prep_ch, cmd);
+		if (ret < 0) {
+			dev_err(&s_rt->slave->dev,
+				"Slave Port Pre-Prepare failed: %d", ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus,
+			struct sdw_slave_runtime *s_rt,
+			struct sdw_port_runtime *p_rt, bool prep)
+{
+	struct completion *port_ready = NULL;
+	struct sdw_dpn_prop *dpn_prop;
+	struct sdw_prepare_ch prep_ch;
+	unsigned int time_left;
+	bool intr = false;
+	int ret = 0, val;
+	u32 addr;
+
+	prep_ch.num = p_rt->num;
+	prep_ch.ch_mask = p_rt->ch_mask;
+
+	dpn_prop = sdw_get_slave_dpn_prop(s_rt->slave,
+					s_rt->direction,
+					prep_ch.num);
+	if (!dpn_prop) {
+		dev_err(bus->dev,
+			"Slave Port:%d properties not found", prep_ch.num);
+		return -EINVAL;
+	}
+
+	prep_ch.prepare = prep;
+
+	prep_ch.bank = bus->params.next_bank;
+
+	if ((dpn_prop->device_interrupts) || (!dpn_prop->simple_ch_prep_sm))
+		intr = true;
+
+	/*
+	 * Enable interrupt before Port prepare.
+	 * For Port de-prepare, it is assumed that port
+	 * was prepared earlier.
+	 **/
+	if (prep && intr) {
+		ret = sdw_configure_dpn_intr(s_rt->slave, p_rt->num, prep,
+						dpn_prop->device_interrupts);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* Inform slave about the impending port prepare */
+	sdw_do_port_prep(s_rt, prep_ch, SDW_OPS_PORT_PRE_PREP);
+
+	/* Prepare Slave port implementing CP_SM */
+	if (!dpn_prop->simple_ch_prep_sm) {
+		addr = SDW_DPN_PREPARECTRL(p_rt->num);
+
+		if (prep)
+			ret = sdw_update(s_rt->slave, addr,
+					0xFF, p_rt->ch_mask);
+		else
+			ret = sdw_update(s_rt->slave, addr, 0xFF, 0x0);
+
+		if (ret < 0) {
+			dev_err(&s_rt->slave->dev,
+				"Slave prep_ctrl reg write failed");
+			return ret;
+		}
+
+		/* Wait for completion on port ready */
+		port_ready = &s_rt->slave->port_ready[prep_ch.num];
+		time_left = wait_for_completion_timeout(port_ready,
+				msecs_to_jiffies(dpn_prop->ch_prep_timeout));
+
+		val = sdw_read(s_rt->slave, SDW_DPN_PREPARESTATUS(p_rt->num));
+		val &= p_rt->ch_mask;
+		if (!time_left && !val) {
+			dev_err(&s_rt->slave->dev,
+				"Chn prep failed for port:%d", prep_ch.num);
+			return -ETIMEDOUT;
+		}
+	}
+
+	/* Inform slaves about ports being prepared */
+	sdw_do_port_prep(s_rt, prep_ch, SDW_OPS_PORT_POST_PREP);
+
+	/* Disable interrupt after Port de-prepare */
+	if ((!prep) && (intr))
+		ret = sdw_configure_dpn_intr(s_rt->slave, p_rt->num, prep,
+						dpn_prop->device_interrupts);
+
+	return ret;
+}
+
+static int sdw_prep_deprep_mstr_ports(struct sdw_master_runtime *m_rt,
+				struct sdw_port_runtime *p_rt, bool prep)
+{
+	struct sdw_transport_params *t_params = &p_rt->transport_params;
+	struct sdw_bus *bus = m_rt->bus;
+	const struct sdw_master_port_ops *ops = bus->port_ops;
+	struct sdw_prepare_ch prep_ch;
+	int ret = 0;
+
+	prep_ch.num = p_rt->num;
+	prep_ch.ch_mask = p_rt->ch_mask;
+	prep_ch.prepare = prep; /* Prepare/De-prepare */
+	prep_ch.bank = bus->params.next_bank;
+
+	/* Pre-prepare/Pre-deprepare port(s) */
+	if (ops->dpn_port_prep) {
+		ret = ops->dpn_port_prep(bus, &prep_ch);
+		if (ret < 0) {
+			dev_err(bus->dev, "Port prepare failed for port:%d",
+					t_params->port_num);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+/**
+ * sdw_prep_deprep_ports: Prepare/De-prepare port(s) for Master(s) and
+ * Slave(s)
+ *
+ * @m_rt: Master runtime handle
+ * @prep: Prepare or De-prepare
+ */
+static int sdw_prep_deprep_ports(struct sdw_master_runtime *m_rt, bool prep)
+{
+	struct sdw_slave_runtime *s_rt = NULL;
+	struct sdw_port_runtime *p_rt;
+	int ret = 0;
+
+	/* Prepare/De-prepare Slave port(s) */
+	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
+		list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
+			ret = sdw_prep_deprep_slave_ports(m_rt->bus, s_rt,
+							p_rt, prep);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	/* Prepare/De-prepare Master port(s) */
+	list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
+		ret = sdw_prep_deprep_mstr_ports(m_rt, p_rt, prep);
+		if (ret < 0)
+			return ret;
+	}
+
+	return ret;
+}
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h
index 57aba663a4e3..39fa86a83a1e 100644
--- a/include/linux/soundwire/sdw.h
+++ b/include/linux/soundwire/sdw.h
@@ -380,6 +380,37 @@ enum sdw_reg_bank {
 };
 
 /**
+ * struct sdw_prepare_ch: Prepare/De-prepare Data Port channel
+ *
+ * @num: Port number
+ * @ch_mask: Active channel mask
+ * @prepare: Prepare (true) /de-prepare (false) channel
+ * @bank: Register bank, which bank Slave/Master driver should program for
+ * implementation defined registers. This is always updated to next_bank
+ * value read from bus params.
+ *
+ */
+struct sdw_prepare_ch {
+	unsigned int num;
+	unsigned int ch_mask;
+	bool prepare;
+	unsigned int bank;
+};
+
+/**
+ * enum sdw_port_prep_ops: Prepare operations for Data Port
+ *
+ * @SDW_OPS_PORT_PRE_PREP: Pre prepare operation for the Port
+ * @SDW_OPS_PORT_PREP: Prepare operation for the Port
+ * @SDW_OPS_PORT_POST_PREP: Post prepare operation for the Port
+ */
+enum sdw_port_prep_ops {
+	SDW_OPS_PORT_PRE_PREP = 0,
+	SDW_OPS_PORT_PREP = 1,
+	SDW_OPS_PORT_POST_PREP = 2,
+};
+
+/**
  * struct sdw_bus_params: Structure holding bus configuration
  *
  * @curr_bank: Current bank in use (BANK0/BANK1)
@@ -398,6 +429,7 @@ struct sdw_bus_params {
  * @interrupt_callback: Device interrupt notification (invoked in thread
  * context)
  * @update_status: Update Slave status
+ * @port_prep: Prepare the port with parameters
  */
 struct sdw_slave_ops {
 	int (*read_prop)(struct sdw_slave *sdw);
@@ -405,6 +437,9 @@ struct sdw_slave_ops {
 			struct sdw_slave_intr_status *status);
 	int (*update_status)(struct sdw_slave *slave,
 			enum sdw_slave_status status);
+	int (*port_prep)(struct sdw_slave *slave,
+			struct sdw_prepare_ch *prepare_ch,
+			enum sdw_port_prep_ops pre_ops);
 };
 
 /**
@@ -509,6 +544,19 @@ struct sdw_transport_params {
 };
 
 /**
+ * struct sdw_enable_ch: Enable/disable Data Port channel
+ *
+ * @num: Port number
+ * @ch_mask: Active channel mask
+ * @enable: Enable (true) /disable (false) channel
+ */
+struct sdw_enable_ch {
+	unsigned int num;
+	unsigned int ch_mask;
+	bool enable;
+};
+
+/**
  * struct sdw_master_port_ops: Callback functions from bus to Master
  * driver to set Master Data ports.
  *
@@ -516,6 +564,8 @@ struct sdw_transport_params {
  * Mandatory callback
  * @dpn_set_port_transport_params: Set transport parameters for the Master
  * Port. Mandatory callback
+ * @dpn_port_prep: Port prepare operations for the Master Data Port.
+ * @dpn_port_enable_ch: Enable the channels of Master Port.
  */
 struct sdw_master_port_ops {
 	int (*dpn_set_port_params)(struct sdw_bus *bus,
@@ -524,6 +574,10 @@ struct sdw_master_port_ops {
 	int (*dpn_set_port_transport_params)(struct sdw_bus *bus,
 			struct sdw_transport_params *transport_params,
 			enum sdw_reg_bank bank);
+	int (*dpn_port_prep)(struct sdw_bus *bus,
+			struct sdw_prepare_ch *prepare_ch);
+	int (*dpn_port_enable_ch)(struct sdw_bus *bus,
+			struct sdw_enable_ch *enable_ch, unsigned int bank);
 };
 
 struct sdw_msg;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 06/13] soundwire: Add bank switch routine
  2018-03-28  9:38 [PATCH 00/13] soundwire: Add stream support Vinod Koul
                   ` (4 preceding siblings ...)
  2018-03-28  9:38 ` [PATCH 05/13] soundwire: Add helpers for ports operations Vinod Koul
@ 2018-03-28  9:38 ` Vinod Koul
  2018-03-28  9:38 ` [PATCH 07/13] soundwire: Add stream configuration APIs Vinod Koul
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Vinod Koul @ 2018-03-28  9:38 UTC (permalink / raw)
  To: Greg KH
  Cc: ALSA, tiwai, Pierre-Louis Bossart, liam.r.girdwood,
	patches.audio, broonie, Sanyog Kale, Vinod Koul

From: Sanyog Kale <sanyog.r.kale@intel.com>

SoundWire supports two registers banks. So, program the alternate bank
with new configuration and then perfoms bank switch.

Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 drivers/soundwire/bus.c       |   3 +
 drivers/soundwire/bus.h       |   5 +
 drivers/soundwire/runtime.c   | 241 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/soundwire/sdw.h |  50 +++++++++
 4 files changed, 299 insertions(+)

diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c
index b8c93f0ac0a0..6cdb08b8781b 100644
--- a/drivers/soundwire/bus.c
+++ b/drivers/soundwire/bus.c
@@ -78,6 +78,9 @@ int sdw_add_bus_master(struct sdw_bus *bus)
 		return ret;
 	}
 
+	bus->params.curr_bank = SDW_BANK0;
+	bus->params.next_bank = SDW_BANK1;
+
 	return 0;
 }
 EXPORT_SYMBOL(sdw_add_bus_master);
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h
index 95764f1b9339..03a56a7feba1 100644
--- a/drivers/soundwire/bus.h
+++ b/drivers/soundwire/bus.h
@@ -45,6 +45,11 @@ struct sdw_msg {
 	bool page;
 };
 
+#define SDW_DOUBLE_RATE_FACTOR		2
+
+extern int rows[SDW_FRAME_ROWS];
+extern int cols[SDW_FRAME_COLS];
+
 /**
  * sdw_port_runtime: Runtime port parameters for Master or Slave
  *
diff --git a/drivers/soundwire/runtime.c b/drivers/soundwire/runtime.c
index 5ceea1052b92..9bc876d23a4c 100644
--- a/drivers/soundwire/runtime.c
+++ b/drivers/soundwire/runtime.c
@@ -14,6 +14,44 @@
 #include <linux/soundwire/sdw.h>
 #include "bus.h"
 
+/*
+ * Array of supported rows and columns as per MIPI SoundWire Specification 1.1
+ *
+ * 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,
+			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};
+
+static int sdw_find_col_index(int col)
+{
+	int i;
+
+	for (i = 0; i < SDW_FRAME_COLS; i++) {
+		if (cols[i] == col)
+			return i;
+	}
+
+	pr_warn("Requested column not found, selecting lowest column no: 2\n");
+	return 0;
+}
+
+static int sdw_find_row_index(int row)
+{
+	int i;
+
+	for (i = 0; i < SDW_FRAME_ROWS; i++) {
+		if (rows[i] == row)
+			return i;
+	}
+
+	pr_warn("Requested row not found, selecting lowest row no: 48\n");
+	return 0;
+}
+
 static int _sdw_program_slave_port_params(struct sdw_bus *bus,
 				struct sdw_slave *slave,
 				struct sdw_transport_params *t_params,
@@ -500,3 +538,206 @@ static int sdw_prep_deprep_ports(struct sdw_master_runtime *m_rt, bool prep)
 
 	return ret;
 }
+
+
+/**
+ * sdw_notify_config: Notify bus configuration
+ *
+ * @m_rt: Master runtime handle
+ *
+ * This function notifies the master(s) and slave(s) of the
+ * new bus configuration.
+ */
+static int sdw_notify_config(struct sdw_master_runtime *m_rt)
+{
+	struct sdw_slave_runtime *s_rt;
+	struct sdw_bus *bus = m_rt->bus;
+	struct sdw_slave *slave;
+	int ret = 0;
+
+	if (bus->ops->set_bus_conf) {
+		ret = bus->ops->set_bus_conf(bus, &bus->params);
+		if (ret < 0)
+			return ret;
+	}
+
+	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
+		slave = s_rt->slave;
+
+		if (slave->ops->bus_config) {
+			ret = slave->ops->bus_config(slave, &bus->params);
+			if (ret < 0)
+				dev_err(bus->dev, "Notify Slave: %d failed",
+								slave->dev_num);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+/**
+ * sdw_program_params: Program transport and port parameters for Master(s)
+ * and Slave(s)
+ *
+ * @bus: SDW bus instance
+ */
+static int sdw_program_params(struct sdw_bus *bus)
+{
+	struct sdw_master_runtime *m_rt = NULL;
+	int ret = 0;
+
+	list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
+		ret = sdw_program_port_params(m_rt);
+		if (ret < 0) {
+			dev_err(bus->dev,
+				"Program transport params failed: %d", ret);
+			return ret;
+		}
+
+		ret = sdw_notify_config(m_rt);
+		if (ret < 0) {
+			dev_err(bus->dev, "Notify bus config failed: %d", ret);
+			return ret;
+		}
+
+		/* Enable port(s) on alternate bank for all active streams */
+		if (m_rt->stream->state != SDW_STREAM_ENABLE)
+			continue;
+
+		ret = sdw_enable_disable_ports(m_rt, true);
+		if (ret < 0) {
+			dev_err(bus->dev, "Enable channel failed: %d", ret);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+static int _sdw_bank_switch(struct sdw_bus *bus)
+{
+	int col_index, row_index;
+	struct sdw_msg *wr_msg;
+	u8 *wbuf = NULL;
+	int ret = 0;
+	u16 addr;
+
+	wr_msg = kzalloc(sizeof(*wr_msg), GFP_KERNEL);
+	if (!wr_msg)
+		return -ENOMEM;
+
+	bus->defer_msg.msg = wr_msg;
+	wbuf = kzalloc(sizeof(*wbuf), GFP_KERNEL);
+	if (!wbuf) {
+		ret = -ENOMEM;
+		goto error_1;
+	}
+
+	/* Get row and column index to program register */
+	col_index = sdw_find_col_index(bus->params.col);
+	row_index = sdw_find_row_index(bus->params.row);
+	wbuf[0] = col_index | (row_index << 3);
+
+	if (bus->params.next_bank)
+		addr = SDW_SCP_FRAMECTRL_B1;
+	else
+		addr = SDW_SCP_FRAMECTRL_B0;
+
+	sdw_fill_msg(wr_msg, NULL, addr, 1, SDW_BROADCAST_DEV_NUM,
+					SDW_MSG_FLAG_WRITE, wbuf);
+	wr_msg->ssp_sync = true;
+
+	if (bus->multi_link)
+		ret = sdw_transfer_defer(bus, wr_msg, &bus->defer_msg);
+	else
+		ret = sdw_transfer(bus, wr_msg);
+
+	if (ret < 0) {
+		dev_err(bus->dev, "Slave frame_ctrl reg write failed");
+		goto error;
+	}
+
+	if (!bus->multi_link) {
+		kfree(wr_msg);
+		kfree(wbuf);
+		bus->defer_msg.msg = NULL;
+		bus->params.curr_bank = !bus->params.curr_bank;
+		bus->params.next_bank = !bus->params.next_bank;
+	}
+
+	return 0;
+
+error:
+	kfree(wbuf);
+error_1:
+	kfree(wr_msg);
+	return ret;
+}
+
+static int sdw_bank_switch(struct sdw_bus *bus)
+{
+	const struct sdw_master_ops *ops = bus->ops;
+	int ret = 0;
+
+	/* Pre-bank switch */
+	if (ops->pre_bank_switch) {
+		ret = ops->pre_bank_switch(bus);
+		if (ret < 0) {
+			dev_err(bus->dev, "Pre bank switch op failed: %d", ret);
+			return ret;
+		}
+	}
+
+	/* Bank-switch */
+	ret = _sdw_bank_switch(bus);
+	if (ret < 0) {
+		dev_err(bus->dev, "Bank switch op failed: %d", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int sdw_post_bank_switch(struct sdw_stream_runtime *stream)
+{
+	struct sdw_master_runtime *m_rt = stream->m_rt;
+	const struct sdw_master_ops *ops;
+	struct sdw_bus *bus = m_rt->bus;
+	int ret = 0;
+
+	ops = bus->ops;
+
+	/* Post-bank switch */
+	if (ops->post_bank_switch) {
+		ret = ops->post_bank_switch(bus);
+		if (ret < 0) {
+			dev_err(bus->dev,
+					"Post bank switch op failed: %d", ret);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+static int do_bank_switch(struct sdw_stream_runtime *stream)
+{
+	struct sdw_master_runtime *m_rt = stream->m_rt;
+	struct sdw_bus *bus = m_rt->bus;
+	int ret = 0;
+
+		/* Bank switch */
+		ret = sdw_bank_switch(bus);
+		if (ret < 0) {
+			dev_err(bus->dev, "Bank switch failed: %d", ret);
+			goto err;
+		}
+
+	ret = sdw_post_bank_switch(stream);
+	if (ret < 0)
+		dev_err(bus->dev, "Post Bank switch failed: %d", ret);
+
+err:
+	return ret;
+}
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h
index 39fa86a83a1e..7c7d9d6b730f 100644
--- a/include/linux/soundwire/sdw.h
+++ b/include/linux/soundwire/sdw.h
@@ -23,7 +23,17 @@ struct sdw_slave;
 #define SDW_MASTER_DEV_NUM		14
 
 #define SDW_NUM_DEV_ID_REGISTERS	6
+/* frame shape defines */
 
+/*
+ * Note: The maximum row define in SoundWire spec 1.1 is 23. In order to
+ * fill hole with 0, one more dummy entry is added
+ */
+#define SDW_FRAME_ROWS		24
+#define SDW_FRAME_COLS		8
+#define SDW_FRAME_ROW_COLS		(SDW_FRAME_ROWS * SDW_FRAME_COLS)
+
+#define SDW_FRAME_CTRL_BITS		48
 #define SDW_MAX_DEVICES			11
 
 #define SDW_VALID_PORT_RANGE(n)		(n <= 14 && n >= 1)
@@ -380,6 +390,21 @@ enum sdw_reg_bank {
 };
 
 /**
+ * struct sdw_bus_conf: Bus configuration
+ *
+ * @clk_freq: Clock frequency, in Hz
+ * @num_rows: Number of rows in frame
+ * @num_cols: Number of columns in frame
+ * @bank: Next register bank
+ */
+struct sdw_bus_conf {
+	unsigned int clk_freq;
+	unsigned int num_rows;
+	unsigned int num_cols;
+	unsigned int bank;
+};
+
+/**
  * struct sdw_prepare_ch: Prepare/De-prepare Data Port channel
  *
  * @num: Port number
@@ -416,10 +441,20 @@ enum sdw_port_prep_ops {
  * @curr_bank: Current bank in use (BANK0/BANK1)
  * @next_bank: Next bank to use (BANK0/BANK1). next_bank will always be
  * set to !curr_bank
+ * @max_dr_freq: Maximum double rate clock frequency supported, in Hz
+ * @curr_dr_freq: Current double rate clock frequency, in Hz
+ * @bandwidth: Current bandwidth
+ * @col: Active columns
+ * @row: Active rows
  */
 struct sdw_bus_params {
 	enum sdw_reg_bank curr_bank;
 	enum sdw_reg_bank next_bank;
+	unsigned int max_dr_freq;
+	unsigned int curr_dr_freq;
+	unsigned int bandwidth;
+	unsigned int col;
+	unsigned int row;
 };
 
 /**
@@ -429,6 +464,7 @@ struct sdw_bus_params {
  * @interrupt_callback: Device interrupt notification (invoked in thread
  * context)
  * @update_status: Update Slave status
+ * @bus_config: Update the bus config for Slave
  * @port_prep: Prepare the port with parameters
  */
 struct sdw_slave_ops {
@@ -437,6 +473,8 @@ struct sdw_slave_ops {
 			struct sdw_slave_intr_status *status);
 	int (*update_status)(struct sdw_slave *slave,
 			enum sdw_slave_status status);
+	int (*bus_config)(struct sdw_slave *slave,
+			struct sdw_bus_params *params);
 	int (*port_prep)(struct sdw_slave *slave,
 			struct sdw_prepare_ch *prepare_ch,
 			enum sdw_port_prep_ops pre_ops);
@@ -600,6 +638,9 @@ struct sdw_defer {
  * @xfer_msg: Transfer message callback
  * @xfer_msg_defer: Defer version of transfer message callback
  * @reset_page_addr: Reset the SCP page address registers
+ * @set_bus_conf: Set the bus configuration
+ * @pre_bank_switch: Callback for pre bank switch
+ * @post_bank_switch: Callback for post bank switch
  */
 struct sdw_master_ops {
 	int (*read_prop)(struct sdw_bus *bus);
@@ -611,6 +652,11 @@ struct sdw_master_ops {
 			struct sdw_defer *defer);
 	enum sdw_command_response (*reset_page_addr)
 			(struct sdw_bus *bus, unsigned int dev_num);
+	int (*set_bus_conf)(struct sdw_bus *bus,
+			struct sdw_bus_params *params);
+	int (*pre_bank_switch)(struct sdw_bus *bus);
+	int (*post_bank_switch)(struct sdw_bus *bus);
+
 };
 
 /**
@@ -629,8 +675,10 @@ 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
+ * @multi_link: if multi links are supported
  * @defer_msg: Defer message
  * @clk_stop_timeout: Clock stop timeout computed
+ * @bank_switch_timeout: Bank switch timeout computed
  */
 struct sdw_bus {
 	struct device *dev;
@@ -644,8 +692,10 @@ struct sdw_bus {
 	struct sdw_bus_params params;
 	struct sdw_master_prop prop;
 	struct list_head m_rt_list;
+	bool multi_link;
 	struct sdw_defer defer_msg;
 	unsigned int clk_stop_timeout;
+	u32 bank_switch_timeout;
 };
 
 int sdw_add_bus_master(struct sdw_bus *bus);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 07/13] soundwire: Add stream configuration APIs
  2018-03-28  9:38 [PATCH 00/13] soundwire: Add stream support Vinod Koul
                   ` (5 preceding siblings ...)
  2018-03-28  9:38 ` [PATCH 06/13] soundwire: Add bank switch routine Vinod Koul
@ 2018-03-28  9:38 ` Vinod Koul
  2018-03-28  9:38 ` [PATCH 08/13] ASoC: Add SoundWire stream programming interface Vinod Koul
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Vinod Koul @ 2018-03-28  9:38 UTC (permalink / raw)
  To: Greg KH
  Cc: ALSA, tiwai, Pierre-Louis Bossart, liam.r.girdwood,
	patches.audio, broonie, Vinod Koul

Add APIs for prepare, enable, disable and de-prepare stream.

Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 drivers/soundwire/bus.c       |  14 ++++
 drivers/soundwire/bus.h       |   6 ++
 drivers/soundwire/runtime.c   | 164 ++++++++++++++++++++++++++++++++++++++++++
 drivers/soundwire/stream.c    | 151 ++++++++++++++++++++++++++++++++++++++
 include/linux/soundwire/sdw.h |   4 ++
 5 files changed, 339 insertions(+)

diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c
index 6cdb08b8781b..6f319bfde569 100644
--- a/drivers/soundwire/bus.c
+++ b/drivers/soundwire/bus.c
@@ -17,6 +17,7 @@
  */
 int sdw_add_bus_master(struct sdw_bus *bus)
 {
+	struct sdw_master_prop *prop = NULL;
 	int ret;
 
 	if (!bus->dev) {
@@ -78,6 +79,19 @@ int sdw_add_bus_master(struct sdw_bus *bus)
 		return ret;
 	}
 
+	/*
+	 * Initialize clock values based on Master properties. The max
+	 * frequency is read from max_freq property. Current assumption
+	 * is that the bus will start at highest clock frequency when
+	 * powered on.
+	 *
+	 * Default active bank will be 0 as out of reset the Slaves have to
+	 * start with bank 0 (Table 40 of Spec)
+	 */
+
+	prop = &bus->prop;
+	bus->params.max_dr_freq = prop->max_freq * SDW_DOUBLE_RATE_FACTOR;
+	bus->params.curr_dr_freq = bus->params.max_dr_freq;
 	bus->params.curr_bank = SDW_BANK0;
 	bus->params.next_bank = SDW_BANK1;
 
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h
index 03a56a7feba1..60419e56bdb7 100644
--- a/drivers/soundwire/bus.h
+++ b/drivers/soundwire/bus.h
@@ -46,6 +46,7 @@ struct sdw_msg {
 };
 
 #define SDW_DOUBLE_RATE_FACTOR		2
+#define SDW_FREQ_MOD_FACTOR		3000
 
 extern int rows[SDW_FRAME_ROWS];
 extern int cols[SDW_FRAME_COLS];
@@ -114,6 +115,11 @@ struct sdw_master_runtime {
 	struct list_head bus_node;
 };
 
+int _sdw_prepare_stream(struct sdw_stream_runtime *stream);
+int _sdw_enable_stream(struct sdw_stream_runtime *stream);
+int _sdw_deprepare_stream(struct sdw_stream_runtime *stream);
+int _sdw_disable_stream(struct sdw_stream_runtime *stream);
+
 struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave,
 				enum sdw_data_direction direction,
 				unsigned int port_num);
diff --git a/drivers/soundwire/runtime.c b/drivers/soundwire/runtime.c
index 9bc876d23a4c..d19c36329033 100644
--- a/drivers/soundwire/runtime.c
+++ b/drivers/soundwire/runtime.c
@@ -741,3 +741,167 @@ static int do_bank_switch(struct sdw_stream_runtime *stream)
 err:
 	return ret;
 }
+
+/**
+ * _sdw_deprepare_stream: De-prepare stream
+ *
+ * @stream: Stream runtime handle
+ */
+int _sdw_deprepare_stream(struct sdw_stream_runtime *stream)
+{
+	struct sdw_master_runtime *m_rt = stream->m_rt;
+	struct sdw_bus *bus = m_rt->bus;
+	int ret = 0;
+
+	/* De-prepare port(s) */
+	ret = sdw_prep_deprep_ports(m_rt, false);
+	if (ret < 0) {
+		dev_err(bus->dev, "De-prepare port(s) failed: %d", ret);
+		return ret;
+	}
+
+	bus->params.bandwidth -= m_rt->stream->params.rate *
+		m_rt->ch_count * m_rt->stream->params.bps;
+
+	if (!bus->params.bandwidth) {
+		bus->params.row = 0;
+		bus->params.col = 0;
+		goto exit;
+
+	}
+
+	/* Program params */
+	ret = sdw_program_params(bus);
+	if (ret < 0) {
+		dev_err(bus->dev, "Program params failed: %d", ret);
+		return ret;
+	}
+
+	return do_bank_switch(stream);
+
+exit:
+	stream->state = SDW_STREAM_DEPREPARE;
+
+	return ret;
+}
+
+/**
+ * _sdw_disable_stream: Disable stream
+ *
+ * @stream: Stream runtime handle
+ */
+int _sdw_disable_stream(struct sdw_stream_runtime *stream)
+{
+	struct sdw_master_runtime *m_rt = stream->m_rt;
+	struct sdw_bus *bus = m_rt->bus;
+	int ret;
+
+	/* Disable port(s) */
+	ret = sdw_enable_disable_ports(m_rt, false);
+	if (ret < 0) {
+		dev_err(bus->dev, "Disable port(s) failed: %d", ret);
+		return ret;
+	}
+
+	stream->state = SDW_STREAM_DISABLE;
+
+	/* Program params */
+	ret = sdw_program_params(bus);
+	if (ret < 0) {
+		dev_err(bus->dev, "Program params failed: %d", ret);
+		return ret;
+	}
+
+	return do_bank_switch(stream);
+}
+
+/**
+ * _sdw_enable_stream: Enable stream
+ *
+ * @stream: Stream runtime handle
+ */
+int _sdw_enable_stream(struct sdw_stream_runtime *stream)
+{
+	struct sdw_master_runtime *m_rt = stream->m_rt;
+	struct sdw_bus *bus = m_rt->bus;
+	int ret;
+
+	/* Program params */
+	ret = sdw_program_params(bus);
+	if (ret < 0) {
+		dev_err(bus->dev, "Program params failed: %d", ret);
+		return ret;
+	}
+
+	/* Enable port(s) */
+	ret = sdw_enable_disable_ports(m_rt, true);
+	if (ret < 0) {
+		dev_err(bus->dev, "Enable port(s) failed ret: %d", ret);
+		return ret;
+	}
+
+	ret = do_bank_switch(stream);
+	if (ret < 0) {
+		dev_err(bus->dev, "Bank switch failed: %d", ret);
+		return ret;
+	}
+
+	stream->state = SDW_STREAM_ENABLE;
+	return 0;
+}
+
+/**
+ * sdw_prepare_stream: Prepare stream
+ *
+ * @stream: Stream runtime handle
+ */
+int _sdw_prepare_stream(struct sdw_stream_runtime *stream)
+{
+	struct sdw_master_runtime *m_rt = stream->m_rt;
+	struct sdw_bus *bus = m_rt->bus;
+	struct sdw_master_prop *prop = NULL;
+	struct sdw_bus_params params;
+	int ret;
+
+	prop = &bus->prop;
+	memcpy(&params, &bus->params, sizeof(params));
+
+	/* TODO: Support Asynchronous mode */
+	if ((prop->max_freq % stream->params.rate) != 0) {
+		dev_err(bus->dev, "Async mode not supported");
+		return -EINVAL;
+	}
+
+	/* Increment cumulative bus bandwidth */
+	bus->params.bandwidth += m_rt->stream->params.rate *
+		m_rt->ch_count * m_rt->stream->params.bps;
+
+	/* Program params */
+	ret = sdw_program_params(bus);
+	if (ret < 0) {
+		dev_err(bus->dev, "Program params failed: %d", ret);
+		goto restore_params;
+	}
+
+	ret = do_bank_switch(stream);
+	if (ret < 0) {
+		dev_err(bus->dev, "Bank switch failed: %d", ret);
+		goto restore_params;
+	}
+
+	/* Prepare port(s) on the new clock configuration */
+	ret = sdw_prep_deprep_ports(m_rt, true);
+	if (ret < 0) {
+		dev_err(bus->dev, "Prepare port(s) failed ret = %d",
+				ret);
+		return ret;
+	}
+
+	stream->state = SDW_STREAM_PREPARE;
+
+	return ret;
+
+restore_params:
+	memcpy(&bus->params, &params, sizeof(params));
+	return ret;
+}
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c
index a29447e9a2f0..f7efb7296877 100644
--- a/drivers/soundwire/stream.c
+++ b/drivers/soundwire/stream.c
@@ -525,3 +525,154 @@ struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave,
 
 	return NULL;
 }
+
+/**
+ * sdw_prepare_stream: Prepare SoundWire stream
+ *
+ * @stream: Soundwire stream
+ *
+ * Documentation/soundwire/stream.txt explains this API in detail
+ */
+int sdw_prepare_stream(struct sdw_stream_runtime *stream)
+{
+	int ret = 0;
+
+	if (!stream) {
+		pr_err("SoundWire: Handle not found for stream");
+		return -EINVAL;
+	}
+
+	mutex_lock(&stream->m_rt->bus->bus_lock);
+
+	if (stream->state == SDW_STREAM_DISABLE)
+		goto error;
+
+	if ((stream->state != SDW_STREAM_CONFIG) &&
+		(stream->state != SDW_STREAM_DEPREPARE)) {
+		ret = -EINVAL;
+		goto error;
+	}
+
+	ret = _sdw_prepare_stream(stream);
+	if (ret < 0) {
+		pr_err("Prepare for stream:%s failed: %d", stream->name, ret);
+		goto error;
+	}
+
+error:
+	mutex_unlock(&stream->m_rt->bus->bus_lock);
+	return ret;
+}
+EXPORT_SYMBOL(sdw_prepare_stream);
+
+/**
+ * sdw_enable_stream: Enable SoundWire stream
+ *
+ * @stream: Soundwire stream
+ *
+ * Documentation/soundwire/stream.txt explains this API in detail
+ */
+int sdw_enable_stream(struct sdw_stream_runtime *stream)
+{
+	int ret = 0;
+
+	if (!stream) {
+		pr_err("SoundWire: Handle not found for stream");
+		return -EINVAL;
+	}
+
+	mutex_lock(&stream->m_rt->bus->bus_lock);
+
+	if (stream->state == SDW_STREAM_ENABLE)
+		goto error;
+
+	if ((stream->state != SDW_STREAM_PREPARE) &&
+		(stream->state != SDW_STREAM_DISABLE)) {
+		ret = -EINVAL;
+		goto error;
+	}
+
+	ret = _sdw_enable_stream(stream);
+	if (ret < 0) {
+		pr_err("Enable for stream:%s failed: %d", stream->name, ret);
+		goto error;
+	}
+
+error:
+	mutex_unlock(&stream->m_rt->bus->bus_lock);
+	return ret;
+}
+EXPORT_SYMBOL(sdw_enable_stream);
+
+/**
+ * sdw_disable_stream: Disable SoundWire stream
+ *
+ * @stream: Soundwire stream
+ *
+ * Documentation/soundwire/stream.txt explains this API in detail
+ */
+int sdw_disable_stream(struct sdw_stream_runtime *stream)
+{
+	int ret = 0;
+
+	if (!stream) {
+		pr_err("SoundWire: Handle not found for stream");
+		return -EINVAL;
+	}
+
+	mutex_lock(&stream->m_rt->bus->bus_lock);
+
+	if (stream->state == SDW_STREAM_DISABLE)
+		goto error;
+
+	if (stream->state != SDW_STREAM_ENABLE) {
+		ret = -EINVAL;
+		goto error;
+	}
+
+	ret = _sdw_disable_stream(stream);
+	if (ret < 0) {
+		pr_err("Disable for stream:%s failed: %d", stream->name, ret);
+		goto error;
+	}
+
+error:
+	mutex_unlock(&stream->m_rt->bus->bus_lock);
+	return ret;
+}
+EXPORT_SYMBOL(sdw_disable_stream);
+
+/**
+ * sdw_deprepare_stream: Deprepare SoundWire stream
+ *
+ * @stream: Soundwire stream
+ *
+ * Documentation/soundwire/stream.txt explains this API in detail
+ */
+int sdw_deprepare_stream(struct sdw_stream_runtime *stream)
+{
+	int ret = 0;
+
+	if (!stream) {
+		pr_err("SoundWire: Handle not found for stream");
+		return -EINVAL;
+	}
+
+	mutex_lock(&stream->m_rt->bus->bus_lock);
+
+	if (stream->state != SDW_STREAM_DISABLE) {
+		ret = -EINVAL;
+		goto error;
+	}
+
+	ret = _sdw_deprepare_stream(stream);
+	if (ret < 0) {
+		pr_err("De-prepare for stream:%d failed: %d", ret, ret);
+		goto error;
+	}
+
+error:
+	mutex_unlock(&stream->m_rt->bus->bus_lock);
+	return ret;
+}
+EXPORT_SYMBOL(sdw_deprepare_stream);
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h
index 7c7d9d6b730f..9b583a8ae3dd 100644
--- a/include/linux/soundwire/sdw.h
+++ b/include/linux/soundwire/sdw.h
@@ -796,6 +796,10 @@ int sdw_stream_remove_master(struct sdw_bus *bus,
 		struct sdw_stream_runtime *stream);
 int sdw_stream_remove_slave(struct sdw_slave *slave,
 		struct sdw_stream_runtime *stream);
+int sdw_prepare_stream(struct sdw_stream_runtime *stream);
+int sdw_enable_stream(struct sdw_stream_runtime *stream);
+int sdw_disable_stream(struct sdw_stream_runtime *stream);
+int sdw_deprepare_stream(struct sdw_stream_runtime *stream);
 
 /* messaging and data APIs */
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 08/13] ASoC: Add SoundWire stream programming interface
  2018-03-28  9:38 [PATCH 00/13] soundwire: Add stream support Vinod Koul
                   ` (6 preceding siblings ...)
  2018-03-28  9:38 ` [PATCH 07/13] soundwire: Add stream configuration APIs Vinod Koul
@ 2018-03-28  9:38 ` Vinod Koul
  2018-03-30  3:05   ` Takashi Sakamoto
  2018-03-28  9:38 ` [PATCH 09/13] soundwire: Remove cdns_master_ops Vinod Koul
                   ` (4 subsequent siblings)
  12 siblings, 1 reply; 25+ messages in thread
From: Vinod Koul @ 2018-03-28  9:38 UTC (permalink / raw)
  To: Greg KH
  Cc: ALSA, tiwai, Pierre-Louis Bossart, liam.r.girdwood,
	patches.audio, broonie, Shreyas NC, Vinod Koul

From: Shreyas NC <shreyas.nc@intel.com>

SoundWire stream needs to be propagated to all the DAIs(cpu, codec).
So, add a snd_soc_dai_set_sdw_stream() API for the same.

Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 include/sound/soc-dai.h |  5 +++++
 sound/soc/soc-core.c    | 20 ++++++++++++++++++++
 2 files changed, 25 insertions(+)

diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index 58acd00cae19..78fa068560cf 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -129,6 +129,9 @@ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
 int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
 	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width);
 
+int snd_soc_dai_set_sdw_stream(struct snd_soc_dai *dai,
+				void *stream, int direction);
+
 int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
 	unsigned int tx_num, unsigned int *tx_slot,
 	unsigned int rx_num, unsigned int *rx_slot);
@@ -168,6 +171,8 @@ struct snd_soc_dai_ops {
 		unsigned int rx_num, unsigned int *rx_slot);
 	int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
 
+	int (*set_sdw_stream)(struct snd_soc_dai *dai,
+			void *stream, int direction);
 	/*
 	 * DAI digital mute - optional.
 	 * Called by soc-core to minimise any pops.
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index c0edac80df34..690e56a35237 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -2882,6 +2882,26 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
 EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
 
 /**
+ * snd_soc_dai_set_sdw_stream() - Configures a DAI for SDW stream operation
+ * @dai: DAI
+ * @stream: STREAM
+ * @direction: Stream direction(Playback/Capture)
+ * SoundWire subsystem doesn't have a notion of direction and we reuse
+ * the ASoC stream direction to configure sink/source ports.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int snd_soc_dai_set_sdw_stream(struct snd_soc_dai *dai,
+				void *stream, int direction)
+{
+	if (dai->driver->ops->set_sdw_stream)
+		return dai->driver->ops->set_sdw_stream(dai, stream, direction);
+	else
+		return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_sdw_stream);
+
+/**
  * snd_soc_dai_set_channel_map - configure DAI audio channel map
  * @dai: DAI
  * @tx_num: how many TX channels
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 09/13] soundwire: Remove cdns_master_ops
  2018-03-28  9:38 [PATCH 00/13] soundwire: Add stream support Vinod Koul
                   ` (7 preceding siblings ...)
  2018-03-28  9:38 ` [PATCH 08/13] ASoC: Add SoundWire stream programming interface Vinod Koul
@ 2018-03-28  9:38 ` Vinod Koul
  2018-03-28  9:38 ` [PATCH 10/13] soundwire: cdns: Add port routines Vinod Koul
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Vinod Koul @ 2018-03-28  9:38 UTC (permalink / raw)
  To: Greg KH
  Cc: ALSA, tiwai, Pierre-Louis Bossart, liam.r.girdwood,
	patches.audio, broonie, Shreyas NC, Vinod Koul

From: Shreyas NC <shreyas.nc@intel.com>

There can be instances where drivers using Cadence IP might want
to set sdw_master_ops differently per instance of it's use, so
remove the cdns_master_ops and export the APIs.

Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 drivers/soundwire/cadence_master.c | 17 ++++++-----------
 drivers/soundwire/cadence_master.h |  8 ++++++++
 drivers/soundwire/intel.c          | 11 +++++++++--
 3 files changed, 23 insertions(+), 13 deletions(-)

diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c
index 3a9b1462039b..b0c09efd8f83 100644
--- a/drivers/soundwire/cadence_master.c
+++ b/drivers/soundwire/cadence_master.c
@@ -396,7 +396,7 @@ static int cdns_prep_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int *cmd)
 	return 0;
 }
 
-static enum sdw_command_response
+enum sdw_command_response
 cdns_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg)
 {
 	struct sdw_cdns *cdns = bus_to_cdns(bus);
@@ -422,8 +422,9 @@ cdns_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg)
 exit:
 	return ret;
 }
+EXPORT_SYMBOL(cdns_xfer_msg);
 
-static enum sdw_command_response
+enum sdw_command_response
 cdns_xfer_msg_defer(struct sdw_bus *bus,
 		struct sdw_msg *msg, struct sdw_defer *defer)
 {
@@ -443,8 +444,9 @@ cdns_xfer_msg_defer(struct sdw_bus *bus,
 
 	return _cdns_xfer_msg(cdns, msg, cmd, 0, msg->len, true);
 }
+EXPORT_SYMBOL(cdns_xfer_msg_defer);
 
-static enum sdw_command_response
+enum sdw_command_response
 cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num)
 {
 	struct sdw_cdns *cdns = bus_to_cdns(bus);
@@ -456,6 +458,7 @@ cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num)
 
 	return cdns_program_scp_addr(cdns, &msg);
 }
+EXPORT_SYMBOL(cdns_reset_page_addr);
 
 /*
  * IRQ handling
@@ -727,14 +730,6 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
 }
 EXPORT_SYMBOL(sdw_cdns_init);
 
-struct sdw_master_ops sdw_cdns_master_ops = {
-	.read_prop = sdw_master_read_prop,
-	.xfer_msg = cdns_xfer_msg,
-	.xfer_msg_defer = cdns_xfer_msg_defer,
-	.reset_page_addr = cdns_reset_page_addr,
-};
-EXPORT_SYMBOL(sdw_cdns_master_ops);
-
 /**
  * sdw_cdns_probe() - Cadence probe routine
  * @cdns: Cadence instance
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h
index beaf6c9804eb..3ec74fa5f4f9 100644
--- a/drivers/soundwire/cadence_master.h
+++ b/drivers/soundwire/cadence_master.h
@@ -44,5 +44,13 @@ irqreturn_t sdw_cdns_thread(int irq, void *dev_id);
 int sdw_cdns_init(struct sdw_cdns *cdns);
 int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns);
 
+enum sdw_command_response
+cdns_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg);
 
+enum sdw_command_response
+cdns_xfer_msg_defer(struct sdw_bus *bus,
+		struct sdw_msg *msg, struct sdw_defer *defer);
+
+enum sdw_command_response
+cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num);
 #endif /* __SDW_CADENCE_H */
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c
index 86a7bd1fc912..aa0c60133de5 100644
--- a/drivers/soundwire/intel.c
+++ b/drivers/soundwire/intel.c
@@ -252,6 +252,13 @@ static int intel_prop_read(struct sdw_bus *bus)
 	return 0;
 }
 
+static struct sdw_master_ops sdw_intel_ops = {
+	.read_prop = sdw_master_read_prop,
+	.xfer_msg = cdns_xfer_msg,
+	.xfer_msg_defer = cdns_xfer_msg_defer,
+	.reset_page_addr = cdns_reset_page_addr,
+};
+
 /*
  * probe and init
  */
@@ -276,8 +283,8 @@ static int intel_probe(struct platform_device *pdev)
 	sdw_cdns_probe(&sdw->cdns);
 
 	/* Set property read ops */
-	sdw_cdns_master_ops.read_prop = intel_prop_read;
-	sdw->cdns.bus.ops = &sdw_cdns_master_ops;
+	sdw_intel_ops.read_prop = intel_prop_read;
+	sdw->cdns.bus.ops = &sdw_intel_ops;
 
 	platform_set_drvdata(pdev, sdw);
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 10/13] soundwire: cdns: Add port routines
  2018-03-28  9:38 [PATCH 00/13] soundwire: Add stream support Vinod Koul
                   ` (8 preceding siblings ...)
  2018-03-28  9:38 ` [PATCH 09/13] soundwire: Remove cdns_master_ops Vinod Koul
@ 2018-03-28  9:38 ` Vinod Koul
  2018-03-28  9:38 ` [PATCH 11/13] soundwire: cdns: Add stream routines Vinod Koul
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Vinod Koul @ 2018-03-28  9:38 UTC (permalink / raw)
  To: Greg KH
  Cc: ALSA, tiwai, Pierre-Louis Bossart, liam.r.girdwood,
	patches.audio, broonie, Vinod Koul

Add support for Cadence port management and implement
master port ops.

Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 drivers/soundwire/cadence_master.c | 256 +++++++++++++++++++++++++++++++++++++
 drivers/soundwire/cadence_master.h | 100 +++++++++++++++
 drivers/soundwire/intel.c          |   1 +
 3 files changed, 357 insertions(+)

diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c
index b0c09efd8f83..89a4ae86d36a 100644
--- a/drivers/soundwire/cadence_master.c
+++ b/drivers/soundwire/cadence_master.c
@@ -669,6 +669,133 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns)
 }
 EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
 
+static int cdns_allocate_pdi(struct sdw_cdns *cdns,
+			struct sdw_cdns_pdi **stream,
+			u32 start, u32 num, u32 pdi_offset)
+{
+	struct sdw_cdns_pdi *pdi;
+	int i;
+
+	if (!num)
+		return 0;
+
+	pdi = devm_kcalloc(cdns->dev, num, sizeof(*pdi), GFP_KERNEL);
+	if (!pdi)
+		return -ENOMEM;
+
+	for (i = 0; i < num; i++) {
+		pdi[i].num = i + pdi_offset;
+		pdi[i].assigned = false;
+	}
+
+	*stream = pdi;
+	return 0;
+}
+
+/**
+ * sdw_cdns_pdi_init: PDI initialization routine
+ *
+ * @cdns: Cadence instance
+ * @config: Stream configurations
+ */
+int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
+			struct sdw_cdns_stream_config config)
+{
+	struct sdw_cdns_streams *stream;
+	int offset, i, ret;
+
+	cdns->pcm.num_bd = config.pcm_bd;
+	cdns->pcm.num_in = config.pcm_in;
+	cdns->pcm.num_out = config.pcm_out;
+	cdns->pdm.num_bd = config.pdm_bd;
+	cdns->pdm.num_in = config.pdm_in;
+	cdns->pdm.num_out = config.pdm_out;
+
+	/* Allocate PDIs for PCMs */
+	stream = &cdns->pcm;
+
+	/* First two PDIs are reserved for bulk transfers */
+	stream->num_bd -= CDNS_PCM_PDI_OFFSET;
+	offset = CDNS_PCM_PDI_OFFSET;
+
+	ret = cdns_allocate_pdi(cdns, &stream->bd, 0,
+				stream->num_bd, offset);
+	if (ret)
+		goto pcm_error;
+
+	offset += stream->num_bd;
+
+	ret = cdns_allocate_pdi(cdns, &stream->in, 0,
+				stream->num_in, offset);
+	if (ret)
+		goto pcm_error;
+
+
+	offset += stream->num_in;
+
+	ret = cdns_allocate_pdi(cdns, &stream->out, 0,
+				stream->num_out, offset);
+	if (ret)
+		goto pcm_error;
+
+	/* Update total number of PCM PDIs */
+	stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out;
+	cdns->num_ports = stream->num_pdi;
+
+	/* Allocate PDIs for PDMs */
+	stream = &cdns->pdm;
+	offset = CDNS_PDM_PDI_OFFSET;
+	ret = cdns_allocate_pdi(cdns, &stream->bd, 0,
+				stream->num_bd, offset);
+	if (ret)
+		goto pdm_error;
+
+	offset += stream->num_bd;
+
+	ret = cdns_allocate_pdi(cdns, &stream->in, 0,
+				stream->num_in, offset);
+	if (ret)
+		goto pdm_error;
+
+	offset += stream->num_in;
+
+	ret = cdns_allocate_pdi(cdns, &stream->out, 0,
+				stream->num_out, offset);
+	if (ret)
+		goto pdm_error;
+
+	/* Update total number of PDM PDIs */
+	stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out;
+	cdns->num_ports += stream->num_pdi;
+
+	cdns->ports = devm_kcalloc(cdns->dev, cdns->num_ports,
+				sizeof(*cdns->ports), GFP_KERNEL);
+	if (!cdns->ports) {
+		ret = -ENOMEM;
+		goto pdm_error;
+	}
+
+	for (i = 0; i < cdns->num_ports; i++) {
+		cdns->ports[i].assigned = false;
+		cdns->ports[i].num = i + 1; /* Port 0 reserved for bulk */
+	}
+
+	return 0;
+
+pdm_error:
+	kfree(stream->bd);
+	kfree(stream->in);
+	kfree(stream->out);
+
+pcm_error:
+	stream = &cdns->pcm;
+	kfree(stream->bd);
+	kfree(stream->in);
+	kfree(stream->out);
+	return ret;
+}
+EXPORT_SYMBOL(sdw_cdns_pdi_init);
+
 /**
  * sdw_cdns_init() - Cadence initialization
  * @cdns: Cadence instance
@@ -730,6 +857,134 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
 }
 EXPORT_SYMBOL(sdw_cdns_init);
 
+int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params)
+{
+	struct sdw_cdns *cdns = bus_to_cdns(bus);
+	int mcp_clkctrl_off, mcp_clkctrl;
+	int divider;
+
+	if (!params->curr_dr_freq) {
+		dev_err(cdns->dev, "NULL curr_dr_freq");
+		return -EINVAL;
+	}
+
+	divider	= (params->max_dr_freq / params->curr_dr_freq) - 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);
+
+	return 0;
+}
+EXPORT_SYMBOL(cdns_bus_conf);
+
+static int cdns_port_params(struct sdw_bus *bus,
+		struct sdw_port_params *p_params, unsigned int bank)
+{
+	struct sdw_cdns *cdns = bus_to_cdns(bus);
+	int dpn_config = 0, dpn_config_off;
+
+	if (bank)
+		dpn_config_off = CDNS_DPN_B1_CONFIG(p_params->num);
+	else
+		dpn_config_off = CDNS_DPN_B0_CONFIG(p_params->num);
+
+	dpn_config = cdns_readl(cdns, dpn_config_off);
+
+	dpn_config |= ((p_params->bps - 1) <<
+				SDW_REG_SHIFT(CDNS_DPN_CONFIG_WL));
+	dpn_config |= (p_params->flow_mode <<
+				SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_FLOW));
+	dpn_config |= (p_params->data_mode <<
+				SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_DAT));
+
+	cdns_writel(cdns, dpn_config_off, dpn_config);
+
+	return 0;
+}
+
+static int cdns_transport_params(struct sdw_bus *bus,
+			struct sdw_transport_params *t_params,
+			enum sdw_reg_bank bank)
+{
+	struct sdw_cdns *cdns = bus_to_cdns(bus);
+	int dpn_offsetctrl = 0, dpn_offsetctrl_off;
+	int dpn_config = 0, dpn_config_off;
+	int dpn_hctrl = 0, dpn_hctrl_off;
+	int num = t_params->port_num;
+	int dpn_samplectrl_off;
+
+	/*
+	 * Note: Only full data port is supported on the Master side for
+	 * both PCM and PDM ports.
+	 */
+
+	if (bank) {
+		dpn_config_off = CDNS_DPN_B1_CONFIG(num);
+		dpn_samplectrl_off = CDNS_DPN_B1_SAMPLE_CTRL(num);
+		dpn_hctrl_off = CDNS_DPN_B1_HCTRL(num);
+		dpn_offsetctrl_off = CDNS_DPN_B1_OFFSET_CTRL(num);
+	} else {
+		dpn_config_off = CDNS_DPN_B0_CONFIG(num);
+		dpn_samplectrl_off = CDNS_DPN_B0_SAMPLE_CTRL(num);
+		dpn_hctrl_off = CDNS_DPN_B0_HCTRL(num);
+		dpn_offsetctrl_off = CDNS_DPN_B0_OFFSET_CTRL(num);
+	}
+
+	dpn_config = cdns_readl(cdns, dpn_config_off);
+
+	dpn_config |= (t_params->blk_grp_ctrl <<
+				SDW_REG_SHIFT(CDNS_DPN_CONFIG_BGC));
+	dpn_config |= (t_params->blk_pkg_mode <<
+				SDW_REG_SHIFT(CDNS_DPN_CONFIG_BPM));
+	cdns_writel(cdns, dpn_config_off, dpn_config);
+
+	dpn_offsetctrl |= (t_params->offset1 <<
+				SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_1));
+	dpn_offsetctrl |= (t_params->offset2 <<
+				SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_2));
+	cdns_writel(cdns, dpn_offsetctrl_off,  dpn_offsetctrl);
+
+	dpn_hctrl |= (t_params->hstart <<
+				SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTART));
+	dpn_hctrl |= (t_params->hstop << SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTOP));
+	dpn_hctrl |= (t_params->lane_ctrl <<
+				SDW_REG_SHIFT(CDNS_DPN_HCTRL_LCTRL));
+
+	cdns_writel(cdns, dpn_hctrl_off, dpn_hctrl);
+	cdns_writel(cdns, dpn_samplectrl_off, (t_params->sample_interval - 1));
+
+	return 0;
+}
+
+static int cdns_port_enable(struct sdw_bus *bus,
+		struct sdw_enable_ch *enable_ch, unsigned int bank)
+{
+	struct sdw_cdns *cdns = bus_to_cdns(bus);
+	int dpn_chnen_off, ch_mask;
+
+	if (bank)
+		dpn_chnen_off = CDNS_DPN_B1_CH_EN(enable_ch->num);
+	else
+		dpn_chnen_off = CDNS_DPN_B0_CH_EN(enable_ch->num);
+
+	ch_mask = enable_ch->ch_mask * enable_ch->enable;
+	cdns_writel(cdns, dpn_chnen_off, ch_mask);
+
+	return 0;
+}
+
+static const struct sdw_master_port_ops cdns_port_ops = {
+	.dpn_set_port_params = cdns_port_params,
+	.dpn_set_port_transport_params = cdns_transport_params,
+	.dpn_port_enable_ch = cdns_port_enable,
+};
+
 /**
  * sdw_cdns_probe() - Cadence probe routine
  * @cdns: Cadence instance
@@ -737,6 +992,7 @@ EXPORT_SYMBOL(sdw_cdns_init);
 int sdw_cdns_probe(struct sdw_cdns *cdns)
 {
 	init_completion(&cdns->tx_complete);
+	cdns->bus.port_ops = &cdns_port_ops;
 
 	return 0;
 }
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h
index 3ec74fa5f4f9..3cdff08d3749 100644
--- a/drivers/soundwire/cadence_master.h
+++ b/drivers/soundwire/cadence_master.h
@@ -5,6 +5,92 @@
 #define __SDW_CADENCE_H
 
 /**
+ * struct sdw_cdns_pdi: PDI (Physical Data Interface) instance
+ *
+ * @assigned: pdi assigned
+ * @num: pdi number
+ * @intel_alh_id: link identifier
+ * @l_ch_num: low channel for PDI
+ * @h_ch_num: high channel for PDI
+ * @ch_count: total channel count for PDI
+ * @dir: data direction
+ * @type: stream type, PDM or PCM
+ */
+struct sdw_cdns_pdi {
+	bool assigned;
+	int num;
+	int intel_alh_id;
+	int l_ch_num;
+	int h_ch_num;
+	int ch_count;
+	enum sdw_data_direction dir;
+	enum sdw_stream_type type;
+};
+
+/**
+ * struct sdw_cdns_port: Cadence port structure
+ *
+ * @num: port number
+ * @assigned: port assigned
+ * @ch: channel count
+ * @direction: data port direction
+ * @pdi: pdi for this port
+ */
+struct sdw_cdns_port {
+	unsigned int num;
+	bool assigned;
+	unsigned int ch;
+	enum sdw_data_direction direction;
+	struct sdw_cdns_pdi *pdi;
+};
+
+/**
+ * struct sdw_cdns_streams: Cadence stream data structure
+ *
+ * @num_bd: number of bidirectional streams
+ * @num_in: number of input streams
+ * @num_out: number of output streams
+ * @num_ch_bd: number of bidirectional stream channels
+ * @num_ch_bd: number of input stream channels
+ * @num_ch_bd: number of output stream channels
+ * @num_pdi: toal number of PDIs
+ * @bd: bidirectional streams
+ * @in: input streams
+ * @out: output streams
+ */
+struct sdw_cdns_streams {
+	unsigned int num_bd;
+	unsigned int num_in;
+	unsigned int num_out;
+	unsigned int num_ch_bd;
+	unsigned int num_ch_in;
+	unsigned int num_ch_out;
+	unsigned int num_pdi;
+	struct sdw_cdns_pdi *bd;
+	struct sdw_cdns_pdi *in;
+	struct sdw_cdns_pdi *out;
+};
+
+/**
+ * struct sdw_cdns_stream_config: stream configuration
+ *
+ * @pcm_bd: number of bidirectional PCM streams supported
+ * @pcm_in: number of input PCM streams supported
+ * @pcm_out: number of output PCM streams supported
+ * @pdm_bd: number of bidirectional PDM streams supported
+ * @pdm_in: number of input PDM streams supported
+ * @pdm_out: number of output PDM streams supported
+ */
+struct sdw_cdns_stream_config {
+	unsigned int pcm_bd;
+	unsigned int pcm_in;
+	unsigned int pcm_out;
+	unsigned int pdm_bd;
+	unsigned int pdm_in;
+	unsigned int pdm_out;
+};
+
+/**
  * struct sdw_cdns - Cadence driver context
  * @dev: Linux device
  * @bus: Bus handle
@@ -12,6 +98,10 @@
  * @response_buf: SoundWire response buffer
  * @tx_complete: Tx completion
  * @defer: Defer pointer
+ * @ports: Data ports
+ * @num_ports: Total number of data ports
+ * @pcm: PCM streams
+ * @pdm: PDM streams
  * @registers: Cadence registers
  * @link_up: Link status
  * @msg_count: Messages sent on bus
@@ -25,6 +115,12 @@ struct sdw_cdns {
 	struct completion tx_complete;
 	struct sdw_defer *defer;
 
+	struct sdw_cdns_port *ports;
+	int num_ports;
+
+	struct sdw_cdns_streams pcm;
+	struct sdw_cdns_streams pdm;
+
 	void __iomem *registers;
 
 	bool link_up;
@@ -42,6 +138,8 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id);
 irqreturn_t sdw_cdns_thread(int irq, void *dev_id);
 
 int sdw_cdns_init(struct sdw_cdns *cdns);
+int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
+			struct sdw_cdns_stream_config config);
 int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns);
 
 enum sdw_command_response
@@ -53,4 +151,6 @@ cdns_xfer_msg_defer(struct sdw_bus *bus,
 
 enum sdw_command_response
 cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num);
+
+int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params);
 #endif /* __SDW_CADENCE_H */
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c
index aa0c60133de5..a64f87a08cfd 100644
--- a/drivers/soundwire/intel.c
+++ b/drivers/soundwire/intel.c
@@ -257,6 +257,7 @@ static struct sdw_master_ops sdw_intel_ops = {
 	.xfer_msg = cdns_xfer_msg,
 	.xfer_msg_defer = cdns_xfer_msg_defer,
 	.reset_page_addr = cdns_reset_page_addr,
+	.set_bus_conf = cdns_bus_conf,
 };
 
 /*
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 11/13] soundwire: cdns: Add stream routines
  2018-03-28  9:38 [PATCH 00/13] soundwire: Add stream support Vinod Koul
                   ` (9 preceding siblings ...)
  2018-03-28  9:38 ` [PATCH 10/13] soundwire: cdns: Add port routines Vinod Koul
@ 2018-03-28  9:38 ` Vinod Koul
  2018-03-28  9:38 ` [PATCH 12/13] soundwire: intel: Add stream initialization Vinod Koul
  2018-03-28  9:38 ` [PATCH 13/13] soundwire: intel: Add audio DAI ops Vinod Koul
  12 siblings, 0 replies; 25+ messages in thread
From: Vinod Koul @ 2018-03-28  9:38 UTC (permalink / raw)
  To: Greg KH
  Cc: ALSA, tiwai, Pierre-Louis Bossart, liam.r.girdwood,
	patches.audio, broonie, Vinod Koul

Add support for Cadence stream initialization and implement
stream APIs.

Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 drivers/soundwire/cadence_master.c | 180 +++++++++++++++++++++++++++++++++++++
 drivers/soundwire/cadence_master.h |  43 +++++++++
 2 files changed, 223 insertions(+)

diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c
index 89a4ae86d36a..e611a29600bd 100644
--- a/drivers/soundwire/cadence_master.c
+++ b/drivers/soundwire/cadence_master.c
@@ -13,6 +13,8 @@
 #include <linux/mod_devicetable.h>
 #include <linux/soundwire/sdw_registers.h>
 #include <linux/soundwire/sdw.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
 #include "bus.h"
 #include "cadence_master.h"
 
@@ -998,5 +1000,183 @@ int sdw_cdns_probe(struct sdw_cdns *cdns)
 }
 EXPORT_SYMBOL(sdw_cdns_probe);
 
+int cdns_set_sdw_stream(struct snd_soc_dai *dai,
+		void *stream, bool pcm, int direction)
+{
+	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
+	struct sdw_cdns_dma_data *dma;
+
+	dma = kzalloc(sizeof(*dma), GFP_KERNEL);
+	if (!dma)
+		return -ENOMEM;
+
+	if (pcm)
+		dma->stream_type = SDW_STREAM_PCM;
+	else
+		dma->stream_type = SDW_STREAM_PDM;
+
+	dma->bus = &cdns->bus;
+	dma->link_id = cdns->instance;
+
+	dma->stream = stream;
+
+	if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+		dai->playback_dma_data = dma;
+	else
+		dai->capture_dma_data = dma;
+
+	return 0;
+
+}
+EXPORT_SYMBOL(cdns_set_sdw_stream);
+
+static struct sdw_cdns_pdi *cdns_find_pdi(struct sdw_cdns *cdns,
+		unsigned int num, struct sdw_cdns_pdi *pdi)
+{
+	int i;
+
+	for (i = 0; i < num; i++) {
+		if (pdi[i].assigned == true)
+			continue;
+		pdi[i].assigned = true;
+		return &pdi[i];
+	}
+
+	return NULL;
+}
+
+/**
+ * sdw_cdns_config_stream: Configure a stream
+ *
+ * @cdns: Cadence instance
+ * @port: Cadence data port
+ * @ch: Channel count
+ * @dir: Data direction
+ * @pdi: PDI to be used
+ */
+void sdw_cdns_config_stream(struct sdw_cdns *cdns,
+				struct sdw_cdns_port *port,
+				u32 ch, u32 dir, struct sdw_cdns_pdi *pdi)
+{
+	u32 offset, val = 0;
+
+	if (dir == SDW_DATA_DIR_IN)
+		val = CDNS_PORTCTRL_DIRN;
+
+	offset = CDNS_PORTCTRL + port->num * CDNS_PORT_OFFSET;
+	cdns_updatel(cdns, offset, CDNS_PORTCTRL_DIRN, val);
+
+	val = port->num;
+	val |= ((1 << ch) - 1) << SDW_REG_SHIFT(CDNS_PDI_CONFIG_CHANNEL);
+	cdns_writel(cdns, CDNS_PDI_CONFIG(pdi->num), val);
+}
+EXPORT_SYMBOL(sdw_cdns_config_stream);
+
+static int cdns_get_pdi(struct sdw_cdns *cdns,
+		struct sdw_cdns_pdi *pdi,
+		unsigned int num, u32 ch)
+{
+	int i, pdis = 0;
+	u32 ch_count = ch;
+
+	for (i = 0; i < num; i++) {
+		if (pdi[i].assigned == true)
+			continue;
+
+		if (pdi[i].ch_count < ch_count)
+			ch_count -= pdi[i].ch_count;
+		else
+			ch_count = 0;
+
+		pdis++;
+
+		if (!ch_count)
+			break;
+	}
+
+	if (ch_count)
+		return 0;
+
+	return pdis;
+}
+
+/**
+ * sdw_cdns_get_stream: Get stream information
+ *
+ * @cdns: Cadence instance
+ * @stream: Stream to be allocated
+ * @ch: Channel count
+ * @dir: Data direction
+ */
+int sdw_cdns_get_stream(struct sdw_cdns *cdns,
+			struct sdw_cdns_streams *stream,
+			u32 ch, u32 dir)
+{
+	int pdis = 0;
+
+	if (dir == SDW_DATA_DIR_IN)
+		pdis = cdns_get_pdi(cdns, stream->in, stream->num_in, ch);
+	else
+		pdis = cdns_get_pdi(cdns, stream->out, stream->num_out, ch);
+
+	/* check if we found PDI, else find in bi-directional */
+	if (!pdis)
+		pdis = cdns_get_pdi(cdns, stream->bd, stream->num_bd, ch);
+
+	return pdis;
+}
+EXPORT_SYMBOL(sdw_cdns_get_stream);
+
+/**
+ * sdw_cdns_alloc_stream: Allocate a stream
+ *
+ * @cdns: Cadence instance
+ * @stream: Stream to be allocated
+ * @port: Cadence data port
+ * @ch: Channel count
+ * @dir: Data direction
+ */
+int sdw_cdns_alloc_stream(struct sdw_cdns *cdns,
+			struct sdw_cdns_streams *stream,
+			struct sdw_cdns_port *port, u32 ch, u32 dir)
+{
+	struct sdw_cdns_pdi *pdi = NULL;
+
+	if (dir == SDW_DATA_DIR_IN)
+		pdi = cdns_find_pdi(cdns, stream->num_in, stream->in);
+	else
+		pdi = cdns_find_pdi(cdns, stream->num_out, stream->out);
+
+	/* check if we found a PDI, else find in bi-directional */
+	if (!pdi)
+		pdi = cdns_find_pdi(cdns, stream->num_bd, stream->bd);
+
+	if (!pdi)
+		return -EIO;
+
+	port->pdi = pdi;
+	pdi->l_ch_num = 0;
+	pdi->h_ch_num = ch - 1;
+	pdi->dir = dir;
+	pdi->ch_count = ch;
+
+	return 0;
+}
+EXPORT_SYMBOL(sdw_cdns_alloc_stream);
+
+void sdw_cdns_shutdown(struct snd_pcm_substream *substream,
+					struct snd_soc_dai *dai)
+{
+	struct sdw_cdns_dma_data *dma;
+
+	dma = snd_soc_dai_get_dma_data(dai, substream);
+	if (!dma)
+		return;
+
+	snd_soc_dai_set_dma_data(dai, substream, NULL);
+	kfree(dma);
+}
+EXPORT_SYMBOL(sdw_cdns_shutdown);
+
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_DESCRIPTION("Cadence Soundwire Library");
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h
index 3cdff08d3749..6909028d8428 100644
--- a/drivers/soundwire/cadence_master.h
+++ b/drivers/soundwire/cadence_master.h
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
 // Copyright(c) 2015-17 Intel Corporation.
+#include <sound/soc.h>
 
 #ifndef __SDW_CADENCE_H
 #define __SDW_CADENCE_H
@@ -91,6 +92,26 @@ struct sdw_cdns_stream_config {
 };
 
 /**
+ * struct sdw_cdns_dma_data: Cadence DMA data
+ *
+ * @name: SoundWire stream name
+ * @nr_ports: Number of ports
+ * @port: Ports
+ * @bus: Bus handle
+ * @stream_type: Stream type
+ * @link_id: Master link id
+ */
+struct sdw_cdns_dma_data {
+	char *name;
+	struct sdw_stream_runtime *stream;
+	int nr_ports;
+	struct sdw_cdns_port **port;
+	struct sdw_bus *bus;
+	enum sdw_stream_type stream_type;
+	int link_id;
+};
+
+/**
  * struct sdw_cdns - Cadence driver context
  * @dev: Linux device
  * @bus: Bus handle
@@ -142,6 +163,25 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
 			struct sdw_cdns_stream_config config);
 int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns);
 
+int sdw_cdns_get_stream(struct sdw_cdns *cdns,
+			struct sdw_cdns_streams *stream,
+			u32 ch, u32 dir);
+int sdw_cdns_alloc_stream(struct sdw_cdns *cdns,
+			struct sdw_cdns_streams *stream,
+			struct sdw_cdns_port *port, u32 ch, u32 dir);
+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,
+				void *stream, int direction);
+
+enum sdw_command_response
+cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num);
+
 enum sdw_command_response
 cdns_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg);
 
@@ -153,4 +193,7 @@ enum sdw_command_response
 cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num);
 
 int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params);
+
+int cdns_set_sdw_stream(struct snd_soc_dai *dai,
+		void *stream, bool pcm, int direction);
 #endif /* __SDW_CADENCE_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 12/13] soundwire: intel: Add stream initialization
  2018-03-28  9:38 [PATCH 00/13] soundwire: Add stream support Vinod Koul
                   ` (10 preceding siblings ...)
  2018-03-28  9:38 ` [PATCH 11/13] soundwire: cdns: Add stream routines Vinod Koul
@ 2018-03-28  9:38 ` Vinod Koul
  2018-03-28  9:38 ` [PATCH 13/13] soundwire: intel: Add audio DAI ops Vinod Koul
  12 siblings, 0 replies; 25+ messages in thread
From: Vinod Koul @ 2018-03-28  9:38 UTC (permalink / raw)
  To: Greg KH
  Cc: ALSA, tiwai, Pierre-Louis Bossart, liam.r.girdwood,
	patches.audio, broonie, Vinod Koul

Add Intel stream init routines which initialize the Physical
Data Interface (PDI), Audio Link Hub (ALH) and Audio shim.
Also add bank switch routines.

Signed-off-by: Hardik T Shah <hardik.t.shah@intel.com>
Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 drivers/soundwire/intel.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 217 insertions(+)

diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c
index a64f87a08cfd..e0fb88397a88 100644
--- a/drivers/soundwire/intel.c
+++ b/drivers/soundwire/intel.c
@@ -9,6 +9,8 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
 #include <linux/soundwire/sdw_registers.h>
 #include <linux/soundwire/sdw.h>
 #include <linux/soundwire/sdw_intel.h>
@@ -234,6 +236,209 @@ static int intel_shim_init(struct sdw_intel *sdw)
 	return ret;
 }
 
+/*
+ * PDI routines
+ */
+static void intel_pdi_init(struct sdw_intel *sdw,
+			struct sdw_cdns_stream_config *config)
+{
+	void __iomem *shim = sdw->res->shim;
+	unsigned int link_id = sdw->instance;
+	int pcm_cap, pdm_cap;
+
+	/* PCM Stream Capability */
+	pcm_cap = intel_readw(shim, SDW_SHIM_PCMSCAP(link_id));
+
+	config->pcm_bd = (pcm_cap & SDW_SHIM_PCMSCAP_BSS) >>
+					SDW_REG_SHIFT(SDW_SHIM_PCMSCAP_BSS);
+	config->pcm_in = (pcm_cap & SDW_SHIM_PCMSCAP_ISS) >>
+					SDW_REG_SHIFT(SDW_SHIM_PCMSCAP_ISS);
+	config->pcm_out = (pcm_cap & SDW_SHIM_PCMSCAP_OSS) >>
+					SDW_REG_SHIFT(SDW_SHIM_PCMSCAP_OSS);
+
+	/* PDM Stream Capability */
+	pdm_cap = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id));
+
+	config->pdm_bd = (pdm_cap & SDW_SHIM_PDMSCAP_BSS) >>
+					SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_BSS);
+	config->pdm_in = (pdm_cap & SDW_SHIM_PDMSCAP_ISS) >>
+					SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_ISS);
+	config->pdm_out = (pdm_cap & SDW_SHIM_PDMSCAP_OSS) >>
+					SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_OSS);
+}
+
+static int
+intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm)
+{
+	void __iomem *shim = sdw->res->shim;
+	unsigned int link_id = sdw->instance;
+	int count;
+
+	if (pcm) {
+
+		count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num));
+
+		/*
+		 * TODO: pdi number 2 reports channel count as 1 even though
+		 * it supports 8 channel. 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) >>
+					SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_CPSS));
+	}
+
+	/* zero based values for channel count in register */
+	count++;
+
+	return count;
+}
+
+static int intel_pdi_get_ch_update(struct sdw_intel *sdw,
+				struct sdw_cdns_pdi *pdi,
+				unsigned int num_pdi,
+				unsigned int *num_ch, bool pcm)
+{
+	int i, ch_count = 0;
+
+	for (i = 0; i < num_pdi; i++) {
+		pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num, pcm);
+		ch_count += pdi->ch_count;
+		pdi++;
+	}
+
+	*num_ch = ch_count;
+	return 0;
+}
+
+static int intel_pdi_stream_ch_update(struct sdw_intel *sdw,
+				struct sdw_cdns_streams *stream, bool pcm)
+{
+	intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd,
+			&stream->num_ch_bd, pcm);
+
+	intel_pdi_get_ch_update(sdw, stream->in, stream->num_in,
+			&stream->num_ch_in, pcm);
+
+	intel_pdi_get_ch_update(sdw, stream->out, stream->num_out,
+			&stream->num_ch_out, pcm);
+
+	return 0;
+}
+
+static int intel_pdi_ch_update(struct sdw_intel *sdw)
+{
+	/* First update PCM streams followed by PDM streams */
+	intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm, true);
+	intel_pdi_stream_ch_update(sdw, &sdw->cdns.pdm, false);
+
+	return 0;
+}
+
+static void
+intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
+{
+	void __iomem *shim = sdw->res->shim;
+	unsigned int link_id = sdw->instance;
+	int pdi_conf = 0;
+
+	pdi->intel_alh_id = (link_id * 16) + pdi->num + 5;
+
+	/*
+	 * Program stream parameters to stream SHIM register
+	 * This is applicable for PCM stream only.
+	 */
+	if (pdi->type != SDW_STREAM_PCM)
+		return;
+
+	if (pdi->dir == SDW_DATA_DIR_IN)
+		pdi_conf |= SDW_SHIM_PCMSYCM_DIR;
+	else
+		pdi_conf &= ~(SDW_SHIM_PCMSYCM_DIR);
+
+	pdi_conf |= (pdi->intel_alh_id <<
+			SDW_REG_SHIFT(SDW_SHIM_PCMSYCM_STREAM));
+	pdi_conf |= (pdi->l_ch_num << SDW_REG_SHIFT(SDW_SHIM_PCMSYCM_LCHN));
+	pdi_conf |= (pdi->h_ch_num << SDW_REG_SHIFT(SDW_SHIM_PCMSYCM_HCHN));
+
+	intel_writew(shim, SDW_SHIM_PCMSYCHM(link_id, pdi->num), pdi_conf);
+}
+
+static void
+intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
+{
+	void __iomem *alh = sdw->res->alh;
+	unsigned int link_id = sdw->instance;
+	unsigned int conf;
+
+	pdi->intel_alh_id = (link_id * 16) + pdi->num + 5;
+
+	/* Program Stream config ALH register */
+	conf = intel_readl(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id));
+
+	conf |= (SDW_ALH_STRMZCFG_DMAT_VAL <<
+			SDW_REG_SHIFT(SDW_ALH_STRMZCFG_DMAT));
+
+	conf |= ((pdi->ch_count - 1) <<
+			SDW_REG_SHIFT(SDW_ALH_STRMZCFG_CHN));
+
+	intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf);
+}
+
+/*
+ * bank switch routines
+ */
+
+static int intel_pre_bank_switch(struct sdw_bus *bus)
+{
+	struct sdw_cdns *cdns = bus_to_cdns(bus);
+	struct sdw_intel *sdw = cdns_to_intel(cdns);
+	void __iomem *shim = sdw->res->shim;
+	int sync_reg;
+
+	if (!bus->multi_link)
+		return 0;
+
+	/* Read SYNC register */
+	sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
+	sync_reg |= SDW_SHIM_SYNC_CMDSYNC << sdw->instance;
+	intel_writel(shim, SDW_SHIM_SYNC, sync_reg);
+
+	return 0;
+}
+
+static int intel_post_bank_switch(struct sdw_bus *bus)
+{
+	struct sdw_cdns *cdns = bus_to_cdns(bus);
+	struct sdw_intel *sdw = cdns_to_intel(cdns);
+	void __iomem *shim = sdw->res->shim;
+	int sync_reg, ret;
+
+	if (!bus->multi_link)
+		return 0;
+
+	/* Read SYNC register */
+	sync_reg = intel_readl(shim, SDW_SHIM_SYNC);
+
+	/* Check CMDSYNC bit set for any Master */
+	if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK))
+		return 0;
+
+	/* Set SyncGO bit */
+	sync_reg |= SDW_SHIM_SYNC_SYNCGO;
+
+	ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg,
+				SDW_SHIM_SYNC_SYNCGO);
+	if (ret < 0)
+		dev_err(sdw->cdns.dev, "Post bank switch failed: %d", ret);
+
+	return ret;
+}
+
 static int intel_prop_read(struct sdw_bus *bus)
 {
 	/* Initialize with default handler to read all DisCo properties */
@@ -265,6 +470,7 @@ static struct sdw_master_ops sdw_intel_ops = {
  */
 static int intel_probe(struct platform_device *pdev)
 {
+	struct sdw_cdns_stream_config config;
 	struct sdw_intel *sdw;
 	int ret;
 
@@ -287,6 +493,11 @@ static int intel_probe(struct platform_device *pdev)
 	sdw_intel_ops.read_prop = intel_prop_read;
 	sdw->cdns.bus.ops = &sdw_intel_ops;
 
+	sdw_intel_ops.pre_bank_switch = intel_pre_bank_switch;
+	sdw_intel_ops.post_bank_switch = intel_post_bank_switch;
+	sdw_intel_ops.read_prop = intel_prop_read;
+	sdw->cdns.bus.ops = &sdw_intel_ops;
+
 	platform_set_drvdata(pdev, sdw);
 
 	ret = sdw_add_bus_master(&sdw->cdns.bus);
@@ -304,9 +515,15 @@ static int intel_probe(struct platform_device *pdev)
 		goto err_init;
 
 	ret = sdw_cdns_enable_interrupt(&sdw->cdns);
+
+	/* Read the PDI config and initialize cadence PDI */
+	intel_pdi_init(sdw, &config);
+	ret = sdw_cdns_pdi_init(&sdw->cdns, config);
 	if (ret)
 		goto err_init;
 
+	intel_pdi_ch_update(sdw);
+
 	/* Acquire IRQ */
 	ret = request_threaded_irq(sdw->res->irq, sdw_cdns_irq,
 			sdw_cdns_thread, IRQF_SHARED, KBUILD_MODNAME,
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 13/13] soundwire: intel: Add audio DAI ops
  2018-03-28  9:38 [PATCH 00/13] soundwire: Add stream support Vinod Koul
                   ` (11 preceding siblings ...)
  2018-03-28  9:38 ` [PATCH 12/13] soundwire: intel: Add stream initialization Vinod Koul
@ 2018-03-28  9:38 ` Vinod Koul
  12 siblings, 0 replies; 25+ messages in thread
From: Vinod Koul @ 2018-03-28  9:38 UTC (permalink / raw)
  To: Greg KH
  Cc: ALSA, tiwai, Pierre-Louis Bossart, liam.r.girdwood,
	patches.audio, broonie, Vinod Koul

Add DAI registration and DAI ops for the Intel driver along with
callback for topology configuration.

Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 drivers/soundwire/Kconfig           |   2 +-
 drivers/soundwire/intel.c           | 360 ++++++++++++++++++++++++++++++++++++
 drivers/soundwire/intel.h           |   4 +
 drivers/soundwire/intel_init.c      |   3 +
 include/linux/soundwire/sdw.h       |   3 +
 include/linux/soundwire/sdw_intel.h |  14 ++
 6 files changed, 385 insertions(+), 1 deletion(-)

diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig
index b46084b4b1f8..19c8efb9a5ee 100644
--- a/drivers/soundwire/Kconfig
+++ b/drivers/soundwire/Kconfig
@@ -27,7 +27,7 @@ config SOUNDWIRE_INTEL
 	tristate "Intel SoundWire Master driver"
 	select SOUNDWIRE_CADENCE
 	select SOUNDWIRE_BUS
-	depends on X86 && ACPI
+	depends on X86 && ACPI && SND_SOC
 	---help---
 	  SoundWire Intel Master driver.
 	  If you have an Intel platform which has a SoundWire Master then
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c
index e0fb88397a88..c7a3848174dd 100644
--- a/drivers/soundwire/intel.c
+++ b/drivers/soundwire/intel.c
@@ -87,6 +87,12 @@
 #define SDW_ALH_STRMZCFG_DMAT		GENMASK(7, 0)
 #define SDW_ALH_STRMZCFG_CHN		GENMASK(19, 16)
 
+enum intel_pdi_type {
+	INTEL_PDI_IN = 0,
+	INTEL_PDI_OUT = 1,
+	INTEL_PDI_BD = 2,
+};
+
 struct sdw_intel {
 	struct sdw_cdns cdns;
 	int instance;
@@ -389,6 +395,18 @@ intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
 	intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf);
 }
 
+static int intel_config_stream(struct sdw_intel *sdw,
+			struct snd_pcm_substream *substream,
+			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,
+				substream, dai, hw_params, link_id);
+
+	return -EIO;
+}
+
 /*
  * bank switch routines
  */
@@ -439,6 +457,337 @@ static int intel_post_bank_switch(struct sdw_bus *bus)
 	return ret;
 }
 
+/*
+ * DAI routines
+ */
+
+static struct sdw_cdns_port *intel_alloc_port(struct sdw_intel *sdw,
+				u32 ch, u32 dir, bool pcm)
+{
+	struct sdw_cdns *cdns = &sdw->cdns;
+	struct sdw_cdns_port *port = NULL;
+	int i, ret = 0;
+
+	for (i = 0; i < cdns->num_ports; i++) {
+		if (cdns->ports[i].assigned == true)
+			continue;
+
+		port = &cdns->ports[i];
+		port->assigned = true;
+		port->direction = dir;
+		port->ch = ch;
+		break;
+	}
+
+	if (!port) {
+		dev_err(cdns->dev, "Unable to find a free port\n");
+		return NULL;
+	}
+
+	if (pcm) {
+		ret = sdw_cdns_alloc_stream(cdns, &cdns->pcm, port, ch, dir);
+		if (ret)
+			goto out;
+
+		intel_pdi_shim_configure(sdw, port->pdi);
+		sdw_cdns_config_stream(cdns, port, ch, dir, port->pdi);
+
+		intel_pdi_alh_configure(sdw, port->pdi);
+
+	} else {
+		ret = sdw_cdns_alloc_stream(cdns, &cdns->pdm, port, ch, dir);
+	}
+
+out:
+	if (ret) {
+		port->assigned = false;
+		port = NULL;
+	}
+
+	return port;
+}
+
+static void intel_port_cleanup(struct sdw_cdns_dma_data *dma)
+{
+	int i;
+
+	for (i = 0; i < dma->nr_ports; i++) {
+		if (dma->port[i]) {
+			dma->port[i]->pdi->assigned = false;
+			dma->port[i]->pdi = NULL;
+			dma->port[i]->assigned = false;
+			dma->port[i] = NULL;
+		}
+	}
+}
+
+static int intel_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
+	struct sdw_intel *sdw = cdns_to_intel(cdns);
+	struct sdw_cdns_dma_data *dma;
+	struct sdw_stream_config sconfig;
+	struct sdw_port_config *pconfig;
+	int ret, i, ch, dir;
+	bool pcm = true;
+
+	dma = snd_soc_dai_get_dma_data(dai, substream);
+	if (!dma)
+		return -EIO;
+
+	ch = params_channels(params);
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		dir = SDW_DATA_DIR_IN;
+	else
+		dir = SDW_DATA_DIR_OUT;
+
+	if (dma->stream_type == SDW_STREAM_PDM) {
+		/* TODO: Check whether PDM decimator is already in use */
+		dma->nr_ports = sdw_cdns_get_stream(cdns, &cdns->pdm, ch, dir);
+		pcm = false;
+	} else {
+		dma->nr_ports = sdw_cdns_get_stream(cdns, &cdns->pcm, ch, dir);
+	}
+
+	if (!dma->nr_ports) {
+		dev_err(dai->dev, "ports/resources not available");
+		return -EINVAL;
+	}
+
+	dma->port = kcalloc(dma->nr_ports, sizeof(*dma->port), GFP_KERNEL);
+	if (!dma->port)
+		return -ENOMEM;
+
+	for (i = 0; i < dma->nr_ports; i++) {
+		dma->port[i] = intel_alloc_port(sdw, ch, dir, pcm);
+		if (!dma->port[i]) {
+			ret = -EINVAL;
+			goto port_error;
+		}
+	}
+
+	/* Inform DSP about PDI stream number */
+	for (i = 0; i < dma->nr_ports; i++) {
+		ret = intel_config_stream(sdw, substream, dai, params,
+				dma->port[i]->pdi->intel_alh_id);
+		if (ret)
+			goto port_error;
+	}
+
+	sconfig.direction = dir;
+	sconfig.ch_count = ch;
+	sconfig.frame_rate = params_rate(params);
+	sconfig.type = dma->stream_type;
+
+	if (dma->stream_type == SDW_STREAM_PDM) {
+		sconfig.frame_rate *= 50;
+		sconfig.bps = 1;
+	} else {
+		sconfig.bps = snd_pcm_format_width(params_format(params));
+	}
+
+	/* Port configuration */
+	pconfig = kcalloc(dma->nr_ports, sizeof(*pconfig), GFP_KERNEL);
+	if (!pconfig) {
+		ret =  -ENOMEM;
+		goto port_error;
+	}
+
+	for (i = 0; i < dma->nr_ports; i++) {
+		pconfig[i].num = dma->port[i]->num;
+		pconfig[i].ch_mask = (1 << ch) - 1;
+	}
+
+	ret = sdw_stream_add_master(&cdns->bus, &sconfig,
+				pconfig, dma->nr_ports, dma->stream);
+	if (ret) {
+		dev_err(cdns->dev, "add master to stream failed:%d", ret);
+		goto stream_error;
+	}
+
+	kfree(pconfig);
+	return ret;
+
+stream_error:
+	kfree(pconfig);
+port_error:
+	intel_port_cleanup(dma);
+	kfree(dma->port);
+	return ret;
+}
+
+static int
+intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
+	struct sdw_cdns_dma_data *dma;
+	int ret;
+
+	dma = snd_soc_dai_get_dma_data(dai, substream);
+	if (!dma)
+		return -EIO;
+
+	ret = sdw_stream_remove_master(&cdns->bus, dma->stream);
+	if (ret < 0)
+		dev_err(dai->dev, "remove master from stream %s failed: %d",
+							dma->stream->name, ret);
+
+	intel_port_cleanup(dma);
+	kfree(dma->port);
+	return ret;
+}
+
+static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai,
+					void *stream, int direction)
+{
+	return cdns_set_sdw_stream(dai, stream, true, direction);
+}
+
+static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai,
+					void *stream, int direction)
+{
+	return cdns_set_sdw_stream(dai, stream, false, direction);
+}
+
+static struct snd_soc_dai_ops intel_pcm_dai_ops = {
+	.hw_params = intel_hw_params,
+	.hw_free = intel_hw_free,
+	.shutdown = sdw_cdns_shutdown,
+	.set_sdw_stream = intel_pcm_set_sdw_stream,
+};
+
+static struct snd_soc_dai_ops intel_pdm_dai_ops = {
+	.hw_params = intel_hw_params,
+	.hw_free = intel_hw_free,
+	.shutdown = sdw_cdns_shutdown,
+	.set_sdw_stream = intel_pdm_set_sdw_stream,
+};
+
+static const struct snd_soc_component_driver dai_component = {
+	.name           = "soundwire",
+};
+
+static int intel_create_dai(struct sdw_cdns *cdns,
+			struct snd_soc_dai_driver *dais,
+			enum intel_pdi_type type,
+			u32 num, u32 off, u32 max_ch, bool pcm)
+{
+	int i;
+
+	if (num == 0)
+		return 0;
+
+	 /* TODO: Read supported rates/formats from hardware */
+	for (i = off; i < (off + num); i++) {
+		dais[i].name = kasprintf(GFP_KERNEL, "SDW%d Pin%d",
+					cdns->instance, i);
+		if (!dais[i].name)
+			return -ENOMEM;
+
+		if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) {
+			dais[i].playback.stream_name = kasprintf(GFP_KERNEL,
+							"SDW%d Tx%d",
+							cdns->instance, i);
+			if (!dais[i].playback.stream_name) {
+				kfree(dais[i].name);
+				return -ENOMEM;
+			}
+
+			dais[i].playback.channels_min = 1;
+			dais[i].playback.channels_max = max_ch;
+			dais[i].playback.rates = SNDRV_PCM_RATE_48000;
+			dais[i].playback.formats = SNDRV_PCM_FMTBIT_S16_LE;
+		}
+
+
+		if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) {
+			dais[i].capture.stream_name = kasprintf(GFP_KERNEL,
+							"SDW%d Rx%d",
+							cdns->instance, i);
+			if (!dais[i].capture.stream_name) {
+				kfree(dais[i].name);
+				kfree(dais[i].playback.stream_name);
+				return -ENOMEM;
+			}
+
+			dais[i].playback.channels_min = 1;
+			dais[i].playback.channels_max = max_ch;
+			dais[i].capture.rates = SNDRV_PCM_RATE_48000;
+			dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE;
+		}
+
+		dais[i].id = SDW_DAI_ID_RANGE_START + i;
+
+		if (pcm)
+			dais[i].ops = &intel_pcm_dai_ops;
+		else
+			dais[i].ops = &intel_pdm_dai_ops;
+	}
+
+	return 0;
+}
+
+static int intel_register_dai(struct sdw_intel *sdw)
+{
+	struct sdw_cdns *cdns = &sdw->cdns;
+	struct sdw_cdns_streams *stream;
+	struct snd_soc_dai_driver *dais;
+	int num_dai, ret, off = 0;
+
+	/* DAIs are created based on total number of PDIs supported */
+	num_dai = cdns->pcm.num_pdi + cdns->pdm.num_pdi;
+
+	dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL);
+	if (!dais)
+		return -ENOMEM;
+
+	/* Create PCM DAIs */
+	stream = &cdns->pcm;
+
+	ret = intel_create_dai(cdns, dais, INTEL_PDI_IN,
+			stream->num_in, off, stream->num_ch_in, true);
+	if (ret)
+		return ret;
+
+	off += cdns->pcm.num_in;
+	ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT,
+			cdns->pcm.num_out, off, stream->num_ch_out, true);
+	if (ret)
+		return ret;
+
+	off += cdns->pcm.num_out;
+	ret = intel_create_dai(cdns, dais, INTEL_PDI_BD,
+			cdns->pcm.num_bd, off, stream->num_ch_bd, true);
+	if (ret)
+		return ret;
+
+	/* Create PDM DAIs */
+	stream = &cdns->pdm;
+	off += cdns->pcm.num_bd;
+	ret = intel_create_dai(cdns, dais, INTEL_PDI_IN,
+			cdns->pdm.num_in, off, stream->num_ch_in, false);
+	if (ret)
+		return ret;
+
+
+	off += cdns->pdm.num_in;
+	ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT,
+			cdns->pdm.num_out, off, stream->num_ch_out, false);
+	if (ret)
+		return ret;
+
+	off += cdns->pdm.num_bd;
+	ret = intel_create_dai(cdns, dais, INTEL_PDI_BD,
+			cdns->pdm.num_bd, off, stream->num_ch_bd, false);
+	if (ret)
+		return ret;
+
+	return snd_soc_register_component(cdns->dev, &dai_component,
+				dais, num_dai);
+}
+
 static int intel_prop_read(struct sdw_bus *bus)
 {
 	/* Initialize with default handler to read all DisCo properties */
@@ -534,8 +883,18 @@ static int intel_probe(struct platform_device *pdev)
 		goto err_init;
 	}
 
+	/* Register DAIs */
+	ret = intel_register_dai(sdw);
+	if (ret) {
+		dev_err(sdw->cdns.dev, "DAI registration failed: %d", ret);
+		snd_soc_unregister_component(sdw->cdns.dev);
+		goto err_dai;
+	}
+
 	return 0;
 
+err_dai:
+	free_irq(sdw->res->irq, sdw);
 err_init:
 	sdw_delete_bus_master(&sdw->cdns.bus);
 err_master_reg:
@@ -549,6 +908,7 @@ 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);
 	sdw_delete_bus_master(&sdw->cdns.bus);
 
 	return 0;
diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h
index ffa30d9535a2..c1a5bac6212e 100644
--- a/drivers/soundwire/intel.h
+++ b/drivers/soundwire/intel.h
@@ -10,6 +10,8 @@
  * @shim: Audio shim pointer
  * @alh: ALH (Audio Link Hub) pointer
  * @irq: Interrupt line
+ * @ops: Shim callback ops
+ * @arg: Shim callback ops argument
  *
  * This is set as pdata for each link instance.
  */
@@ -18,6 +20,8 @@ struct sdw_intel_link_res {
 	void __iomem *shim;
 	void __iomem *alh;
 	int irq;
+	const struct sdw_intel_ops *ops;
+	void *arg;
 };
 
 #endif /* __SDW_INTEL_LOCAL_H */
diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c
index 6f2bb99526f2..d1ea6b4d0ad3 100644
--- a/drivers/soundwire/intel_init.c
+++ b/drivers/soundwire/intel_init.c
@@ -111,6 +111,9 @@ static struct sdw_intel_ctx
 		link->res.shim = res->mmio_base + SDW_SHIM_BASE;
 		link->res.alh = res->mmio_base + SDW_ALH_BASE;
 
+		link->res.ops = res->ops;
+		link->res.arg = res->arg;
+
 		memset(&pdevinfo, 0, sizeof(pdevinfo));
 
 		pdevinfo.parent = res->parent;
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h
index 9b583a8ae3dd..83a036e2e6ac 100644
--- a/include/linux/soundwire/sdw.h
+++ b/include/linux/soundwire/sdw.h
@@ -38,6 +38,9 @@ struct sdw_slave;
 
 #define SDW_VALID_PORT_RANGE(n)		(n <= 14 && n >= 1)
 
+#define SDW_DAI_ID_RANGE_START		100
+#define SDW_DAI_ID_RANGE_END		200
+
 /**
  * enum sdw_slave_status - Slave status
  * @SDW_SLAVE_UNATTACHED: Slave is not attached with the bus.
diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h
index 4b37528f592d..2b9573b8aedd 100644
--- a/include/linux/soundwire/sdw_intel.h
+++ b/include/linux/soundwire/sdw_intel.h
@@ -5,17 +5,31 @@
 #define __SDW_INTEL_H
 
 /**
+ * struct sdw_intel_ops: Intel audio driver callback ops
+ *
+ * @config_stream: configure the stream with the hw_params
+ */
+struct sdw_intel_ops {
+	int (*config_stream)(void *arg, void *substream,
+			void *dai, void *hw_params, int stream_num);
+};
+
+/**
  * struct sdw_intel_res - Soundwire Intel resource structure
  * @mmio_base: mmio base of SoundWire registers
  * @irq: interrupt number
  * @handle: ACPI parent handle
  * @parent: parent device
+ * @ops: callback ops
+ * @arg: callback arg
  */
 struct sdw_intel_res {
 	void __iomem *mmio_base;
 	int irq;
 	acpi_handle handle;
 	struct device *parent;
+	const struct sdw_intel_ops *ops;
+	void *arg;
 };
 
 void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* Re: [PATCH 01/13] soundwire: Add more documentation
  2018-03-28  9:38 ` [PATCH 01/13] soundwire: Add more documentation Vinod Koul
@ 2018-03-30  1:47   ` Pierre-Louis Bossart
  2018-03-30  6:38     ` Vinod Koul
  0 siblings, 1 reply; 25+ messages in thread
From: Pierre-Louis Bossart @ 2018-03-30  1:47 UTC (permalink / raw)
  To: Vinod Koul, Greg KH
  Cc: ALSA, tiwai, liam.r.girdwood, patches.audio, broonie, Sanyog Kale

On 3/28/18 4:38 AM, Vinod Koul wrote:
> From: Sanyog Kale <sanyog.r.kale@intel.com>
> 
> This adds documentation for error handling, locking and streams.
> 
> Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
> Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
> Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
> ---
>   .../driver-api/soundwire/error_handling.rst        |  65 ++++
>   Documentation/driver-api/soundwire/index.rst       |   3 +
>   Documentation/driver-api/soundwire/locking.rst     | 106 ++++++
>   Documentation/driver-api/soundwire/stream.rst      | 369 +++++++++++++++++++++
>   4 files changed, 543 insertions(+)
>   create mode 100644 Documentation/driver-api/soundwire/error_handling.rst
>   create mode 100644 Documentation/driver-api/soundwire/locking.rst
>   create mode 100644 Documentation/driver-api/soundwire/stream.rst
> 
> diff --git a/Documentation/driver-api/soundwire/error_handling.rst b/Documentation/driver-api/soundwire/error_handling.rst
> new file mode 100644
> index 000000000000..aa3a0a23a066
> --- /dev/null
> +++ b/Documentation/driver-api/soundwire/error_handling.rst
> @@ -0,0 +1,65 @@
> +========================
> +SoundWire Error Handling
> +========================
> +
> +The SoundWire PHY was designed with care and errors on the bus are going to
> +be very unlikely, and if they happen it should be limited to single bit
> +errors. Examples of this design can be found in the synchronization
> +mechanism (sync loss after two errors) and short CRCs used for the Bulk
> +Register Access.
> +
> +The errors can be detected with multiple mechanisms:
> +
> +1. Bus clash or parity errors: This mechanism relies on low-level detectors
> +   that are independent of the payload and usages, and they cover both control
> +   and audio data. The current implementation only logs such errors.
> +   Improvements could be invalidating an entire programming sequence and
> +   restarting from a known position. In the case of such errors outside of a
> +   control/command sequence, there is no concealment or recovery for audio
> +   data enabled by the SoundWire protocol, the location of the error will also
> +   impact its audibility (most-significant bits will be more impacted in PCM),
> +   and after a number of such errors are detected the bus might be reset. Note
> +   that bus clashes due to programming errors (two streams using the same bit
> +   slots) or electrical issues during the transmit/receive transition cannot
> +   be distinguished, although a recurring bus clash when audio is enabled is a
> +   indication of a bus allocation issue. The interrupt mechanism can also help
> +   identify Slaves which detected a Bus Clash or a Parity Error, but they may
> +   not be responsible for the errors so resetting them individually is not a
> +   viable recovery strategy.
> +
> +2. Command status: Each command is associated with a status, which only
> +   covers transmission of the data between devices. The ACK status indicates
> +   that the command was received and will be executed by the end of the
> +   current frame. A NAK indicates that the command was in error and will not
> +   be applied. In case of a bad programming (command sent to non-existent
> +   Slave or to a non-implemented register) or electrical issue, no response
> +   signals the command was ignored. Some Master implementations allow for a
> +   command to be retransmitted several times.  If the retransmission fails,
> +   backtracking and restarting the entire programming sequence might be a
> +   solution. Alternatively some implementations might directly issue a bus
> +   reset and re-enumerate all devices.
> +
> +3. Timeouts: In a number of cases such as ChannelPrepare or
> +   ClockStopPrepare, the bus driver is supposed to poll a register field until
> +   it transitions to a NotFinished value of zero. The MIPI SoundWire spec 1.1
> +   does not define timeouts but the MIPI SoundWire DisCo document adds
> +   recommendation on timeouts. If such configurations do not complete, the
> +   driver will return a -ETIMEOUT. Such timeouts are symptoms of a faulty
> +   Slave device and are likely impossible to recover from.
> +
> +Errors during global reconfiguration sequences are extremely difficult to
> +handle:
> +
> +1. BankSwitch: An error during the last command issuing a BankSwitch is
> +   difficult to backtrack from. Retransmitting the Bank Switch command may be
> +   possible in a single segment setup, but this can lead to synchronization
> +   problems when enabling multiple bus segments (a command with side effects
> +   such as frame reconfiguration would be handled at different times). A global
> +   hard-reset might be the best solution.
> +
> +Note that SoundWire does not provide a mechanism to detect illegal values
> +written in valid registers. In a number of cases the standard even mentions
> +that the Slave might behave in implementation-defined ways. The bus
> +implementation does not provide a recovery mechanism for such errors, Slave
> +or Master driver implementers are responsible for writing valid values in
> +valid registers and implement additional range checking if needed.
> diff --git a/Documentation/driver-api/soundwire/index.rst b/Documentation/driver-api/soundwire/index.rst
> index 647e94654752..6db026028f27 100644
> --- a/Documentation/driver-api/soundwire/index.rst
> +++ b/Documentation/driver-api/soundwire/index.rst
> @@ -6,6 +6,9 @@ SoundWire Documentation
>      :maxdepth: 1
>   
>      summary
> +   stream
> +   error_handling
> +   locking
>   
>   .. only::  subproject
>   
> diff --git a/Documentation/driver-api/soundwire/locking.rst b/Documentation/driver-api/soundwire/locking.rst
> new file mode 100644
> index 000000000000..06c7000160ee
> --- /dev/null
> +++ b/Documentation/driver-api/soundwire/locking.rst
> @@ -0,0 +1,106 @@
> +=================
> +SoundWire Locking
> +=================
> +
> +This document explains locking mechanism of the SoundWire Bus. Bus uses
> +following locks in order to avoid race conditions in Bus operations on
> +shared resources.
> +
> +  - Bus lock
> +
> +  - Message lock
> +
> +Bus lock
> +========
> +
> +SoundWire Bus lock is a mutex and is part of Bus data structure
> +(sdw_bus) which is used for every Bus instance. This lock is used to
> +serialize each of the following operations(s) within SoundWire Bus instance.
> +
> +  - Addition and removal of Slave(s), changing Slave status.
> +
> +  - Prepare, Enable, Disable and De-prepare stream operations.
> +
> +  - Access of Stream data structure.
> +
> +Message lock
> +============
> +
> +SoundWire message transfer lock. This mutex is part of
> +Bus data structure (sdw_bus). This lock is used to serialize the message
> +transfers (read/write) within a SoundWire Bus instance.
> +
> +Below examples show how locks are acquired.
> +
> +Example 1
> +---------
> +
> +Message transfer.
> +
> +  1. For every message transfer
> +
> +     a. Acquire Message lock.
> +
> +     b. Transfer message (Read/Write) to Slave1 or broadcast message on
> +        Bus in case of bank switch.
> +
> +     c. Release Message lock ::
> +
> +	+----------+                    +---------+
> +	|          |                    |         |
> +	|   Bus    |                    | Master  |
> +	|          |                    | Driver  |
> +	|          |                    |         |
> +	+----+-----+                    +----+----+
> +	     |                               |
> +	     |     bus->ops->xfer_msg()      |
> +	     +------------------------------->   a. Acquire Message lock
> +	     |                               |   b. Transfer message
> +	     |                               |
> +	     <-------------------------------+   c. Release Message lock
> +	     |    return success/error       |   d. Return success/error
> +	     |                               |
> +	     +                               +
> +
> +Example 2
> +---------
> +
> +Prepare operation.
> +
> +  1. Acquire lock for Bus instance associated with Master 1.
> +
> +  2. For every message transfer in Prepare operation
> +
> +     a. Acquire Message lock.
> +
> +     b. Transfer message (Read/Write) to Slave1 or broadcast message on
> +        Bus in case of bank switch.
> +
> +     c. Release Message lock.
> +
> +  3. Release lock for Bus instance associated with Master 1 ::
> +
> +	+----------+                    +---------+
> +	|          |                    |         |
> +	|   Bus    |                    | Master  |
> +	|          |                    | Driver  |
> +	|          |                    |         |
> +	+----+-----+                    +----+----+
> +	     |                               |
> +	     |    sdw_prepare_stream()       |
> +	     +------------------------------->   1. Acquire bus lock
> +	     |                               |   2. Perform stream prepare
> +	     |                               |
> +	     |                               |
> +	     |     bus->ops->xfer_msg()      |
> +	     +------------------------------->   a. Acquire Message lock
> +	     |                               |   b. Transfer message
> +	     |                               |
> +	     <-------------------------------+   c. Release Message lock
> +	     |    return success/error       |   d. Return success/error
> +	     |                               |
> +	     |                               |
> +	     |    return success/error       |   3. Release bus lock
> +	     <-------------------------------+   4. Return success/error
> +	     |                               |
> +	     +                               +
> diff --git a/Documentation/driver-api/soundwire/stream.rst b/Documentation/driver-api/soundwire/stream.rst
> new file mode 100644
> index 000000000000..94fc0a31df77
> --- /dev/null
> +++ b/Documentation/driver-api/soundwire/stream.rst
> @@ -0,0 +1,369 @@
> +=========================
> +Audio Stream in SoundWire
> +=========================
> +
> +An audio stream is a logical or virtual connection created between
> +
> +  (1) System memory buffer(s) and Codec(s)
> +
> +  (2) DSP memory buffer(s) and Codec(s)
> +
> +  (3) FIFO(s) and Codec(s)
> +
> +  (4) Codec(s) and Codec(s)
> +
> +which is typically driven by a DMA(s) channel through the data link. An
> +audio stream contains one or more channels of data. All channels within
> +stream must have same sample rate and same sample size.
> +
> +Assume a stream with two channels (Left & Right) is opened using SoundWire
> +interface. Below are some ways a stream can be represented in SoundWire.
> +
> +Stream Sample in memory (System memory, DSP memory or FIFOs) ::
> +
> +	-------------------------
> +	| L | R | L | R | L | R |
> +	-------------------------
> +
> +Example 1: Stereo Stream with L and R channels is rendered from Master to
> +Slave. Both Master and Slave is using single port. ::
> +
> +	+---------------+                    Clock Signal  +---------------+
> +	|    Master     +----------------------------------+     Slave     |
> +	|   Interface   |                                  |   Interface   |
> +	|               |                                  |       1       |
> +	|               |                     Data Signal  |               |
> +	|    L  +  R    +----------------------------------+    L  +  R    |
> +	|     (Data)    |     Data Direction               |     (Data)    |
> +	+---------------+  +----------------------->       +---------------+
> +
> +
> +Example 2: Stereo Stream with L and R channels is captured from Slave to
> +Master. Both Master and Slave is using single port. ::
> +
> +
> +	+---------------+                    Clock Signal  +---------------+
> +	|    Master     +----------------------------------+     Slave     |
> +	|   Interface   |                                  |   Interface   |
> +	|               |                                  |       1       |
> +	|               |                     Data Signal  |               |
> +	|    L  +  R    +----------------------------------+    L  +  R    |
> +	|     (Data)    |     Data Direction               |     (Data)    |
> +	+---------------+  <-----------------------+       +---------------+
> +
> +
> +Example 3: Stereo Stream with L and R channels is rendered by Master. Each
> +of the L and R channel is received by two different Slaves. Master and both
> +Slaves are using single port. ::
> +
> +	+---------------+                    Clock Signal  +---------------+
> +	|    Master     +---------+------------------------+     Slave     |
> +	|   Interface   |         |                        |   Interface   |
> +	|               |         |                        |       1       |
> +	|               |         |           Data Signal  |               |
> +	|    L  +  R    +---+------------------------------+       L       |
> +	|     (Data)    |   |     |    Data Direction      |     (Data)    |
> +	+---------------+   |     |   +------------->      +---------------+
> +	                    |     |
> +	                    |     |
> +	                    |     |                        +---------------+
> +	                    |     +----------------------> |     Slave     |
> +	                    |                              |   Interface   |
> +	                    |                              |       2       |
> +	                    |                              |               |
> +	                    +----------------------------> |       R       |
> +	                                                   |     (Data)    |
> +	                                                   +---------------+
> +
> +
> +Example 4: Stereo Stream with L and R channel is rendered by two different
> +Ports of the Master and is received by only single Port of the Slave
> +interface. ::
> +
> +	+--------------------+
> +	|                    |
> +	|     +--------------+                             +----------------+
> +	|     |             ||                             |                |
> +	|     |  Data Port  ||  L Channel                  |                |
> +	|     |      1      |------------+                 |                |
> +	|     |  L Channel  ||           |                 +-----+----+     |
> +	|     |   (Data)    ||           |   L + R Channel ||    Data |     |
> +	| Master  +----------+           | +---+---------> ||    Port |     |
> +	| Interface          |           |                 ||     1   |     |
> +	|     +--------------+           |                 ||         |     |
> +	|     |             ||           |                 +----------+     |
> +	|     |  Data Port  |------------+                 |                |
> +	|     |      2      ||  R Channel                  |     Slave      |
> +	|     |  R Channel  ||                             |   Interface    |
> +	|     |   (Data)    ||                             |       1        |
> +	|     +--------------+         Clock Signal        |     L  +  R    |
> +	|                    +---------------------------> |      (Data)    |
> +	+--------------------+                             |                |
> +							   +----------------+
> +
> +SoundWire Stream Management flow
> +================================
> +
> +Stream definitions
> +------------------
> +
> +  (1) Current stream: This is classified as the stream on which operation has
> +      to be performed like prepare, enable, disable, de-prepare etc.
> +
> +  (2) Active stream: This is classified as the stream which is already active
> +      on Bus other than current stream. There can be multiple active streams
> +      on the Bus.
> +
> +SoundWire Bus manages stream operations for each stream getting
> +rendered/captured on the SoundWire Bus. This section explains Bus operations
> +done for each of the stream allocated/released on Bus.  Following are the
> +stream states maintained by the Bus for each of the audio stream.
> +
> +
> +SoundWire stream states
> +-----------------------
> +
> +Below shows the SoundWire stream states and state transition diagram. ::
> +
> +	+---------+     +----------+     +----------+     +----------+
> +	|  ALLOC  +---->|  CONFIG  +---->| PREPARE  +---->|  ENABLE  |
> +	|  STATE  |     |  STATE   |     |  STATE   |     |  STATE   |
> +	+---------+     +----------+     +----------+     +----+-----+
> +	                                                       ^
> +	                                                       |
> +	                                                       |
> +	                                                       v
> +	         +----------+           +-----------+     +----+-----+
> +	         |  RELEASE |<----------+ DEPREPARE |<----+  DISABLE |
> +	         |   STATE  |           |  STATE    |     |  STATE   |
> +	         +----------+           +-----------+     +----------+
> +
> +NOTE: All stream transitions in MIPI Spec are NOT supported by software.

specifically that there are not transitions between prepare and deprepare.

While I am at it, didn't I provide feedback that the documentation and 
code confuse states and transitions between the states? Also there is a 
connection between released and allocated, this state machine isn't 
really working or representative of what the code does initially.

See my feedback dated February 26 on the previous version.

"the name of the states are equivalent to an action. It would be more 
consistent to e.g. have an ENABLED state reached after an 
sdw_stream_enable() function.

same with sdw_stream_prepared leading to the PREPARED state. I think 
it's how most standards or state machine designs are defined (and how 
English grammar works as well)." ALSA also uses OPEN, PREPARED, PAUSED, 
see asound.h


> +
> +Stream State Operations
> +-----------------------
> +
> +Below section explains the operations done by the Bus on Master(s) and
> +Slave(s) as part of stream state transitions.
> +
> +SDW_STREAM_ALLOC
> +~~~~~~~~~~~~~~~~
> +
> +Allocation state for stream. This is the entry state
> +of the stream. Operations performed before entering in this state:
> +
> +  (1) A stream runtime is allocated for the stream. This stream
> +      runtime is used as a reference for all the operations performed
> +      on the stream.
> +
> +  (2) The resources required for holding stream runtime information are
> +      allocated and initialized. This holds all stream related information
> +      such as stream type (PCM/PDM) and parameters, Master and Slave
> +      interface associated with the stream, reference counting, stream
> +      state etc.
> +
> +After all above operations are successful, stream state is set to
> +``SDW_STREAM_ALLOC``.
> +
> +Bus implements below API for allocate a stream which needs to be called once
> +per stream. From ASoC DPCM framework, this stream state maybe linked to
> +.startup() operation.
> +
> +  .. code-block:: c
> +  int sdw_alloc_stream(char * stream_name);
> +
> +
> +SDW_STREAM_CONFIG
> +~~~~~~~~~~~~~~~~~
> +
> +Configuration state of stream. Operations performed before entering in
> +this state:
> +
> +  (1) The resources allocated for stream information in SDW_STREAM_ALLOC
> +      state are updated here. This includes stream parameters, Master(s)
> +      and Slave(s) runtime information associated with current stream.
> +
> +  (2) All the Master(s) and Slave(s) associated with current stream provide
> +      the port information to Bus which includes port numbers allocated by
> +      Master(s) and Slave(s) for current stream and their channel mask.
> +
> +After all above operations are successful, stream state is set to
> +``SDW_STREAM_CONFIG``.
> +
> +Bus implements below APIs for CONFIG state which needs to be called by
> +the respective Master(s) and Slave(s) associated with stream. These APIs can
> +only be invoked once by respective Master(s) and Slave(s). From ASoC DPCM
> +framework, this stream state is linked to .hw_params() operation.
> +
> +  .. code-block:: c
> +  int sdw_stream_add_master(struct sdw_bus * bus,
> +		struct sdw_stream_config * stream_config,
> +		struct sdw_ports_config * ports_config,
> +		struct sdw_stream_runtime * stream);
> +
> +  int sdw_stream_add_slave(struct sdw_slave * slave,
> +		struct sdw_stream_config * stream_config,
> +		struct sdw_ports_config * ports_config,
> +		struct sdw_stream_runtime * stream);
> +
> +
> +SDW_STREAM_PREPARE
> +~~~~~~~~~~~~~~~~~~
> +
> +Prepare state of stream. Operations performed before entering in this state:
> +
> +  (1) Bus parameters such as bandwidth, frame shape, clock frequency,
> +      are computed based on current stream as well as already active
> +      stream(s) on Bus. Re-computation is required to accommodate current
> +      stream on the Bus.
> +
> +  (2) Transport and port parameters of all Master(s) and Slave(s) port(s) are
> +      computed for the current as well as already active stream based on frame
> +      shape and clock frequency computed in step 1.
> +
> +  (3) Computed Bus and transport parameters are programmed in Master(s) and
> +      Slave(s) registers. The banked registers programming is done on the
> +      alternate bank (bank currently unused). Port(s) are enabled for the
> +      already active stream(s) on the alternate bank (bank currently unused).
> +      This is done in order to not disrupt already active stream(s).
> +
> +  (4) Once all the values are programmed, Bus initiates switch to alternate
> +      bank where all new values programmed gets into effect.
> +
> +  (5) Ports of Master(s) and Slave(s) for current stream are prepared by
> +      programming PrepareCtrl register.
> +
> +After all above operations are successful, stream state is set to
> +``SDW_STREAM_PREPARE``.
> +
> +Bus implements below API for PREPARE state which needs to be called once per
> +stream. From ASoC DPCM framework, this stream state is linked to
> +.prepare() operation.
> +
> +  .. code-block:: c
> +  int sdw_prepare_stream(struct sdw_stream_runtime * stream);
> +
> +
> +SDW_STREAM_ENABLE
> +~~~~~~~~~~~~~~~~~
> +
> +Enable state of stream. The data port(s) are enabled upon entering this state.
> +Operations performed before entering in this state:
> +
> +  (1) All the values computed in SDW_STREAM_PREPARE state are programmed
> +      in alternate bank (bank currently unused). It includes programming of
> +      already active stream(s) as well.
> +
> +  (2) All the Master(s) and Slave(s) port(s) for the current stream are
> +      enabled on alternate bank (bank currently unused) by programming
> +      ChannelEn register.
> +
> +  (3) Once all the values are programmed, Bus initiates switch to alternate
> +      bank where all new values programmed gets into effect and port(s)
> +      associated with current stream are enabled.
> +
> +After all above operations are successful, stream state is set to
> +``SDW_STREAM_ENABLE``.
> +
> +Bus implements below API for ENABLE state which needs to be called once per
> +stream. From ASoC DPCM framework, this stream state is linked to
> +.trigger() start operation.
> +
> +  .. code-block:: c
> +  int sdw_enable_stream(struct sdw_stream_runtime * stream);
> +
> +SDW_STREAM_DISABLE
> +~~~~~~~~~~~~~~~~~~
> +
> +Disable state of stream. The data port(s) are disabled upon exiting this state.
> +Operations performed before entering in this state:
> +
> +  (1) All the Master(s) and Slave(s) port(s) for the current stream are
> +      disabled on alternate bank (bank currently unused) by programming
> +      ChannelEn register.
> +
> +  (2) All the current configuration of Bus and active stream(s) are programmed
> +      into alternate bank (bank currently unused).
> +
> +  (3) Once all the values are programmed, Bus initiates switch to alternate
> +      bank where all new values programmed gets into effect and port(s) associated
> +      with current stream are disabled.
> +
> +After all above operations are successful, stream state is set to
> +``SDW_STREAM_DISABLE``.
> +
> +Bus implements below API for DISABLE state which needs to be called once
> +per stream. From ASoC DPCM framework, this stream state is linked to
> +.trigger() stop operation.
> +
> +  .. code-block:: c
> +  int sdw_disable_stream(struct sdw_stream_runtime * stream);
> +
> +
> +SDW_STREAM_DEPREPARE
> +~~~~~~~~~~~~~~~~~~~~
> +
> +De-prepare state of stream. Operations performed before entering in this
> +state:
> +
> +  (1) All the port(s) of Master(s) and Slave(s) for current stream are
> +      de-prepared by programming PrepareCtrl register.
> +
> +  (2) The payload bandwidth of current stream is reduce from the total
> +      bandwidth requirement of bus.
> +
> +After all above operations are successful, stream state is set to
> +``SDW_STREAM_DEPREPARE``.
> +
> +Bus implements below API for DEPREPARE state which needs to be called once
> +per stream. From ASoC DPCM framework, this stream state is linked to
> +.trigger() stop operation.
> +
> +  .. code-block:: c
> +  int sdw_deprepare_stream(struct sdw_stream_runtime * stream);
> +
> +
> +SDW_STREAM_RELEASE
> +~~~~~~~~~~~~~~~~~~
> +
> +Release state of stream. Operations performed before entering in this state:
> +
> +  (1) Release port resources for all Master(s) and Slave(s) port(s)
> +      associated with current stream.
> +
> +  (2) Release Master(s) and Slave(s) runtime resources associated with
> +      current stream.
> +
> +  (3) Release stream runtime resources associated with current stream.
> +
> +After all above operations are successful, stream state is set to
> +``SDW_STREAM_RELEASE``.
> +
> +Bus implements below APIs for RELEASE state which needs to be called by
> +all the Master(s) and Slave(s) associated with stream. From ASoC DPCM
> +framework, this stream state is linked to .hw_free() operation.
> +
> +  .. code-block:: c
> +  int sdw_stream_remove_master(struct sdw_bus * bus,
> +		struct sdw_stream_runtime * stream);
> +  int sdw_stream_remove_slave(struct sdw_slave * slave,
> +		struct sdw_stream_runtime * stream);
> +
> +
> +The .shutdown() ASoC DPCM operation calls below Bus API to release
> +stream assigned as part of ALLOC state. free sdw_stream_runtime
> +> data structure.
> +
> +
> +In .shutdown() the data structure maintaining stream state are freed up.
> +
> +  .. code-block:: c
> +  void sdw_release_stream(struct sdw_stream_runtime * stream);
> +
> +Not Supported
> +=============
> +
> +1. A single port with multiple channels supported cannot be used between two
> +streams or across stream. For example a port with 4 channels cannot be used
> +to handle 2 independent stereo streams even though it's possible in theory
> +in SoundWire.
> 

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 02/13] soundwire: Add support for SoundWire stream management
  2018-03-28  9:38 ` [PATCH 02/13] soundwire: Add support for SoundWire stream management Vinod Koul
@ 2018-03-30  1:57   ` Pierre-Louis Bossart
  2018-03-30  6:42     ` Vinod Koul
  0 siblings, 1 reply; 25+ messages in thread
From: Pierre-Louis Bossart @ 2018-03-30  1:57 UTC (permalink / raw)
  To: Vinod Koul, Greg KH
  Cc: ALSA, tiwai, liam.r.girdwood, patches.audio, broonie, Sanyog Kale

On 3/28/18 4:38 AM, Vinod Koul wrote:
> From: Sanyog Kale <sanyog.r.kale@intel.com>
> 
> This patch adds APIs and relevant stream data structures
> for initialization and release of stream.
> 
> Signed-off-by: Hardik T Shah <hardik.t.shah@intel.com>
> Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
> Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
> ---
>   drivers/soundwire/Makefile    |   2 +-
>   drivers/soundwire/bus.c       |   1 +
>   drivers/soundwire/bus.h       |  32 ++++
>   drivers/soundwire/stream.c    | 337 ++++++++++++++++++++++++++++++++++++++++++
>   include/linux/soundwire/sdw.h | 112 ++++++++++++++
>   5 files changed, 483 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/soundwire/stream.c
> 
> diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile
> index e1a74c5692aa..5817beaca0e1 100644
> --- a/drivers/soundwire/Makefile
> +++ b/drivers/soundwire/Makefile
> @@ -3,7 +3,7 @@
>   #
>   
>   #Bus Objs
> -soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o
> +soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o stream.o
>   obj-$(CONFIG_SOUNDWIRE_BUS) += soundwire-bus.o
>   
>   #Cadence Objs
> diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c
> index d6dc8e7a8614..abf046f6b188 100644
> --- a/drivers/soundwire/bus.c
> +++ b/drivers/soundwire/bus.c
> @@ -32,6 +32,7 @@ int sdw_add_bus_master(struct sdw_bus *bus)
>   	mutex_init(&bus->msg_lock);
>   	mutex_init(&bus->bus_lock);
>   	INIT_LIST_HEAD(&bus->slaves);
> +	INIT_LIST_HEAD(&bus->m_rt_list);
>   
>   	if (bus->ops->read_prop) {
>   		ret = bus->ops->read_prop(bus);
> diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h
> index 345c34d697e9..0f55a18fa652 100644
> --- a/drivers/soundwire/bus.h
> +++ b/drivers/soundwire/bus.h
> @@ -45,6 +45,38 @@ struct sdw_msg {
>   	bool page;
>   };
>   
> +/**
> + * sdw_slave_runtime: Runtime Stream parameters for Slave
> + *
> + * @slave: Slave handle
> + * @direction: Data direction w.r.t Slave
> + * @ch_count: Channel count of the Slave w.r.t stream

same here, I flagged all this already as needing to be fixed. 
Something's not right here, looks to me like you send the wrong version 
of the patches...

> + * @m_rt_node: sdw_master_runtime list node
> + */
> +struct sdw_slave_runtime {
> +	struct sdw_slave *slave;
> +	enum sdw_data_direction direction;
> +	unsigned int ch_count;
> +	struct list_head m_rt_node;
> +};
> +
> +/**
> + * sdw_master_runtime: Runtime stream parameters for Master
> + *
> + * @bus: Bus handle
> + * @stream: Stream runtime handle
> + * @ch_count: Master channel count
> + * @slave_rt_list: Slave runtime list
> + * @bus_node: sdw_bus m_rt_list node
> + */
> +struct sdw_master_runtime {
> +	struct sdw_bus *bus;
> +	struct sdw_stream_runtime *stream;
> +	unsigned int ch_count;
> +	struct list_head slave_rt_list;
> +	struct list_head bus_node;
> +};

no direction?

I will stop here, this series is not ready for further review. Not going 
to rehash the same comments twice on a public mailing list after 
providing direct feedback.

> +
>   int sdw_transfer(struct sdw_bus *bus, struct sdw_msg *msg);
>   int sdw_transfer_defer(struct sdw_bus *bus, struct sdw_msg *msg,
>   				struct sdw_defer *defer);
> diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c
> new file mode 100644
> index 000000000000..00b9fcea4369
> --- /dev/null
> +++ b/drivers/soundwire/stream.c
> @@ -0,0 +1,337 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
> +// Copyright(c) 2015-18 Intel Corporation.
> +
> +/*
> + *  stream.c - SoundWire Bus stream operations.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/slab.h>
> +#include <linux/soundwire/sdw.h>
> +#include "bus.h"
> +
> +/**
> + * sdw_release_stream: Free the assigned stream runtime
> + *
> + * @stream: SoundWire stream runtime
> + *
> + * sdw_release_stream should be called only once per stream
> + */
> +void sdw_release_stream(struct sdw_stream_runtime *stream)
> +{
> +	kfree(stream);
> +}
> +EXPORT_SYMBOL(sdw_release_stream);
> +
> +/**
> + * sdw_alloc_stream: Allocate and return stream runtime
> + *
> + * @stream_name: SoundWire stream name
> + *
> + * Allocates a SoundWire stream runtime instance.
> + * sdw_alloc_stream should be called only once per stream
> + */
> +struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name)
> +{
> +	struct sdw_stream_runtime *stream;
> +
> +	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
> +	if (!stream)
> +		return NULL;
> +
> +	stream->name = stream_name;
> +	stream->state = SDW_STREAM_ALLOC;
> +
> +	return stream;
> +}
> +EXPORT_SYMBOL(sdw_alloc_stream);
> +
> +/**
> + * sdw_alloc_master_rt: Allocates and initialize Master runtime handle
> + *
> + * @bus: SDW bus instance
> + * @stream_config: Stream configuration
> + * @stream: Stream runtime handle.
> + *
> + * This function is to be called with bus_lock held.
> + */
> +static struct sdw_master_runtime
> +*sdw_alloc_master_rt(struct sdw_bus *bus,
> +			struct sdw_stream_config *stream_config,
> +			struct sdw_stream_runtime *stream)
> +{
> +	struct sdw_master_runtime *m_rt;
> +
> +	m_rt = stream->m_rt;
> +	if (m_rt)
> +		goto stream_config;
> +
> +	m_rt = kzalloc(sizeof(*m_rt), GFP_KERNEL);
> +	if (!m_rt)
> +		return NULL;
> +
> +	/* Initialization of Master runtime handle */
> +	INIT_LIST_HEAD(&m_rt->slave_rt_list);
> +	stream->m_rt = m_rt;
> +
> +	list_add_tail(&m_rt->bus_node, &bus->m_rt_list);
> +
> +stream_config:
> +	m_rt->ch_count = stream_config->ch_count;
> +	m_rt->bus = bus;
> +	m_rt->stream = stream;
> +
> +	return m_rt;
> +}
> +
> +/**
> + * sdw_alloc_slave_rt: Allocate and initialize Slave runtime handle.
> + *
> + * @slave: Slave handle
> + * @stream_config: Stream configuration
> + * @stream: Stream runtime handle
> + *
> + * This function is to be called with bus_lock held.
> + */
> +static struct sdw_slave_runtime
> +*sdw_alloc_slave_rt(struct sdw_slave *slave,
> +			struct sdw_stream_config *stream_config,
> +			struct sdw_stream_runtime *stream)
> +{
> +	struct sdw_slave_runtime *s_rt = NULL;
> +
> +	s_rt = kzalloc(sizeof(*s_rt), GFP_KERNEL);
> +	if (!s_rt)
> +		return NULL;
> +
> +
> +	s_rt->ch_count = stream_config->ch_count;
> +	s_rt->direction = stream_config->direction;
> +	s_rt->slave = slave;
> +
> +	return s_rt;
> +}
> +
> +/**
> + * sdw_release_slave_stream: Free Slave(s) runtime handle
> + *
> + * @slave: Slave handle.
> + * @stream: Stream runtime handle.
> + *
> + * This function is to be called with bus_lock held.
> + */
> +static void sdw_release_slave_stream(struct sdw_slave *slave,
> +			struct sdw_stream_runtime *stream)
> +{
> +	struct sdw_slave_runtime *s_rt, *_s_rt;
> +	struct sdw_master_runtime *m_rt = stream->m_rt;
> +
> +	/* Retrieve Slave runtime handle */
> +	list_for_each_entry_safe(s_rt, _s_rt,
> +			&m_rt->slave_rt_list, m_rt_node) {
> +
> +		if (s_rt->slave == slave) {
> +			list_del(&s_rt->m_rt_node);
> +			kfree(s_rt);
> +			return;
> +		}
> +	}
> +}
> +
> +static void sdw_release_master_stream(struct sdw_stream_runtime *stream)
> +{
> +	struct sdw_master_runtime *m_rt = stream->m_rt;
> +	struct sdw_slave_runtime *s_rt, *_s_rt;
> +
> +	list_for_each_entry_safe(s_rt, _s_rt,
> +			&m_rt->slave_rt_list, m_rt_node)
> +		sdw_release_slave_stream(s_rt->slave, stream);
> +
> +	list_del(&m_rt->bus_node);
> +}
> +
> +/**
> + * sdw_stream_remove_master: Remove master from sdw_stream
> + *
> + * @bus: SDW Bus instance
> + * @stream: Soundwire stream
> + *
> + * This removes and frees master_rt from a stream
> + */
> +
> +int sdw_stream_remove_master(struct sdw_bus *bus,
> +		struct sdw_stream_runtime *stream)
> +{
> +	mutex_lock(&bus->bus_lock);
> +
> +	sdw_release_master_stream(stream);
> +	stream->state = SDW_STREAM_RELEASE;
> +	kfree(stream->m_rt);
> +	stream->m_rt = NULL;
> +
> +	mutex_unlock(&bus->bus_lock);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sdw_stream_remove_master);
> +
> +/**
> + * sdw_stream_remove_slave: Remove slave from sdw_stream
> + *
> + * @slave: SDW Slave instance
> + * @stream: Soundwire stream
> + *
> + * This removes and frees slave_rt from a stream
> + */
> +
> +int sdw_stream_remove_slave(struct sdw_slave *slave,
> +		struct sdw_stream_runtime *stream)
> +{
> +	mutex_lock(&slave->bus->bus_lock);
> +
> +	sdw_release_slave_stream(slave, stream);
> +
> +	mutex_unlock(&slave->bus->bus_lock);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(sdw_stream_remove_slave);
> +
> +static int sdw_config_stream(struct device *dev,
> +		struct sdw_stream_runtime *stream,
> +		struct sdw_stream_config *stream_config)
> +{
> +
> +	/*
> +	 * Update the stream rate, channel and bps based on data
> +	 * transmitter. For more than one transmitter (multilink),
> +	 * match the rate, bps and increment number of channels.
> +	 */
> +	if ((stream->params.rate) &&
> +			(stream->params.rate != stream_config->frame_rate)) {
> +		dev_err(dev, "rate for multilink not matching, stream:%s",
> +				stream->name);
> +		return -EINVAL;
> +	}
> +
> +	if ((stream->params.bps) &&
> +			(stream->params.bps != stream_config->bps)) {
> +		dev_err(dev, "bps for multilink not matching, stream:%s",
> +				stream->name);
> +		return -EINVAL;
> +	}
> +
> +	stream->params.rate = stream_config->frame_rate;
> +	stream->params.bps = stream_config->bps;
> +	stream->params.ch_count += stream_config->ch_count;
> +	stream->type = stream_config->type;
> +
> +	return 0;
> +}
> +
> +/**
> + * sdw_stream_add_master: Allocate and add master runtime to a stream
> + *
> + * @bus: SDW Bus instance
> + * @stream_config: Stream configuration for audio stream
> + * @stream: Soundwire stream
> + */
> +int sdw_stream_add_master(struct sdw_bus *bus,
> +		struct sdw_stream_config *stream_config,
> +		struct sdw_stream_runtime *stream)
> +{
> +	struct sdw_master_runtime *m_rt = NULL;
> +	int ret;
> +
> +	if (stream->state != SDW_STREAM_ALLOC &&
> +			stream->state != SDW_STREAM_CONFIG) {
> +		dev_err(bus->dev,
> +			"Invalid stream state %d", stream->state);
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&bus->bus_lock);
> +
> +	m_rt = sdw_alloc_master_rt(bus, stream_config, stream);
> +	if (!m_rt) {
> +		dev_err(bus->dev,
> +				"Master runtime config failed for stream:%s",
> +				stream->name);
> +		ret = -ENOMEM;
> +		goto error;
> +	}
> +
> +	if (!list_empty(&m_rt->slave_rt_list) &&
> +			stream->state == SDW_STREAM_ALLOC)
> +		stream->state = SDW_STREAM_CONFIG;
> +
> +error:
> +	mutex_unlock(&bus->bus_lock);
> +	return ret;
> +
> +}
> +EXPORT_SYMBOL(sdw_stream_add_master);
> +
> +/**
> + * sdw_stream_add_slave: Allocate and add master/slave runtime to a stream
> + *
> + * @slave: SDW Slave instance
> + * @stream_config: Stream configuration for audio stream
> + * @stream: Soundwire stream
> + */
> +int sdw_stream_add_slave(struct sdw_slave *slave,
> +		struct sdw_stream_config *stream_config,
> +		struct sdw_stream_runtime *stream)
> +{
> +	struct sdw_slave_runtime *s_rt;
> +	struct sdw_master_runtime *m_rt;
> +	int ret;
> +
> +	if (stream->state != SDW_STREAM_ALLOC &&
> +			stream->state != SDW_STREAM_CONFIG) {
> +		dev_err(&slave->dev,
> +			"Invalid stream state %d", stream->state);
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&slave->bus->bus_lock);
> +
> +	/*
> +	 * If this API is invoked by slave first then m_rt is not valid.
> +	 * So, allocate that and add the slave to it.
> +	 */
> +	m_rt = sdw_alloc_master_rt(slave->bus, stream_config, stream);
> +	if (!m_rt) {
> +		dev_err(&slave->dev,
> +				"alloc master runtime failed for stream:%s",
> +				stream->name);
> +		ret = -ENOMEM;
> +		goto error;
> +	}
> +
> +	s_rt = sdw_alloc_slave_rt(slave, stream_config, stream);
> +	if (!s_rt) {
> +		dev_err(&slave->dev,
> +				"Slave runtime config failed for stream:%s",
> +				stream->name);
> +		ret = -ENOMEM;
> +		goto error;
> +	}
> +
> +	ret = sdw_config_stream(&slave->dev, stream, stream_config);
> +	if (ret)
> +		goto error;
> +
> +	list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list);
> +
> +	stream->state = SDW_STREAM_CONFIG;
> +
> +error:
> +	mutex_unlock(&slave->bus->bus_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL(sdw_stream_add_slave);
> diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h
> index e91fdcf41049..cb006cfd1e31 100644
> --- a/include/linux/soundwire/sdw.h
> +++ b/include/linux/soundwire/sdw.h
> @@ -61,6 +61,33 @@ enum sdw_command_response {
>   	SDW_CMD_FAIL_OTHER = 4,
>   };
>   
> +/**
> + * enum sdw_stream_type: data stream type
> + *
> + * @SDW_STREAM_PCM: PCM data stream
> + * @SDW_STREAM_PDM: PDM data stream
> + *
> + * spec doesn't define this, but is used in implementation
> + */
> +enum sdw_stream_type {
> +	SDW_STREAM_PCM = 0,
> +	SDW_STREAM_PDM = 1,
> +};
> +
> +/**
> + * enum sdw_data_direction: Data direction
> + *
> + * @SDW_DATA_DIR_IN: Data into Port
> + * @SDW_DATA_DIR_OUT: Data out of Port
> + *
> + * For TX it would refer to SDW_DATA_DIR_OUT. For RX it would be
> + * SDW_DATA_DIR_IN
> + */
> +enum sdw_data_direction {
> +	SDW_DATA_DIR_IN = 0,
> +	SDW_DATA_DIR_OUT = 1,
> +};

WTH? Didn't we agree that we would use SoundWire concepts instead of 
redefining new concepts.

Looks like all my previous feedback was ignored...

> +
>   /*
>    * SDW properties, defined in MIPI DisCo spec v1.0
>    */
> @@ -450,6 +477,9 @@ struct sdw_master_ops {
>    * @msg_lock: message lock
>    * @ops: Master callback ops
>    * @prop: Master properti
> + * @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
>    * @defer_msg: Defer message
>    * @clk_stop_timeout: Clock stop timeout computed
>    */
> @@ -462,6 +492,7 @@ struct sdw_bus {
>   	struct mutex msg_lock;
>   	const struct sdw_master_ops *ops;
>   	struct sdw_master_prop prop;
> +	struct list_head m_rt_list;
>   	struct sdw_defer defer_msg;
>   	unsigned int clk_stop_timeout;
>   };
> @@ -469,6 +500,87 @@ struct sdw_bus {
>   int sdw_add_bus_master(struct sdw_bus *bus);
>   void sdw_delete_bus_master(struct sdw_bus *bus);
>   
> +/**
> + * sdw_stream_config: Master or Slave stream configuration
> + *
> + * @frame_rate: Audio frame rate of the stream, in Hz
> + * @ch_count: Channel count of the stream
> + * @bps: Number of bits per audio sample
> + * @direction: Data direction
> + * @type: Stream type PCM or PDM
> + */
> +struct sdw_stream_config {
> +	unsigned int frame_rate;
> +	unsigned int ch_count;
> +	unsigned int bps;
> +	enum sdw_data_direction direction;
> +	enum sdw_stream_type type;
> +};
> +
> +/**
> + * sdw_stream_state: Stream states
> + *
> + * @SDW_STREAM_RELEASE: Stream released
> + * @SDW_STREAM_ALLOC: New stream allocated.
> + * @SDW_STREAM_CONFIG: Stream configured
> + * @SDW_STREAM_PREPARE: Stream prepared
> + * @SDW_STREAM_ENABLE: Stream enabled
> + * @SDW_STREAM_DISABLE: Stream disabled
> + * @SDW_STREAM_DEPREPARE: Stream de-prepared
> + */
> +enum sdw_stream_state {
> +	SDW_STREAM_RELEASE = 0,
> +	SDW_STREAM_ALLOC = 1,
> +	SDW_STREAM_CONFIG = 2,
> +	SDW_STREAM_PREPARE = 3,
> +	SDW_STREAM_ENABLE = 4,
> +	SDW_STREAM_DISABLE = 5,
> +	SDW_STREAM_DEPREPARE = 6,
> +};
> +
> +/**
> + * sdw_stream_params: Stream parameters
> + *
> + * @rate: Sampling frequency, in Hz
> + * @ch_count: Number of channels
> + * @bps: bits per channel sample
> + */
> +struct sdw_stream_params {
> +	unsigned int rate;
> +	unsigned int ch_count;
> +	unsigned int bps;
> +};
> +
> +/**
> + * sdw_stream_runtime: Runtime stream parameters
> + *
> + * @name: SoundWire stream name
> + * @params: Stream parameters
> + * @state: Current state of the stream
> + * @type: Stream type PCM or PDM
> + * @m_rt: Master runtime
> + */
> +struct sdw_stream_runtime {
> +	char *name;
> +	struct sdw_stream_params params;
> +	enum sdw_stream_state state;
> +	enum sdw_stream_type type;
> +	struct sdw_master_runtime *m_rt;
> +};
> +
> +struct sdw_stream_runtime *sdw_alloc_stream(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,
> +		struct sdw_stream_runtime *stream);
> +int sdw_stream_add_slave(struct sdw_slave *slave,
> +		struct sdw_stream_config *stream_config,
> +		struct sdw_stream_runtime *stream);
> +int sdw_stream_remove_master(struct sdw_bus *bus,
> +		struct sdw_stream_runtime *stream);
> +int sdw_stream_remove_slave(struct sdw_slave *slave,
> +		struct sdw_stream_runtime *stream);
> +
>   /* messaging and data APIs */
>   
>   int sdw_read(struct sdw_slave *slave, u32 addr);
> 

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 08/13] ASoC: Add SoundWire stream programming interface
  2018-03-28  9:38 ` [PATCH 08/13] ASoC: Add SoundWire stream programming interface Vinod Koul
@ 2018-03-30  3:05   ` Takashi Sakamoto
  2018-03-30  6:03     ` Greg KH
  0 siblings, 1 reply; 25+ messages in thread
From: Takashi Sakamoto @ 2018-03-30  3:05 UTC (permalink / raw)
  To: Vinod Koul, Greg KH
  Cc: ALSA, tiwai, Pierre-Louis Bossart, liam.r.girdwood,
	patches.audio, broonie, Shreyas NC

Hi,

On Mar 28 2018 18:38, Vinod Koul wrote:
> diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
> index c0edac80df34..690e56a35237 100644
> --- a/sound/soc/soc-core.c
> +++ b/sound/soc/soc-core.c
> @@ -2882,6 +2882,26 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
>   EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
>   
>   /**
> + * snd_soc_dai_set_sdw_stream() - Configures a DAI for SDW stream operation
> + * @dai: DAI
> + * @stream: STREAM
> + * @direction: Stream direction(Playback/Capture)
> + * SoundWire subsystem doesn't have a notion of direction and we reuse
> + * the ASoC stream direction to configure sink/source ports.
> + *
> + * Returns 0 on success, a negative error code otherwise.
> + */
> +int snd_soc_dai_set_sdw_stream(struct snd_soc_dai *dai,
> +				void *stream, int direction)
> +{
> +	if (dai->driver->ops->set_sdw_stream)
> +		return dai->driver->ops->set_sdw_stream(dai, stream, direction);
> +	else
> +		return -ENOTSUPP;
> +}
> +EXPORT_SYMBOL_GPL(snd_soc_dai_set_sdw_stream);

It's better for this kind of code to be incline function in any header. 
In general, new symbols increase maintenance cost of binary of 
kernel-related stuffs. It's preferable to avoid the addition if 
possible, IMO.


Regards

Takashi Sakamoto

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 08/13] ASoC: Add SoundWire stream programming interface
  2018-03-30  3:05   ` Takashi Sakamoto
@ 2018-03-30  6:03     ` Greg KH
  2018-04-02  6:26       ` Takashi Sakamoto
  0 siblings, 1 reply; 25+ messages in thread
From: Greg KH @ 2018-03-30  6:03 UTC (permalink / raw)
  To: Takashi Sakamoto
  Cc: ALSA, Vinod Koul, Pierre-Louis Bossart, patches.audio,
	liam.r.girdwood, tiwai, broonie, Shreyas NC

On Fri, Mar 30, 2018 at 12:05:00PM +0900, Takashi Sakamoto wrote:
> Hi,
> 
> On Mar 28 2018 18:38, Vinod Koul wrote:
> > diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
> > index c0edac80df34..690e56a35237 100644
> > --- a/sound/soc/soc-core.c
> > +++ b/sound/soc/soc-core.c
> > @@ -2882,6 +2882,26 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
> >   EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
> >   /**
> > + * snd_soc_dai_set_sdw_stream() - Configures a DAI for SDW stream operation
> > + * @dai: DAI
> > + * @stream: STREAM
> > + * @direction: Stream direction(Playback/Capture)
> > + * SoundWire subsystem doesn't have a notion of direction and we reuse
> > + * the ASoC stream direction to configure sink/source ports.
> > + *
> > + * Returns 0 on success, a negative error code otherwise.
> > + */
> > +int snd_soc_dai_set_sdw_stream(struct snd_soc_dai *dai,
> > +				void *stream, int direction)
> > +{
> > +	if (dai->driver->ops->set_sdw_stream)
> > +		return dai->driver->ops->set_sdw_stream(dai, stream, direction);
> > +	else
> > +		return -ENOTSUPP;
> > +}
> > +EXPORT_SYMBOL_GPL(snd_soc_dai_set_sdw_stream);
> 
> It's better for this kind of code to be incline function in any header. In
> general, new symbols increase maintenance cost of binary of kernel-related
> stuffs. It's preferable to avoid the addition if possible, IMO.

I don't understand, functionally it's the same, there should not be any
increased maintenance either way.  Please explain how this makes things
"harder"?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 01/13] soundwire: Add more documentation
  2018-03-30  1:47   ` Pierre-Louis Bossart
@ 2018-03-30  6:38     ` Vinod Koul
  0 siblings, 0 replies; 25+ messages in thread
From: Vinod Koul @ 2018-03-30  6:38 UTC (permalink / raw)
  To: Pierre-Louis Bossart
  Cc: ALSA, tiwai, Greg KH, liam.r.girdwood, patches.audio, broonie,
	Sanyog Kale

Just a note, it helps to trim the context. Pls do so in replies.

On Thu, Mar 29, 2018 at 08:47:28PM -0500, Pierre-Louis Bossart wrote:
> On 3/28/18 4:38 AM, Vinod Koul wrote:

> >+SoundWire stream states
> >+-----------------------
> >+
> >+Below shows the SoundWire stream states and state transition diagram. ::
> >+
> >+	+---------+     +----------+     +----------+     +----------+
> >+	|  ALLOC  +---->|  CONFIG  +---->| PREPARE  +---->|  ENABLE  |
> >+	|  STATE  |     |  STATE   |     |  STATE   |     |  STATE   |
> >+	+---------+     +----------+     +----------+     +----+-----+
> >+	                                                       ^
> >+	                                                       |
> >+	                                                       |
> >+	                                                       v
> >+	         +----------+           +-----------+     +----+-----+
> >+	         |  RELEASE |<----------+ DEPREPARE |<----+  DISABLE |
> >+	         |   STATE  |           |  STATE    |     |  STATE   |
> >+	         +----------+           +-----------+     +----------+
> >+
> >+NOTE: All stream transitions in MIPI Spec are NOT supported by software.
> 
> specifically that there are not transitions between prepare and deprepare.

Okay we can add that.

> While I am at it, didn't I provide feedback that the documentation and code
> confuse states and transitions between the states?

> Also there is a
> connection between released and allocated, this state machine isn't really
> working or representative of what the code does initially.

When we allocate sdw_stream_runtime, the initial state of the stream
would be garbage/NULL. It is initialized to ALLOC at that time.

        stream = kzalloc(sizeof(*stream), GFP_KERNEL);
        if (!stream)
                return NULL;

        stream->name = stream_name;
        stream->state = SDW_STREAM_ALLOC;

One can argue that we use kzalloc so we are in RELEASE, but I think that is
bike shedding. The lifetime of stream starts with ALLOC and ends at RELEASE
and we freeup the data structure. So I don't think there is a relation
between ALLOC and RELEASE. Any attempt to make such a relation would confuse
folks!

> See my feedback dated February 26 on the previous version.
> 
> "the name of the states are equivalent to an action. It would be more
> consistent to e.g. have an ENABLED state reached after an
> sdw_stream_enable() function.
> 
> same with sdw_stream_prepared leading to the PREPARED state. I think it's
> how most standards or state machine designs are defined (and how English
> grammar works as well)." ALSA also uses OPEN, PREPARED, PAUSED, see asound.h

Hmmm after immersing in bunch of English literature we finally figured
that wrapped logic here that out stream states shall be named as not action.
Well that could have possibly be communicated in plain speak too :D

No worries, will change the state names aptly.

-- 
~Vinod

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 02/13] soundwire: Add support for SoundWire stream management
  2018-03-30  1:57   ` Pierre-Louis Bossart
@ 2018-03-30  6:42     ` Vinod Koul
  2018-03-30  6:44       ` Vinod Koul
  0 siblings, 1 reply; 25+ messages in thread
From: Vinod Koul @ 2018-03-30  6:42 UTC (permalink / raw)
  To: Pierre-Louis Bossart
  Cc: ALSA, tiwai, Greg KH, liam.r.girdwood, patches.audio, broonie,
	Sanyog Kale

On Thu, Mar 29, 2018 at 08:57:59PM -0500, Pierre-Louis Bossart wrote:
> On 3/28/18 4:38 AM, Vinod Koul wrote:

> >+/**
> >+ * sdw_slave_runtime: Runtime Stream parameters for Slave
> >+ *
> >+ * @slave: Slave handle
> >+ * @direction: Data direction w.r.t Slave
> >+ * @ch_count: Channel count of the Slave w.r.t stream
> 
> same here, I flagged all this already as needing to be fixed. Something's
> not right here, looks to me like you send the wrong version of the
> patches...

Somehow missed this bit will fix

> >+struct sdw_master_runtime {
> >+	struct sdw_bus *bus;
> >+	struct sdw_stream_runtime *stream;
> >+	unsigned int ch_count;
> >+	struct list_head slave_rt_list;
> >+	struct list_head bus_node;
> >+};
> 
> no direction?

Yes no direction here. Why would sdw_master_runtime need direction.

If you look closely we do have direction but in data ports.

One can argue we can move direction up here and remove from data ports,
that seems logical, is that something you would like to see?

> >+enum sdw_data_direction {
> >+	SDW_DATA_DIR_IN = 0,
> >+	SDW_DATA_DIR_OUT = 1,
> >+};
> 
> WTH? Didn't we agree that we would use SoundWire concepts instead of
> redefining new concepts.

So we will make this TX/RX as discussed.

-- 
~Vinod

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 02/13] soundwire: Add support for SoundWire stream management
  2018-03-30  6:42     ` Vinod Koul
@ 2018-03-30  6:44       ` Vinod Koul
  0 siblings, 0 replies; 25+ messages in thread
From: Vinod Koul @ 2018-03-30  6:44 UTC (permalink / raw)
  To: Pierre-Louis Bossart
  Cc: ALSA, tiwai, Greg KH, liam.r.girdwood, patches.audio, broonie,
	Sanyog Kale

On Fri, Mar 30, 2018 at 12:12:48PM +0530, Vinod Koul wrote:
> On Thu, Mar 29, 2018 at 08:57:59PM -0500, Pierre-Louis Bossart wrote:
> > On 3/28/18 4:38 AM, Vinod Koul wrote:
> 
> > >+/**
> > >+ * sdw_slave_runtime: Runtime Stream parameters for Slave
> > >+ *
> > >+ * @slave: Slave handle
> > >+ * @direction: Data direction w.r.t Slave
> > >+ * @ch_count: Channel count of the Slave w.r.t stream
> > 
> > same here, I flagged all this already as needing to be fixed. Something's
> > not right here, looks to me like you send the wrong version of the
> > patches...
> 
> Somehow missed this bit will fix

On this one, I think i need more help.

SO is your question on direction right, we are thinking of making it:

enum sdw_data_direction {
	SDW_DATA_TX,
	SDW_DATA_RX,
}

and then use it is SDW. Is that okay for you or not..

For ch_count, we will update the comment.

> 
> > >+struct sdw_master_runtime {
> > >+	struct sdw_bus *bus;
> > >+	struct sdw_stream_runtime *stream;
> > >+	unsigned int ch_count;
> > >+	struct list_head slave_rt_list;
> > >+	struct list_head bus_node;
> > >+};
> > 
> > no direction?
> 
> Yes no direction here. Why would sdw_master_runtime need direction.
> 
> If you look closely we do have direction but in data ports.
> 
> One can argue we can move direction up here and remove from data ports,
> that seems logical, is that something you would like to see?
> 
> > >+enum sdw_data_direction {
> > >+	SDW_DATA_DIR_IN = 0,
> > >+	SDW_DATA_DIR_OUT = 1,
> > >+};
> > 
> > WTH? Didn't we agree that we would use SoundWire concepts instead of
> > redefining new concepts.
> 
> So we will make this TX/RX as discussed.
> 
> -- 
> ~Vinod

-- 
~Vinod

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 08/13] ASoC: Add SoundWire stream programming interface
  2018-03-30  6:03     ` Greg KH
@ 2018-04-02  6:26       ` Takashi Sakamoto
  2018-04-03 12:03         ` Takashi Iwai
  0 siblings, 1 reply; 25+ messages in thread
From: Takashi Sakamoto @ 2018-04-02  6:26 UTC (permalink / raw)
  To: Greg KH
  Cc: ALSA, Vinod Koul, Pierre-Louis Bossart, patches.audio,
	liam.r.girdwood, tiwai, broonie, Shreyas NC

Hi Greg,

I'm sorry for delay but I had a short trip.

On Mar 30 2018 15:03, Greg KH wrote:
> On Fri, Mar 30, 2018 at 12:05:00PM +0900, Takashi Sakamoto wrote:
>> Hi,
>>
>> On Mar 28 2018 18:38, Vinod Koul wrote:
>>> diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
>>> index c0edac80df34..690e56a35237 100644
>>> --- a/sound/soc/soc-core.c
>>> +++ b/sound/soc/soc-core.c
>>> @@ -2882,6 +2882,26 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
>>>    EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
>>>    /**
>>> + * snd_soc_dai_set_sdw_stream() - Configures a DAI for SDW stream operation
>>> + * @dai: DAI
>>> + * @stream: STREAM
>>> + * @direction: Stream direction(Playback/Capture)
>>> + * SoundWire subsystem doesn't have a notion of direction and we reuse
>>> + * the ASoC stream direction to configure sink/source ports.
>>> + *
>>> + * Returns 0 on success, a negative error code otherwise.
>>> + */
>>> +int snd_soc_dai_set_sdw_stream(struct snd_soc_dai *dai,
>>> +				void *stream, int direction)
>>> +{
>>> +	if (dai->driver->ops->set_sdw_stream)
>>> +		return dai->driver->ops->set_sdw_stream(dai, stream, direction);
>>> +	else
>>> +		return -ENOTSUPP;
>>> +}
>>> +EXPORT_SYMBOL_GPL(snd_soc_dai_set_sdw_stream);
>>
>> It's better for this kind of code to be incline function in any header. In
>> general, new symbols increase maintenance cost of binary of kernel-related
>> stuffs. It's preferable to avoid the addition if possible, IMO.
> 
> I don't understand, functionally it's the same, there should not be any
> increased maintenance either way.  Please explain how this makes things
> "harder"?

Hm, if so it might be my misunderstanding to reasons for typical usage 
of inline functions in kernel source, sorry.

In my understanding, exported symbols are put into some sections of ELF 
binary. Addition of new symbols increases size of the section. 
Additionally, after linking vmlinux, kbuild scans built-in symbols and 
make a file with entries of them. The addition increases time of this 
step. Furthermore, at the end of building kernel, kmod is called to 
generate some map files for exported symbols in loadable module. In a 
view of distributors, these files are maintained by binary packages of 
any type carefully because some incompatibilities can be delivered such 
as version mismatch.

For these reasons, I think thing goes harder when people carelessly add 
new symbols for functions with a few codes; e.g. accessing to a member 
of structure, then simply check an return it. Actually I can see some 
examples in upstreamed headers.


Thanks

Takashi Sakamoto

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 08/13] ASoC: Add SoundWire stream programming interface
  2018-04-02  6:26       ` Takashi Sakamoto
@ 2018-04-03 12:03         ` Takashi Iwai
  2018-04-05  5:03           ` Takashi Sakamoto
  0 siblings, 1 reply; 25+ messages in thread
From: Takashi Iwai @ 2018-04-03 12:03 UTC (permalink / raw)
  To: Takashi Sakamoto
  Cc: ALSA, Vinod Koul, Greg KH, Pierre-Louis Bossart, liam.r.girdwood,
	patches.audio, broonie, Shreyas NC

On Mon, 02 Apr 2018 08:26:35 +0200,
Takashi Sakamoto wrote:
> 
> Hi Greg,
> 
> I'm sorry for delay but I had a short trip.
> 
> On Mar 30 2018 15:03, Greg KH wrote:
> > On Fri, Mar 30, 2018 at 12:05:00PM +0900, Takashi Sakamoto wrote:
> >> Hi,
> >>
> >> On Mar 28 2018 18:38, Vinod Koul wrote:
> >>> diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
> >>> index c0edac80df34..690e56a35237 100644
> >>> --- a/sound/soc/soc-core.c
> >>> +++ b/sound/soc/soc-core.c
> >>> @@ -2882,6 +2882,26 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
> >>>    EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
> >>>    /**
> >>> + * snd_soc_dai_set_sdw_stream() - Configures a DAI for SDW stream operation
> >>> + * @dai: DAI
> >>> + * @stream: STREAM
> >>> + * @direction: Stream direction(Playback/Capture)
> >>> + * SoundWire subsystem doesn't have a notion of direction and we reuse
> >>> + * the ASoC stream direction to configure sink/source ports.
> >>> + *
> >>> + * Returns 0 on success, a negative error code otherwise.
> >>> + */
> >>> +int snd_soc_dai_set_sdw_stream(struct snd_soc_dai *dai,
> >>> +				void *stream, int direction)
> >>> +{
> >>> +	if (dai->driver->ops->set_sdw_stream)
> >>> +		return dai->driver->ops->set_sdw_stream(dai, stream, direction);
> >>> +	else
> >>> +		return -ENOTSUPP;
> >>> +}
> >>> +EXPORT_SYMBOL_GPL(snd_soc_dai_set_sdw_stream);
> >>
> >> It's better for this kind of code to be incline function in any header. In
> >> general, new symbols increase maintenance cost of binary of kernel-related
> >> stuffs. It's preferable to avoid the addition if possible, IMO.
> >
> > I don't understand, functionally it's the same, there should not be any
> > increased maintenance either way.  Please explain how this makes things
> > "harder"?
> 
> Hm, if so it might be my misunderstanding to reasons for typical usage
> of inline functions in kernel source, sorry.
> 
> In my understanding, exported symbols are put into some sections of
> ELF binary. Addition of new symbols increases size of the
> section. Additionally, after linking vmlinux, kbuild scans built-in
> symbols and make a file with entries of them. The addition increases
> time of this step. Furthermore, at the end of building kernel, kmod is
> called to generate some map files for exported symbols in loadable
> module. In a view of distributors, these files are maintained by
> binary packages of any type carefully because some incompatibilities
> can be delivered such as version mismatch.
> 
> For these reasons, I think thing goes harder when people carelessly
> add new symbols for functions with a few codes; e.g. accessing to a
> member of structure, then simply check an return it. Actually I can
> see some examples in upstreamed headers.

The advantage of inline function isn't about the maintenance cost.
It's mostly for performance, as well as the binary size reduction.

Actually, when a kernel live-patching comes into play, an inline
function is worse from the maintenance POV.  Then we'd have to patch
every place that is expanded instead of a single place.

However, this doesn't discourage the use of inline function, either.
Overall, the performance is still the most important factor for major
cases.  So I agree with that this particular case can be well inlined,
supposing that no complex code is planned to be added in future.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 08/13] ASoC: Add SoundWire stream programming interface
  2018-04-03 12:03         ` Takashi Iwai
@ 2018-04-05  5:03           ` Takashi Sakamoto
  2018-04-05  6:38             ` Vinod Koul
  0 siblings, 1 reply; 25+ messages in thread
From: Takashi Sakamoto @ 2018-04-05  5:03 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: ALSA, Vinod Koul, Greg KH, Pierre-Louis Bossart, liam.r.girdwood,
	patches.audio, broonie, Shreyas NC

Hi,

On Apr 3 2018 21:03, Takashi Iwai wrote:
>>>> It's better for this kind of code to be incline function in any header. In
>>>> general, new symbols increase maintenance cost of binary of kernel-related
>>>> stuffs. It's preferable to avoid the addition if possible, IMO.
>>>
>>> I don't understand, functionally it's the same, there should not be any
>>> increased maintenance either way.  Please explain how this makes things
>>> "harder"?
>>
>> Hm, if so it might be my misunderstanding to reasons for typical usage
>> of inline functions in kernel source, sorry.
>>
>> In my understanding, exported symbols are put into some sections of
>> ELF binary. Addition of new symbols increases size of the
>> section. Additionally, after linking vmlinux, kbuild scans built-in
>> symbols and make a file with entries of them. The addition increases
>> time of this step. Furthermore, at the end of building kernel, kmod is
>> called to generate some map files for exported symbols in loadable
>> module. In a view of distributors, these files are maintained by
>> binary packages of any type carefully because some incompatibilities
>> can be delivered such as version mismatch.
>>
>> For these reasons, I think thing goes harder when people carelessly
>> add new symbols for functions with a few codes; e.g. accessing to a
>> member of structure, then simply check an return it. Actually I can
>> see some examples in upstreamed headers.
> 
> The advantage of inline function isn't about the maintenance cost.
> It's mostly for performance, as well as the binary size reduction.
> 
> Actually, when a kernel live-patching comes into play, an inline
> function is worse from the maintenance POV.  Then we'd have to patch
> every place that is expanded instead of a single place.
> 
> However, this doesn't discourage the use of inline function, either.

I'm OK for this view, and let me add it to my criteria for my daily
reviewing work. Thanks for sharing the view.

> Overall, the performance is still the most important factor for major
> cases.  So I agree with that this particular case can be well inlined,
> supposing that no complex code is planned to be added in future.

I agree with it as well. When developers add more complexity to content
of the inline function, then let them convert it to exported symbols.


Thanks

Takashi Sakamoto

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 08/13] ASoC: Add SoundWire stream programming interface
  2018-04-05  5:03           ` Takashi Sakamoto
@ 2018-04-05  6:38             ` Vinod Koul
  0 siblings, 0 replies; 25+ messages in thread
From: Vinod Koul @ 2018-04-05  6:38 UTC (permalink / raw)
  To: Takashi Sakamoto
  Cc: ALSA, Takashi Iwai, Greg KH, Pierre-Louis Bossart,
	liam.r.girdwood, patches.audio, broonie, Shreyas NC

On Thu, Apr 05, 2018 at 02:03:31PM +0900, Takashi Sakamoto wrote:
> Hi,
> 
> On Apr 3 2018 21:03, Takashi Iwai wrote:
> >>>>It's better for this kind of code to be incline function in any header. In
> >>>>general, new symbols increase maintenance cost of binary of kernel-related
> >>>>stuffs. It's preferable to avoid the addition if possible, IMO.
> >>>
> >>>I don't understand, functionally it's the same, there should not be any
> >>>increased maintenance either way.  Please explain how this makes things
> >>>"harder"?
> >>
> >>Hm, if so it might be my misunderstanding to reasons for typical usage
> >>of inline functions in kernel source, sorry.
> >>
> >>In my understanding, exported symbols are put into some sections of
> >>ELF binary. Addition of new symbols increases size of the
> >>section. Additionally, after linking vmlinux, kbuild scans built-in
> >>symbols and make a file with entries of them. The addition increases
> >>time of this step. Furthermore, at the end of building kernel, kmod is
> >>called to generate some map files for exported symbols in loadable
> >>module. In a view of distributors, these files are maintained by
> >>binary packages of any type carefully because some incompatibilities
> >>can be delivered such as version mismatch.
> >>
> >>For these reasons, I think thing goes harder when people carelessly
> >>add new symbols for functions with a few codes; e.g. accessing to a
> >>member of structure, then simply check an return it. Actually I can
> >>see some examples in upstreamed headers.
> >
> >The advantage of inline function isn't about the maintenance cost.
> >It's mostly for performance, as well as the binary size reduction.
> >
> >Actually, when a kernel live-patching comes into play, an inline
> >function is worse from the maintenance POV.  Then we'd have to patch
> >every place that is expanded instead of a single place.
> >
> >However, this doesn't discourage the use of inline function, either.
> 
> I'm OK for this view, and let me add it to my criteria for my daily
> reviewing work. Thanks for sharing the view.

For us the motivation to keep as proposed was prior art. Currently all of
the snd_soc_dai_set_* APIs are doing similar functionally of setting
something on DAIs and not inlined.

Said that I agree this can be inlined so we shall do so.

> >Overall, the performance is still the most important factor for major
> >cases.  So I agree with that this particular case can be well inlined,
> >supposing that no complex code is planned to be added in future.
> 
> I agree with it as well. When developers add more complexity to content
> of the inline function, then let them convert it to exported symbols.
> 
> 
> Thanks
> 
> Takashi Sakamoto

-- 
~Vinod

^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2018-04-05  6:34 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-03-28  9:38 [PATCH 00/13] soundwire: Add stream support Vinod Koul
2018-03-28  9:38 ` [PATCH 01/13] soundwire: Add more documentation Vinod Koul
2018-03-30  1:47   ` Pierre-Louis Bossart
2018-03-30  6:38     ` Vinod Koul
2018-03-28  9:38 ` [PATCH 02/13] soundwire: Add support for SoundWire stream management Vinod Koul
2018-03-30  1:57   ` Pierre-Louis Bossart
2018-03-30  6:42     ` Vinod Koul
2018-03-30  6:44       ` Vinod Koul
2018-03-28  9:38 ` [PATCH 03/13] soundwire: Add support for port management Vinod Koul
2018-03-28  9:38 ` [PATCH 04/13] soundwire: Add Master and Slave port programming Vinod Koul
2018-03-28  9:38 ` [PATCH 05/13] soundwire: Add helpers for ports operations Vinod Koul
2018-03-28  9:38 ` [PATCH 06/13] soundwire: Add bank switch routine Vinod Koul
2018-03-28  9:38 ` [PATCH 07/13] soundwire: Add stream configuration APIs Vinod Koul
2018-03-28  9:38 ` [PATCH 08/13] ASoC: Add SoundWire stream programming interface Vinod Koul
2018-03-30  3:05   ` Takashi Sakamoto
2018-03-30  6:03     ` Greg KH
2018-04-02  6:26       ` Takashi Sakamoto
2018-04-03 12:03         ` Takashi Iwai
2018-04-05  5:03           ` Takashi Sakamoto
2018-04-05  6:38             ` Vinod Koul
2018-03-28  9:38 ` [PATCH 09/13] soundwire: Remove cdns_master_ops Vinod Koul
2018-03-28  9:38 ` [PATCH 10/13] soundwire: cdns: Add port routines Vinod Koul
2018-03-28  9:38 ` [PATCH 11/13] soundwire: cdns: Add stream routines Vinod Koul
2018-03-28  9:38 ` [PATCH 12/13] soundwire: intel: Add stream initialization Vinod Koul
2018-03-28  9:38 ` [PATCH 13/13] soundwire: intel: Add audio DAI ops Vinod Koul

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.