Linux Audio

Check our new training course

Loading...
v6.8
  1// SPDX-License-Identifier: GPL-2.0
  2// Copyright (c) 2018, Linaro Limited
  3
  4#include <linux/kernel.h>
  5#include <linux/errno.h>
  6#include <linux/slab.h>
  7#include <linux/list.h>
  8#include <linux/slimbus.h>
  9#include <uapi/sound/asound.h>
 10#include "slimbus.h"
 11
 12/**
 13 * struct segdist_code - Segment Distributions code from
 14 *	Table 20 of SLIMbus Specs Version 2.0
 15 *
 16 * @ratem: Channel Rate Multipler(Segments per Superframe)
 17 * @seg_interval: Number of slots between the first Slot of Segment
 18 *		and the first slot of the next  consecutive Segment.
 19 * @segdist_code: Segment Distribution Code SD[11:0]
 20 * @seg_offset_mask: Segment offset mask in SD[11:0]
 21 * @segdist_codes: List of all possible Segmet Distribution codes.
 22 */
 23static const struct segdist_code {
 24	int ratem;
 25	int seg_interval;
 26	int segdist_code;
 27	u32 seg_offset_mask;
 28
 29} segdist_codes[] = {
 30	{1,	1536,	0x200,	 0xdff},
 31	{2,	768,	0x100,	 0xcff},
 32	{4,	384,	0x080,	 0xc7f},
 33	{8,	192,	0x040,	 0xc3f},
 34	{16,	96,	0x020,	 0xc1f},
 35	{32,	48,	0x010,	 0xc0f},
 36	{64,	24,	0x008,	 0xc07},
 37	{128,	12,	0x004,	 0xc03},
 38	{256,	6,	0x002,	 0xc01},
 39	{512,	3,	0x001,	 0xc00},
 40	{3,	512,	0xe00,	 0x1ff},
 41	{6,	256,	0xd00,	 0x0ff},
 42	{12,	128,	0xc80,	 0x07f},
 43	{24,	64,	0xc40,	 0x03f},
 44	{48,	32,	0xc20,	 0x01f},
 45	{96,	16,	0xc10,	 0x00f},
 46	{192,	8,	0xc08,	 0x007},
 47	{364,	4,	0xc04,	 0x003},
 48	{768,	2,	0xc02,	 0x001},
 49};
 50
 51/*
 52 * Presence Rate table for all Natural Frequencies
 53 * The Presence rate of a constant bitrate stream is mean flow rate of the
 54 * stream expressed in occupied Segments of that Data Channel per second.
 55 * Table 66 from SLIMbus 2.0 Specs
 56 *
 57 * Index of the table corresponds to Presence rate code for the respective rate
 58 * in the table.
 59 */
 60static const int slim_presence_rate_table[] = {
 61	0, /* Not Indicated */
 62	12000,
 63	24000,
 64	48000,
 65	96000,
 66	192000,
 67	384000,
 68	768000,
 69	0, /* Reserved */
 70	11025,
 71	22050,
 72	44100,
 73	88200,
 74	176400,
 75	352800,
 76	705600,
 77	4000,
 78	8000,
 79	16000,
 80	32000,
 81	64000,
 82	128000,
 83	256000,
 84	512000,
 85};
 86
 87/**
 88 * slim_stream_allocate() - Allocate a new SLIMbus Stream
 89 * @dev:Slim device to be associated with
 90 * @name: name of the stream
 91 *
 92 * This is very first call for SLIMbus streaming, this API will allocate
 93 * a new SLIMbus stream and return a valid stream runtime pointer for client
 94 * to use it in subsequent stream apis. state of stream is set to ALLOCATED
 95 *
 96 * Return: valid pointer on success and error code on failure.
 97 * From ASoC DPCM framework, this state is linked to startup() operation.
 98 */
 99struct slim_stream_runtime *slim_stream_allocate(struct slim_device *dev,
100						 const char *name)
101{
102	struct slim_stream_runtime *rt;
103
104	rt = kzalloc(sizeof(*rt), GFP_KERNEL);
105	if (!rt)
106		return ERR_PTR(-ENOMEM);
107
108	rt->name = kasprintf(GFP_KERNEL, "slim-%s", name);
109	if (!rt->name) {
110		kfree(rt);
111		return ERR_PTR(-ENOMEM);
112	}
113
114	rt->dev = dev;
115	spin_lock(&dev->stream_list_lock);
116	list_add_tail(&rt->node, &dev->stream_list);
117	spin_unlock(&dev->stream_list_lock);
118
119	return rt;
120}
121EXPORT_SYMBOL_GPL(slim_stream_allocate);
122
123static int slim_connect_port_channel(struct slim_stream_runtime *stream,
124				     struct slim_port *port)
125{
126	struct slim_device *sdev = stream->dev;
127	u8 wbuf[2];
128	struct slim_val_inf msg = {0, 2, NULL, wbuf, NULL};
129	u8 mc = SLIM_MSG_MC_CONNECT_SOURCE;
130	DEFINE_SLIM_LDEST_TXN(txn, mc, 6, stream->dev->laddr, &msg);
131
132	if (port->direction == SLIM_PORT_SINK)
133		txn.mc = SLIM_MSG_MC_CONNECT_SINK;
134
135	wbuf[0] = port->id;
136	wbuf[1] = port->ch.id;
137	port->ch.state = SLIM_CH_STATE_ASSOCIATED;
138	port->state = SLIM_PORT_UNCONFIGURED;
139
140	return slim_do_transfer(sdev->ctrl, &txn);
141}
142
143static int slim_disconnect_port(struct slim_stream_runtime *stream,
144				struct slim_port *port)
145{
146	struct slim_device *sdev = stream->dev;
147	u8 wbuf[1];
148	struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL};
149	u8 mc = SLIM_MSG_MC_DISCONNECT_PORT;
150	DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg);
151
152	wbuf[0] = port->id;
153	port->ch.state = SLIM_CH_STATE_DISCONNECTED;
154	port->state = SLIM_PORT_DISCONNECTED;
155
156	return slim_do_transfer(sdev->ctrl, &txn);
157}
158
159static int slim_deactivate_remove_channel(struct slim_stream_runtime *stream,
160					  struct slim_port *port)
161{
162	struct slim_device *sdev = stream->dev;
163	u8 wbuf[1];
164	struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL};
165	u8 mc = SLIM_MSG_MC_NEXT_DEACTIVATE_CHANNEL;
166	DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg);
167	int ret;
168
169	wbuf[0] = port->ch.id;
170	ret = slim_do_transfer(sdev->ctrl, &txn);
171	if (ret)
172		return ret;
173
174	txn.mc = SLIM_MSG_MC_NEXT_REMOVE_CHANNEL;
175	port->ch.state = SLIM_CH_STATE_REMOVED;
176
177	return slim_do_transfer(sdev->ctrl, &txn);
178}
179
180static int slim_get_prate_code(int rate)
181{
182	int i;
183
184	for (i = 0; i < ARRAY_SIZE(slim_presence_rate_table); i++) {
185		if (rate == slim_presence_rate_table[i])
186			return i;
187	}
188
189	return -EINVAL;
190}
191
192/**
193 * slim_stream_prepare() - Prepare a SLIMbus Stream
194 *
195 * @rt: instance of slim stream runtime to configure
196 * @cfg: new configuration for the stream
197 *
198 * This API will configure SLIMbus stream with config parameters from cfg.
199 * return zero on success and error code on failure. From ASoC DPCM framework,
200 * this state is linked to hw_params() operation.
201 */
202int slim_stream_prepare(struct slim_stream_runtime *rt,
203			struct slim_stream_config *cfg)
204{
205	struct slim_controller *ctrl = rt->dev->ctrl;
206	struct slim_port *port;
207	int num_ports, i, port_id, prrate;
208
209	if (rt->ports) {
210		dev_err(&rt->dev->dev, "Stream already Prepared\n");
211		return -EINVAL;
212	}
213
214	num_ports = hweight32(cfg->port_mask);
215	rt->ports = kcalloc(num_ports, sizeof(*port), GFP_KERNEL);
216	if (!rt->ports)
217		return -ENOMEM;
218
219	rt->num_ports = num_ports;
220	rt->rate = cfg->rate;
221	rt->bps = cfg->bps;
222	rt->direction = cfg->direction;
223
224	prrate = slim_get_prate_code(cfg->rate);
225	if (prrate < 0) {
226		dev_err(&rt->dev->dev, "Cannot get presence rate for rate %d Hz\n",
227			cfg->rate);
228		return prrate;
229	}
230
231	if (cfg->rate % ctrl->a_framer->superfreq) {
232		/*
233		 * data rate not exactly multiple of super frame,
234		 * use PUSH/PULL protocol
235		 */
236		if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK)
237			rt->prot = SLIM_PROTO_PUSH;
238		else
239			rt->prot = SLIM_PROTO_PULL;
240	} else {
241		rt->prot = SLIM_PROTO_ISO;
242	}
243
244	rt->ratem = cfg->rate/ctrl->a_framer->superfreq;
245
246	i = 0;
247	for_each_set_bit(port_id, &cfg->port_mask, SLIM_DEVICE_MAX_PORTS) {
248		port = &rt->ports[i];
249		port->state = SLIM_PORT_DISCONNECTED;
250		port->id = port_id;
251		port->ch.prrate = prrate;
252		port->ch.id = cfg->chs[i];
253		port->ch.data_fmt = SLIM_CH_DATA_FMT_NOT_DEFINED;
254		port->ch.aux_fmt = SLIM_CH_AUX_FMT_NOT_APPLICABLE;
255		port->ch.state = SLIM_CH_STATE_ALLOCATED;
256
257		if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK)
258			port->direction = SLIM_PORT_SINK;
259		else
260			port->direction = SLIM_PORT_SOURCE;
261
262		slim_connect_port_channel(rt, port);
263		i++;
264	}
265
266	return 0;
267}
268EXPORT_SYMBOL_GPL(slim_stream_prepare);
269
270static int slim_define_channel_content(struct slim_stream_runtime *stream,
271				       struct slim_port *port)
272{
273	struct slim_device *sdev = stream->dev;
274	u8 wbuf[4];
275	struct slim_val_inf msg = {0, 4, NULL, wbuf, NULL};
276	u8 mc = SLIM_MSG_MC_NEXT_DEFINE_CONTENT;
277	DEFINE_SLIM_LDEST_TXN(txn, mc, 8, stream->dev->laddr, &msg);
278
279	wbuf[0] = port->ch.id;
280	wbuf[1] = port->ch.prrate;
281
282	/* Frequency Locked for ISO Protocol */
283	if (stream->prot != SLIM_PROTO_ISO)
284		wbuf[1] |= SLIM_CHANNEL_CONTENT_FL;
285
286	wbuf[2] = port->ch.data_fmt | (port->ch.aux_fmt << 4);
287	wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS;
288	port->ch.state = SLIM_CH_STATE_CONTENT_DEFINED;
289
290	return slim_do_transfer(sdev->ctrl, &txn);
291}
292
293static int slim_get_segdist_code(int ratem)
294{
295	int i;
296
297	for (i = 0; i < ARRAY_SIZE(segdist_codes); i++) {
298		if (segdist_codes[i].ratem == ratem)
299			return segdist_codes[i].segdist_code;
300	}
301
302	return -EINVAL;
303}
304
305static int slim_define_channel(struct slim_stream_runtime *stream,
306				       struct slim_port *port)
307{
308	struct slim_device *sdev = stream->dev;
309	u8 wbuf[4];
310	struct slim_val_inf msg = {0, 4, NULL, wbuf, NULL};
311	u8 mc = SLIM_MSG_MC_NEXT_DEFINE_CHANNEL;
312	DEFINE_SLIM_LDEST_TXN(txn, mc, 8, stream->dev->laddr, &msg);
313
314	port->ch.seg_dist = slim_get_segdist_code(stream->ratem);
315
316	wbuf[0] = port->ch.id;
317	wbuf[1] = port->ch.seg_dist & 0xFF;
318	wbuf[2] = (stream->prot << 4) | ((port->ch.seg_dist & 0xF00) >> 8);
319	if (stream->prot == SLIM_PROTO_ISO)
320		wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS;
321	else
322		wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS + 1;
323
324	port->ch.state = SLIM_CH_STATE_DEFINED;
325
326	return slim_do_transfer(sdev->ctrl, &txn);
327}
328
329static int slim_activate_channel(struct slim_stream_runtime *stream,
330				 struct slim_port *port)
331{
332	struct slim_device *sdev = stream->dev;
333	u8 wbuf[1];
334	struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL};
335	u8 mc = SLIM_MSG_MC_NEXT_ACTIVATE_CHANNEL;
336	DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg);
337
338	txn.msg->num_bytes = 1;
339	txn.msg->wbuf = wbuf;
340	wbuf[0] = port->ch.id;
341	port->ch.state = SLIM_CH_STATE_ACTIVE;
342
343	return slim_do_transfer(sdev->ctrl, &txn);
344}
345
346/**
347 * slim_stream_enable() - Enable a prepared SLIMbus Stream
348 *
349 * @stream: instance of slim stream runtime to enable
350 *
351 * This API will enable all the ports and channels associated with
352 * SLIMbus stream
353 *
354 * Return: zero on success and error code on failure. From ASoC DPCM framework,
355 * this state is linked to trigger() start operation.
356 */
357int slim_stream_enable(struct slim_stream_runtime *stream)
358{
359	DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION,
360				3, SLIM_LA_MANAGER, NULL);
361	struct slim_controller *ctrl = stream->dev->ctrl;
362	int ret, i;
363
364	if (ctrl->enable_stream) {
365		ret = ctrl->enable_stream(stream);
366		if (ret)
367			return ret;
368
369		for (i = 0; i < stream->num_ports; i++)
370			stream->ports[i].ch.state = SLIM_CH_STATE_ACTIVE;
371
372		return ret;
373	}
374
375	ret = slim_do_transfer(ctrl, &txn);
376	if (ret)
377		return ret;
378
379	/* define channels first before activating them */
380	for (i = 0; i < stream->num_ports; i++) {
381		struct slim_port *port = &stream->ports[i];
382
383		slim_define_channel(stream, port);
384		slim_define_channel_content(stream, port);
385	}
386
387	for (i = 0; i < stream->num_ports; i++) {
388		struct slim_port *port = &stream->ports[i];
389
390		slim_activate_channel(stream, port);
391		port->state = SLIM_PORT_CONFIGURED;
392	}
393	txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW;
394
395	return slim_do_transfer(ctrl, &txn);
396}
397EXPORT_SYMBOL_GPL(slim_stream_enable);
398
399/**
400 * slim_stream_disable() - Disable a SLIMbus Stream
401 *
402 * @stream: instance of slim stream runtime to disable
403 *
404 * This API will disable all the ports and channels associated with
405 * SLIMbus stream
406 *
407 * Return: zero on success and error code on failure. From ASoC DPCM framework,
408 * this state is linked to trigger() pause operation.
409 */
410int slim_stream_disable(struct slim_stream_runtime *stream)
411{
412	DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION,
413				3, SLIM_LA_MANAGER, NULL);
414	struct slim_controller *ctrl = stream->dev->ctrl;
415	int ret, i;
416
417	if (!stream->ports || !stream->num_ports)
418		return -EINVAL;
419
420	if (ctrl->disable_stream)
421		ctrl->disable_stream(stream);
422
423	ret = slim_do_transfer(ctrl, &txn);
424	if (ret)
425		return ret;
426
427	for (i = 0; i < stream->num_ports; i++)
428		slim_deactivate_remove_channel(stream, &stream->ports[i]);
429
430	txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW;
431
432	return slim_do_transfer(ctrl, &txn);
433}
434EXPORT_SYMBOL_GPL(slim_stream_disable);
435
436/**
437 * slim_stream_unprepare() - Un-prepare a SLIMbus Stream
438 *
439 * @stream: instance of slim stream runtime to unprepare
440 *
441 * This API will un allocate all the ports and channels associated with
442 * SLIMbus stream
443 *
444 * Return: zero on success and error code on failure. From ASoC DPCM framework,
445 * this state is linked to trigger() stop operation.
446 */
447int slim_stream_unprepare(struct slim_stream_runtime *stream)
448{
449	int i;
450
451	if (!stream->ports || !stream->num_ports)
452		return -EINVAL;
453
454	for (i = 0; i < stream->num_ports; i++)
455		slim_disconnect_port(stream, &stream->ports[i]);
456
457	kfree(stream->ports);
458	stream->ports = NULL;
459	stream->num_ports = 0;
460
461	return 0;
462}
463EXPORT_SYMBOL_GPL(slim_stream_unprepare);
464
465/**
466 * slim_stream_free() - Free a SLIMbus Stream
467 *
468 * @stream: instance of slim stream runtime to free
469 *
470 * This API will un allocate all the memory associated with
471 * slim stream runtime, user is not allowed to make an dereference
472 * to stream after this call.
473 *
474 * Return: zero on success and error code on failure. From ASoC DPCM framework,
475 * this state is linked to shutdown() operation.
476 */
477int slim_stream_free(struct slim_stream_runtime *stream)
478{
479	struct slim_device *sdev = stream->dev;
480
481	spin_lock(&sdev->stream_list_lock);
482	list_del(&stream->node);
483	spin_unlock(&sdev->stream_list_lock);
484
485	kfree(stream->name);
486	kfree(stream);
487
488	return 0;
489}
490EXPORT_SYMBOL_GPL(slim_stream_free);
v5.4
  1// SPDX-License-Identifier: GPL-2.0
  2// Copyright (c) 2018, Linaro Limited
  3
  4#include <linux/kernel.h>
  5#include <linux/errno.h>
  6#include <linux/slab.h>
  7#include <linux/list.h>
  8#include <linux/slimbus.h>
  9#include <uapi/sound/asound.h>
 10#include "slimbus.h"
 11
 12/**
 13 * struct segdist_code - Segment Distributions code from
 14 *	Table 20 of SLIMbus Specs Version 2.0
 15 *
 16 * @ratem: Channel Rate Multipler(Segments per Superframe)
 17 * @seg_interval: Number of slots between the first Slot of Segment
 18 *		and the first slot of the next  consecutive Segment.
 19 * @segdist_code: Segment Distribution Code SD[11:0]
 20 * @seg_offset_mask: Segment offset mask in SD[11:0]
 21 * @segdist_codes: List of all possible Segmet Distribution codes.
 22 */
 23static const struct segdist_code {
 24	int ratem;
 25	int seg_interval;
 26	int segdist_code;
 27	u32 seg_offset_mask;
 28
 29} segdist_codes[] = {
 30	{1,	1536,	0x200,	 0xdff},
 31	{2,	768,	0x100,	 0xcff},
 32	{4,	384,	0x080,	 0xc7f},
 33	{8,	192,	0x040,	 0xc3f},
 34	{16,	96,	0x020,	 0xc1f},
 35	{32,	48,	0x010,	 0xc0f},
 36	{64,	24,	0x008,	 0xc07},
 37	{128,	12,	0x004,	 0xc03},
 38	{256,	6,	0x002,	 0xc01},
 39	{512,	3,	0x001,	 0xc00},
 40	{3,	512,	0xe00,	 0x1ff},
 41	{6,	256,	0xd00,	 0x0ff},
 42	{12,	128,	0xc80,	 0x07f},
 43	{24,	64,	0xc40,	 0x03f},
 44	{48,	32,	0xc20,	 0x01f},
 45	{96,	16,	0xc10,	 0x00f},
 46	{192,	8,	0xc08,	 0x007},
 47	{364,	4,	0xc04,	 0x003},
 48	{768,	2,	0xc02,	 0x001},
 49};
 50
 51/*
 52 * Presence Rate table for all Natural Frequencies
 53 * The Presence rate of a constant bitrate stream is mean flow rate of the
 54 * stream expressed in occupied Segments of that Data Channel per second.
 55 * Table 66 from SLIMbus 2.0 Specs
 56 *
 57 * Index of the table corresponds to Presence rate code for the respective rate
 58 * in the table.
 59 */
 60static const int slim_presence_rate_table[] = {
 61	0, /* Not Indicated */
 62	12000,
 63	24000,
 64	48000,
 65	96000,
 66	192000,
 67	384000,
 68	768000,
 69	0, /* Reserved */
 70	110250,
 71	220500,
 72	441000,
 73	882000,
 74	176400,
 75	352800,
 76	705600,
 77	4000,
 78	8000,
 79	16000,
 80	32000,
 81	64000,
 82	128000,
 83	256000,
 84	512000,
 85};
 86
 87/**
 88 * slim_stream_allocate() - Allocate a new SLIMbus Stream
 89 * @dev:Slim device to be associated with
 90 * @name: name of the stream
 91 *
 92 * This is very first call for SLIMbus streaming, this API will allocate
 93 * a new SLIMbus stream and return a valid stream runtime pointer for client
 94 * to use it in subsequent stream apis. state of stream is set to ALLOCATED
 95 *
 96 * Return: valid pointer on success and error code on failure.
 97 * From ASoC DPCM framework, this state is linked to startup() operation.
 98 */
 99struct slim_stream_runtime *slim_stream_allocate(struct slim_device *dev,
100						 const char *name)
101{
102	struct slim_stream_runtime *rt;
103
104	rt = kzalloc(sizeof(*rt), GFP_KERNEL);
105	if (!rt)
106		return ERR_PTR(-ENOMEM);
107
108	rt->name = kasprintf(GFP_KERNEL, "slim-%s", name);
109	if (!rt->name) {
110		kfree(rt);
111		return ERR_PTR(-ENOMEM);
112	}
113
114	rt->dev = dev;
115	spin_lock(&dev->stream_list_lock);
116	list_add_tail(&rt->node, &dev->stream_list);
117	spin_unlock(&dev->stream_list_lock);
118
119	return rt;
120}
121EXPORT_SYMBOL_GPL(slim_stream_allocate);
122
123static int slim_connect_port_channel(struct slim_stream_runtime *stream,
124				     struct slim_port *port)
125{
126	struct slim_device *sdev = stream->dev;
127	u8 wbuf[2];
128	struct slim_val_inf msg = {0, 2, NULL, wbuf, NULL};
129	u8 mc = SLIM_MSG_MC_CONNECT_SOURCE;
130	DEFINE_SLIM_LDEST_TXN(txn, mc, 6, stream->dev->laddr, &msg);
131
132	if (port->direction == SLIM_PORT_SINK)
133		txn.mc = SLIM_MSG_MC_CONNECT_SINK;
134
135	wbuf[0] = port->id;
136	wbuf[1] = port->ch.id;
137	port->ch.state = SLIM_CH_STATE_ASSOCIATED;
138	port->state = SLIM_PORT_UNCONFIGURED;
139
140	return slim_do_transfer(sdev->ctrl, &txn);
141}
142
143static int slim_disconnect_port(struct slim_stream_runtime *stream,
144				struct slim_port *port)
145{
146	struct slim_device *sdev = stream->dev;
147	u8 wbuf[1];
148	struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL};
149	u8 mc = SLIM_MSG_MC_DISCONNECT_PORT;
150	DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg);
151
152	wbuf[0] = port->id;
153	port->ch.state = SLIM_CH_STATE_DISCONNECTED;
154	port->state = SLIM_PORT_DISCONNECTED;
155
156	return slim_do_transfer(sdev->ctrl, &txn);
157}
158
159static int slim_deactivate_remove_channel(struct slim_stream_runtime *stream,
160					  struct slim_port *port)
161{
162	struct slim_device *sdev = stream->dev;
163	u8 wbuf[1];
164	struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL};
165	u8 mc = SLIM_MSG_MC_NEXT_DEACTIVATE_CHANNEL;
166	DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg);
167	int ret;
168
169	wbuf[0] = port->ch.id;
170	ret = slim_do_transfer(sdev->ctrl, &txn);
171	if (ret)
172		return ret;
173
174	txn.mc = SLIM_MSG_MC_NEXT_REMOVE_CHANNEL;
175	port->ch.state = SLIM_CH_STATE_REMOVED;
176
177	return slim_do_transfer(sdev->ctrl, &txn);
178}
179
180static int slim_get_prate_code(int rate)
181{
182	int i;
183
184	for (i = 0; i < ARRAY_SIZE(slim_presence_rate_table); i++) {
185		if (rate == slim_presence_rate_table[i])
186			return i;
187	}
188
189	return -EINVAL;
190}
191
192/**
193 * slim_stream_prepare() - Prepare a SLIMbus Stream
194 *
195 * @rt: instance of slim stream runtime to configure
196 * @cfg: new configuration for the stream
197 *
198 * This API will configure SLIMbus stream with config parameters from cfg.
199 * return zero on success and error code on failure. From ASoC DPCM framework,
200 * this state is linked to hw_params() operation.
201 */
202int slim_stream_prepare(struct slim_stream_runtime *rt,
203			struct slim_stream_config *cfg)
204{
205	struct slim_controller *ctrl = rt->dev->ctrl;
206	struct slim_port *port;
207	int num_ports, i, port_id;
208
209	if (rt->ports) {
210		dev_err(&rt->dev->dev, "Stream already Prepared\n");
211		return -EINVAL;
212	}
213
214	num_ports = hweight32(cfg->port_mask);
215	rt->ports = kcalloc(num_ports, sizeof(*port), GFP_KERNEL);
216	if (!rt->ports)
217		return -ENOMEM;
218
219	rt->num_ports = num_ports;
220	rt->rate = cfg->rate;
221	rt->bps = cfg->bps;
222	rt->direction = cfg->direction;
223
 
 
 
 
 
 
 
224	if (cfg->rate % ctrl->a_framer->superfreq) {
225		/*
226		 * data rate not exactly multiple of super frame,
227		 * use PUSH/PULL protocol
228		 */
229		if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK)
230			rt->prot = SLIM_PROTO_PUSH;
231		else
232			rt->prot = SLIM_PROTO_PULL;
233	} else {
234		rt->prot = SLIM_PROTO_ISO;
235	}
236
237	rt->ratem = cfg->rate/ctrl->a_framer->superfreq;
238
239	i = 0;
240	for_each_set_bit(port_id, &cfg->port_mask, SLIM_DEVICE_MAX_PORTS) {
241		port = &rt->ports[i];
242		port->state = SLIM_PORT_DISCONNECTED;
243		port->id = port_id;
244		port->ch.prrate = slim_get_prate_code(cfg->rate);
245		port->ch.id = cfg->chs[i];
246		port->ch.data_fmt = SLIM_CH_DATA_FMT_NOT_DEFINED;
247		port->ch.aux_fmt = SLIM_CH_AUX_FMT_NOT_APPLICABLE;
248		port->ch.state = SLIM_CH_STATE_ALLOCATED;
249
250		if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK)
251			port->direction = SLIM_PORT_SINK;
252		else
253			port->direction = SLIM_PORT_SOURCE;
254
255		slim_connect_port_channel(rt, port);
256		i++;
257	}
258
259	return 0;
260}
261EXPORT_SYMBOL_GPL(slim_stream_prepare);
262
263static int slim_define_channel_content(struct slim_stream_runtime *stream,
264				       struct slim_port *port)
265{
266	struct slim_device *sdev = stream->dev;
267	u8 wbuf[4];
268	struct slim_val_inf msg = {0, 4, NULL, wbuf, NULL};
269	u8 mc = SLIM_MSG_MC_NEXT_DEFINE_CONTENT;
270	DEFINE_SLIM_LDEST_TXN(txn, mc, 8, stream->dev->laddr, &msg);
271
272	wbuf[0] = port->ch.id;
273	wbuf[1] = port->ch.prrate;
274
275	/* Frequency Locked for ISO Protocol */
276	if (stream->prot != SLIM_PROTO_ISO)
277		wbuf[1] |= SLIM_CHANNEL_CONTENT_FL;
278
279	wbuf[2] = port->ch.data_fmt | (port->ch.aux_fmt << 4);
280	wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS;
281	port->ch.state = SLIM_CH_STATE_CONTENT_DEFINED;
282
283	return slim_do_transfer(sdev->ctrl, &txn);
284}
285
286static int slim_get_segdist_code(int ratem)
287{
288	int i;
289
290	for (i = 0; i < ARRAY_SIZE(segdist_codes); i++) {
291		if (segdist_codes[i].ratem == ratem)
292			return segdist_codes[i].segdist_code;
293	}
294
295	return -EINVAL;
296}
297
298static int slim_define_channel(struct slim_stream_runtime *stream,
299				       struct slim_port *port)
300{
301	struct slim_device *sdev = stream->dev;
302	u8 wbuf[4];
303	struct slim_val_inf msg = {0, 4, NULL, wbuf, NULL};
304	u8 mc = SLIM_MSG_MC_NEXT_DEFINE_CHANNEL;
305	DEFINE_SLIM_LDEST_TXN(txn, mc, 8, stream->dev->laddr, &msg);
306
307	port->ch.seg_dist = slim_get_segdist_code(stream->ratem);
308
309	wbuf[0] = port->ch.id;
310	wbuf[1] = port->ch.seg_dist & 0xFF;
311	wbuf[2] = (stream->prot << 4) | ((port->ch.seg_dist & 0xF00) >> 8);
312	if (stream->prot == SLIM_PROTO_ISO)
313		wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS;
314	else
315		wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS + 1;
316
317	port->ch.state = SLIM_CH_STATE_DEFINED;
318
319	return slim_do_transfer(sdev->ctrl, &txn);
320}
321
322static int slim_activate_channel(struct slim_stream_runtime *stream,
323				 struct slim_port *port)
324{
325	struct slim_device *sdev = stream->dev;
326	u8 wbuf[1];
327	struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL};
328	u8 mc = SLIM_MSG_MC_NEXT_ACTIVATE_CHANNEL;
329	DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg);
330
331	txn.msg->num_bytes = 1;
332	txn.msg->wbuf = wbuf;
333	wbuf[0] = port->ch.id;
334	port->ch.state = SLIM_CH_STATE_ACTIVE;
335
336	return slim_do_transfer(sdev->ctrl, &txn);
337}
338
339/**
340 * slim_stream_enable() - Enable a prepared SLIMbus Stream
341 *
342 * @stream: instance of slim stream runtime to enable
343 *
344 * This API will enable all the ports and channels associated with
345 * SLIMbus stream
346 *
347 * Return: zero on success and error code on failure. From ASoC DPCM framework,
348 * this state is linked to trigger() start operation.
349 */
350int slim_stream_enable(struct slim_stream_runtime *stream)
351{
352	DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION,
353				3, SLIM_LA_MANAGER, NULL);
354	struct slim_controller *ctrl = stream->dev->ctrl;
355	int ret, i;
356
357	if (ctrl->enable_stream) {
358		ret = ctrl->enable_stream(stream);
359		if (ret)
360			return ret;
361
362		for (i = 0; i < stream->num_ports; i++)
363			stream->ports[i].ch.state = SLIM_CH_STATE_ACTIVE;
364
365		return ret;
366	}
367
368	ret = slim_do_transfer(ctrl, &txn);
369	if (ret)
370		return ret;
371
372	/* define channels first before activating them */
373	for (i = 0; i < stream->num_ports; i++) {
374		struct slim_port *port = &stream->ports[i];
375
376		slim_define_channel(stream, port);
377		slim_define_channel_content(stream, port);
378	}
379
380	for (i = 0; i < stream->num_ports; i++) {
381		struct slim_port *port = &stream->ports[i];
382
383		slim_activate_channel(stream, port);
384		port->state = SLIM_PORT_CONFIGURED;
385	}
386	txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW;
387
388	return slim_do_transfer(ctrl, &txn);
389}
390EXPORT_SYMBOL_GPL(slim_stream_enable);
391
392/**
393 * slim_stream_disable() - Disable a SLIMbus Stream
394 *
395 * @stream: instance of slim stream runtime to disable
396 *
397 * This API will disable all the ports and channels associated with
398 * SLIMbus stream
399 *
400 * Return: zero on success and error code on failure. From ASoC DPCM framework,
401 * this state is linked to trigger() pause operation.
402 */
403int slim_stream_disable(struct slim_stream_runtime *stream)
404{
405	DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION,
406				3, SLIM_LA_MANAGER, NULL);
407	struct slim_controller *ctrl = stream->dev->ctrl;
408	int ret, i;
409
 
 
 
