162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Copyright (c) 2018, Linaro Limited 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/kernel.h> 562306a36Sopenharmony_ci#include <linux/errno.h> 662306a36Sopenharmony_ci#include <linux/slab.h> 762306a36Sopenharmony_ci#include <linux/list.h> 862306a36Sopenharmony_ci#include <linux/slimbus.h> 962306a36Sopenharmony_ci#include <uapi/sound/asound.h> 1062306a36Sopenharmony_ci#include "slimbus.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/** 1362306a36Sopenharmony_ci * struct segdist_code - Segment Distributions code from 1462306a36Sopenharmony_ci * Table 20 of SLIMbus Specs Version 2.0 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * @ratem: Channel Rate Multipler(Segments per Superframe) 1762306a36Sopenharmony_ci * @seg_interval: Number of slots between the first Slot of Segment 1862306a36Sopenharmony_ci * and the first slot of the next consecutive Segment. 1962306a36Sopenharmony_ci * @segdist_code: Segment Distribution Code SD[11:0] 2062306a36Sopenharmony_ci * @seg_offset_mask: Segment offset mask in SD[11:0] 2162306a36Sopenharmony_ci * @segdist_codes: List of all possible Segmet Distribution codes. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_cistatic const struct segdist_code { 2462306a36Sopenharmony_ci int ratem; 2562306a36Sopenharmony_ci int seg_interval; 2662306a36Sopenharmony_ci int segdist_code; 2762306a36Sopenharmony_ci u32 seg_offset_mask; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci} segdist_codes[] = { 3062306a36Sopenharmony_ci {1, 1536, 0x200, 0xdff}, 3162306a36Sopenharmony_ci {2, 768, 0x100, 0xcff}, 3262306a36Sopenharmony_ci {4, 384, 0x080, 0xc7f}, 3362306a36Sopenharmony_ci {8, 192, 0x040, 0xc3f}, 3462306a36Sopenharmony_ci {16, 96, 0x020, 0xc1f}, 3562306a36Sopenharmony_ci {32, 48, 0x010, 0xc0f}, 3662306a36Sopenharmony_ci {64, 24, 0x008, 0xc07}, 3762306a36Sopenharmony_ci {128, 12, 0x004, 0xc03}, 3862306a36Sopenharmony_ci {256, 6, 0x002, 0xc01}, 3962306a36Sopenharmony_ci {512, 3, 0x001, 0xc00}, 4062306a36Sopenharmony_ci {3, 512, 0xe00, 0x1ff}, 4162306a36Sopenharmony_ci {6, 256, 0xd00, 0x0ff}, 4262306a36Sopenharmony_ci {12, 128, 0xc80, 0x07f}, 4362306a36Sopenharmony_ci {24, 64, 0xc40, 0x03f}, 4462306a36Sopenharmony_ci {48, 32, 0xc20, 0x01f}, 4562306a36Sopenharmony_ci {96, 16, 0xc10, 0x00f}, 4662306a36Sopenharmony_ci {192, 8, 0xc08, 0x007}, 4762306a36Sopenharmony_ci {364, 4, 0xc04, 0x003}, 4862306a36Sopenharmony_ci {768, 2, 0xc02, 0x001}, 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* 5262306a36Sopenharmony_ci * Presence Rate table for all Natural Frequencies 5362306a36Sopenharmony_ci * The Presence rate of a constant bitrate stream is mean flow rate of the 5462306a36Sopenharmony_ci * stream expressed in occupied Segments of that Data Channel per second. 5562306a36Sopenharmony_ci * Table 66 from SLIMbus 2.0 Specs 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * Index of the table corresponds to Presence rate code for the respective rate 5862306a36Sopenharmony_ci * in the table. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_cistatic const int slim_presence_rate_table[] = { 6162306a36Sopenharmony_ci 0, /* Not Indicated */ 6262306a36Sopenharmony_ci 12000, 6362306a36Sopenharmony_ci 24000, 6462306a36Sopenharmony_ci 48000, 6562306a36Sopenharmony_ci 96000, 6662306a36Sopenharmony_ci 192000, 6762306a36Sopenharmony_ci 384000, 6862306a36Sopenharmony_ci 768000, 6962306a36Sopenharmony_ci 0, /* Reserved */ 7062306a36Sopenharmony_ci 11025, 7162306a36Sopenharmony_ci 22050, 7262306a36Sopenharmony_ci 44100, 7362306a36Sopenharmony_ci 88200, 7462306a36Sopenharmony_ci 176400, 7562306a36Sopenharmony_ci 352800, 7662306a36Sopenharmony_ci 705600, 7762306a36Sopenharmony_ci 4000, 7862306a36Sopenharmony_ci 8000, 7962306a36Sopenharmony_ci 16000, 8062306a36Sopenharmony_ci 32000, 8162306a36Sopenharmony_ci 64000, 8262306a36Sopenharmony_ci 128000, 8362306a36Sopenharmony_ci 256000, 8462306a36Sopenharmony_ci 512000, 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/** 8862306a36Sopenharmony_ci * slim_stream_allocate() - Allocate a new SLIMbus Stream 8962306a36Sopenharmony_ci * @dev:Slim device to be associated with 9062306a36Sopenharmony_ci * @name: name of the stream 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * This is very first call for SLIMbus streaming, this API will allocate 9362306a36Sopenharmony_ci * a new SLIMbus stream and return a valid stream runtime pointer for client 9462306a36Sopenharmony_ci * to use it in subsequent stream apis. state of stream is set to ALLOCATED 9562306a36Sopenharmony_ci * 9662306a36Sopenharmony_ci * Return: valid pointer on success and error code on failure. 9762306a36Sopenharmony_ci * From ASoC DPCM framework, this state is linked to startup() operation. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_cistruct slim_stream_runtime *slim_stream_allocate(struct slim_device *dev, 10062306a36Sopenharmony_ci const char *name) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct slim_stream_runtime *rt; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci rt = kzalloc(sizeof(*rt), GFP_KERNEL); 10562306a36Sopenharmony_ci if (!rt) 10662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci rt->name = kasprintf(GFP_KERNEL, "slim-%s", name); 10962306a36Sopenharmony_ci if (!rt->name) { 11062306a36Sopenharmony_ci kfree(rt); 11162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci rt->dev = dev; 11562306a36Sopenharmony_ci spin_lock(&dev->stream_list_lock); 11662306a36Sopenharmony_ci list_add_tail(&rt->node, &dev->stream_list); 11762306a36Sopenharmony_ci spin_unlock(&dev->stream_list_lock); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return rt; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_stream_allocate); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int slim_connect_port_channel(struct slim_stream_runtime *stream, 12462306a36Sopenharmony_ci struct slim_port *port) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct slim_device *sdev = stream->dev; 12762306a36Sopenharmony_ci u8 wbuf[2]; 12862306a36Sopenharmony_ci struct slim_val_inf msg = {0, 2, NULL, wbuf, NULL}; 12962306a36Sopenharmony_ci u8 mc = SLIM_MSG_MC_CONNECT_SOURCE; 13062306a36Sopenharmony_ci DEFINE_SLIM_LDEST_TXN(txn, mc, 6, stream->dev->laddr, &msg); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (port->direction == SLIM_PORT_SINK) 13362306a36Sopenharmony_ci txn.mc = SLIM_MSG_MC_CONNECT_SINK; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci wbuf[0] = port->id; 13662306a36Sopenharmony_ci wbuf[1] = port->ch.id; 13762306a36Sopenharmony_ci port->ch.state = SLIM_CH_STATE_ASSOCIATED; 13862306a36Sopenharmony_ci port->state = SLIM_PORT_UNCONFIGURED; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return slim_do_transfer(sdev->ctrl, &txn); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int slim_disconnect_port(struct slim_stream_runtime *stream, 14462306a36Sopenharmony_ci struct slim_port *port) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct slim_device *sdev = stream->dev; 14762306a36Sopenharmony_ci u8 wbuf[1]; 14862306a36Sopenharmony_ci struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL}; 14962306a36Sopenharmony_ci u8 mc = SLIM_MSG_MC_DISCONNECT_PORT; 15062306a36Sopenharmony_ci DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci wbuf[0] = port->id; 15362306a36Sopenharmony_ci port->ch.state = SLIM_CH_STATE_DISCONNECTED; 15462306a36Sopenharmony_ci port->state = SLIM_PORT_DISCONNECTED; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return slim_do_transfer(sdev->ctrl, &txn); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int slim_deactivate_remove_channel(struct slim_stream_runtime *stream, 16062306a36Sopenharmony_ci struct slim_port *port) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct slim_device *sdev = stream->dev; 16362306a36Sopenharmony_ci u8 wbuf[1]; 16462306a36Sopenharmony_ci struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL}; 16562306a36Sopenharmony_ci u8 mc = SLIM_MSG_MC_NEXT_DEACTIVATE_CHANNEL; 16662306a36Sopenharmony_ci DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg); 16762306a36Sopenharmony_ci int ret; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci wbuf[0] = port->ch.id; 17062306a36Sopenharmony_ci ret = slim_do_transfer(sdev->ctrl, &txn); 17162306a36Sopenharmony_ci if (ret) 17262306a36Sopenharmony_ci return ret; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci txn.mc = SLIM_MSG_MC_NEXT_REMOVE_CHANNEL; 17562306a36Sopenharmony_ci port->ch.state = SLIM_CH_STATE_REMOVED; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return slim_do_transfer(sdev->ctrl, &txn); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic int slim_get_prate_code(int rate) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci int i; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(slim_presence_rate_table); i++) { 18562306a36Sopenharmony_ci if (rate == slim_presence_rate_table[i]) 18662306a36Sopenharmony_ci return i; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return -EINVAL; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/** 19362306a36Sopenharmony_ci * slim_stream_prepare() - Prepare a SLIMbus Stream 19462306a36Sopenharmony_ci * 19562306a36Sopenharmony_ci * @rt: instance of slim stream runtime to configure 19662306a36Sopenharmony_ci * @cfg: new configuration for the stream 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * This API will configure SLIMbus stream with config parameters from cfg. 19962306a36Sopenharmony_ci * return zero on success and error code on failure. From ASoC DPCM framework, 20062306a36Sopenharmony_ci * this state is linked to hw_params() operation. 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_ciint slim_stream_prepare(struct slim_stream_runtime *rt, 20362306a36Sopenharmony_ci struct slim_stream_config *cfg) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct slim_controller *ctrl = rt->dev->ctrl; 20662306a36Sopenharmony_ci struct slim_port *port; 20762306a36Sopenharmony_ci int num_ports, i, port_id, prrate; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (rt->ports) { 21062306a36Sopenharmony_ci dev_err(&rt->dev->dev, "Stream already Prepared\n"); 21162306a36Sopenharmony_ci return -EINVAL; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci num_ports = hweight32(cfg->port_mask); 21562306a36Sopenharmony_ci rt->ports = kcalloc(num_ports, sizeof(*port), GFP_KERNEL); 21662306a36Sopenharmony_ci if (!rt->ports) 21762306a36Sopenharmony_ci return -ENOMEM; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci rt->num_ports = num_ports; 22062306a36Sopenharmony_ci rt->rate = cfg->rate; 22162306a36Sopenharmony_ci rt->bps = cfg->bps; 22262306a36Sopenharmony_ci rt->direction = cfg->direction; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci prrate = slim_get_prate_code(cfg->rate); 22562306a36Sopenharmony_ci if (prrate < 0) { 22662306a36Sopenharmony_ci dev_err(&rt->dev->dev, "Cannot get presence rate for rate %d Hz\n", 22762306a36Sopenharmony_ci cfg->rate); 22862306a36Sopenharmony_ci return prrate; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (cfg->rate % ctrl->a_framer->superfreq) { 23262306a36Sopenharmony_ci /* 23362306a36Sopenharmony_ci * data rate not exactly multiple of super frame, 23462306a36Sopenharmony_ci * use PUSH/PULL protocol 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK) 23762306a36Sopenharmony_ci rt->prot = SLIM_PROTO_PUSH; 23862306a36Sopenharmony_ci else 23962306a36Sopenharmony_ci rt->prot = SLIM_PROTO_PULL; 24062306a36Sopenharmony_ci } else { 24162306a36Sopenharmony_ci rt->prot = SLIM_PROTO_ISO; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci rt->ratem = cfg->rate/ctrl->a_framer->superfreq; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci i = 0; 24762306a36Sopenharmony_ci for_each_set_bit(port_id, &cfg->port_mask, SLIM_DEVICE_MAX_PORTS) { 24862306a36Sopenharmony_ci port = &rt->ports[i]; 24962306a36Sopenharmony_ci port->state = SLIM_PORT_DISCONNECTED; 25062306a36Sopenharmony_ci port->id = port_id; 25162306a36Sopenharmony_ci port->ch.prrate = prrate; 25262306a36Sopenharmony_ci port->ch.id = cfg->chs[i]; 25362306a36Sopenharmony_ci port->ch.data_fmt = SLIM_CH_DATA_FMT_NOT_DEFINED; 25462306a36Sopenharmony_ci port->ch.aux_fmt = SLIM_CH_AUX_FMT_NOT_APPLICABLE; 25562306a36Sopenharmony_ci port->ch.state = SLIM_CH_STATE_ALLOCATED; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK) 25862306a36Sopenharmony_ci port->direction = SLIM_PORT_SINK; 25962306a36Sopenharmony_ci else 26062306a36Sopenharmony_ci port->direction = SLIM_PORT_SOURCE; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci slim_connect_port_channel(rt, port); 26362306a36Sopenharmony_ci i++; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_stream_prepare); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic int slim_define_channel_content(struct slim_stream_runtime *stream, 27162306a36Sopenharmony_ci struct slim_port *port) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct slim_device *sdev = stream->dev; 27462306a36Sopenharmony_ci u8 wbuf[4]; 27562306a36Sopenharmony_ci struct slim_val_inf msg = {0, 4, NULL, wbuf, NULL}; 27662306a36Sopenharmony_ci u8 mc = SLIM_MSG_MC_NEXT_DEFINE_CONTENT; 27762306a36Sopenharmony_ci DEFINE_SLIM_LDEST_TXN(txn, mc, 8, stream->dev->laddr, &msg); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci wbuf[0] = port->ch.id; 28062306a36Sopenharmony_ci wbuf[1] = port->ch.prrate; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* Frequency Locked for ISO Protocol */ 28362306a36Sopenharmony_ci if (stream->prot != SLIM_PROTO_ISO) 28462306a36Sopenharmony_ci wbuf[1] |= SLIM_CHANNEL_CONTENT_FL; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci wbuf[2] = port->ch.data_fmt | (port->ch.aux_fmt << 4); 28762306a36Sopenharmony_ci wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS; 28862306a36Sopenharmony_ci port->ch.state = SLIM_CH_STATE_CONTENT_DEFINED; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return slim_do_transfer(sdev->ctrl, &txn); 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int slim_get_segdist_code(int ratem) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci int i; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(segdist_codes); i++) { 29862306a36Sopenharmony_ci if (segdist_codes[i].ratem == ratem) 29962306a36Sopenharmony_ci return segdist_codes[i].segdist_code; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return -EINVAL; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int slim_define_channel(struct slim_stream_runtime *stream, 30662306a36Sopenharmony_ci struct slim_port *port) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct slim_device *sdev = stream->dev; 30962306a36Sopenharmony_ci u8 wbuf[4]; 31062306a36Sopenharmony_ci struct slim_val_inf msg = {0, 4, NULL, wbuf, NULL}; 31162306a36Sopenharmony_ci u8 mc = SLIM_MSG_MC_NEXT_DEFINE_CHANNEL; 31262306a36Sopenharmony_ci DEFINE_SLIM_LDEST_TXN(txn, mc, 8, stream->dev->laddr, &msg); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci port->ch.seg_dist = slim_get_segdist_code(stream->ratem); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci wbuf[0] = port->ch.id; 31762306a36Sopenharmony_ci wbuf[1] = port->ch.seg_dist & 0xFF; 31862306a36Sopenharmony_ci wbuf[2] = (stream->prot << 4) | ((port->ch.seg_dist & 0xF00) >> 8); 31962306a36Sopenharmony_ci if (stream->prot == SLIM_PROTO_ISO) 32062306a36Sopenharmony_ci wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS; 32162306a36Sopenharmony_ci else 32262306a36Sopenharmony_ci wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS + 1; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci port->ch.state = SLIM_CH_STATE_DEFINED; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return slim_do_transfer(sdev->ctrl, &txn); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic int slim_activate_channel(struct slim_stream_runtime *stream, 33062306a36Sopenharmony_ci struct slim_port *port) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct slim_device *sdev = stream->dev; 33362306a36Sopenharmony_ci u8 wbuf[1]; 33462306a36Sopenharmony_ci struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL}; 33562306a36Sopenharmony_ci u8 mc = SLIM_MSG_MC_NEXT_ACTIVATE_CHANNEL; 33662306a36Sopenharmony_ci DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci txn.msg->num_bytes = 1; 33962306a36Sopenharmony_ci txn.msg->wbuf = wbuf; 34062306a36Sopenharmony_ci wbuf[0] = port->ch.id; 34162306a36Sopenharmony_ci port->ch.state = SLIM_CH_STATE_ACTIVE; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return slim_do_transfer(sdev->ctrl, &txn); 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci/** 34762306a36Sopenharmony_ci * slim_stream_enable() - Enable a prepared SLIMbus Stream 34862306a36Sopenharmony_ci * 34962306a36Sopenharmony_ci * @stream: instance of slim stream runtime to enable 35062306a36Sopenharmony_ci * 35162306a36Sopenharmony_ci * This API will enable all the ports and channels associated with 35262306a36Sopenharmony_ci * SLIMbus stream 35362306a36Sopenharmony_ci * 35462306a36Sopenharmony_ci * Return: zero on success and error code on failure. From ASoC DPCM framework, 35562306a36Sopenharmony_ci * this state is linked to trigger() start operation. 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_ciint slim_stream_enable(struct slim_stream_runtime *stream) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION, 36062306a36Sopenharmony_ci 3, SLIM_LA_MANAGER, NULL); 36162306a36Sopenharmony_ci struct slim_controller *ctrl = stream->dev->ctrl; 36262306a36Sopenharmony_ci int ret, i; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (ctrl->enable_stream) { 36562306a36Sopenharmony_ci ret = ctrl->enable_stream(stream); 36662306a36Sopenharmony_ci if (ret) 36762306a36Sopenharmony_ci return ret; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci for (i = 0; i < stream->num_ports; i++) 37062306a36Sopenharmony_ci stream->ports[i].ch.state = SLIM_CH_STATE_ACTIVE; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return ret; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci ret = slim_do_transfer(ctrl, &txn); 37662306a36Sopenharmony_ci if (ret) 37762306a36Sopenharmony_ci return ret; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* define channels first before activating them */ 38062306a36Sopenharmony_ci for (i = 0; i < stream->num_ports; i++) { 38162306a36Sopenharmony_ci struct slim_port *port = &stream->ports[i]; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci slim_define_channel(stream, port); 38462306a36Sopenharmony_ci slim_define_channel_content(stream, port); 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci for (i = 0; i < stream->num_ports; i++) { 38862306a36Sopenharmony_ci struct slim_port *port = &stream->ports[i]; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci slim_activate_channel(stream, port); 39162306a36Sopenharmony_ci port->state = SLIM_PORT_CONFIGURED; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return slim_do_transfer(ctrl, &txn); 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_stream_enable); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci/** 40062306a36Sopenharmony_ci * slim_stream_disable() - Disable a SLIMbus Stream 40162306a36Sopenharmony_ci * 40262306a36Sopenharmony_ci * @stream: instance of slim stream runtime to disable 40362306a36Sopenharmony_ci * 40462306a36Sopenharmony_ci * This API will disable all the ports and channels associated with 40562306a36Sopenharmony_ci * SLIMbus stream 40662306a36Sopenharmony_ci * 40762306a36Sopenharmony_ci * Return: zero on success and error code on failure. From ASoC DPCM framework, 40862306a36Sopenharmony_ci * this state is linked to trigger() pause operation. 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_ciint slim_stream_disable(struct slim_stream_runtime *stream) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION, 41362306a36Sopenharmony_ci 3, SLIM_LA_MANAGER, NULL); 41462306a36Sopenharmony_ci struct slim_controller *ctrl = stream->dev->ctrl; 41562306a36Sopenharmony_ci int ret, i; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (!stream->ports || !stream->num_ports) 41862306a36Sopenharmony_ci return -EINVAL; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (ctrl->disable_stream) 42162306a36Sopenharmony_ci ctrl->disable_stream(stream); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci ret = slim_do_transfer(ctrl, &txn); 42462306a36Sopenharmony_ci if (ret) 42562306a36Sopenharmony_ci return ret; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci for (i = 0; i < stream->num_ports; i++) 42862306a36Sopenharmony_ci slim_deactivate_remove_channel(stream, &stream->ports[i]); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return slim_do_transfer(ctrl, &txn); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_stream_disable); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/** 43762306a36Sopenharmony_ci * slim_stream_unprepare() - Un-prepare a SLIMbus Stream 43862306a36Sopenharmony_ci * 43962306a36Sopenharmony_ci * @stream: instance of slim stream runtime to unprepare 44062306a36Sopenharmony_ci * 44162306a36Sopenharmony_ci * This API will un allocate all the ports and channels associated with 44262306a36Sopenharmony_ci * SLIMbus stream 44362306a36Sopenharmony_ci * 44462306a36Sopenharmony_ci * Return: zero on success and error code on failure. From ASoC DPCM framework, 44562306a36Sopenharmony_ci * this state is linked to trigger() stop operation. 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_ciint slim_stream_unprepare(struct slim_stream_runtime *stream) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci int i; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (!stream->ports || !stream->num_ports) 45262306a36Sopenharmony_ci return -EINVAL; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci for (i = 0; i < stream->num_ports; i++) 45562306a36Sopenharmony_ci slim_disconnect_port(stream, &stream->ports[i]); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci kfree(stream->ports); 45862306a36Sopenharmony_ci stream->ports = NULL; 45962306a36Sopenharmony_ci stream->num_ports = 0; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci return 0; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_stream_unprepare); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci/** 46662306a36Sopenharmony_ci * slim_stream_free() - Free a SLIMbus Stream 46762306a36Sopenharmony_ci * 46862306a36Sopenharmony_ci * @stream: instance of slim stream runtime to free 46962306a36Sopenharmony_ci * 47062306a36Sopenharmony_ci * This API will un allocate all the memory associated with 47162306a36Sopenharmony_ci * slim stream runtime, user is not allowed to make an dereference 47262306a36Sopenharmony_ci * to stream after this call. 47362306a36Sopenharmony_ci * 47462306a36Sopenharmony_ci * Return: zero on success and error code on failure. From ASoC DPCM framework, 47562306a36Sopenharmony_ci * this state is linked to shutdown() operation. 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ciint slim_stream_free(struct slim_stream_runtime *stream) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct slim_device *sdev = stream->dev; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci spin_lock(&sdev->stream_list_lock); 48262306a36Sopenharmony_ci list_del(&stream->node); 48362306a36Sopenharmony_ci spin_unlock(&sdev->stream_list_lock); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci kfree(stream->name); 48662306a36Sopenharmony_ci kfree(stream); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return 0; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_stream_free); 491