diff mbox

[v3,02/13] soundwire: Add support for SoundWire stream management

Message ID 1523892221-16169-3-git-send-email-vinod.koul@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Vinod Koul April 16, 2018, 3:23 p.m. UTC
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       |  36 +++++
 drivers/soundwire/stream.c    | 348 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/soundwire/sdw.h | 109 +++++++++++++
 5 files changed, 495 insertions(+), 1 deletion(-)
 create mode 100644 drivers/soundwire/stream.c

Comments

Pierre-Louis Bossart April 16, 2018, 11:15 p.m. UTC | #1
On 04/16/2018 10:23 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       |  36 +++++
>   drivers/soundwire/stream.c    | 348 ++++++++++++++++++++++++++++++++++++++++++
>   include/linux/soundwire/sdw.h | 109 +++++++++++++
>   5 files changed, 495 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..2e5043de9a4b 100644
> --- a/drivers/soundwire/bus.h
> +++ b/drivers/soundwire/bus.h
> @@ -45,6 +45,42 @@ struct sdw_msg {
>   	bool page;
>   };
>   
> +/**
> + * sdw_slave_runtime: Runtime Stream parameters for Slave
> + *
> + * @slave: Slave handle
> + * @direction: Data direction for Slave
> + * @ch_count: Number of channels handled by the Slave for
> + * this 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
> + * @direction: Data direction for Master
> + * @ch_count: Number of channels handled by the Master for
> + * this stream, can be zero.
> + * @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;
> +	enum sdw_data_direction direction;
> +	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..7ec1a3e4b2d6
> --- /dev/null
> +++ b/drivers/soundwire/stream.c
> @@ -0,0 +1,348 @@
> +// 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. Typically
> + * invoked from ALSA/ASoC machine/platform driver.
> + */
> +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_ALLOCATED;
> +
> +	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;
> +
> +	/*
> +	 * check if Master is already allocated (as a result of Slave adding
> +	 * it first), if so skip allocation and go to configure
> +	 */
> +	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;
> +	m_rt->direction = stream_config->direction;
> +
> +	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;
> +
> +
remove extra line
> +	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;
> +		}
> +	}
> +}
add kernel doc style for sdw_release_master_stream(), with same note 
that it's called with bus_lock held?

> +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);
So if the release_master_stream is called first it'll call 
sdw_release_slave_stream(), so sdw_stream_remove_slave() will in effect 
do nothing?

I guess it's the dual of what happens for allocation - where the 
master_rt might be allocated by the first slave_add but might be worth a 
comment or two so that people understand the intent and don't believe 
it's a design issue.

