18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// Copyright (c) 2018, Linaro Limited 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/kernel.h> 58c2ecf20Sopenharmony_ci#include <linux/errno.h> 68c2ecf20Sopenharmony_ci#include <linux/slab.h> 78c2ecf20Sopenharmony_ci#include <linux/list.h> 88c2ecf20Sopenharmony_ci#include <linux/slimbus.h> 98c2ecf20Sopenharmony_ci#include <uapi/sound/asound.h> 108c2ecf20Sopenharmony_ci#include "slimbus.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/** 138c2ecf20Sopenharmony_ci * struct segdist_code - Segment Distributions code from 148c2ecf20Sopenharmony_ci * Table 20 of SLIMbus Specs Version 2.0 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * @ratem: Channel Rate Multipler(Segments per Superframe) 178c2ecf20Sopenharmony_ci * @seg_interval: Number of slots between the first Slot of Segment 188c2ecf20Sopenharmony_ci * and the first slot of the next consecutive Segment. 198c2ecf20Sopenharmony_ci * @segdist_code: Segment Distribution Code SD[11:0] 208c2ecf20Sopenharmony_ci * @seg_offset_mask: Segment offset mask in SD[11:0] 218c2ecf20Sopenharmony_ci * @segdist_codes: List of all possible Segmet Distribution codes. 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_cistatic const struct segdist_code { 248c2ecf20Sopenharmony_ci int ratem; 258c2ecf20Sopenharmony_ci int seg_interval; 268c2ecf20Sopenharmony_ci int segdist_code; 278c2ecf20Sopenharmony_ci u32 seg_offset_mask; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci} segdist_codes[] = { 308c2ecf20Sopenharmony_ci {1, 1536, 0x200, 0xdff}, 318c2ecf20Sopenharmony_ci {2, 768, 0x100, 0xcff}, 328c2ecf20Sopenharmony_ci {4, 384, 0x080, 0xc7f}, 338c2ecf20Sopenharmony_ci {8, 192, 0x040, 0xc3f}, 348c2ecf20Sopenharmony_ci {16, 96, 0x020, 0xc1f}, 358c2ecf20Sopenharmony_ci {32, 48, 0x010, 0xc0f}, 368c2ecf20Sopenharmony_ci {64, 24, 0x008, 0xc07}, 378c2ecf20Sopenharmony_ci {128, 12, 0x004, 0xc03}, 388c2ecf20Sopenharmony_ci {256, 6, 0x002, 0xc01}, 398c2ecf20Sopenharmony_ci {512, 3, 0x001, 0xc00}, 408c2ecf20Sopenharmony_ci {3, 512, 0xe00, 0x1ff}, 418c2ecf20Sopenharmony_ci {6, 256, 0xd00, 0x0ff}, 428c2ecf20Sopenharmony_ci {12, 128, 0xc80, 0x07f}, 438c2ecf20Sopenharmony_ci {24, 64, 0xc40, 0x03f}, 448c2ecf20Sopenharmony_ci {48, 32, 0xc20, 0x01f}, 458c2ecf20Sopenharmony_ci {96, 16, 0xc10, 0x00f}, 468c2ecf20Sopenharmony_ci {192, 8, 0xc08, 0x007}, 478c2ecf20Sopenharmony_ci {364, 4, 0xc04, 0x003}, 488c2ecf20Sopenharmony_ci {768, 2, 0xc02, 0x001}, 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* 528c2ecf20Sopenharmony_ci * Presence Rate table for all Natural Frequencies 538c2ecf20Sopenharmony_ci * The Presence rate of a constant bitrate stream is mean flow rate of the 548c2ecf20Sopenharmony_ci * stream expressed in occupied Segments of that Data Channel per second. 558c2ecf20Sopenharmony_ci * Table 66 from SLIMbus 2.0 Specs 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * Index of the table corresponds to Presence rate code for the respective rate 588c2ecf20Sopenharmony_ci * in the table. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_cistatic const int slim_presence_rate_table[] = { 618c2ecf20Sopenharmony_ci 0, /* Not Indicated */ 628c2ecf20Sopenharmony_ci 12000, 638c2ecf20Sopenharmony_ci 24000, 648c2ecf20Sopenharmony_ci 48000, 658c2ecf20Sopenharmony_ci 96000, 668c2ecf20Sopenharmony_ci 192000, 678c2ecf20Sopenharmony_ci 384000, 688c2ecf20Sopenharmony_ci 768000, 698c2ecf20Sopenharmony_ci 0, /* Reserved */ 708c2ecf20Sopenharmony_ci 11025, 718c2ecf20Sopenharmony_ci 22050, 728c2ecf20Sopenharmony_ci 44100, 738c2ecf20Sopenharmony_ci 88200, 748c2ecf20Sopenharmony_ci 176400, 758c2ecf20Sopenharmony_ci 352800, 768c2ecf20Sopenharmony_ci 705600, 778c2ecf20Sopenharmony_ci 4000, 788c2ecf20Sopenharmony_ci 8000, 798c2ecf20Sopenharmony_ci 16000, 808c2ecf20Sopenharmony_ci 32000, 818c2ecf20Sopenharmony_ci 64000, 828c2ecf20Sopenharmony_ci 128000, 838c2ecf20Sopenharmony_ci 256000, 848c2ecf20Sopenharmony_ci 512000, 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/** 888c2ecf20Sopenharmony_ci * slim_stream_allocate() - Allocate a new SLIMbus Stream 898c2ecf20Sopenharmony_ci * @dev:Slim device to be associated with 908c2ecf20Sopenharmony_ci * @name: name of the stream 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * This is very first call for SLIMbus streaming, this API will allocate 938c2ecf20Sopenharmony_ci * a new SLIMbus stream and return a valid stream runtime pointer for client 948c2ecf20Sopenharmony_ci * to use it in subsequent stream apis. state of stream is set to ALLOCATED 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * Return: valid pointer on success and error code on failure. 978c2ecf20Sopenharmony_ci * From ASoC DPCM framework, this state is linked to startup() operation. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_cistruct slim_stream_runtime *slim_stream_allocate(struct slim_device *dev, 1008c2ecf20Sopenharmony_ci const char *name) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct slim_stream_runtime *rt; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci rt = kzalloc(sizeof(*rt), GFP_KERNEL); 1058c2ecf20Sopenharmony_ci if (!rt) 1068c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci rt->name = kasprintf(GFP_KERNEL, "slim-%s", name); 1098c2ecf20Sopenharmony_ci if (!rt->name) { 1108c2ecf20Sopenharmony_ci kfree(rt); 1118c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci rt->dev = dev; 1158c2ecf20Sopenharmony_ci spin_lock(&dev->stream_list_lock); 1168c2ecf20Sopenharmony_ci list_add_tail(&rt->node, &dev->stream_list); 1178c2ecf20Sopenharmony_ci spin_unlock(&dev->stream_list_lock); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return rt; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_stream_allocate); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int slim_connect_port_channel(struct slim_stream_runtime *stream, 1248c2ecf20Sopenharmony_ci struct slim_port *port) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct slim_device *sdev = stream->dev; 1278c2ecf20Sopenharmony_ci u8 wbuf[2]; 1288c2ecf20Sopenharmony_ci struct slim_val_inf msg = {0, 2, NULL, wbuf, NULL}; 1298c2ecf20Sopenharmony_ci u8 mc = SLIM_MSG_MC_CONNECT_SOURCE; 1308c2ecf20Sopenharmony_ci DEFINE_SLIM_LDEST_TXN(txn, mc, 6, stream->dev->laddr, &msg); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (port->direction == SLIM_PORT_SINK) 1338c2ecf20Sopenharmony_ci txn.mc = SLIM_MSG_MC_CONNECT_SINK; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci wbuf[0] = port->id; 1368c2ecf20Sopenharmony_ci wbuf[1] = port->ch.id; 1378c2ecf20Sopenharmony_ci port->ch.state = SLIM_CH_STATE_ASSOCIATED; 1388c2ecf20Sopenharmony_ci port->state = SLIM_PORT_UNCONFIGURED; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return slim_do_transfer(sdev->ctrl, &txn); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int slim_disconnect_port(struct slim_stream_runtime *stream, 1448c2ecf20Sopenharmony_ci struct slim_port *port) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct slim_device *sdev = stream->dev; 1478c2ecf20Sopenharmony_ci u8 wbuf[1]; 1488c2ecf20Sopenharmony_ci struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL}; 1498c2ecf20Sopenharmony_ci u8 mc = SLIM_MSG_MC_DISCONNECT_PORT; 1508c2ecf20Sopenharmony_ci DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci wbuf[0] = port->id; 1538c2ecf20Sopenharmony_ci port->ch.state = SLIM_CH_STATE_DISCONNECTED; 1548c2ecf20Sopenharmony_ci port->state = SLIM_PORT_DISCONNECTED; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return slim_do_transfer(sdev->ctrl, &txn); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int slim_deactivate_remove_channel(struct slim_stream_runtime *stream, 1608c2ecf20Sopenharmony_ci struct slim_port *port) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct slim_device *sdev = stream->dev; 1638c2ecf20Sopenharmony_ci u8 wbuf[1]; 1648c2ecf20Sopenharmony_ci struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL}; 1658c2ecf20Sopenharmony_ci u8 mc = SLIM_MSG_MC_NEXT_DEACTIVATE_CHANNEL; 1668c2ecf20Sopenharmony_ci DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg); 1678c2ecf20Sopenharmony_ci int ret; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci wbuf[0] = port->ch.id; 1708c2ecf20Sopenharmony_ci ret = slim_do_transfer(sdev->ctrl, &txn); 1718c2ecf20Sopenharmony_ci if (ret) 1728c2ecf20Sopenharmony_ci return ret; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci txn.mc = SLIM_MSG_MC_NEXT_REMOVE_CHANNEL; 1758c2ecf20Sopenharmony_ci port->ch.state = SLIM_CH_STATE_REMOVED; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return slim_do_transfer(sdev->ctrl, &txn); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic int slim_get_prate_code(int rate) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci int i; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(slim_presence_rate_table); i++) { 1858c2ecf20Sopenharmony_ci if (rate == slim_presence_rate_table[i]) 1868c2ecf20Sopenharmony_ci return i; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return -EINVAL; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/** 1938c2ecf20Sopenharmony_ci * slim_stream_prepare() - Prepare a SLIMbus Stream 1948c2ecf20Sopenharmony_ci * 1958c2ecf20Sopenharmony_ci * @rt: instance of slim stream runtime to configure 1968c2ecf20Sopenharmony_ci * @cfg: new configuration for the stream 1978c2ecf20Sopenharmony_ci * 1988c2ecf20Sopenharmony_ci * This API will configure SLIMbus stream with config parameters from cfg. 1998c2ecf20Sopenharmony_ci * return zero on success and error code on failure. From ASoC DPCM framework, 2008c2ecf20Sopenharmony_ci * this state is linked to hw_params() operation. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ciint slim_stream_prepare(struct slim_stream_runtime *rt, 2038c2ecf20Sopenharmony_ci struct slim_stream_config *cfg) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct slim_controller *ctrl = rt->dev->ctrl; 2068c2ecf20Sopenharmony_ci struct slim_port *port; 2078c2ecf20Sopenharmony_ci int num_ports, i, port_id; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (rt->ports) { 2108c2ecf20Sopenharmony_ci dev_err(&rt->dev->dev, "Stream already Prepared\n"); 2118c2ecf20Sopenharmony_ci return -EINVAL; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci num_ports = hweight32(cfg->port_mask); 2158c2ecf20Sopenharmony_ci rt->ports = kcalloc(num_ports, sizeof(*port), GFP_KERNEL); 2168c2ecf20Sopenharmony_ci if (!rt->ports) 2178c2ecf20Sopenharmony_ci return -ENOMEM; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci rt->num_ports = num_ports; 2208c2ecf20Sopenharmony_ci rt->rate = cfg->rate; 2218c2ecf20Sopenharmony_ci rt->bps = cfg->bps; 2228c2ecf20Sopenharmony_ci rt->direction = cfg->direction; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (cfg->rate % ctrl->a_framer->superfreq) { 2258c2ecf20Sopenharmony_ci /* 2268c2ecf20Sopenharmony_ci * data rate not exactly multiple of super frame, 2278c2ecf20Sopenharmony_ci * use PUSH/PULL protocol 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_ci if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK) 2308c2ecf20Sopenharmony_ci rt->prot = SLIM_PROTO_PUSH; 2318c2ecf20Sopenharmony_ci else 2328c2ecf20Sopenharmony_ci rt->prot = SLIM_PROTO_PULL; 2338c2ecf20Sopenharmony_ci } else { 2348c2ecf20Sopenharmony_ci rt->prot = SLIM_PROTO_ISO; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci rt->ratem = cfg->rate/ctrl->a_framer->superfreq; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci i = 0; 2408c2ecf20Sopenharmony_ci for_each_set_bit(port_id, &cfg->port_mask, SLIM_DEVICE_MAX_PORTS) { 2418c2ecf20Sopenharmony_ci port = &rt->ports[i]; 2428c2ecf20Sopenharmony_ci port->state = SLIM_PORT_DISCONNECTED; 2438c2ecf20Sopenharmony_ci port->id = port_id; 2448c2ecf20Sopenharmony_ci port->ch.prrate = slim_get_prate_code(cfg->rate); 2458c2ecf20Sopenharmony_ci port->ch.id = cfg->chs[i]; 2468c2ecf20Sopenharmony_ci port->ch.data_fmt = SLIM_CH_DATA_FMT_NOT_DEFINED; 2478c2ecf20Sopenharmony_ci port->ch.aux_fmt = SLIM_CH_AUX_FMT_NOT_APPLICABLE; 2488c2ecf20Sopenharmony_ci port->ch.state = SLIM_CH_STATE_ALLOCATED; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK) 2518c2ecf20Sopenharmony_ci port->direction = SLIM_PORT_SINK; 2528c2ecf20Sopenharmony_ci else 2538c2ecf20Sopenharmony_ci port->direction = SLIM_PORT_SOURCE; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci slim_connect_port_channel(rt, port); 2568c2ecf20Sopenharmony_ci i++; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_stream_prepare); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic int slim_define_channel_content(struct slim_stream_runtime *stream, 2648c2ecf20Sopenharmony_ci struct slim_port *port) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct slim_device *sdev = stream->dev; 2678c2ecf20Sopenharmony_ci u8 wbuf[4]; 2688c2ecf20Sopenharmony_ci struct slim_val_inf msg = {0, 4, NULL, wbuf, NULL}; 2698c2ecf20Sopenharmony_ci u8 mc = SLIM_MSG_MC_NEXT_DEFINE_CONTENT; 2708c2ecf20Sopenharmony_ci DEFINE_SLIM_LDEST_TXN(txn, mc, 8, stream->dev->laddr, &msg); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci wbuf[0] = port->ch.id; 2738c2ecf20Sopenharmony_ci wbuf[1] = port->ch.prrate; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* Frequency Locked for ISO Protocol */ 2768c2ecf20Sopenharmony_ci if (stream->prot != SLIM_PROTO_ISO) 2778c2ecf20Sopenharmony_ci wbuf[1] |= SLIM_CHANNEL_CONTENT_FL; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci wbuf[2] = port->ch.data_fmt | (port->ch.aux_fmt << 4); 2808c2ecf20Sopenharmony_ci wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS; 2818c2ecf20Sopenharmony_ci port->ch.state = SLIM_CH_STATE_CONTENT_DEFINED; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return slim_do_transfer(sdev->ctrl, &txn); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int slim_get_segdist_code(int ratem) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci int i; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(segdist_codes); i++) { 2918c2ecf20Sopenharmony_ci if (segdist_codes[i].ratem == ratem) 2928c2ecf20Sopenharmony_ci return segdist_codes[i].segdist_code; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return -EINVAL; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int slim_define_channel(struct slim_stream_runtime *stream, 2998c2ecf20Sopenharmony_ci struct slim_port *port) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct slim_device *sdev = stream->dev; 3028c2ecf20Sopenharmony_ci u8 wbuf[4]; 3038c2ecf20Sopenharmony_ci struct slim_val_inf msg = {0, 4, NULL, wbuf, NULL}; 3048c2ecf20Sopenharmony_ci u8 mc = SLIM_MSG_MC_NEXT_DEFINE_CHANNEL; 3058c2ecf20Sopenharmony_ci DEFINE_SLIM_LDEST_TXN(txn, mc, 8, stream->dev->laddr, &msg); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci port->ch.seg_dist = slim_get_segdist_code(stream->ratem); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci wbuf[0] = port->ch.id; 3108c2ecf20Sopenharmony_ci wbuf[1] = port->ch.seg_dist & 0xFF; 3118c2ecf20Sopenharmony_ci wbuf[2] = (stream->prot << 4) | ((port->ch.seg_dist & 0xF00) >> 8); 3128c2ecf20Sopenharmony_ci if (stream->prot == SLIM_PROTO_ISO) 3138c2ecf20Sopenharmony_ci wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS; 3148c2ecf20Sopenharmony_ci else 3158c2ecf20Sopenharmony_ci wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS + 1; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci port->ch.state = SLIM_CH_STATE_DEFINED; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return slim_do_transfer(sdev->ctrl, &txn); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic int slim_activate_channel(struct slim_stream_runtime *stream, 3238c2ecf20Sopenharmony_ci struct slim_port *port) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct slim_device *sdev = stream->dev; 3268c2ecf20Sopenharmony_ci u8 wbuf[1]; 3278c2ecf20Sopenharmony_ci struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL}; 3288c2ecf20Sopenharmony_ci u8 mc = SLIM_MSG_MC_NEXT_ACTIVATE_CHANNEL; 3298c2ecf20Sopenharmony_ci DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci txn.msg->num_bytes = 1; 3328c2ecf20Sopenharmony_ci txn.msg->wbuf = wbuf; 3338c2ecf20Sopenharmony_ci wbuf[0] = port->ch.id; 3348c2ecf20Sopenharmony_ci port->ch.state = SLIM_CH_STATE_ACTIVE; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return slim_do_transfer(sdev->ctrl, &txn); 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci/** 3408c2ecf20Sopenharmony_ci * slim_stream_enable() - Enable a prepared SLIMbus Stream 3418c2ecf20Sopenharmony_ci * 3428c2ecf20Sopenharmony_ci * @stream: instance of slim stream runtime to enable 3438c2ecf20Sopenharmony_ci * 3448c2ecf20Sopenharmony_ci * This API will enable all the ports and channels associated with 3458c2ecf20Sopenharmony_ci * SLIMbus stream 3468c2ecf20Sopenharmony_ci * 3478c2ecf20Sopenharmony_ci * Return: zero on success and error code on failure. From ASoC DPCM framework, 3488c2ecf20Sopenharmony_ci * this state is linked to trigger() start operation. 3498c2ecf20Sopenharmony_ci */ 3508c2ecf20Sopenharmony_ciint slim_stream_enable(struct slim_stream_runtime *stream) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION, 3538c2ecf20Sopenharmony_ci 3, SLIM_LA_MANAGER, NULL); 3548c2ecf20Sopenharmony_ci struct slim_controller *ctrl = stream->dev->ctrl; 3558c2ecf20Sopenharmony_ci int ret, i; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (ctrl->enable_stream) { 3588c2ecf20Sopenharmony_ci ret = ctrl->enable_stream(stream); 3598c2ecf20Sopenharmony_ci if (ret) 3608c2ecf20Sopenharmony_ci return ret; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci for (i = 0; i < stream->num_ports; i++) 3638c2ecf20Sopenharmony_ci stream->ports[i].ch.state = SLIM_CH_STATE_ACTIVE; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci return ret; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci ret = slim_do_transfer(ctrl, &txn); 3698c2ecf20Sopenharmony_ci if (ret) 3708c2ecf20Sopenharmony_ci return ret; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* define channels first before activating them */ 3738c2ecf20Sopenharmony_ci for (i = 0; i < stream->num_ports; i++) { 3748c2ecf20Sopenharmony_ci struct slim_port *port = &stream->ports[i]; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci slim_define_channel(stream, port); 3778c2ecf20Sopenharmony_ci slim_define_channel_content(stream, port); 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci for (i = 0; i < stream->num_ports; i++) { 3818c2ecf20Sopenharmony_ci struct slim_port *port = &stream->ports[i]; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci slim_activate_channel(stream, port); 3848c2ecf20Sopenharmony_ci port->state = SLIM_PORT_CONFIGURED; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci return slim_do_transfer(ctrl, &txn); 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_stream_enable); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci/** 3938c2ecf20Sopenharmony_ci * slim_stream_disable() - Disable a SLIMbus Stream 3948c2ecf20Sopenharmony_ci * 3958c2ecf20Sopenharmony_ci * @stream: instance of slim stream runtime to disable 3968c2ecf20Sopenharmony_ci * 3978c2ecf20Sopenharmony_ci * This API will disable all the ports and channels associated with 3988c2ecf20Sopenharmony_ci * SLIMbus stream 3998c2ecf20Sopenharmony_ci * 4008c2ecf20Sopenharmony_ci * Return: zero on success and error code on failure. From ASoC DPCM framework, 4018c2ecf20Sopenharmony_ci * this state is linked to trigger() pause operation. 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_ciint slim_stream_disable(struct slim_stream_runtime *stream) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION, 4068c2ecf20Sopenharmony_ci 3, SLIM_LA_MANAGER, NULL); 4078c2ecf20Sopenharmony_ci struct slim_controller *ctrl = stream->dev->ctrl; 4088c2ecf20Sopenharmony_ci int ret, i; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (ctrl->disable_stream) 4118c2ecf20Sopenharmony_ci ctrl->disable_stream(stream); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci ret = slim_do_transfer(ctrl, &txn); 4148c2ecf20Sopenharmony_ci if (ret) 4158c2ecf20Sopenharmony_ci return ret; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci for (i = 0; i < stream->num_ports; i++) 4188c2ecf20Sopenharmony_ci slim_deactivate_remove_channel(stream, &stream->ports[i]); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return slim_do_transfer(ctrl, &txn); 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_stream_disable); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci/** 4278c2ecf20Sopenharmony_ci * slim_stream_unprepare() - Un-prepare a SLIMbus Stream 4288c2ecf20Sopenharmony_ci * 4298c2ecf20Sopenharmony_ci * @stream: instance of slim stream runtime to unprepare 4308c2ecf20Sopenharmony_ci * 4318c2ecf20Sopenharmony_ci * This API will un allocate all the ports and channels associated with 4328c2ecf20Sopenharmony_ci * SLIMbus stream 4338c2ecf20Sopenharmony_ci * 4348c2ecf20Sopenharmony_ci * Return: zero on success and error code on failure. From ASoC DPCM framework, 4358c2ecf20Sopenharmony_ci * this state is linked to trigger() stop operation. 4368c2ecf20Sopenharmony_ci */ 4378c2ecf20Sopenharmony_ciint slim_stream_unprepare(struct slim_stream_runtime *stream) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci int i; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci for (i = 0; i < stream->num_ports; i++) 4428c2ecf20Sopenharmony_ci slim_disconnect_port(stream, &stream->ports[i]); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci kfree(stream->ports); 4458c2ecf20Sopenharmony_ci stream->ports = NULL; 4468c2ecf20Sopenharmony_ci stream->num_ports = 0; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci return 0; 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_stream_unprepare); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci/** 4538c2ecf20Sopenharmony_ci * slim_stream_free() - Free a SLIMbus Stream 4548c2ecf20Sopenharmony_ci * 4558c2ecf20Sopenharmony_ci * @stream: instance of slim stream runtime to free 4568c2ecf20Sopenharmony_ci * 4578c2ecf20Sopenharmony_ci * This API will un allocate all the memory associated with 4588c2ecf20Sopenharmony_ci * slim stream runtime, user is not allowed to make an dereference 4598c2ecf20Sopenharmony_ci * to stream after this call. 4608c2ecf20Sopenharmony_ci * 4618c2ecf20Sopenharmony_ci * Return: zero on success and error code on failure. From ASoC DPCM framework, 4628c2ecf20Sopenharmony_ci * this state is linked to shutdown() operation. 4638c2ecf20Sopenharmony_ci */ 4648c2ecf20Sopenharmony_ciint slim_stream_free(struct slim_stream_runtime *stream) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci struct slim_device *sdev = stream->dev; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci spin_lock(&sdev->stream_list_lock); 4698c2ecf20Sopenharmony_ci list_del(&stream->node); 4708c2ecf20Sopenharmony_ci spin_unlock(&sdev->stream_list_lock); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci kfree(stream->name); 4738c2ecf20Sopenharmony_ci kfree(stream); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci return 0; 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(slim_stream_free); 478