Merge branch 'net-renesas-rswitch-fix-a-lot-of-redundant-irq-issue'
[linux-2.6-block.git] / drivers / slimbus / stream.c
CommitLineData
abb9c9b8
SK
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 */
23static 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 */
60static 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 */
b9c19396
KK
70 11025,
71 22050,
72 44100,
73 88200,
abb9c9b8
SK
74 176400,
75 352800,
76 705600,
77 4000,
78 8000,
79 16000,
80 32000,
81 64000,
82 128000,
83 256000,
84 512000,
85};
86
2f0f2441 87/**
abb9c9b8
SK
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 */
99struct 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}
121EXPORT_SYMBOL_GPL(slim_stream_allocate);
122
123static 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
143static 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
159static 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
180static 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
2f0f2441 192/**
abb9c9b8
SK
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 */
202int 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;
434d2572 207 int num_ports, i, port_id, prrate;
abb9c9b8
SK
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
434d2572
KK
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
abb9c9b8
SK
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;
434d2572 251 port->ch.prrate = prrate;
abb9c9b8
SK
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}
268EXPORT_SYMBOL_GPL(slim_stream_prepare);
269
270static 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
293static 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
305static 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
329static 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
2f0f2441 346/**
abb9c9b8
SK
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 */
357int 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}
397EXPORT_SYMBOL_GPL(slim_stream_enable);
398
2f0f2441 399/**
abb9c9b8
SK
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 */
410int 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
a82b1ec3
KK
417 if (!stream->ports || !stream->num_ports)
418 return -EINVAL;
419
abb9c9b8
SK
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}
434EXPORT_SYMBOL_GPL(slim_stream_disable);
435
2f0f2441 436/**
abb9c9b8
SK
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 */
447int slim_stream_unprepare(struct slim_stream_runtime *stream)
448{
449 int i;
450
a82b1ec3
KK
451 if (!stream->ports || !stream->num_ports)
452 return -EINVAL;
453
abb9c9b8
SK
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}
463EXPORT_SYMBOL_GPL(slim_stream_unprepare);
464
2f0f2441 465/**
abb9c9b8
SK
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 */
477int 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}
490EXPORT_SYMBOL_GPL(slim_stream_free);