xref: /openbmc/linux/drivers/slimbus/stream.c (revision a82b1ec3)
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  */
23 static 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  */
60 static 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  */
slim_stream_allocate(struct slim_device * dev,const char * name)99 struct 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 }
121 EXPORT_SYMBOL_GPL(slim_stream_allocate);
122 
slim_connect_port_channel(struct slim_stream_runtime * stream,struct slim_port * port)123 static 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 
slim_disconnect_port(struct slim_stream_runtime * stream,struct slim_port * port)143 static 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 
slim_deactivate_remove_channel(struct slim_stream_runtime * stream,struct slim_port * port)159 static 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 
slim_get_prate_code(int rate)180 static 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  */
slim_stream_prepare(struct slim_stream_runtime * rt,struct slim_stream_config * cfg)202 int 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 }
268 EXPORT_SYMBOL_GPL(slim_stream_prepare);
269 
slim_define_channel_content(struct slim_stream_runtime * stream,struct slim_port * port)270 static 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 
slim_get_segdist_code(int ratem)293 static 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 
slim_define_channel(struct slim_stream_runtime * stream,struct slim_port * port)305 static 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 
slim_activate_channel(struct slim_stream_runtime * stream,struct slim_port * port)329 static 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  */
slim_stream_enable(struct slim_stream_runtime * stream)357 int 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 }
397 EXPORT_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  */
slim_stream_disable(struct slim_stream_runtime * stream)410 int 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 }
434 EXPORT_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  */
slim_stream_unprepare(struct slim_stream_runtime * stream)447 int 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 }
463 EXPORT_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  */
slim_stream_free(struct slim_stream_runtime * stream)477 int 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 }
490 EXPORT_SYMBOL_GPL(slim_stream_free);
491