Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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 */
 22struct 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. */
 31static 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 */
 62static 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 */
101struct 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}
123EXPORT_SYMBOL_GPL(slim_stream_allocate);
124
125static 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
145static 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
161static 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
182static 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 */
204int 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}
270EXPORT_SYMBOL_GPL(slim_stream_prepare);
271
272static 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
295static 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
307static 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
331static 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 */
359int 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}
399EXPORT_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 */
412int 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}
436EXPORT_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 */
449int 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}
465EXPORT_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 */
479int 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}
492EXPORT_SYMBOL_GPL(slim_stream_free);