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);
491