]> Git Repo - linux.git/blob - drivers/slimbus/stream.c
Merge tag 'uml-for-linus-6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.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  */
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  */
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
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
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
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
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  */
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
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
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
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
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  */
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  */
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  */
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  */
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);
This page took 0.056899 seconds and 4 git commands to generate.