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