> +
> +	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_RELEASED;
> +	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);
> +
> +/**
> + * sdw_config_stream: Configure the allocated stream
> + *
> + * @dev: SDW device
> + * @stream: SoundWire stream
> + * @stream_config: Stream configuration for audio stream
> + * @is_slave: is API called from Slave or Master
> + *
> + * This function is to be called with bus_lock held.
> + */
> +static int sdw_config_stream(struct device *dev,
> +		struct sdw_stream_runtime *stream,
> +		struct sdw_stream_config *stream_config, bool is_slave)
> +{
> +
> +	/*
> +	 * Update the stream rate, channel and bps based on data
> +	 * source. For more than one data source (multilink),
> +	 * match the rate, bps, stream type and increment number of channels.
> +	 */
> +	if ((stream->params.rate) &&
> +			(stream->params.rate != stream_config->frame_rate)) {
> +		dev_err(dev, "rate not matching, stream:%s", stream->name);
> +		return -EINVAL;
> +	}
> +
> +	if ((stream->params.bps) &&
> +			(stream->params.bps != stream_config->bps)) {
> +		dev_err(dev, "bps not matching, stream:%s", stream->name);
> +		return -EINVAL;
> +	}
> +
> +	stream->type = stream_config->type;
> +	stream->params.rate = stream_config->frame_rate;
> +	stream->params.bps = stream_config->bps;
> +	if (is_slave)
> +		stream->params.ch_count += stream_config->ch_count;
I just realized this will not work for device-to-device communication. 
You have to count all TX ports or all RX ports, not the ports for Slaves.

> +
> +	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;
> +
> +	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;
> +	}
> +
> +	ret = sdw_config_stream(bus->dev, stream, stream_config, false);
> +	if (ret)
> +		goto stream_error;
> +
> +	stream->state = SDW_STREAM_CONFIGURED;
> +
> +stream_error:
> +	sdw_release_master_stream(stream);
> +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;
> +
> +	mutex_lock(&slave->bus->bus_lock);
> +
> +	/*
> +	 * If this API is invoked by Slave first then m_rt is not valid.
> +	 * So, allocate m_rt and add 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 stream_error;
> +	}
> +
> +	ret = sdw_config_stream(&slave->dev, stream, stream_config, true);
> +	if (ret)
> +		goto stream_error;
> +
> +	list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list);
> +
> +	stream->state = SDW_STREAM_CONFIGURED;
> +	goto error;
> +
> +stream_error:
> +	/*
> +	 * we hit error so cleanup the stream, release all Slave(s) and
> +	 * Master runtime
> +	 */
> +	sdw_release_master_stream(stream);
> +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..7a41e3860efc 100644
> --- a/include/linux/soundwire/sdw.h
> +++ b/include/linux/soundwire/sdw.h
> @@ -61,6 +61,30 @@ 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_RX: Data into Port
> + * @SDW_DATA_DIR_TX: Data out of Port
> + */
> +enum sdw_data_direction {
> +	SDW_DATA_DIR_RX = 0,
> +	SDW_DATA_DIR_TX = 1,
> +};
> +
>   /*
>    * SDW properties, defined in MIPI DisCo spec v1.0
>    */
> @@ -450,6 +474,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 +489,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 +497,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_RELEASED: Stream released
> + * @SDW_STREAM_ALLOCATED: New stream allocated.
> + * @SDW_STREAM_CONFIGURED: Stream configured
> + * @SDW_STREAM_PREPARED: Stream prepared
> + * @SDW_STREAM_ENABLED: Stream enabled
> + * @SDW_STREAM_DISABLED: Stream disabled
> + * @SDW_STREAM_DEPREPARED: Stream de-prepared
> + */
> +enum sdw_stream_state {
> +	SDW_STREAM_ALLOCATED = 0,
> +	SDW_STREAM_CONFIGURED = 1,
> +	SDW_STREAM_PREPARED = 2,
> +	SDW_STREAM_ENABLED = 3,
> +	SDW_STREAM_DISABLED = 4,
> +	SDW_STREAM_DEPREPARED = 5,
> +	SDW_STREAM_RELEASED = 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);
Vinod Koul April 17, 2018, 3:46 a.m. UTC | #2
On Mon, Apr 16, 2018 at 06:15:11PM -0500, Pierre-Louis Bossart wrote:

> >+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;
> >+
> >+
> remove extra line

ok

> >+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;
> >+		}
> >+	}
> >+}
> add kernel doc style for sdw_release_master_stream(), with same note that
> it's called with bus_lock held?

done

> 
> >+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);
> So if the release_master_stream is called first it'll call
> sdw_release_slave_stream(), so sdw_stream_remove_slave() will in effect do
> nothing?
> 
> I guess it's the dual of what happens for allocation - where the master_rt
> might be allocated by the first slave_add but might be worth a comment or
> two so that people understand the intent and don't believe it's a design
> issue.

Yes makes sense, added now

