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