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