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