root/drivers/slimbus/stream.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. slim_stream_allocate
  2. slim_connect_port_channel
  3. slim_disconnect_port
  4. slim_deactivate_remove_channel
  5. slim_get_prate_code
  6. slim_stream_prepare
  7. slim_define_channel_content
  8. slim_get_segdist_code
  9. slim_define_channel
  10. slim_activate_channel
  11. slim_stream_enable
  12. slim_stream_disable
  13. slim_stream_unprepare
  14. slim_stream_free

   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         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  */
  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;
 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 }
 261 EXPORT_SYMBOL_GPL(slim_stream_prepare);
 262 
 263 static 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 
 286 static 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 
 298 static 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 
 322 static 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  */
 350 int 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 }
 390 EXPORT_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  */
 403 int 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 }
 424 EXPORT_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  */
 437 int 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 }
 450 EXPORT_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  */
 464 int 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 }
 477 EXPORT_SYMBOL_GPL(slim_stream_free);

/* [<][>][^][v][top][bottom][index][help] */