410	if (ctrl->disable_stream)
411		ctrl->disable_stream(stream);
412
413	ret = slim_do_transfer(ctrl, &txn);
414	if (ret)
415		return ret;
416
417	for (i = 0; i < stream->num_ports; i++)
418		slim_deactivate_remove_channel(stream, &stream->ports[i]);
419
420	txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW;
421
422	return slim_do_transfer(ctrl, &txn);
423}
424EXPORT_SYMBOL_GPL(slim_stream_disable);
425
426/**
427 * slim_stream_unprepare() - Un-prepare a SLIMbus Stream
428 *
429 * @stream: instance of slim stream runtime to unprepare
430 *
431 * This API will un allocate all the ports and channels associated with
432 * SLIMbus stream
433 *
434 * Return: zero on success and error code on failure. From ASoC DPCM framework,
435 * this state is linked to trigger() stop operation.
436 */
437int slim_stream_unprepare(struct slim_stream_runtime *stream)
438{
439	int i;
 
 
 
440
441	for (i = 0; i < stream->num_ports; i++)
442		slim_disconnect_port(stream, &stream->ports[i]);
443
444	kfree(stream->ports);
445	stream->ports = NULL;
446	stream->num_ports = 0;
447
448	return 0;
449}
450EXPORT_SYMBOL_GPL(slim_stream_unprepare);
451
452/**
453 * slim_stream_free() - Free a SLIMbus Stream
454 *
455 * @stream: instance of slim stream runtime to free
456 *
457 * This API will un allocate all the memory associated with
458 * slim stream runtime, user is not allowed to make an dereference
459 * to stream after this call.
460 *
461 * Return: zero on success and error code on failure. From ASoC DPCM framework,
462 * this state is linked to shutdown() operation.
463 */
464int slim_stream_free(struct slim_stream_runtime *stream)
465{
466	struct slim_device *sdev = stream->dev;
467
468	spin_lock(&sdev->stream_list_lock);
469	list_del(&stream->node);
470	spin_unlock(&sdev->stream_list_lock);
471
472	kfree(stream->name);
473	kfree(stream);
474
475	return 0;
476}
477EXPORT_SYMBOL_GPL(slim_stream_free);