493 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2018, Linaro Limited3 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 from14 * Table 20 of SLIMbus Specs Version 2.015 *16 * @ratem: Channel Rate Multipler(Segments per Superframe)17 * @seg_interval: Number of slots between the first Slot of Segment18 * 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 */22struct 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. */31static 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 Frequencies55 * The Presence rate of a constant bitrate stream is mean flow rate of the56 * stream expressed in occupied Segments of that Data Channel per second.57 * Table 66 from SLIMbus 2.0 Specs58 *59 * Index of the table corresponds to Presence rate code for the respective rate60 * in the table.61 */62static 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 Stream91 * @dev:Slim device to be associated with92 * @name: name of the stream93 *94 * This is very first call for SLIMbus streaming, this API will allocate95 * a new SLIMbus stream and return a valid stream runtime pointer for client96 * to use it in subsequent stream apis. state of stream is set to ALLOCATED97 *98 * Return: valid pointer on success and error code on failure.99 * From ASoC DPCM framework, this state is linked to startup() operation.100 */101struct 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}123EXPORT_SYMBOL_GPL(slim_stream_allocate);124 125static 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 145static 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 161static 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 182static 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 Stream196 *197 * @rt: instance of slim stream runtime to configure198 * @cfg: new configuration for the stream199 *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 */204int 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 protocol237 */238 if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK)239 rt->prot = SLIM_PROTO_PUSH;240 else241 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 else262 port->direction = SLIM_PORT_SOURCE;263 264 slim_connect_port_channel(rt, port);265 i++;266 }267 268 return 0;269}270EXPORT_SYMBOL_GPL(slim_stream_prepare);271 272static 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 295static 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 307static 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 else324 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 331static 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 Stream350 *351 * @stream: instance of slim stream runtime to enable352 *353 * This API will enable all the ports and channels associated with354 * SLIMbus stream355 *356 * Return: zero on success and error code on failure. From ASoC DPCM framework,357 * this state is linked to trigger() start operation.358 */359int 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}399EXPORT_SYMBOL_GPL(slim_stream_enable);400 401/**402 * slim_stream_disable() - Disable a SLIMbus Stream403 *404 * @stream: instance of slim stream runtime to disable405 *406 * This API will disable all the ports and channels associated with407 * SLIMbus stream408 *409 * Return: zero on success and error code on failure. From ASoC DPCM framework,410 * this state is linked to trigger() pause operation.411 */412int 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}436EXPORT_SYMBOL_GPL(slim_stream_disable);437 438/**439 * slim_stream_unprepare() - Un-prepare a SLIMbus Stream440 *441 * @stream: instance of slim stream runtime to unprepare442 *443 * This API will un allocate all the ports and channels associated with444 * SLIMbus stream445 *446 * Return: zero on success and error code on failure. From ASoC DPCM framework,447 * this state is linked to trigger() stop operation.448 */449int 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}465EXPORT_SYMBOL_GPL(slim_stream_unprepare);466 467/**468 * slim_stream_free() - Free a SLIMbus Stream469 *470 * @stream: instance of slim stream runtime to free471 *472 * This API will un allocate all the memory associated with473 * slim stream runtime, user is not allowed to make an dereference474 * 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 */479int 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}492EXPORT_SYMBOL_GPL(slim_stream_free);493