> >+static int sdw_config_stream(struct device *dev,
> >+		struct sdw_stream_runtime *stream,
> >+		struct sdw_stream_config *stream_config, bool is_slave)
> >+{
> >+
> >+	/*
> >+	 * Update the stream rate, channel and bps based on data
> >+	 * source. For more than one data source (multilink),
> >+	 * match the rate, bps, stream type and increment number of channels.
> >+	 */
> >+	if ((stream->params.rate) &&
> >+			(stream->params.rate != stream_config->frame_rate)) {
> >+		dev_err(dev, "rate not matching, stream:%s", stream->name);
> >+		return -EINVAL;
> >+	}
> >+
> >+	if ((stream->params.bps) &&
> >+			(stream->params.bps != stream_config->bps)) {
> >+		dev_err(dev, "bps not matching, stream:%s", stream->name);
> >+		return -EINVAL;
> >+	}
> >+
> >+	stream->type = stream_config->type;
> >+	stream->params.rate = stream_config->frame_rate;
> >+	stream->params.bps = stream_config->bps;
> >+	if (is_slave)
> >+		stream->params.ch_count += stream_config->ch_count;
> I just realized this will not work for device-to-device communication. You
> have to count all TX ports or all RX ports, not the ports for Slaves.

Correct, will update the check when we add support for device-to-device
communication
diff mbox

Patch

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..2e5043de9a4b 100644
--- a/drivers/soundwire/bus.h
+++ b/drivers/soundwire/bus.h
@@ -45,6 +45,42 @@  struct sdw_msg {
 	bool page;
 };
 
+/**
+ * sdw_slave_runtime: Runtime Stream parameters for Slave
+ *
+ * @slave: Slave handle
+ * @direction: Data direction for Slave
+ * @ch_count: Number of channels handled by the Slave for
+ * this 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
+ * @direction: Data direction for Master
+ * @ch_count: Number of channels handled by the Master for
+ * this stream, can be zero.
+ * @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;
+	enum sdw_data_direction direction;
+	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..7ec1a3e4b2d6
--- /dev/null
+++ b/drivers/soundwire/stream.c
@@ -0,0 +1,348 @@ 
+// 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. Typically
+ * invoked from ALSA/ASoC machine/platform driver.
+ */
+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_ALLOCATED;
+
+	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;
+
+	/*
+	 * check if Master is already allocated (as a result of Slave adding
+	 * it first), if so skip allocation and go to configure
+	 */
+	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;
+	m_rt->direction = stream_config->direction;
+
+	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_RELEASED;
+	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);
+
+/**
+ * sdw_config_stream: Configure the allocated stream
+ *
+ * @dev: SDW device
+ * @stream: SoundWire stream
+ * @stream_config: Stream configuration for audio stream
+ * @is_slave: is API called from Slave or Master
+ *
+ * This function is to be called with bus_lock held.
+ */
+static int sdw_config_stream(struct device *dev,
+		struct sdw_stream_runtime *stream,
+		struct sdw_stream_config *stream_config, bool is_slave)
+{
+
+	/*
+	 * Update the stream rate, channel and bps based on data
+	 * source. For more than one data source (multilink),
+	 * match the rate, bps, stream type and increment number of channels.
+	 */
+	if ((stream->params.rate) &&
+			(stream->params.rate != stream_config->frame_rate)) {
+		dev_err(dev, "rate not matching, stream:%s", stream->name);
+		return -EINVAL;
+	}
+
+	if ((stream->params.bps) &&
+			(stream->params.bps != stream_config->bps)) {
+		dev_err(dev, "bps not matching, stream:%s", stream->name);
+		return -EINVAL;
+	}
+
+	stream->type = stream_config->type;
+	stream->params.rate = stream_config->frame_rate;
+	stream->params.bps = stream_config->bps;
+	if (is_slave)
+		stream->params.ch_count += stream_config->ch_count;
+
+	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;
+
+	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;
+	}
+
+	ret = sdw_config_stream(bus->dev, stream, stream_config, false);
+	if (ret)
+		goto stream_error;
+
+	stream->state = SDW_STREAM_CONFIGURED;
+
+stream_error:
+	sdw_release_master_stream(stream);
+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;
+
+	mutex_lock(&slave->bus->bus_lock);
+
+	/*
+	 * If this API is invoked by Slave first then m_rt is not valid.
+	 * So, allocate m_rt and add 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 stream_error;
+	}
+
+	ret = sdw_config_stream(&slave->dev, stream, stream_config, true);
+	if (ret)
+		goto stream_error;
+
+	list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list);
+
+	stream->state = SDW_STREAM_CONFIGURED;
+	goto error;
+
+stream_error:
+	/*
+	 * we hit error so cleanup the stream, release all Slave(s) and
+	 * Master runtime
+	 */
+	sdw_release_master_stream(stream);
+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..7a41e3860efc 100644
--- a/include/linux/soundwire/sdw.h
+++ b/include/linux/soundwire/sdw.h
@@ -61,6 +61,30 @@  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_RX: Data into Port
+ * @SDW_DATA_DIR_TX: Data out of Port
+ */
+enum sdw_data_direction {
+	SDW_DATA_DIR_RX = 0,
+	SDW_DATA_DIR_TX = 1,
+};
+
 /*
  * SDW properties, defined in MIPI DisCo spec v1.0
  */
@@ -450,6 +474,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 +489,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 +497,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_RELEASED: Stream released
+ * @SDW_STREAM_ALLOCATED: New stream allocated.
+ * @SDW_STREAM_CONFIGURED: Stream configured
+ * @SDW_STREAM_PREPARED: Stream prepared
+ * @SDW_STREAM_ENABLED: Stream enabled
+ * @SDW_STREAM_DISABLED: Stream disabled
+ * @SDW_STREAM_DEPREPARED: Stream de-prepared
+ */
+enum sdw_stream_state {
+	SDW_STREAM_ALLOCATED = 0,
+	SDW_STREAM_CONFIGURED = 1,
+	SDW_STREAM_PREPARED = 2,
+	SDW_STREAM_ENABLED = 3,
+	SDW_STREAM_DISABLED = 4,
+	SDW_STREAM_DEPREPARED = 5,
+	SDW_STREAM_RELEASED = 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);