cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

stream.c (12377B)


      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 */
     70	110250,
     71	220500,
     72	441000,
     73	882000,
     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 */
     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
    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 */
    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;
    207	int num_ports, i, port_id;
    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	if (cfg->rate % ctrl->a_framer->superfreq) {
    225		/*
    226		 * data rate not exactly multiple of super frame,
    227		 * use PUSH/PULL protocol
    228		 */
    229		if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK)
    230			rt->prot = SLIM_PROTO_PUSH;
    231		else
    232			rt->prot = SLIM_PROTO_PULL;
    233	} else {
    234		rt->prot = SLIM_PROTO_ISO;
    235	}
    236
    237	rt->ratem = cfg->rate/ctrl->a_framer->superfreq;
    238
    239	i = 0;
    240	for_each_set_bit(port_id, &cfg->port_mask, SLIM_DEVICE_MAX_PORTS) {
    241		port = &rt->ports[i];
    242		port->state = SLIM_PORT_DISCONNECTED;
    243		port->id = port_id;
    244		port->ch.prrate = slim_get_prate_code(cfg->rate);
    245		port->ch.id = cfg->chs[i];
    246		port->ch.data_fmt = SLIM_CH_DATA_FMT_NOT_DEFINED;
    247		port->ch.aux_fmt = SLIM_CH_AUX_FMT_NOT_APPLICABLE;
    248		port->ch.state = SLIM_CH_STATE_ALLOCATED;
    249
    250		if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK)
    251			port->direction = SLIM_PORT_SINK;
    252		else
    253			port->direction = SLIM_PORT_SOURCE;
    254
    255		slim_connect_port_channel(rt, port);
    256		i++;
    257	}
    258
    259	return 0;
    260}
    261EXPORT_SYMBOL_GPL(slim_stream_prepare);
    262
    263static int slim_define_channel_content(struct slim_stream_runtime *stream,
    264				       struct slim_port *port)
    265{
    266	struct slim_device *sdev = stream->dev;
    267	u8 wbuf[4];
    268	struct slim_val_inf msg = {0, 4, NULL, wbuf, NULL};
    269	u8 mc = SLIM_MSG_MC_NEXT_DEFINE_CONTENT;
    270	DEFINE_SLIM_LDEST_TXN(txn, mc, 8, stream->dev->laddr, &msg);
    271
    272	wbuf[0] = port->ch.id;
    273	wbuf[1] = port->ch.prrate;
    274
    275	/* Frequency Locked for ISO Protocol */
    276	if (stream->prot != SLIM_PROTO_ISO)
    277		wbuf[1] |= SLIM_CHANNEL_CONTENT_FL;
    278
    279	wbuf[2] = port->ch.data_fmt | (port->ch.aux_fmt << 4);
    280	wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS;
    281	port->ch.state = SLIM_CH_STATE_CONTENT_DEFINED;
    282
    283	return slim_do_transfer(sdev->ctrl, &txn);
    284}
    285
    286static int slim_get_segdist_code(int ratem)
    287{
    288	int i;
    289
    290	for (i = 0; i < ARRAY_SIZE(segdist_codes); i++) {
    291		if (segdist_codes[i].ratem == ratem)
    292			return segdist_codes[i].segdist_code;
    293	}
    294
    295	return -EINVAL;
    296}
    297
    298static int slim_define_channel(struct slim_stream_runtime *stream,
    299				       struct slim_port *port)
    300{
    301	struct slim_device *sdev = stream->dev;
    302	u8 wbuf[4];
    303	struct slim_val_inf msg = {0, 4, NULL, wbuf, NULL};
    304	u8 mc = SLIM_MSG_MC_NEXT_DEFINE_CHANNEL;
    305	DEFINE_SLIM_LDEST_TXN(txn, mc, 8, stream->dev->laddr, &msg);
    306
    307	port->ch.seg_dist = slim_get_segdist_code(stream->ratem);
    308
    309	wbuf[0] = port->ch.id;
    310	wbuf[1] = port->ch.seg_dist & 0xFF;
    311	wbuf[2] = (stream->prot << 4) | ((port->ch.seg_dist & 0xF00) >> 8);
    312	if (stream->prot == SLIM_PROTO_ISO)
    313		wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS;
    314	else
    315		wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS + 1;
    316
    317	port->ch.state = SLIM_CH_STATE_DEFINED;
    318
    319	return slim_do_transfer(sdev->ctrl, &txn);
    320}
    321
    322static int slim_activate_channel(struct slim_stream_runtime *stream,
    323				 struct slim_port *port)
    324{
    325	struct slim_device *sdev = stream->dev;
    326	u8 wbuf[1];
    327	struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL};
    328	u8 mc = SLIM_MSG_MC_NEXT_ACTIVATE_CHANNEL;
    329	DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg);
    330
    331	txn.msg->num_bytes = 1;
    332	txn.msg->wbuf = wbuf;
    333	wbuf[0] = port->ch.id;
    334	port->ch.state = SLIM_CH_STATE_ACTIVE;
    335
    336	return slim_do_transfer(sdev->ctrl, &txn);
    337}
    338
    339/**
    340 * slim_stream_enable() - Enable a prepared SLIMbus Stream
    341 *
    342 * @stream: instance of slim stream runtime to enable
    343 *
    344 * This API will enable all the ports and channels associated with
    345 * SLIMbus stream
    346 *
    347 * Return: zero on success and error code on failure. From ASoC DPCM framework,
    348 * this state is linked to trigger() start operation.
    349 */
    350int slim_stream_enable(struct slim_stream_runtime *stream)
    351{
    352	DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION,
    353				3, SLIM_LA_MANAGER, NULL);
    354	struct slim_controller *ctrl = stream->dev->ctrl;
    355	int ret, i;
    356
    357	if (ctrl->enable_stream) {
    358		ret = ctrl->enable_stream(stream);
    359		if (ret)
    360			return ret;
    361
    362		for (i = 0; i < stream->num_ports; i++)
    363			stream->ports[i].ch.state = SLIM_CH_STATE_ACTIVE;
    364
    365		return ret;
    366	}
    367
    368	ret = slim_do_transfer(ctrl, &txn);
    369	if (ret)
    370		return ret;
    371
    372	/* define channels first before activating them */
    373	for (i = 0; i < stream->num_ports; i++) {
    374		struct slim_port *port = &stream->ports[i];
    375
    376		slim_define_channel(stream, port);
    377		slim_define_channel_content(stream, port);
    378	}
    379
    380	for (i = 0; i < stream->num_ports; i++) {
    381		struct slim_port *port = &stream->ports[i];
    382
    383		slim_activate_channel(stream, port);
    384		port->state = SLIM_PORT_CONFIGURED;
    385	}
    386	txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW;
    387
    388	return slim_do_transfer(ctrl, &txn);
    389}
    390EXPORT_SYMBOL_GPL(slim_stream_enable);
    391
    392/**
    393 * slim_stream_disable() - Disable a SLIMbus Stream
    394 *
    395 * @stream: instance of slim stream runtime to disable
    396 *
    397 * This API will disable all the ports and channels associated with
    398 * SLIMbus stream
    399 *
    400 * Return: zero on success and error code on failure. From ASoC DPCM framework,
    401 * this state is linked to trigger() pause operation.
    402 */
    403int slim_stream_disable(struct slim_stream_runtime *stream)
    404{
    405	DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION,
    406				3, SLIM_LA_MANAGER, NULL);
    407	struct slim_controller *ctrl = stream->dev->ctrl;
    408	int ret, i;
    409
    410	if (ctrl->disable_stream)
    411		ctrl->disable_stream(stream);
    412
    413	ret = slim_do_transfer(ctrl, &txn);
    414	if (ret)
    415		return ret;
    416
    417	for (i = 0; i < stream->num_ports; i++)
    418		slim_deactivate_remove_channel(stream, &stream->ports[i]);
    419
    420	txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW;
    421
    422	return slim_do_transfer(ctrl, &txn);
    423}
    424EXPORT_SYMBOL_GPL(slim_stream_disable);
    425
    426/**
    427 * slim_stream_unprepare() - Un-prepare a SLIMbus Stream
    428 *
    429 * @stream: instance of slim stream runtime to unprepare
    430 *
    431 * This API will un allocate all the ports and channels associated with
    432 * SLIMbus stream
    433 *
    434 * Return: zero on success and error code on failure. From ASoC DPCM framework,
    435 * this state is linked to trigger() stop operation.
    436 */
    437int slim_stream_unprepare(struct slim_stream_runtime *stream)
    438{
    439	int i;
    440
    441	for (i = 0; i < stream->num_ports; i++)
    442		slim_disconnect_port(stream, &stream->ports[i]);
    443
    444	kfree(stream->ports);
    445	stream->ports = NULL;
    446	stream->num_ports = 0;
    447
    448	return 0;
    449}
    450EXPORT_SYMBOL_GPL(slim_stream_unprepare);
    451
    452/**
    453 * slim_stream_free() - Free a SLIMbus Stream
    454 *
    455 * @stream: instance of slim stream runtime to free
    456 *
    457 * This API will un allocate all the memory associated with
    458 * slim stream runtime, user is not allowed to make an dereference
    459 * to stream after this call.
    460 *
    461 * Return: zero on success and error code on failure. From ASoC DPCM framework,
    462 * this state is linked to shutdown() operation.
    463 */
    464int slim_stream_free(struct slim_stream_runtime *stream)
    465{
    466	struct slim_device *sdev = stream->dev;
    467
    468	spin_lock(&sdev->stream_list_lock);
    469	list_del(&stream->node);
    470	spin_unlock(&sdev->stream_list_lock);
    471
    472	kfree(stream->name);
    473	kfree(stream);
    474
    475	return 0;
    476}
    477EXPORT_SYMBOL_GPL(slim_stream_free);