brintos

brintos / linux-shallow public Read only

0
0
Text · 12.4 KiB · 863ab30 Raw
493 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2018, Linaro Limited3 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 from14 *	Table 20 of SLIMbus Specs Version 2.015 *16 * @ratem: Channel Rate Multipler(Segments per Superframe)17 * @seg_interval: Number of slots between the first Slot of Segment18 *		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 Frequencies55 * The Presence rate of a constant bitrate stream is mean flow rate of the56 * stream expressed in occupied Segments of that Data Channel per second.57 * Table 66 from SLIMbus 2.0 Specs58 *59 * Index of the table corresponds to Presence rate code for the respective rate60 * 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 Stream91 * @dev:Slim device to be associated with92 * @name: name of the stream93 *94 * This is very first call for SLIMbus streaming, this API will allocate95 * a new SLIMbus stream and return a valid stream runtime pointer for client96 * to use it in subsequent stream apis. state of stream is set to ALLOCATED97 *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 Stream196 *197 * @rt: instance of slim stream runtime to configure198 * @cfg: new configuration for the stream199 *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 protocol237		 */238		if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK)239			rt->prot = SLIM_PROTO_PUSH;240		else241			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		else262			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	else324		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 Stream350 *351 * @stream: instance of slim stream runtime to enable352 *353 * This API will enable all the ports and channels associated with354 * SLIMbus stream355 *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 Stream403 *404 * @stream: instance of slim stream runtime to disable405 *406 * This API will disable all the ports and channels associated with407 * SLIMbus stream408 *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 Stream440 *441 * @stream: instance of slim stream runtime to unprepare442 *443 * This API will un allocate all the ports and channels associated with444 * SLIMbus stream445 *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 Stream469 *470 * @stream: instance of slim stream runtime to free471 *472 * This API will un allocate all the memory associated with473 * slim stream runtime, user is not allowed to make an dereference474 * 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);493