diff options
| -rw-r--r-- | Documentation/driver-api/slimbus.rst | 5 | ||||
| -rw-r--r-- | drivers/slimbus/Makefile | 2 | ||||
| -rw-r--r-- | drivers/slimbus/core.c | 2 | ||||
| -rw-r--r-- | drivers/slimbus/slimbus.h | 189 | ||||
| -rw-r--r-- | drivers/slimbus/stream.c | 477 | ||||
| -rw-r--r-- | include/linux/slimbus.h | 46 | 
6 files changed, 720 insertions, 1 deletions
diff --git a/Documentation/driver-api/slimbus.rst b/Documentation/driver-api/slimbus.rst index a97449cf603a..410eec79b2a1 100644 --- a/Documentation/driver-api/slimbus.rst +++ b/Documentation/driver-api/slimbus.rst @@ -125,3 +125,8 @@ Messaging APIs:  ~~~~~~~~~~~~~~~  .. kernel-doc:: drivers/slimbus/messaging.c     :export: + +Streaming APIs: +~~~~~~~~~~~~~~~ +.. kernel-doc:: drivers/slimbus/stream.c +   :export: diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile index c78c6e16c2df..d9aa011b6804 100644 --- a/drivers/slimbus/Makefile +++ b/drivers/slimbus/Makefile @@ -3,7 +3,7 @@  # Makefile for kernel SLIMbus framework.  #  obj-$(CONFIG_SLIMBUS)			+= slimbus.o -slimbus-y				:= core.o messaging.o sched.o +slimbus-y				:= core.o messaging.o sched.o stream.o  #Controllers  obj-$(CONFIG_SLIM_QCOM_CTRL)		+= slim-qcom-ctrl.o diff --git a/drivers/slimbus/core.c b/drivers/slimbus/core.c index 88248a4ecad9..95b00d28ad6e 100644 --- a/drivers/slimbus/core.c +++ b/drivers/slimbus/core.c @@ -114,6 +114,8 @@ static int slim_add_device(struct slim_controller *ctrl,  	sbdev->dev.release = slim_dev_release;  	sbdev->dev.driver = NULL;  	sbdev->ctrl = ctrl; +	INIT_LIST_HEAD(&sbdev->stream_list); +	spin_lock_init(&sbdev->stream_list_lock);  	if (node)  		sbdev->dev.of_node = of_node_get(node); diff --git a/drivers/slimbus/slimbus.h b/drivers/slimbus/slimbus.h index 63229e8cd050..6dbc2b320704 100644 --- a/drivers/slimbus/slimbus.h +++ b/drivers/slimbus/slimbus.h @@ -45,9 +45,20 @@  #define SLIM_MSG_MC_ASSIGN_LOGICAL_ADDRESS       0x2  #define SLIM_MSG_MC_REPORT_ABSENT                0xF +/* Data channel management messages */ +#define SLIM_MSG_MC_CONNECT_SOURCE		0x10 +#define SLIM_MSG_MC_CONNECT_SINK		0x11 +#define SLIM_MSG_MC_DISCONNECT_PORT		0x14 +#define SLIM_MSG_MC_CHANGE_CONTENT		0x18 +  /* Clock pause Reconfiguration messages */  #define SLIM_MSG_MC_BEGIN_RECONFIGURATION        0x40  #define SLIM_MSG_MC_NEXT_PAUSE_CLOCK             0x4A +#define SLIM_MSG_MC_NEXT_DEFINE_CHANNEL          0x50 +#define SLIM_MSG_MC_NEXT_DEFINE_CONTENT          0x51 +#define SLIM_MSG_MC_NEXT_ACTIVATE_CHANNEL        0x54 +#define SLIM_MSG_MC_NEXT_DEACTIVATE_CHANNEL      0x55 +#define SLIM_MSG_MC_NEXT_REMOVE_CHANNEL          0x58  #define SLIM_MSG_MC_RECONFIGURE_NOW              0x5F  /* @@ -69,7 +80,15 @@  /* Standard values per SLIMbus spec needed by controllers and devices */  #define SLIM_MAX_CLK_GEAR		10  #define SLIM_MIN_CLK_GEAR		1 +#define SLIM_SLOT_LEN_BITS		4 + +/* Indicate that the frequency of the flow and the bus frequency are locked */ +#define SLIM_CHANNEL_CONTENT_FL		BIT(7) +/* Standard values per SLIMbus spec needed by controllers and devices */ +#define SLIM_CL_PER_SUPERFRAME		6144 +#define SLIM_SLOTS_PER_SUPERFRAME	(SLIM_CL_PER_SUPERFRAME >> 2) +#define SLIM_SL_PER_SUPERFRAME		(SLIM_CL_PER_SUPERFRAME >> 2)  /* Manager's logical address is set to 0xFF per spec */  #define SLIM_LA_MANAGER 0xFF @@ -168,6 +187,170 @@ struct slim_sched {  };  /** + * enum slim_port_direction: SLIMbus port direction + * + * @SLIM_PORT_SINK: SLIMbus port is a sink + * @SLIM_PORT_SOURCE: SLIMbus port is a source + */ +enum slim_port_direction { +	SLIM_PORT_SINK = 0, +	SLIM_PORT_SOURCE, +}; +/** + * enum slim_port_state: SLIMbus Port/Endpoint state machine + *	according to SLIMbus Spec 2.0 + * @SLIM_PORT_DISCONNECTED: SLIMbus port is disconnected + *	entered from Unconfigure/configured state after + *	DISCONNECT_PORT or REMOVE_CHANNEL core command + * @SLIM_PORT_UNCONFIGURED: SLIMbus port is in unconfigured state. + *	entered from disconnect state after CONNECT_SOURCE/SINK core command + * @SLIM_PORT_CONFIGURED: SLIMbus port is in configured state. + *	entered from unconfigured state after DEFINE_CHANNEL, DEFINE_CONTENT + *	and ACTIVATE_CHANNEL core commands. Ready for data transmission. + */ +enum slim_port_state { +	SLIM_PORT_DISCONNECTED = 0, +	SLIM_PORT_UNCONFIGURED, +	SLIM_PORT_CONFIGURED, +}; + +/** + * enum slim_channel_state: SLIMbus channel state machine used by core. + * @SLIM_CH_STATE_DISCONNECTED: SLIMbus channel is disconnected + * @SLIM_CH_STATE_ALLOCATED: SLIMbus channel is allocated + * @SLIM_CH_STATE_ASSOCIATED: SLIMbus channel is associated with port + * @SLIM_CH_STATE_DEFINED: SLIMbus channel parameters are defined + * @SLIM_CH_STATE_CONTENT_DEFINED: SLIMbus channel content is defined + * @SLIM_CH_STATE_ACTIVE: SLIMbus channel is active and ready for data + * @SLIM_CH_STATE_REMOVED: SLIMbus channel is inactive and removed + */ +enum slim_channel_state { +	SLIM_CH_STATE_DISCONNECTED = 0, +	SLIM_CH_STATE_ALLOCATED, +	SLIM_CH_STATE_ASSOCIATED, +	SLIM_CH_STATE_DEFINED, +	SLIM_CH_STATE_CONTENT_DEFINED, +	SLIM_CH_STATE_ACTIVE, +	SLIM_CH_STATE_REMOVED, +}; + +/** + * enum slim_ch_data_fmt: SLIMbus channel data Type identifiers according to + *	Table 60 of SLIMbus Spec 1.01.01 + * @SLIM_CH_DATA_FMT_NOT_DEFINED: Undefined + * @SLIM_CH_DATA_FMT_LPCM_AUDIO: LPCM audio + * @SLIM_CH_DATA_FMT_IEC61937_COMP_AUDIO: IEC61937 Compressed audio + * @SLIM_CH_DATA_FMT_PACKED_PDM_AUDIO: Packed PDM audio + */ +enum slim_ch_data_fmt { +	SLIM_CH_DATA_FMT_NOT_DEFINED = 0, +	SLIM_CH_DATA_FMT_LPCM_AUDIO = 1, +	SLIM_CH_DATA_FMT_IEC61937_COMP_AUDIO = 2, +	SLIM_CH_DATA_FMT_PACKED_PDM_AUDIO = 3, +}; + +/** + * enum slim_ch_aux_fmt: SLIMbus channel Aux Field format IDs according to + *	Table 63 of SLIMbus Spec 2.0 + * @SLIM_CH_AUX_FMT_NOT_APPLICABLE: Undefined + * @SLIM_CH_AUX_FMT_ZCUV_TUNNEL_IEC60958: ZCUV for tunneling IEC60958 + * @SLIM_CH_AUX_FMT_USER_DEFINED: User defined + */ +enum slim_ch_aux_bit_fmt { +	SLIM_CH_AUX_FMT_NOT_APPLICABLE = 0, +	SLIM_CH_AUX_FMT_ZCUV_TUNNEL_IEC60958 = 1, +	SLIM_CH_AUX_FMT_USER_DEFINED = 0xF, +}; + +/** + * struct slim_channel  - SLIMbus channel, used for state machine + * + * @id: ID of channel + * @prrate: Presense rate of channel from Table 66 of SLIMbus 2.0 Specs + * @seg_dist: segment distribution code from Table 20 of SLIMbus 2.0 Specs + * @data_fmt: Data format of channel. + * @aux_fmt: Aux format for this channel. + * @state: channel state machine + */ +struct slim_channel { +	int id; +	int prrate; +	int seg_dist; +	enum slim_ch_data_fmt data_fmt; +	enum slim_ch_aux_bit_fmt aux_fmt; +	enum slim_channel_state state; +}; + +/** + * struct slim_port  - SLIMbus port + * + * @id: Port id + * @direction: Port direction, Source or Sink. + * @state: state machine of port. + * @ch: channel associated with this port. + */ +struct slim_port { +	int id; +	enum slim_port_direction direction; +	enum slim_port_state state; +	struct slim_channel ch; +}; + +/** + * enum slim_transport_protocol: SLIMbus Transport protocol list from + *	Table 47 of SLIMbus 2.0 specs. + * @SLIM_PROTO_ISO: Isochronous Protocol, no flow control as data rate match + *		channel rate flow control embedded in the data. + * @SLIM_PROTO_PUSH: Pushed Protocol, includes flow control, Used to carry + *		data whose rate	is equal to, or lower than the channel rate. + * @SLIM_PROTO_PULL: Pulled Protocol, similar usage as pushed protocol + *		but pull is a unicast. + * @SLIM_PROTO_LOCKED: Locked Protocol + * @SLIM_PROTO_ASYNC_SMPLX: Asynchronous Protocol-Simplex + * @SLIM_PROTO_ASYNC_HALF_DUP: Asynchronous Protocol-Half-duplex + * @SLIM_PROTO_EXT_SMPLX: Extended Asynchronous Protocol-Simplex + * @SLIM_PROTO_EXT_HALF_DUP: Extended Asynchronous Protocol-Half-duplex + */ +enum slim_transport_protocol { +	SLIM_PROTO_ISO = 0, +	SLIM_PROTO_PUSH, +	SLIM_PROTO_PULL, +	SLIM_PROTO_LOCKED, +	SLIM_PROTO_ASYNC_SMPLX, +	SLIM_PROTO_ASYNC_HALF_DUP, +	SLIM_PROTO_EXT_SMPLX, +	SLIM_PROTO_EXT_HALF_DUP, +}; + +/** + * struct slim_stream_runtime  - SLIMbus stream runtime instance + * + * @dev: Name of the stream + * @dev: SLIM Device instance associated with this stream + * @state: state of stream + * @direction: direction of stream + * @prot: Transport protocol used in this stream + * @rate: Data rate of samples * + * @bps: bits per sample + * @ratem: rate multipler which is super frame rate/data rate + * @num_ports: number of ports + * @ports: pointer to instance of ports + * @node: list head for stream associated with slim device. + */ +struct slim_stream_runtime { +	const char *name; +	struct slim_device *dev; +	int direction; +	enum slim_transport_protocol prot; +	unsigned int rate; +	unsigned int bps; +	unsigned int ratem; +	int num_ports; +	struct slim_port *ports; +	struct list_head node; +}; + +/**   * struct slim_controller  - Controls every instance of SLIMbus   *				(similar to 'master' on SPI)   * @dev: Device interface to this driver @@ -196,6 +379,10 @@ struct slim_sched {   * @wakeup: This function pointer implements controller-specific procedure   *	to wake it up from clock-pause. Framework will call this to bring   *	the controller out of clock pause. + * @enable_stream: This function pointer implements controller-specific procedure + *	to enable a stream. + * @disable_stream: This function pointer implements controller-specific procedure + *	to disable stream.   *   *	'Manager device' is responsible for  device management, bandwidth   *	allocation, channel setup, and port associations per channel. @@ -237,6 +424,8 @@ struct slim_controller {  					     struct slim_eaddr *ea, u8 laddr);  	int			(*get_laddr)(struct slim_controller *ctrl,  					     struct slim_eaddr *ea, u8 *laddr); +	int		(*enable_stream)(struct slim_stream_runtime *rt); +	int		(*disable_stream)(struct slim_stream_runtime *rt);  	int			(*wakeup)(struct slim_controller *ctrl);  }; diff --git a/drivers/slimbus/stream.c b/drivers/slimbus/stream.c new file mode 100644 index 000000000000..2fa05324ed07 --- /dev/null +++ b/drivers/slimbus/stream.c @@ -0,0 +1,477 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018, Linaro Limited + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/slimbus.h> +#include <uapi/sound/asound.h> +#include "slimbus.h" + +/** + * struct segdist_code - Segment Distributions code from + *	Table 20 of SLIMbus Specs Version 2.0 + * + * @ratem: Channel Rate Multipler(Segments per Superframe) + * @seg_interval: Number of slots between the first Slot of Segment + *		and the first slot of the next  consecutive Segment. + * @segdist_code: Segment Distribution Code SD[11:0] + * @seg_offset_mask: Segment offset mask in SD[11:0] + * @segdist_codes: List of all possible Segmet Distribution codes. + */ +static const struct segdist_code { +	int ratem; +	int seg_interval; +	int segdist_code; +	u32 seg_offset_mask; + +} segdist_codes[] = { +	{1,	1536,	0x200,	 0xdff}, +	{2,	768,	0x100,	 0xcff}, +	{4,	384,	0x080,	 0xc7f}, +	{8,	192,	0x040,	 0xc3f}, +	{16,	96,	0x020,	 0xc1f}, +	{32,	48,	0x010,	 0xc0f}, +	{64,	24,	0x008,	 0xc07}, +	{128,	12,	0x004,	 0xc03}, +	{256,	6,	0x002,	 0xc01}, +	{512,	3,	0x001,	 0xc00}, +	{3,	512,	0xe00,	 0x1ff}, +	{6,	256,	0xd00,	 0x0ff}, +	{12,	128,	0xc80,	 0x07f}, +	{24,	64,	0xc40,	 0x03f}, +	{48,	32,	0xc20,	 0x01f}, +	{96,	16,	0xc10,	 0x00f}, +	{192,	8,	0xc08,	 0x007}, +	{364,	4,	0xc04,	 0x003}, +	{768,	2,	0xc02,	 0x001}, +}; + +/* + * Presence Rate table for all Natural Frequencies + * The Presence rate of a constant bitrate stream is mean flow rate of the + * stream expressed in occupied Segments of that Data Channel per second. + * Table 66 from SLIMbus 2.0 Specs + * + * Index of the table corresponds to Presence rate code for the respective rate + * in the table. + */ +static const int slim_presence_rate_table[] = { +	0, /* Not Indicated */ +	12000, +	24000, +	48000, +	96000, +	192000, +	384000, +	768000, +	0, /* Reserved */ +	110250, +	220500, +	441000, +	882000, +	176400, +	352800, +	705600, +	4000, +	8000, +	16000, +	32000, +	64000, +	128000, +	256000, +	512000, +}; + +/* + * slim_stream_allocate() - Allocate a new SLIMbus Stream + * @dev:Slim device to be associated with + * @name: name of the stream + * + * This is very first call for SLIMbus streaming, this API will allocate + * a new SLIMbus stream and return a valid stream runtime pointer for client + * to use it in subsequent stream apis. state of stream is set to ALLOCATED + * + * Return: valid pointer on success and error code on failure. + * From ASoC DPCM framework, this state is linked to startup() operation. + */ +struct slim_stream_runtime *slim_stream_allocate(struct slim_device *dev, +						 const char *name) +{ +	struct slim_stream_runtime *rt; + +	rt = kzalloc(sizeof(*rt), GFP_KERNEL); +	if (!rt) +		return ERR_PTR(-ENOMEM); + +	rt->name = kasprintf(GFP_KERNEL, "slim-%s", name); +	if (!rt->name) { +		kfree(rt); +		return ERR_PTR(-ENOMEM); +	} + +	rt->dev = dev; +	spin_lock(&dev->stream_list_lock); +	list_add_tail(&rt->node, &dev->stream_list); +	spin_unlock(&dev->stream_list_lock); + +	return rt; +} +EXPORT_SYMBOL_GPL(slim_stream_allocate); + +static int slim_connect_port_channel(struct slim_stream_runtime *stream, +				     struct slim_port *port) +{ +	struct slim_device *sdev = stream->dev; +	u8 wbuf[2]; +	struct slim_val_inf msg = {0, 2, NULL, wbuf, NULL}; +	u8 mc = SLIM_MSG_MC_CONNECT_SOURCE; +	DEFINE_SLIM_LDEST_TXN(txn, mc, 6, stream->dev->laddr, &msg); + +	if (port->direction == SLIM_PORT_SINK) +		txn.mc = SLIM_MSG_MC_CONNECT_SINK; + +	wbuf[0] = port->id; +	wbuf[1] = port->ch.id; +	port->ch.state = SLIM_CH_STATE_ASSOCIATED; +	port->state = SLIM_PORT_UNCONFIGURED; + +	return slim_do_transfer(sdev->ctrl, &txn); +} + +static int slim_disconnect_port(struct slim_stream_runtime *stream, +				struct slim_port *port) +{ +	struct slim_device *sdev = stream->dev; +	u8 wbuf[1]; +	struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL}; +	u8 mc = SLIM_MSG_MC_DISCONNECT_PORT; +	DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg); + +	wbuf[0] = port->id; +	port->ch.state = SLIM_CH_STATE_DISCONNECTED; +	port->state = SLIM_PORT_DISCONNECTED; + +	return slim_do_transfer(sdev->ctrl, &txn); +} + +static int slim_deactivate_remove_channel(struct slim_stream_runtime *stream, +					  struct slim_port *port) +{ +	struct slim_device *sdev = stream->dev; +	u8 wbuf[1]; +	struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL}; +	u8 mc = SLIM_MSG_MC_NEXT_DEACTIVATE_CHANNEL; +	DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg); +	int ret; + +	wbuf[0] = port->ch.id; +	ret = slim_do_transfer(sdev->ctrl, &txn); +	if (ret) +		return ret; + +	txn.mc = SLIM_MSG_MC_NEXT_REMOVE_CHANNEL; +	port->ch.state = SLIM_CH_STATE_REMOVED; + +	return slim_do_transfer(sdev->ctrl, &txn); +} + +static int slim_get_prate_code(int rate) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(slim_presence_rate_table); i++) { +		if (rate == slim_presence_rate_table[i]) +			return i; +	} + +	return -EINVAL; +} + +/* + * slim_stream_prepare() - Prepare a SLIMbus Stream + * + * @rt: instance of slim stream runtime to configure + * @cfg: new configuration for the stream + * + * This API will configure SLIMbus stream with config parameters from cfg. + * return zero on success and error code on failure. From ASoC DPCM framework, + * this state is linked to hw_params() operation. + */ +int slim_stream_prepare(struct slim_stream_runtime *rt, +			struct slim_stream_config *cfg) +{ +	struct slim_controller *ctrl = rt->dev->ctrl; +	struct slim_port *port; +	int num_ports, i, port_id; + +	if (rt->ports) { +		dev_err(&rt->dev->dev, "Stream already Prepared\n"); +		return -EINVAL; +	} + +	num_ports = hweight32(cfg->port_mask); +	rt->ports = kcalloc(num_ports, sizeof(*port), GFP_KERNEL); +	if (!rt->ports) +		return -ENOMEM; + +	rt->num_ports = num_ports; +	rt->rate = cfg->rate; +	rt->bps = cfg->bps; +	rt->direction = cfg->direction; + +	if (cfg->rate % ctrl->a_framer->superfreq) { +		/* +		 * data rate not exactly multiple of super frame, +		 * use PUSH/PULL protocol +		 */ +		if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK) +			rt->prot = SLIM_PROTO_PUSH; +		else +			rt->prot = SLIM_PROTO_PULL; +	} else { +		rt->prot = SLIM_PROTO_ISO; +	} + +	rt->ratem = cfg->rate/ctrl->a_framer->superfreq; + +	i = 0; +	for_each_set_bit(port_id, &cfg->port_mask, SLIM_DEVICE_MAX_PORTS) { +		port = &rt->ports[i]; +		port->state = SLIM_PORT_DISCONNECTED; +		port->id = port_id; +		port->ch.prrate = slim_get_prate_code(cfg->rate); +		port->ch.id = cfg->chs[i]; +		port->ch.data_fmt = SLIM_CH_DATA_FMT_NOT_DEFINED; +		port->ch.aux_fmt = SLIM_CH_AUX_FMT_NOT_APPLICABLE; +		port->ch.state = SLIM_CH_STATE_ALLOCATED; + +		if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK) +			port->direction = SLIM_PORT_SINK; +		else +			port->direction = SLIM_PORT_SOURCE; + +		slim_connect_port_channel(rt, port); +		i++; +	} + +	return 0; +} +EXPORT_SYMBOL_GPL(slim_stream_prepare); + +static int slim_define_channel_content(struct slim_stream_runtime *stream, +				       struct slim_port *port) +{ +	struct slim_device *sdev = stream->dev; +	u8 wbuf[4]; +	struct slim_val_inf msg = {0, 4, NULL, wbuf, NULL}; +	u8 mc = SLIM_MSG_MC_NEXT_DEFINE_CONTENT; +	DEFINE_SLIM_LDEST_TXN(txn, mc, 8, stream->dev->laddr, &msg); + +	wbuf[0] = port->ch.id; +	wbuf[1] = port->ch.prrate; + +	/* Frequency Locked for ISO Protocol */ +	if (stream->prot != SLIM_PROTO_ISO) +		wbuf[1] |= SLIM_CHANNEL_CONTENT_FL; + +	wbuf[2] = port->ch.data_fmt | (port->ch.aux_fmt << 4); +	wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS; +	port->ch.state = SLIM_CH_STATE_CONTENT_DEFINED; + +	return slim_do_transfer(sdev->ctrl, &txn); +} + +static int slim_get_segdist_code(int ratem) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(segdist_codes); i++) { +		if (segdist_codes[i].ratem == ratem) +			return segdist_codes[i].segdist_code; +	} + +	return -EINVAL; +} + +static int slim_define_channel(struct slim_stream_runtime *stream, +				       struct slim_port *port) +{ +	struct slim_device *sdev = stream->dev; +	u8 wbuf[4]; +	struct slim_val_inf msg = {0, 4, NULL, wbuf, NULL}; +	u8 mc = SLIM_MSG_MC_NEXT_DEFINE_CHANNEL; +	DEFINE_SLIM_LDEST_TXN(txn, mc, 8, stream->dev->laddr, &msg); + +	port->ch.seg_dist = slim_get_segdist_code(stream->ratem); + +	wbuf[0] = port->ch.id; +	wbuf[1] = port->ch.seg_dist & 0xFF; +	wbuf[2] = (stream->prot << 4) | ((port->ch.seg_dist & 0xF00) >> 8); +	if (stream->prot == SLIM_PROTO_ISO) +		wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS; +	else +		wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS + 1; + +	port->ch.state = SLIM_CH_STATE_DEFINED; + +	return slim_do_transfer(sdev->ctrl, &txn); +} + +static int slim_activate_channel(struct slim_stream_runtime *stream, +				 struct slim_port *port) +{ +	struct slim_device *sdev = stream->dev; +	u8 wbuf[1]; +	struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL}; +	u8 mc = SLIM_MSG_MC_NEXT_ACTIVATE_CHANNEL; +	DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg); + +	txn.msg->num_bytes = 1; +	txn.msg->wbuf = wbuf; +	wbuf[0] = port->ch.id; +	port->ch.state = SLIM_CH_STATE_ACTIVE; + +	return slim_do_transfer(sdev->ctrl, &txn); +} + +/* + * slim_stream_enable() - Enable a prepared SLIMbus Stream + * + * @stream: instance of slim stream runtime to enable + * + * This API will enable all the ports and channels associated with + * SLIMbus stream + * + * Return: zero on success and error code on failure. From ASoC DPCM framework, + * this state is linked to trigger() start operation. + */ +int slim_stream_enable(struct slim_stream_runtime *stream) +{ +	DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION, +				3, SLIM_LA_MANAGER, NULL); +	struct slim_controller *ctrl = stream->dev->ctrl; +	int ret, i; + +	if (ctrl->enable_stream) { +		ret = ctrl->enable_stream(stream); +		if (ret) +			return ret; + +		for (i = 0; i < stream->num_ports; i++) +			stream->ports[i].ch.state = SLIM_CH_STATE_ACTIVE; + +		return ret; +	} + +	ret = slim_do_transfer(ctrl, &txn); +	if (ret) +		return ret; + +	/* define channels first before activating them */ +	for (i = 0; i < stream->num_ports; i++) { +		struct slim_port *port = &stream->ports[i]; + +		slim_define_channel(stream, port); +		slim_define_channel_content(stream, port); +	} + +	for (i = 0; i < stream->num_ports; i++) { +		struct slim_port *port = &stream->ports[i]; + +		slim_activate_channel(stream, port); +		port->state = SLIM_PORT_CONFIGURED; +	} +	txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW; + +	return slim_do_transfer(ctrl, &txn); +} +EXPORT_SYMBOL_GPL(slim_stream_enable); + +/* + * slim_stream_disable() - Disable a SLIMbus Stream + * + * @stream: instance of slim stream runtime to disable + * + * This API will disable all the ports and channels associated with + * SLIMbus stream + * + * Return: zero on success and error code on failure. From ASoC DPCM framework, + * this state is linked to trigger() pause operation. + */ +int slim_stream_disable(struct slim_stream_runtime *stream) +{ +	DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION, +				3, SLIM_LA_MANAGER, NULL); +	struct slim_controller *ctrl = stream->dev->ctrl; +	int ret, i; + +	if (ctrl->disable_stream) +		ctrl->disable_stream(stream); + +	ret = slim_do_transfer(ctrl, &txn); +	if (ret) +		return ret; + +	for (i = 0; i < stream->num_ports; i++) +		slim_deactivate_remove_channel(stream, &stream->ports[i]); + +	txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW; + +	return slim_do_transfer(ctrl, &txn); +} +EXPORT_SYMBOL_GPL(slim_stream_disable); + +/* + * slim_stream_unprepare() - Un-prepare a SLIMbus Stream + * + * @stream: instance of slim stream runtime to unprepare + * + * This API will un allocate all the ports and channels associated with + * SLIMbus stream + * + * Return: zero on success and error code on failure. From ASoC DPCM framework, + * this state is linked to trigger() stop operation. + */ +int slim_stream_unprepare(struct slim_stream_runtime *stream) +{ +	int i; + +	for (i = 0; i < stream->num_ports; i++) +		slim_disconnect_port(stream, &stream->ports[i]); + +	kfree(stream->ports); +	stream->ports = NULL; +	stream->num_ports = 0; + +	return 0; +} +EXPORT_SYMBOL_GPL(slim_stream_unprepare); + +/* + * slim_stream_free() - Free a SLIMbus Stream + * + * @stream: instance of slim stream runtime to free + * + * This API will un allocate all the memory associated with + * slim stream runtime, user is not allowed to make an dereference + * to stream after this call. + * + * Return: zero on success and error code on failure. From ASoC DPCM framework, + * this state is linked to shutdown() operation. + */ +int slim_stream_free(struct slim_stream_runtime *stream) +{ +	struct slim_device *sdev = stream->dev; + +	spin_lock(&sdev->stream_list_lock); +	list_del(&stream->node); +	spin_unlock(&sdev->stream_list_lock); + +	kfree(stream->name); +	kfree(stream); + +	return 0; +} +EXPORT_SYMBOL_GPL(slim_stream_free); diff --git a/include/linux/slimbus.h b/include/linux/slimbus.h index 63801bcc5e60..12c9719b2a55 100644 --- a/include/linux/slimbus.h +++ b/include/linux/slimbus.h @@ -48,6 +48,8 @@ struct slim_controller;   * @ctrl: slim controller instance.   * @laddr: 1-byte Logical address of this device.   * @is_laddr_valid: indicates if the laddr is valid or not + * @stream_list: List of streams on this device + * @stream_list_lock: lock to protect the stream list   *   * This is the client/device handle returned when a SLIMbus   * device is registered with a controller. @@ -60,6 +62,8 @@ struct slim_device {  	enum slim_device_status	status;  	u8			laddr;  	bool			is_laddr_valid; +	struct list_head	stream_list; +	spinlock_t stream_list_lock;  };  #define to_slim_device(d) container_of(d, struct slim_device, dev) @@ -108,6 +112,36 @@ struct slim_val_inf {  	struct	completion	*comp;  }; +#define SLIM_DEVICE_MAX_CHANNELS	256 +/* A SLIMBus Device may have frmo 0 to 31 Ports (inclusive) */ +#define SLIM_DEVICE_MAX_PORTS		32 + +/** + * struct slim_stream_config - SLIMbus stream configuration + *	Configuring a stream is done at hw_params or prepare call + *	from audio drivers where they have all the required information + *	regarding rate, number of channels and so on. + *	There is a 1:1 mapping of channel and ports. + * + * @rate: data rate + * @bps: bits per data sample + * @ch_count: number of channels + * @chs: pointer to list of channel numbers + * @port_mask: port mask of ports to use for this stream + * @direction: direction of the stream, SNDRV_PCM_STREAM_PLAYBACK + *	or SNDRV_PCM_STREAM_CAPTURE. + */ +struct slim_stream_config { +	unsigned int rate; +	unsigned int bps; +	/* MAX 256 channels */ +	unsigned int ch_count; +	unsigned int *chs; +	/* Max 32 ports per device */ +	unsigned long port_mask; +	int direction; +}; +  /*   * use a macro to avoid include chaining to get THIS_MODULE   */ @@ -163,4 +197,16 @@ int slim_readb(struct slim_device *sdev, u32 addr);  int slim_writeb(struct slim_device *sdev, u32 addr, u8 value);  int slim_read(struct slim_device *sdev, u32 addr, size_t count, u8 *val);  int slim_write(struct slim_device *sdev, u32 addr, size_t count, u8 *val); + +/* SLIMbus Stream apis */ +struct slim_stream_runtime; +struct slim_stream_runtime *slim_stream_allocate(struct slim_device *dev, +						 const char *sname); +int slim_stream_prepare(struct slim_stream_runtime *stream, +			struct slim_stream_config *c); +int slim_stream_enable(struct slim_stream_runtime *stream); +int slim_stream_disable(struct slim_stream_runtime *stream); +int slim_stream_unprepare(struct slim_stream_runtime *stream); +int slim_stream_free(struct slim_stream_runtime *stream); +  #endif /* _LINUX_SLIMBUS_H */  | 
