Linux 6.9-rc1
[linux-2.6-microblaze.git] / drivers / slimbus / stream.c
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  */
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
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
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
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
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  */
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
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
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
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
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  */
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  */
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  */
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  */
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);