linux/drivers/slimbus/stream.c
<<
>>
Prefs
   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);
 478