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);