162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci// Copyright (c) 2018, Linaro Limited
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/kernel.h>
562306a36Sopenharmony_ci#include <linux/errno.h>
662306a36Sopenharmony_ci#include <linux/slab.h>
762306a36Sopenharmony_ci#include <linux/list.h>
862306a36Sopenharmony_ci#include <linux/slimbus.h>
962306a36Sopenharmony_ci#include <uapi/sound/asound.h>
1062306a36Sopenharmony_ci#include "slimbus.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/**
1362306a36Sopenharmony_ci * struct segdist_code - Segment Distributions code from
1462306a36Sopenharmony_ci *	Table 20 of SLIMbus Specs Version 2.0
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * @ratem: Channel Rate Multipler(Segments per Superframe)
1762306a36Sopenharmony_ci * @seg_interval: Number of slots between the first Slot of Segment
1862306a36Sopenharmony_ci *		and the first slot of the next  consecutive Segment.
1962306a36Sopenharmony_ci * @segdist_code: Segment Distribution Code SD[11:0]
2062306a36Sopenharmony_ci * @seg_offset_mask: Segment offset mask in SD[11:0]
2162306a36Sopenharmony_ci * @segdist_codes: List of all possible Segmet Distribution codes.
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_cistatic const struct segdist_code {
2462306a36Sopenharmony_ci	int ratem;
2562306a36Sopenharmony_ci	int seg_interval;
2662306a36Sopenharmony_ci	int segdist_code;
2762306a36Sopenharmony_ci	u32 seg_offset_mask;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci} segdist_codes[] = {
3062306a36Sopenharmony_ci	{1,	1536,	0x200,	 0xdff},
3162306a36Sopenharmony_ci	{2,	768,	0x100,	 0xcff},
3262306a36Sopenharmony_ci	{4,	384,	0x080,	 0xc7f},
3362306a36Sopenharmony_ci	{8,	192,	0x040,	 0xc3f},
3462306a36Sopenharmony_ci	{16,	96,	0x020,	 0xc1f},
3562306a36Sopenharmony_ci	{32,	48,	0x010,	 0xc0f},
3662306a36Sopenharmony_ci	{64,	24,	0x008,	 0xc07},
3762306a36Sopenharmony_ci	{128,	12,	0x004,	 0xc03},
3862306a36Sopenharmony_ci	{256,	6,	0x002,	 0xc01},
3962306a36Sopenharmony_ci	{512,	3,	0x001,	 0xc00},
4062306a36Sopenharmony_ci	{3,	512,	0xe00,	 0x1ff},
4162306a36Sopenharmony_ci	{6,	256,	0xd00,	 0x0ff},
4262306a36Sopenharmony_ci	{12,	128,	0xc80,	 0x07f},
4362306a36Sopenharmony_ci	{24,	64,	0xc40,	 0x03f},
4462306a36Sopenharmony_ci	{48,	32,	0xc20,	 0x01f},
4562306a36Sopenharmony_ci	{96,	16,	0xc10,	 0x00f},
4662306a36Sopenharmony_ci	{192,	8,	0xc08,	 0x007},
4762306a36Sopenharmony_ci	{364,	4,	0xc04,	 0x003},
4862306a36Sopenharmony_ci	{768,	2,	0xc02,	 0x001},
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/*
5262306a36Sopenharmony_ci * Presence Rate table for all Natural Frequencies
5362306a36Sopenharmony_ci * The Presence rate of a constant bitrate stream is mean flow rate of the
5462306a36Sopenharmony_ci * stream expressed in occupied Segments of that Data Channel per second.
5562306a36Sopenharmony_ci * Table 66 from SLIMbus 2.0 Specs
5662306a36Sopenharmony_ci *
5762306a36Sopenharmony_ci * Index of the table corresponds to Presence rate code for the respective rate
5862306a36Sopenharmony_ci * in the table.
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_cistatic const int slim_presence_rate_table[] = {
6162306a36Sopenharmony_ci	0, /* Not Indicated */
6262306a36Sopenharmony_ci	12000,
6362306a36Sopenharmony_ci	24000,
6462306a36Sopenharmony_ci	48000,
6562306a36Sopenharmony_ci	96000,
6662306a36Sopenharmony_ci	192000,
6762306a36Sopenharmony_ci	384000,
6862306a36Sopenharmony_ci	768000,
6962306a36Sopenharmony_ci	0, /* Reserved */
7062306a36Sopenharmony_ci	11025,
7162306a36Sopenharmony_ci	22050,
7262306a36Sopenharmony_ci	44100,
7362306a36Sopenharmony_ci	88200,
7462306a36Sopenharmony_ci	176400,
7562306a36Sopenharmony_ci	352800,
7662306a36Sopenharmony_ci	705600,
7762306a36Sopenharmony_ci	4000,
7862306a36Sopenharmony_ci	8000,
7962306a36Sopenharmony_ci	16000,
8062306a36Sopenharmony_ci	32000,
8162306a36Sopenharmony_ci	64000,
8262306a36Sopenharmony_ci	128000,
8362306a36Sopenharmony_ci	256000,
8462306a36Sopenharmony_ci	512000,
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/**
8862306a36Sopenharmony_ci * slim_stream_allocate() - Allocate a new SLIMbus Stream
8962306a36Sopenharmony_ci * @dev:Slim device to be associated with
9062306a36Sopenharmony_ci * @name: name of the stream
9162306a36Sopenharmony_ci *
9262306a36Sopenharmony_ci * This is very first call for SLIMbus streaming, this API will allocate
9362306a36Sopenharmony_ci * a new SLIMbus stream and return a valid stream runtime pointer for client
9462306a36Sopenharmony_ci * to use it in subsequent stream apis. state of stream is set to ALLOCATED
9562306a36Sopenharmony_ci *
9662306a36Sopenharmony_ci * Return: valid pointer on success and error code on failure.
9762306a36Sopenharmony_ci * From ASoC DPCM framework, this state is linked to startup() operation.
9862306a36Sopenharmony_ci */
9962306a36Sopenharmony_cistruct slim_stream_runtime *slim_stream_allocate(struct slim_device *dev,
10062306a36Sopenharmony_ci						 const char *name)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct slim_stream_runtime *rt;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	rt = kzalloc(sizeof(*rt), GFP_KERNEL);
10562306a36Sopenharmony_ci	if (!rt)
10662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	rt->name = kasprintf(GFP_KERNEL, "slim-%s", name);
10962306a36Sopenharmony_ci	if (!rt->name) {
11062306a36Sopenharmony_ci		kfree(rt);
11162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	rt->dev = dev;
11562306a36Sopenharmony_ci	spin_lock(&dev->stream_list_lock);
11662306a36Sopenharmony_ci	list_add_tail(&rt->node, &dev->stream_list);
11762306a36Sopenharmony_ci	spin_unlock(&dev->stream_list_lock);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return rt;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_stream_allocate);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic int slim_connect_port_channel(struct slim_stream_runtime *stream,
12462306a36Sopenharmony_ci				     struct slim_port *port)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct slim_device *sdev = stream->dev;
12762306a36Sopenharmony_ci	u8 wbuf[2];
12862306a36Sopenharmony_ci	struct slim_val_inf msg = {0, 2, NULL, wbuf, NULL};
12962306a36Sopenharmony_ci	u8 mc = SLIM_MSG_MC_CONNECT_SOURCE;
13062306a36Sopenharmony_ci	DEFINE_SLIM_LDEST_TXN(txn, mc, 6, stream->dev->laddr, &msg);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (port->direction == SLIM_PORT_SINK)
13362306a36Sopenharmony_ci		txn.mc = SLIM_MSG_MC_CONNECT_SINK;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	wbuf[0] = port->id;
13662306a36Sopenharmony_ci	wbuf[1] = port->ch.id;
13762306a36Sopenharmony_ci	port->ch.state = SLIM_CH_STATE_ASSOCIATED;
13862306a36Sopenharmony_ci	port->state = SLIM_PORT_UNCONFIGURED;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return slim_do_transfer(sdev->ctrl, &txn);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic int slim_disconnect_port(struct slim_stream_runtime *stream,
14462306a36Sopenharmony_ci				struct slim_port *port)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct slim_device *sdev = stream->dev;
14762306a36Sopenharmony_ci	u8 wbuf[1];
14862306a36Sopenharmony_ci	struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL};
14962306a36Sopenharmony_ci	u8 mc = SLIM_MSG_MC_DISCONNECT_PORT;
15062306a36Sopenharmony_ci	DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	wbuf[0] = port->id;
15362306a36Sopenharmony_ci	port->ch.state = SLIM_CH_STATE_DISCONNECTED;
15462306a36Sopenharmony_ci	port->state = SLIM_PORT_DISCONNECTED;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	return slim_do_transfer(sdev->ctrl, &txn);
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic int slim_deactivate_remove_channel(struct slim_stream_runtime *stream,
16062306a36Sopenharmony_ci					  struct slim_port *port)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct slim_device *sdev = stream->dev;
16362306a36Sopenharmony_ci	u8 wbuf[1];
16462306a36Sopenharmony_ci	struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL};
16562306a36Sopenharmony_ci	u8 mc = SLIM_MSG_MC_NEXT_DEACTIVATE_CHANNEL;
16662306a36Sopenharmony_ci	DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg);
16762306a36Sopenharmony_ci	int ret;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	wbuf[0] = port->ch.id;
17062306a36Sopenharmony_ci	ret = slim_do_transfer(sdev->ctrl, &txn);
17162306a36Sopenharmony_ci	if (ret)
17262306a36Sopenharmony_ci		return ret;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	txn.mc = SLIM_MSG_MC_NEXT_REMOVE_CHANNEL;
17562306a36Sopenharmony_ci	port->ch.state = SLIM_CH_STATE_REMOVED;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return slim_do_transfer(sdev->ctrl, &txn);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int slim_get_prate_code(int rate)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	int i;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(slim_presence_rate_table); i++) {
18562306a36Sopenharmony_ci		if (rate == slim_presence_rate_table[i])
18662306a36Sopenharmony_ci			return i;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return -EINVAL;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci/**
19362306a36Sopenharmony_ci * slim_stream_prepare() - Prepare a SLIMbus Stream
19462306a36Sopenharmony_ci *
19562306a36Sopenharmony_ci * @rt: instance of slim stream runtime to configure
19662306a36Sopenharmony_ci * @cfg: new configuration for the stream
19762306a36Sopenharmony_ci *
19862306a36Sopenharmony_ci * This API will configure SLIMbus stream with config parameters from cfg.
19962306a36Sopenharmony_ci * return zero on success and error code on failure. From ASoC DPCM framework,
20062306a36Sopenharmony_ci * this state is linked to hw_params() operation.
20162306a36Sopenharmony_ci */
20262306a36Sopenharmony_ciint slim_stream_prepare(struct slim_stream_runtime *rt,
20362306a36Sopenharmony_ci			struct slim_stream_config *cfg)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct slim_controller *ctrl = rt->dev->ctrl;
20662306a36Sopenharmony_ci	struct slim_port *port;
20762306a36Sopenharmony_ci	int num_ports, i, port_id, prrate;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (rt->ports) {
21062306a36Sopenharmony_ci		dev_err(&rt->dev->dev, "Stream already Prepared\n");
21162306a36Sopenharmony_ci		return -EINVAL;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	num_ports = hweight32(cfg->port_mask);
21562306a36Sopenharmony_ci	rt->ports = kcalloc(num_ports, sizeof(*port), GFP_KERNEL);
21662306a36Sopenharmony_ci	if (!rt->ports)
21762306a36Sopenharmony_ci		return -ENOMEM;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	rt->num_ports = num_ports;
22062306a36Sopenharmony_ci	rt->rate = cfg->rate;
22162306a36Sopenharmony_ci	rt->bps = cfg->bps;
22262306a36Sopenharmony_ci	rt->direction = cfg->direction;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	prrate = slim_get_prate_code(cfg->rate);
22562306a36Sopenharmony_ci	if (prrate < 0) {
22662306a36Sopenharmony_ci		dev_err(&rt->dev->dev, "Cannot get presence rate for rate %d Hz\n",
22762306a36Sopenharmony_ci			cfg->rate);
22862306a36Sopenharmony_ci		return prrate;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	if (cfg->rate % ctrl->a_framer->superfreq) {
23262306a36Sopenharmony_ci		/*
23362306a36Sopenharmony_ci		 * data rate not exactly multiple of super frame,
23462306a36Sopenharmony_ci		 * use PUSH/PULL protocol
23562306a36Sopenharmony_ci		 */
23662306a36Sopenharmony_ci		if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK)
23762306a36Sopenharmony_ci			rt->prot = SLIM_PROTO_PUSH;
23862306a36Sopenharmony_ci		else
23962306a36Sopenharmony_ci			rt->prot = SLIM_PROTO_PULL;
24062306a36Sopenharmony_ci	} else {
24162306a36Sopenharmony_ci		rt->prot = SLIM_PROTO_ISO;
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	rt->ratem = cfg->rate/ctrl->a_framer->superfreq;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	i = 0;
24762306a36Sopenharmony_ci	for_each_set_bit(port_id, &cfg->port_mask, SLIM_DEVICE_MAX_PORTS) {
24862306a36Sopenharmony_ci		port = &rt->ports[i];
24962306a36Sopenharmony_ci		port->state = SLIM_PORT_DISCONNECTED;
25062306a36Sopenharmony_ci		port->id = port_id;
25162306a36Sopenharmony_ci		port->ch.prrate = prrate;
25262306a36Sopenharmony_ci		port->ch.id = cfg->chs[i];
25362306a36Sopenharmony_ci		port->ch.data_fmt = SLIM_CH_DATA_FMT_NOT_DEFINED;
25462306a36Sopenharmony_ci		port->ch.aux_fmt = SLIM_CH_AUX_FMT_NOT_APPLICABLE;
25562306a36Sopenharmony_ci		port->ch.state = SLIM_CH_STATE_ALLOCATED;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK)
25862306a36Sopenharmony_ci			port->direction = SLIM_PORT_SINK;
25962306a36Sopenharmony_ci		else
26062306a36Sopenharmony_ci			port->direction = SLIM_PORT_SOURCE;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		slim_connect_port_channel(rt, port);
26362306a36Sopenharmony_ci		i++;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	return 0;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_stream_prepare);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic int slim_define_channel_content(struct slim_stream_runtime *stream,
27162306a36Sopenharmony_ci				       struct slim_port *port)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct slim_device *sdev = stream->dev;
27462306a36Sopenharmony_ci	u8 wbuf[4];
27562306a36Sopenharmony_ci	struct slim_val_inf msg = {0, 4, NULL, wbuf, NULL};
27662306a36Sopenharmony_ci	u8 mc = SLIM_MSG_MC_NEXT_DEFINE_CONTENT;
27762306a36Sopenharmony_ci	DEFINE_SLIM_LDEST_TXN(txn, mc, 8, stream->dev->laddr, &msg);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	wbuf[0] = port->ch.id;
28062306a36Sopenharmony_ci	wbuf[1] = port->ch.prrate;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/* Frequency Locked for ISO Protocol */
28362306a36Sopenharmony_ci	if (stream->prot != SLIM_PROTO_ISO)
28462306a36Sopenharmony_ci		wbuf[1] |= SLIM_CHANNEL_CONTENT_FL;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	wbuf[2] = port->ch.data_fmt | (port->ch.aux_fmt << 4);
28762306a36Sopenharmony_ci	wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS;
28862306a36Sopenharmony_ci	port->ch.state = SLIM_CH_STATE_CONTENT_DEFINED;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return slim_do_transfer(sdev->ctrl, &txn);
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic int slim_get_segdist_code(int ratem)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	int i;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(segdist_codes); i++) {
29862306a36Sopenharmony_ci		if (segdist_codes[i].ratem == ratem)
29962306a36Sopenharmony_ci			return segdist_codes[i].segdist_code;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return -EINVAL;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic int slim_define_channel(struct slim_stream_runtime *stream,
30662306a36Sopenharmony_ci				       struct slim_port *port)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct slim_device *sdev = stream->dev;
30962306a36Sopenharmony_ci	u8 wbuf[4];
31062306a36Sopenharmony_ci	struct slim_val_inf msg = {0, 4, NULL, wbuf, NULL};
31162306a36Sopenharmony_ci	u8 mc = SLIM_MSG_MC_NEXT_DEFINE_CHANNEL;
31262306a36Sopenharmony_ci	DEFINE_SLIM_LDEST_TXN(txn, mc, 8, stream->dev->laddr, &msg);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	port->ch.seg_dist = slim_get_segdist_code(stream->ratem);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	wbuf[0] = port->ch.id;
31762306a36Sopenharmony_ci	wbuf[1] = port->ch.seg_dist & 0xFF;
31862306a36Sopenharmony_ci	wbuf[2] = (stream->prot << 4) | ((port->ch.seg_dist & 0xF00) >> 8);
31962306a36Sopenharmony_ci	if (stream->prot == SLIM_PROTO_ISO)
32062306a36Sopenharmony_ci		wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS;
32162306a36Sopenharmony_ci	else
32262306a36Sopenharmony_ci		wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS + 1;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	port->ch.state = SLIM_CH_STATE_DEFINED;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	return slim_do_transfer(sdev->ctrl, &txn);
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic int slim_activate_channel(struct slim_stream_runtime *stream,
33062306a36Sopenharmony_ci				 struct slim_port *port)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct slim_device *sdev = stream->dev;
33362306a36Sopenharmony_ci	u8 wbuf[1];
33462306a36Sopenharmony_ci	struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL};
33562306a36Sopenharmony_ci	u8 mc = SLIM_MSG_MC_NEXT_ACTIVATE_CHANNEL;
33662306a36Sopenharmony_ci	DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	txn.msg->num_bytes = 1;
33962306a36Sopenharmony_ci	txn.msg->wbuf = wbuf;
34062306a36Sopenharmony_ci	wbuf[0] = port->ch.id;
34162306a36Sopenharmony_ci	port->ch.state = SLIM_CH_STATE_ACTIVE;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	return slim_do_transfer(sdev->ctrl, &txn);
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci/**
34762306a36Sopenharmony_ci * slim_stream_enable() - Enable a prepared SLIMbus Stream
34862306a36Sopenharmony_ci *
34962306a36Sopenharmony_ci * @stream: instance of slim stream runtime to enable
35062306a36Sopenharmony_ci *
35162306a36Sopenharmony_ci * This API will enable all the ports and channels associated with
35262306a36Sopenharmony_ci * SLIMbus stream
35362306a36Sopenharmony_ci *
35462306a36Sopenharmony_ci * Return: zero on success and error code on failure. From ASoC DPCM framework,
35562306a36Sopenharmony_ci * this state is linked to trigger() start operation.
35662306a36Sopenharmony_ci */
35762306a36Sopenharmony_ciint slim_stream_enable(struct slim_stream_runtime *stream)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION,
36062306a36Sopenharmony_ci				3, SLIM_LA_MANAGER, NULL);
36162306a36Sopenharmony_ci	struct slim_controller *ctrl = stream->dev->ctrl;
36262306a36Sopenharmony_ci	int ret, i;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (ctrl->enable_stream) {
36562306a36Sopenharmony_ci		ret = ctrl->enable_stream(stream);
36662306a36Sopenharmony_ci		if (ret)
36762306a36Sopenharmony_ci			return ret;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci		for (i = 0; i < stream->num_ports; i++)
37062306a36Sopenharmony_ci			stream->ports[i].ch.state = SLIM_CH_STATE_ACTIVE;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		return ret;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	ret = slim_do_transfer(ctrl, &txn);
37662306a36Sopenharmony_ci	if (ret)
37762306a36Sopenharmony_ci		return ret;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	/* define channels first before activating them */
38062306a36Sopenharmony_ci	for (i = 0; i < stream->num_ports; i++) {
38162306a36Sopenharmony_ci		struct slim_port *port = &stream->ports[i];
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		slim_define_channel(stream, port);
38462306a36Sopenharmony_ci		slim_define_channel_content(stream, port);
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	for (i = 0; i < stream->num_ports; i++) {
38862306a36Sopenharmony_ci		struct slim_port *port = &stream->ports[i];
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		slim_activate_channel(stream, port);
39162306a36Sopenharmony_ci		port->state = SLIM_PORT_CONFIGURED;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci	txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	return slim_do_transfer(ctrl, &txn);
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_stream_enable);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci/**
40062306a36Sopenharmony_ci * slim_stream_disable() - Disable a SLIMbus Stream
40162306a36Sopenharmony_ci *
40262306a36Sopenharmony_ci * @stream: instance of slim stream runtime to disable
40362306a36Sopenharmony_ci *
40462306a36Sopenharmony_ci * This API will disable all the ports and channels associated with
40562306a36Sopenharmony_ci * SLIMbus stream
40662306a36Sopenharmony_ci *
40762306a36Sopenharmony_ci * Return: zero on success and error code on failure. From ASoC DPCM framework,
40862306a36Sopenharmony_ci * this state is linked to trigger() pause operation.
40962306a36Sopenharmony_ci */
41062306a36Sopenharmony_ciint slim_stream_disable(struct slim_stream_runtime *stream)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION,
41362306a36Sopenharmony_ci				3, SLIM_LA_MANAGER, NULL);
41462306a36Sopenharmony_ci	struct slim_controller *ctrl = stream->dev->ctrl;
41562306a36Sopenharmony_ci	int ret, i;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	if (!stream->ports || !stream->num_ports)
41862306a36Sopenharmony_ci		return -EINVAL;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (ctrl->disable_stream)
42162306a36Sopenharmony_ci		ctrl->disable_stream(stream);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	ret = slim_do_transfer(ctrl, &txn);
42462306a36Sopenharmony_ci	if (ret)
42562306a36Sopenharmony_ci		return ret;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	for (i = 0; i < stream->num_ports; i++)
42862306a36Sopenharmony_ci		slim_deactivate_remove_channel(stream, &stream->ports[i]);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	return slim_do_transfer(ctrl, &txn);
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_stream_disable);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci/**
43762306a36Sopenharmony_ci * slim_stream_unprepare() - Un-prepare a SLIMbus Stream
43862306a36Sopenharmony_ci *
43962306a36Sopenharmony_ci * @stream: instance of slim stream runtime to unprepare
44062306a36Sopenharmony_ci *
44162306a36Sopenharmony_ci * This API will un allocate all the ports and channels associated with
44262306a36Sopenharmony_ci * SLIMbus stream
44362306a36Sopenharmony_ci *
44462306a36Sopenharmony_ci * Return: zero on success and error code on failure. From ASoC DPCM framework,
44562306a36Sopenharmony_ci * this state is linked to trigger() stop operation.
44662306a36Sopenharmony_ci */
44762306a36Sopenharmony_ciint slim_stream_unprepare(struct slim_stream_runtime *stream)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	int i;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	if (!stream->ports || !stream->num_ports)
45262306a36Sopenharmony_ci		return -EINVAL;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	for (i = 0; i < stream->num_ports; i++)
45562306a36Sopenharmony_ci		slim_disconnect_port(stream, &stream->ports[i]);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	kfree(stream->ports);
45862306a36Sopenharmony_ci	stream->ports = NULL;
45962306a36Sopenharmony_ci	stream->num_ports = 0;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	return 0;
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_stream_unprepare);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci/**
46662306a36Sopenharmony_ci * slim_stream_free() - Free a SLIMbus Stream
46762306a36Sopenharmony_ci *
46862306a36Sopenharmony_ci * @stream: instance of slim stream runtime to free
46962306a36Sopenharmony_ci *
47062306a36Sopenharmony_ci * This API will un allocate all the memory associated with
47162306a36Sopenharmony_ci * slim stream runtime, user is not allowed to make an dereference
47262306a36Sopenharmony_ci * to stream after this call.
47362306a36Sopenharmony_ci *
47462306a36Sopenharmony_ci * Return: zero on success and error code on failure. From ASoC DPCM framework,
47562306a36Sopenharmony_ci * this state is linked to shutdown() operation.
47662306a36Sopenharmony_ci */
47762306a36Sopenharmony_ciint slim_stream_free(struct slim_stream_runtime *stream)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	struct slim_device *sdev = stream->dev;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	spin_lock(&sdev->stream_list_lock);
48262306a36Sopenharmony_ci	list_del(&stream->node);
48362306a36Sopenharmony_ci	spin_unlock(&sdev->stream_list_lock);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	kfree(stream->name);
48662306a36Sopenharmony_ci	kfree(stream);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	return 0;
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_stream_free);
491