xref: /openbmc/linux/drivers/slimbus/stream.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
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