Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 // Copyright (c) 2018, Linaro Limited
0003 
0004 #include <linux/kernel.h>
0005 #include <linux/errno.h>
0006 #include <linux/slab.h>
0007 #include <linux/list.h>
0008 #include <linux/slimbus.h>
0009 #include <uapi/sound/asound.h>
0010 #include "slimbus.h"
0011 
0012 /**
0013  * struct segdist_code - Segment Distributions code from
0014  *  Table 20 of SLIMbus Specs Version 2.0
0015  *
0016  * @ratem: Channel Rate Multipler(Segments per Superframe)
0017  * @seg_interval: Number of slots between the first Slot of Segment
0018  *      and the first slot of the next  consecutive Segment.
0019  * @segdist_code: Segment Distribution Code SD[11:0]
0020  * @seg_offset_mask: Segment offset mask in SD[11:0]
0021  * @segdist_codes: List of all possible Segmet Distribution codes.
0022  */
0023 static const struct segdist_code {
0024     int ratem;
0025     int seg_interval;
0026     int segdist_code;
0027     u32 seg_offset_mask;
0028 
0029 } segdist_codes[] = {
0030     {1, 1536,   0x200,   0xdff},
0031     {2, 768,    0x100,   0xcff},
0032     {4, 384,    0x080,   0xc7f},
0033     {8, 192,    0x040,   0xc3f},
0034     {16,    96, 0x020,   0xc1f},
0035     {32,    48, 0x010,   0xc0f},
0036     {64,    24, 0x008,   0xc07},
0037     {128,   12, 0x004,   0xc03},
0038     {256,   6,  0x002,   0xc01},
0039     {512,   3,  0x001,   0xc00},
0040     {3, 512,    0xe00,   0x1ff},
0041     {6, 256,    0xd00,   0x0ff},
0042     {12,    128,    0xc80,   0x07f},
0043     {24,    64, 0xc40,   0x03f},
0044     {48,    32, 0xc20,   0x01f},
0045     {96,    16, 0xc10,   0x00f},
0046     {192,   8,  0xc08,   0x007},
0047     {364,   4,  0xc04,   0x003},
0048     {768,   2,  0xc02,   0x001},
0049 };
0050 
0051 /*
0052  * Presence Rate table for all Natural Frequencies
0053  * The Presence rate of a constant bitrate stream is mean flow rate of the
0054  * stream expressed in occupied Segments of that Data Channel per second.
0055  * Table 66 from SLIMbus 2.0 Specs
0056  *
0057  * Index of the table corresponds to Presence rate code for the respective rate
0058  * in the table.
0059  */
0060 static const int slim_presence_rate_table[] = {
0061     0, /* Not Indicated */
0062     12000,
0063     24000,
0064     48000,
0065     96000,
0066     192000,
0067     384000,
0068     768000,
0069     0, /* Reserved */
0070     110250,
0071     220500,
0072     441000,
0073     882000,
0074     176400,
0075     352800,
0076     705600,
0077     4000,
0078     8000,
0079     16000,
0080     32000,
0081     64000,
0082     128000,
0083     256000,
0084     512000,
0085 };
0086 
0087 /**
0088  * slim_stream_allocate() - Allocate a new SLIMbus Stream
0089  * @dev:Slim device to be associated with
0090  * @name: name of the stream
0091  *
0092  * This is very first call for SLIMbus streaming, this API will allocate
0093  * a new SLIMbus stream and return a valid stream runtime pointer for client
0094  * to use it in subsequent stream apis. state of stream is set to ALLOCATED
0095  *
0096  * Return: valid pointer on success and error code on failure.
0097  * From ASoC DPCM framework, this state is linked to startup() operation.
0098  */
0099 struct slim_stream_runtime *slim_stream_allocate(struct slim_device *dev,
0100                          const char *name)
0101 {
0102     struct slim_stream_runtime *rt;
0103 
0104     rt = kzalloc(sizeof(*rt), GFP_KERNEL);
0105     if (!rt)
0106         return ERR_PTR(-ENOMEM);
0107 
0108     rt->name = kasprintf(GFP_KERNEL, "slim-%s", name);
0109     if (!rt->name) {
0110         kfree(rt);
0111         return ERR_PTR(-ENOMEM);
0112     }
0113 
0114     rt->dev = dev;
0115     spin_lock(&dev->stream_list_lock);
0116     list_add_tail(&rt->node, &dev->stream_list);
0117     spin_unlock(&dev->stream_list_lock);
0118 
0119     return rt;
0120 }
0121 EXPORT_SYMBOL_GPL(slim_stream_allocate);
0122 
0123 static int slim_connect_port_channel(struct slim_stream_runtime *stream,
0124                      struct slim_port *port)
0125 {
0126     struct slim_device *sdev = stream->dev;
0127     u8 wbuf[2];
0128     struct slim_val_inf msg = {0, 2, NULL, wbuf, NULL};
0129     u8 mc = SLIM_MSG_MC_CONNECT_SOURCE;
0130     DEFINE_SLIM_LDEST_TXN(txn, mc, 6, stream->dev->laddr, &msg);
0131 
0132     if (port->direction == SLIM_PORT_SINK)
0133         txn.mc = SLIM_MSG_MC_CONNECT_SINK;
0134 
0135     wbuf[0] = port->id;
0136     wbuf[1] = port->ch.id;
0137     port->ch.state = SLIM_CH_STATE_ASSOCIATED;
0138     port->state = SLIM_PORT_UNCONFIGURED;
0139 
0140     return slim_do_transfer(sdev->ctrl, &txn);
0141 }
0142 
0143 static int slim_disconnect_port(struct slim_stream_runtime *stream,
0144                 struct slim_port *port)
0145 {
0146     struct slim_device *sdev = stream->dev;
0147     u8 wbuf[1];
0148     struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL};
0149     u8 mc = SLIM_MSG_MC_DISCONNECT_PORT;
0150     DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg);
0151 
0152     wbuf[0] = port->id;
0153     port->ch.state = SLIM_CH_STATE_DISCONNECTED;
0154     port->state = SLIM_PORT_DISCONNECTED;
0155 
0156     return slim_do_transfer(sdev->ctrl, &txn);
0157 }
0158 
0159 static int slim_deactivate_remove_channel(struct slim_stream_runtime *stream,
0160                       struct slim_port *port)
0161 {
0162     struct slim_device *sdev = stream->dev;
0163     u8 wbuf[1];
0164     struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL};
0165     u8 mc = SLIM_MSG_MC_NEXT_DEACTIVATE_CHANNEL;
0166     DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg);
0167     int ret;
0168 
0169     wbuf[0] = port->ch.id;
0170     ret = slim_do_transfer(sdev->ctrl, &txn);
0171     if (ret)
0172         return ret;
0173 
0174     txn.mc = SLIM_MSG_MC_NEXT_REMOVE_CHANNEL;
0175     port->ch.state = SLIM_CH_STATE_REMOVED;
0176 
0177     return slim_do_transfer(sdev->ctrl, &txn);
0178 }
0179 
0180 static int slim_get_prate_code(int rate)
0181 {
0182     int i;
0183 
0184     for (i = 0; i < ARRAY_SIZE(slim_presence_rate_table); i++) {
0185         if (rate == slim_presence_rate_table[i])
0186             return i;
0187     }
0188 
0189     return -EINVAL;
0190 }
0191 
0192 /**
0193  * slim_stream_prepare() - Prepare a SLIMbus Stream
0194  *
0195  * @rt: instance of slim stream runtime to configure
0196  * @cfg: new configuration for the stream
0197  *
0198  * This API will configure SLIMbus stream with config parameters from cfg.
0199  * return zero on success and error code on failure. From ASoC DPCM framework,
0200  * this state is linked to hw_params() operation.
0201  */
0202 int slim_stream_prepare(struct slim_stream_runtime *rt,
0203             struct slim_stream_config *cfg)
0204 {
0205     struct slim_controller *ctrl = rt->dev->ctrl;
0206     struct slim_port *port;
0207     int num_ports, i, port_id;
0208 
0209     if (rt->ports) {
0210         dev_err(&rt->dev->dev, "Stream already Prepared\n");
0211         return -EINVAL;
0212     }
0213 
0214     num_ports = hweight32(cfg->port_mask);
0215     rt->ports = kcalloc(num_ports, sizeof(*port), GFP_KERNEL);
0216     if (!rt->ports)
0217         return -ENOMEM;
0218 
0219     rt->num_ports = num_ports;
0220     rt->rate = cfg->rate;
0221     rt->bps = cfg->bps;
0222     rt->direction = cfg->direction;
0223 
0224     if (cfg->rate % ctrl->a_framer->superfreq) {
0225         /*
0226          * data rate not exactly multiple of super frame,
0227          * use PUSH/PULL protocol
0228          */
0229         if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK)
0230             rt->prot = SLIM_PROTO_PUSH;
0231         else
0232             rt->prot = SLIM_PROTO_PULL;
0233     } else {
0234         rt->prot = SLIM_PROTO_ISO;
0235     }
0236 
0237     rt->ratem = cfg->rate/ctrl->a_framer->superfreq;
0238 
0239     i = 0;
0240     for_each_set_bit(port_id, &cfg->port_mask, SLIM_DEVICE_MAX_PORTS) {
0241         port = &rt->ports[i];
0242         port->state = SLIM_PORT_DISCONNECTED;
0243         port->id = port_id;
0244         port->ch.prrate = slim_get_prate_code(cfg->rate);
0245         port->ch.id = cfg->chs[i];
0246         port->ch.data_fmt = SLIM_CH_DATA_FMT_NOT_DEFINED;
0247         port->ch.aux_fmt = SLIM_CH_AUX_FMT_NOT_APPLICABLE;
0248         port->ch.state = SLIM_CH_STATE_ALLOCATED;
0249 
0250         if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK)
0251             port->direction = SLIM_PORT_SINK;
0252         else
0253             port->direction = SLIM_PORT_SOURCE;
0254 
0255         slim_connect_port_channel(rt, port);
0256         i++;
0257     }
0258 
0259     return 0;
0260 }
0261 EXPORT_SYMBOL_GPL(slim_stream_prepare);
0262 
0263 static int slim_define_channel_content(struct slim_stream_runtime *stream,
0264                        struct slim_port *port)
0265 {
0266     struct slim_device *sdev = stream->dev;
0267     u8 wbuf[4];
0268     struct slim_val_inf msg = {0, 4, NULL, wbuf, NULL};
0269     u8 mc = SLIM_MSG_MC_NEXT_DEFINE_CONTENT;
0270     DEFINE_SLIM_LDEST_TXN(txn, mc, 8, stream->dev->laddr, &msg);
0271 
0272     wbuf[0] = port->ch.id;
0273     wbuf[1] = port->ch.prrate;
0274 
0275     /* Frequency Locked for ISO Protocol */
0276     if (stream->prot != SLIM_PROTO_ISO)
0277         wbuf[1] |= SLIM_CHANNEL_CONTENT_FL;
0278 
0279     wbuf[2] = port->ch.data_fmt | (port->ch.aux_fmt << 4);
0280     wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS;
0281     port->ch.state = SLIM_CH_STATE_CONTENT_DEFINED;
0282 
0283     return slim_do_transfer(sdev->ctrl, &txn);
0284 }
0285 
0286 static int slim_get_segdist_code(int ratem)
0287 {
0288     int i;
0289 
0290     for (i = 0; i < ARRAY_SIZE(segdist_codes); i++) {
0291         if (segdist_codes[i].ratem == ratem)
0292             return segdist_codes[i].segdist_code;
0293     }
0294 
0295     return -EINVAL;
0296 }
0297 
0298 static int slim_define_channel(struct slim_stream_runtime *stream,
0299                        struct slim_port *port)
0300 {
0301     struct slim_device *sdev = stream->dev;
0302     u8 wbuf[4];
0303     struct slim_val_inf msg = {0, 4, NULL, wbuf, NULL};
0304     u8 mc = SLIM_MSG_MC_NEXT_DEFINE_CHANNEL;
0305     DEFINE_SLIM_LDEST_TXN(txn, mc, 8, stream->dev->laddr, &msg);
0306 
0307     port->ch.seg_dist = slim_get_segdist_code(stream->ratem);
0308 
0309     wbuf[0] = port->ch.id;
0310     wbuf[1] = port->ch.seg_dist & 0xFF;
0311     wbuf[2] = (stream->prot << 4) | ((port->ch.seg_dist & 0xF00) >> 8);
0312     if (stream->prot == SLIM_PROTO_ISO)
0313         wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS;
0314     else
0315         wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS + 1;
0316 
0317     port->ch.state = SLIM_CH_STATE_DEFINED;
0318 
0319     return slim_do_transfer(sdev->ctrl, &txn);
0320 }
0321 
0322 static int slim_activate_channel(struct slim_stream_runtime *stream,
0323                  struct slim_port *port)
0324 {
0325     struct slim_device *sdev = stream->dev;
0326     u8 wbuf[1];
0327     struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL};
0328     u8 mc = SLIM_MSG_MC_NEXT_ACTIVATE_CHANNEL;
0329     DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg);
0330 
0331     txn.msg->num_bytes = 1;
0332     txn.msg->wbuf = wbuf;
0333     wbuf[0] = port->ch.id;
0334     port->ch.state = SLIM_CH_STATE_ACTIVE;
0335 
0336     return slim_do_transfer(sdev->ctrl, &txn);
0337 }
0338 
0339 /**
0340  * slim_stream_enable() - Enable a prepared SLIMbus Stream
0341  *
0342  * @stream: instance of slim stream runtime to enable
0343  *
0344  * This API will enable all the ports and channels associated with
0345  * SLIMbus stream
0346  *
0347  * Return: zero on success and error code on failure. From ASoC DPCM framework,
0348  * this state is linked to trigger() start operation.
0349  */
0350 int slim_stream_enable(struct slim_stream_runtime *stream)
0351 {
0352     DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION,
0353                 3, SLIM_LA_MANAGER, NULL);
0354     struct slim_controller *ctrl = stream->dev->ctrl;
0355     int ret, i;
0356 
0357     if (ctrl->enable_stream) {
0358         ret = ctrl->enable_stream(stream);
0359         if (ret)
0360             return ret;
0361 
0362         for (i = 0; i < stream->num_ports; i++)
0363             stream->ports[i].ch.state = SLIM_CH_STATE_ACTIVE;
0364 
0365         return ret;
0366     }
0367 
0368     ret = slim_do_transfer(ctrl, &txn);
0369     if (ret)
0370         return ret;
0371 
0372     /* define channels first before activating them */
0373     for (i = 0; i < stream->num_ports; i++) {
0374         struct slim_port *port = &stream->ports[i];
0375 
0376         slim_define_channel(stream, port);
0377         slim_define_channel_content(stream, port);
0378     }
0379 
0380     for (i = 0; i < stream->num_ports; i++) {
0381         struct slim_port *port = &stream->ports[i];
0382 
0383         slim_activate_channel(stream, port);
0384         port->state = SLIM_PORT_CONFIGURED;
0385     }
0386     txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW;
0387 
0388     return slim_do_transfer(ctrl, &txn);
0389 }
0390 EXPORT_SYMBOL_GPL(slim_stream_enable);
0391 
0392 /**
0393  * slim_stream_disable() - Disable a SLIMbus Stream
0394  *
0395  * @stream: instance of slim stream runtime to disable
0396  *
0397  * This API will disable all the ports and channels associated with
0398  * SLIMbus stream
0399  *
0400  * Return: zero on success and error code on failure. From ASoC DPCM framework,
0401  * this state is linked to trigger() pause operation.
0402  */
0403 int slim_stream_disable(struct slim_stream_runtime *stream)
0404 {
0405     DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION,
0406                 3, SLIM_LA_MANAGER, NULL);
0407     struct slim_controller *ctrl = stream->dev->ctrl;
0408     int ret, i;
0409 
0410     if (ctrl->disable_stream)
0411         ctrl->disable_stream(stream);
0412 
0413     ret = slim_do_transfer(ctrl, &txn);
0414     if (ret)
0415         return ret;
0416 
0417     for (i = 0; i < stream->num_ports; i++)
0418         slim_deactivate_remove_channel(stream, &stream->ports[i]);
0419 
0420     txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW;
0421 
0422     return slim_do_transfer(ctrl, &txn);
0423 }
0424 EXPORT_SYMBOL_GPL(slim_stream_disable);
0425 
0426 /**
0427  * slim_stream_unprepare() - Un-prepare a SLIMbus Stream
0428  *
0429  * @stream: instance of slim stream runtime to unprepare
0430  *
0431  * This API will un allocate all the ports and channels associated with
0432  * SLIMbus stream
0433  *
0434  * Return: zero on success and error code on failure. From ASoC DPCM framework,
0435  * this state is linked to trigger() stop operation.
0436  */
0437 int slim_stream_unprepare(struct slim_stream_runtime *stream)
0438 {
0439     int i;
0440 
0441     for (i = 0; i < stream->num_ports; i++)
0442         slim_disconnect_port(stream, &stream->ports[i]);
0443 
0444     kfree(stream->ports);
0445     stream->ports = NULL;
0446     stream->num_ports = 0;
0447 
0448     return 0;
0449 }
0450 EXPORT_SYMBOL_GPL(slim_stream_unprepare);
0451 
0452 /**
0453  * slim_stream_free() - Free a SLIMbus Stream
0454  *
0455  * @stream: instance of slim stream runtime to free
0456  *
0457  * This API will un allocate all the memory associated with
0458  * slim stream runtime, user is not allowed to make an dereference
0459  * to stream after this call.
0460  *
0461  * Return: zero on success and error code on failure. From ASoC DPCM framework,
0462  * this state is linked to shutdown() operation.
0463  */
0464 int slim_stream_free(struct slim_stream_runtime *stream)
0465 {
0466     struct slim_device *sdev = stream->dev;
0467 
0468     spin_lock(&sdev->stream_list_lock);
0469     list_del(&stream->node);
0470     spin_unlock(&sdev->stream_list_lock);
0471 
0472     kfree(stream->name);
0473     kfree(stream);
0474 
0475     return 0;
0476 }
0477 EXPORT_SYMBOL_GPL(slim_stream_free);