From patchwork Thu Jul 5 13:54:25 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Srinivas Kandagatla X-Patchwork-Id: 141184 Delivered-To: patch@linaro.org Received: by 2002:a2e:9754:0:0:0:0:0 with SMTP id f20-v6csp1971950ljj; Thu, 5 Jul 2018 06:57:02 -0700 (PDT) X-Google-Smtp-Source: AAOMgpc7mhaQuML9uHOGPlKX9NfUVPloBelX42wFQvSlBdC5LDbdBrBOonvW+dsjYBpMiG956SRg X-Received: by 2002:a1c:ea9c:: with SMTP id g28-v6mr4153882wmi.65.1530799021998; Thu, 05 Jul 2018 06:57:01 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1530799021; cv=none; d=google.com; s=arc-20160816; b=bH7xk+epvtJ7WoAsu4ybST6SRaHw1e+4Zj6MI/501f4kCotynhpd4opjqxvUt2PmMn f7KdCLunHHOcKouKWwVHzed+/Wo87gVkMhQPHERlENUKrzr23nM1V1FbH9nMDNxU2NW+ FrRvezEntrT9nqWdP71fA9mcCxbx9rpbZTl7/SBq9PtSAidinoHOesJIxkVRz19HJasR 2powuQx70uOk6ljlG2d2Pj2OpmBXdbYTtH7ydCUHYnqT5E6zAu0Wd3KEo3amogMx41uA KEjmUL3wbcB1ObRntnNayxEZyqnOypuGxuuZahN2tE5F21kscqxdnZNF1clPkpXTy6wQ E81A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:cc:references:in-reply-to:message-id :date:to:from:dkim-signature:delivered-to:arc-authentication-results; bh=QOmMOlfKh2tF7YBa2DUf5uewxm12njycyjIPVDO51EA=; b=DnOWoDM3KdgTIT9YDEZJK4CKbRmBGSayCpaFjsPDN6i1s+m2a+0RJWt1bSBFjtRN+9 Y9hLfvbEWQqMkp0T9ZvGdVPZMvay5GdtfuqXCMOpUs62DbVfpkRiA84CwNk9fJCNdm6f hz6IZPej21ctNgy0hKDYm+F8BeBt/8U+BE/mkU2BoDjm250Rksv7HHwVYJzKNfFPL9Cv KURALYbORbHl09D+wAO4LG3RotKW1RMM3FxaSjqaBvMmpFZRnmM6LnUOMZq8z3YDOq5x FafW7a++zYG1bpxfovlqPWfNMTFhn/LP4xDYciFtHBIvee9+gVYG+Tq+27GRylpjN/yi RSFg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=J8Yl7RuW; spf=pass (google.com: domain of alsa-devel-bounces@alsa-project.org designates 77.48.224.243 as permitted sender) smtp.mailfrom=alsa-devel-bounces@alsa-project.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from alsa0.perex.cz (alsa0.perex.cz. [77.48.224.243]) by mx.google.com with ESMTP id u10-v6si4689914wmg.215.2018.07.05.06.57.01; Thu, 05 Jul 2018 06:57:01 -0700 (PDT) Received-SPF: pass (google.com: domain of alsa-devel-bounces@alsa-project.org designates 77.48.224.243 as permitted sender) client-ip=77.48.224.243; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=J8Yl7RuW; spf=pass (google.com: domain of alsa-devel-bounces@alsa-project.org designates 77.48.224.243 as permitted sender) smtp.mailfrom=alsa-devel-bounces@alsa-project.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id 6C7CB2676E7; Thu, 5 Jul 2018 15:56:54 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 840C32673F5; Thu, 5 Jul 2018 15:56:49 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail1.perex.cz X-Spam-Level: X-Spam-Status: No, score=-0.1 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU, RCVD_IN_MSPIKE_H2, SPF_PASS autolearn=disabled version=3.4.0 Received: from mail-wr1-f67.google.com (mail-wr1-f67.google.com [209.85.221.67]) by alsa0.perex.cz (Postfix) with ESMTP id E6DD4266E76 for ; Thu, 5 Jul 2018 15:56:46 +0200 (CEST) Received: by mail-wr1-f67.google.com with SMTP id j33-v6so1230599wrj.5 for ; Thu, 05 Jul 2018 06:56:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=vqRrwNSx/ZfuuFIPchHCjcnRYf+6w4eKnzhrKH7azWU=; b=J8Yl7RuW6e/F2GbeGpwQbGadn4pEnC7I3B9rGcyRuNz5eflR2NsKtcEmJWaQivjbMt 9vGz+3OMHfxz1hkB5UsHhlIERyeFGdWDxVa6nsa/BKn3BktFxljJVZ8P0P44SZnjPEIH tHTLsBDwXLLJV/kB+TEPyJfaLSfewxxhThnu8= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=vqRrwNSx/ZfuuFIPchHCjcnRYf+6w4eKnzhrKH7azWU=; b=EJz0WWoQt0j09wljWVlkH4R+9mBlv8HAF7tdmGwOxWKGSYYQ/FuO0CE5xq0YG73Qzj FHVvlc7FLHEQGwCmeUZlMR9ut91+XsK2vsyd/yfU/DznEaIRTIAmt2GjetI2DDruMus0 jc0Mvj/p93i8D9aMGB5h8O7/spk4ToccBwHVtB91fQ95cuHhxrbG6Lv8ztgvjZVlI8ro WyBRT3RgvG7muaY3Xvonjk5t/dqu9YyXsosyn6U4rEEoMeQDEYxn3Go8Y3D2llzhK/aG XCemGR8lilDNgLcD6XS7pN2+EMQFbSikxt5I9xti4zMOK3zUOEf4T/iT/Y0YJQH/NrTf ElSg== X-Gm-Message-State: APt69E0ggGDgoJ2vy2xU5bzyOlBUkhBmez1Q9SRZLmDFoW5n2RQ5nsSw RscVsKcjBqTMbPWf6W19Ywo9Og== X-Received: by 2002:adf:f5c3:: with SMTP id k3-v6mr4517951wrp.219.1530799006129; Thu, 05 Jul 2018 06:56:46 -0700 (PDT) Received: from localhost.localdomain (cpc90716-aztw32-2-0-cust92.18-1.cable.virginm.net. [86.26.100.93]) by smtp.gmail.com with ESMTPSA id l17-v6sm11003297wrh.45.2018.07.05.06.56.44 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 05 Jul 2018 06:56:45 -0700 (PDT) From: Srinivas Kandagatla To: gregkh@linuxfoundation.org, broonie@kernel.org Date: Thu, 5 Jul 2018 14:54:25 +0100 Message-Id: <20180705135426.20748-2-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.16.2 In-Reply-To: <20180705135426.20748-1-srinivas.kandagatla@linaro.org> References: <20180705135426.20748-1-srinivas.kandagatla@linaro.org> Cc: alsa-devel@alsa-project.org, ctatlor97@gmail.com, linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, vkoul@kernel.org, Srinivas Kandagatla , sdharia@quicinc.com Subject: [alsa-devel] [PATCH v2 1/2] slimbus: stream: add stream support X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org This patch adds support to SLIMbus stream apis for slimbus device. SLIMbus streaming involves adding support to Data Channel Management and channel Reconfiguration Messages to slim core plus few stream apis. >From slim device side the apis are very simple mostly inline with other stream apis. Currently it only supports Isochronous and Push/Pull transport protocols, which are sufficient for audio use cases. Signed-off-by: Srinivas Kandagatla --- Documentation/driver-api/slimbus.rst | 5 + drivers/slimbus/Makefile | 2 +- drivers/slimbus/core.c | 2 + drivers/slimbus/slimbus.h | 189 ++++++++++++++ drivers/slimbus/stream.c | 477 +++++++++++++++++++++++++++++++++++ include/linux/slimbus.h | 46 ++++ 6 files changed, 720 insertions(+), 1 deletion(-) create mode 100644 drivers/slimbus/stream.c -- 2.16.2 _______________________________________________ Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel diff --git a/Documentation/driver-api/slimbus.rst b/Documentation/driver-api/slimbus.rst index a97449cf603a..410eec79b2a1 100644 --- a/Documentation/driver-api/slimbus.rst +++ b/Documentation/driver-api/slimbus.rst @@ -125,3 +125,8 @@ Messaging APIs: ~~~~~~~~~~~~~~~ .. kernel-doc:: drivers/slimbus/messaging.c :export: + +Streaming APIs: +~~~~~~~~~~~~~~~ +.. kernel-doc:: drivers/slimbus/stream.c + :export: diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile index c78c6e16c2df..d9aa011b6804 100644 --- a/drivers/slimbus/Makefile +++ b/drivers/slimbus/Makefile @@ -3,7 +3,7 @@ # Makefile for kernel SLIMbus framework. # obj-$(CONFIG_SLIMBUS) += slimbus.o -slimbus-y := core.o messaging.o sched.o +slimbus-y := core.o messaging.o sched.o stream.o #Controllers obj-$(CONFIG_SLIM_QCOM_CTRL) += slim-qcom-ctrl.o diff --git a/drivers/slimbus/core.c b/drivers/slimbus/core.c index 88248a4ecad9..95b00d28ad6e 100644 --- a/drivers/slimbus/core.c +++ b/drivers/slimbus/core.c @@ -114,6 +114,8 @@ static int slim_add_device(struct slim_controller *ctrl, sbdev->dev.release = slim_dev_release; sbdev->dev.driver = NULL; sbdev->ctrl = ctrl; + INIT_LIST_HEAD(&sbdev->stream_list); + spin_lock_init(&sbdev->stream_list_lock); if (node) sbdev->dev.of_node = of_node_get(node); diff --git a/drivers/slimbus/slimbus.h b/drivers/slimbus/slimbus.h index 63229e8cd050..6dbc2b320704 100644 --- a/drivers/slimbus/slimbus.h +++ b/drivers/slimbus/slimbus.h @@ -45,9 +45,20 @@ #define SLIM_MSG_MC_ASSIGN_LOGICAL_ADDRESS 0x2 #define SLIM_MSG_MC_REPORT_ABSENT 0xF +/* Data channel management messages */ +#define SLIM_MSG_MC_CONNECT_SOURCE 0x10 +#define SLIM_MSG_MC_CONNECT_SINK 0x11 +#define SLIM_MSG_MC_DISCONNECT_PORT 0x14 +#define SLIM_MSG_MC_CHANGE_CONTENT 0x18 + /* Clock pause Reconfiguration messages */ #define SLIM_MSG_MC_BEGIN_RECONFIGURATION 0x40 #define SLIM_MSG_MC_NEXT_PAUSE_CLOCK 0x4A +#define SLIM_MSG_MC_NEXT_DEFINE_CHANNEL 0x50 +#define SLIM_MSG_MC_NEXT_DEFINE_CONTENT 0x51 +#define SLIM_MSG_MC_NEXT_ACTIVATE_CHANNEL 0x54 +#define SLIM_MSG_MC_NEXT_DEACTIVATE_CHANNEL 0x55 +#define SLIM_MSG_MC_NEXT_REMOVE_CHANNEL 0x58 #define SLIM_MSG_MC_RECONFIGURE_NOW 0x5F /* @@ -69,7 +80,15 @@ /* Standard values per SLIMbus spec needed by controllers and devices */ #define SLIM_MAX_CLK_GEAR 10 #define SLIM_MIN_CLK_GEAR 1 +#define SLIM_SLOT_LEN_BITS 4 + +/* Indicate that the frequency of the flow and the bus frequency are locked */ +#define SLIM_CHANNEL_CONTENT_FL BIT(7) +/* Standard values per SLIMbus spec needed by controllers and devices */ +#define SLIM_CL_PER_SUPERFRAME 6144 +#define SLIM_SLOTS_PER_SUPERFRAME (SLIM_CL_PER_SUPERFRAME >> 2) +#define SLIM_SL_PER_SUPERFRAME (SLIM_CL_PER_SUPERFRAME >> 2) /* Manager's logical address is set to 0xFF per spec */ #define SLIM_LA_MANAGER 0xFF @@ -167,6 +186,170 @@ struct slim_sched { struct mutex m_reconf; }; +/** + * enum slim_port_direction: SLIMbus port direction + * + * @SLIM_PORT_SINK: SLIMbus port is a sink + * @SLIM_PORT_SOURCE: SLIMbus port is a source + */ +enum slim_port_direction { + SLIM_PORT_SINK = 0, + SLIM_PORT_SOURCE, +}; +/** + * enum slim_port_state: SLIMbus Port/Endpoint state machine + * according to SLIMbus Spec 2.0 + * @SLIM_PORT_DISCONNECTED: SLIMbus port is disconnected + * entered from Unconfigure/configured state after + * DISCONNECT_PORT or REMOVE_CHANNEL core command + * @SLIM_PORT_UNCONFIGURED: SLIMbus port is in unconfigured state. + * entered from disconnect state after CONNECT_SOURCE/SINK core command + * @SLIM_PORT_CONFIGURED: SLIMbus port is in configured state. + * entered from unconfigured state after DEFINE_CHANNEL, DEFINE_CONTENT + * and ACTIVATE_CHANNEL core commands. Ready for data transmission. + */ +enum slim_port_state { + SLIM_PORT_DISCONNECTED = 0, + SLIM_PORT_UNCONFIGURED, + SLIM_PORT_CONFIGURED, +}; + +/** + * enum slim_channel_state: SLIMbus channel state machine used by core. + * @SLIM_CH_STATE_DISCONNECTED: SLIMbus channel is disconnected + * @SLIM_CH_STATE_ALLOCATED: SLIMbus channel is allocated + * @SLIM_CH_STATE_ASSOCIATED: SLIMbus channel is associated with port + * @SLIM_CH_STATE_DEFINED: SLIMbus channel parameters are defined + * @SLIM_CH_STATE_CONTENT_DEFINED: SLIMbus channel content is defined + * @SLIM_CH_STATE_ACTIVE: SLIMbus channel is active and ready for data + * @SLIM_CH_STATE_REMOVED: SLIMbus channel is inactive and removed + */ +enum slim_channel_state { + SLIM_CH_STATE_DISCONNECTED = 0, + SLIM_CH_STATE_ALLOCATED, + SLIM_CH_STATE_ASSOCIATED, + SLIM_CH_STATE_DEFINED, + SLIM_CH_STATE_CONTENT_DEFINED, + SLIM_CH_STATE_ACTIVE, + SLIM_CH_STATE_REMOVED, +}; + +/** + * enum slim_ch_data_fmt: SLIMbus channel data Type identifiers according to + * Table 60 of SLIMbus Spec 1.01.01 + * @SLIM_CH_DATA_FMT_NOT_DEFINED: Undefined + * @SLIM_CH_DATA_FMT_LPCM_AUDIO: LPCM audio + * @SLIM_CH_DATA_FMT_IEC61937_COMP_AUDIO: IEC61937 Compressed audio + * @SLIM_CH_DATA_FMT_PACKED_PDM_AUDIO: Packed PDM audio + */ +enum slim_ch_data_fmt { + SLIM_CH_DATA_FMT_NOT_DEFINED = 0, + SLIM_CH_DATA_FMT_LPCM_AUDIO = 1, + SLIM_CH_DATA_FMT_IEC61937_COMP_AUDIO = 2, + SLIM_CH_DATA_FMT_PACKED_PDM_AUDIO = 3, +}; + +/** + * enum slim_ch_aux_fmt: SLIMbus channel Aux Field format IDs according to + * Table 63 of SLIMbus Spec 2.0 + * @SLIM_CH_AUX_FMT_NOT_APPLICABLE: Undefined + * @SLIM_CH_AUX_FMT_ZCUV_TUNNEL_IEC60958: ZCUV for tunneling IEC60958 + * @SLIM_CH_AUX_FMT_USER_DEFINED: User defined + */ +enum slim_ch_aux_bit_fmt { + SLIM_CH_AUX_FMT_NOT_APPLICABLE = 0, + SLIM_CH_AUX_FMT_ZCUV_TUNNEL_IEC60958 = 1, + SLIM_CH_AUX_FMT_USER_DEFINED = 0xF, +}; + +/** + * struct slim_channel - SLIMbus channel, used for state machine + * + * @id: ID of channel + * @prrate: Presense rate of channel from Table 66 of SLIMbus 2.0 Specs + * @seg_dist: segment distribution code from Table 20 of SLIMbus 2.0 Specs + * @data_fmt: Data format of channel. + * @aux_fmt: Aux format for this channel. + * @state: channel state machine + */ +struct slim_channel { + int id; + int prrate; + int seg_dist; + enum slim_ch_data_fmt data_fmt; + enum slim_ch_aux_bit_fmt aux_fmt; + enum slim_channel_state state; +}; + +/** + * struct slim_port - SLIMbus port + * + * @id: Port id + * @direction: Port direction, Source or Sink. + * @state: state machine of port. + * @ch: channel associated with this port. + */ +struct slim_port { + int id; + enum slim_port_direction direction; + enum slim_port_state state; + struct slim_channel ch; +}; + +/** + * enum slim_transport_protocol: SLIMbus Transport protocol list from + * Table 47 of SLIMbus 2.0 specs. + * @SLIM_PROTO_ISO: Isochronous Protocol, no flow control as data rate match + * channel rate flow control embedded in the data. + * @SLIM_PROTO_PUSH: Pushed Protocol, includes flow control, Used to carry + * data whose rate is equal to, or lower than the channel rate. + * @SLIM_PROTO_PULL: Pulled Protocol, similar usage as pushed protocol + * but pull is a unicast. + * @SLIM_PROTO_LOCKED: Locked Protocol + * @SLIM_PROTO_ASYNC_SMPLX: Asynchronous Protocol-Simplex + * @SLIM_PROTO_ASYNC_HALF_DUP: Asynchronous Protocol-Half-duplex + * @SLIM_PROTO_EXT_SMPLX: Extended Asynchronous Protocol-Simplex + * @SLIM_PROTO_EXT_HALF_DUP: Extended Asynchronous Protocol-Half-duplex + */ +enum slim_transport_protocol { + SLIM_PROTO_ISO = 0, + SLIM_PROTO_PUSH, + SLIM_PROTO_PULL, + SLIM_PROTO_LOCKED, + SLIM_PROTO_ASYNC_SMPLX, + SLIM_PROTO_ASYNC_HALF_DUP, + SLIM_PROTO_EXT_SMPLX, + SLIM_PROTO_EXT_HALF_DUP, +}; + +/** + * struct slim_stream_runtime - SLIMbus stream runtime instance + * + * @dev: Name of the stream + * @dev: SLIM Device instance associated with this stream + * @state: state of stream + * @direction: direction of stream + * @prot: Transport protocol used in this stream + * @rate: Data rate of samples * + * @bps: bits per sample + * @ratem: rate multipler which is super frame rate/data rate + * @num_ports: number of ports + * @ports: pointer to instance of ports + * @node: list head for stream associated with slim device. + */ +struct slim_stream_runtime { + const char *name; + struct slim_device *dev; + int direction; + enum slim_transport_protocol prot; + unsigned int rate; + unsigned int bps; + unsigned int ratem; + int num_ports; + struct slim_port *ports; + struct list_head node; +}; + /** * struct slim_controller - Controls every instance of SLIMbus * (similar to 'master' on SPI) @@ -196,6 +379,10 @@ struct slim_sched { * @wakeup: This function pointer implements controller-specific procedure * to wake it up from clock-pause. Framework will call this to bring * the controller out of clock pause. + * @enable_stream: This function pointer implements controller-specific procedure + * to enable a stream. + * @disable_stream: This function pointer implements controller-specific procedure + * to disable stream. * * 'Manager device' is responsible for device management, bandwidth * allocation, channel setup, and port associations per channel. @@ -237,6 +424,8 @@ struct slim_controller { struct slim_eaddr *ea, u8 laddr); int (*get_laddr)(struct slim_controller *ctrl, struct slim_eaddr *ea, u8 *laddr); + int (*enable_stream)(struct slim_stream_runtime *rt); + int (*disable_stream)(struct slim_stream_runtime *rt); int (*wakeup)(struct slim_controller *ctrl); }; diff --git a/drivers/slimbus/stream.c b/drivers/slimbus/stream.c new file mode 100644 index 000000000000..2fa05324ed07 --- /dev/null +++ b/drivers/slimbus/stream.c @@ -0,0 +1,477 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018, Linaro Limited + +#include +#include +#include +#include +#include +#include +#include "slimbus.h" + +/** + * struct segdist_code - Segment Distributions code from + * Table 20 of SLIMbus Specs Version 2.0 + * + * @ratem: Channel Rate Multipler(Segments per Superframe) + * @seg_interval: Number of slots between the first Slot of Segment + * and the first slot of the next consecutive Segment. + * @segdist_code: Segment Distribution Code SD[11:0] + * @seg_offset_mask: Segment offset mask in SD[11:0] + * @segdist_codes: List of all possible Segmet Distribution codes. + */ +static const struct segdist_code { + int ratem; + int seg_interval; + int segdist_code; + u32 seg_offset_mask; + +} segdist_codes[] = { + {1, 1536, 0x200, 0xdff}, + {2, 768, 0x100, 0xcff}, + {4, 384, 0x080, 0xc7f}, + {8, 192, 0x040, 0xc3f}, + {16, 96, 0x020, 0xc1f}, + {32, 48, 0x010, 0xc0f}, + {64, 24, 0x008, 0xc07}, + {128, 12, 0x004, 0xc03}, + {256, 6, 0x002, 0xc01}, + {512, 3, 0x001, 0xc00}, + {3, 512, 0xe00, 0x1ff}, + {6, 256, 0xd00, 0x0ff}, + {12, 128, 0xc80, 0x07f}, + {24, 64, 0xc40, 0x03f}, + {48, 32, 0xc20, 0x01f}, + {96, 16, 0xc10, 0x00f}, + {192, 8, 0xc08, 0x007}, + {364, 4, 0xc04, 0x003}, + {768, 2, 0xc02, 0x001}, +}; + +/* + * Presence Rate table for all Natural Frequencies + * The Presence rate of a constant bitrate stream is mean flow rate of the + * stream expressed in occupied Segments of that Data Channel per second. + * Table 66 from SLIMbus 2.0 Specs + * + * Index of the table corresponds to Presence rate code for the respective rate + * in the table. + */ +static const int slim_presence_rate_table[] = { + 0, /* Not Indicated */ + 12000, + 24000, + 48000, + 96000, + 192000, + 384000, + 768000, + 0, /* Reserved */ + 110250, + 220500, + 441000, + 882000, + 176400, + 352800, + 705600, + 4000, + 8000, + 16000, + 32000, + 64000, + 128000, + 256000, + 512000, +}; + +/* + * slim_stream_allocate() - Allocate a new SLIMbus Stream + * @dev:Slim device to be associated with + * @name: name of the stream + * + * This is very first call for SLIMbus streaming, this API will allocate + * a new SLIMbus stream and return a valid stream runtime pointer for client + * to use it in subsequent stream apis. state of stream is set to ALLOCATED + * + * Return: valid pointer on success and error code on failure. + * From ASoC DPCM framework, this state is linked to startup() operation. + */ +struct slim_stream_runtime *slim_stream_allocate(struct slim_device *dev, + const char *name) +{ + struct slim_stream_runtime *rt; + + rt = kzalloc(sizeof(*rt), GFP_KERNEL); + if (!rt) + return ERR_PTR(-ENOMEM); + + rt->name = kasprintf(GFP_KERNEL, "slim-%s", name); + if (!rt->name) { + kfree(rt); + return ERR_PTR(-ENOMEM); + } + + rt->dev = dev; + spin_lock(&dev->stream_list_lock); + list_add_tail(&rt->node, &dev->stream_list); + spin_unlock(&dev->stream_list_lock); + + return rt; +} +EXPORT_SYMBOL_GPL(slim_stream_allocate); + +static int slim_connect_port_channel(struct slim_stream_runtime *stream, + struct slim_port *port) +{ + struct slim_device *sdev = stream->dev; + u8 wbuf[2]; + struct slim_val_inf msg = {0, 2, NULL, wbuf, NULL}; + u8 mc = SLIM_MSG_MC_CONNECT_SOURCE; + DEFINE_SLIM_LDEST_TXN(txn, mc, 6, stream->dev->laddr, &msg); + + if (port->direction == SLIM_PORT_SINK) + txn.mc = SLIM_MSG_MC_CONNECT_SINK; + + wbuf[0] = port->id; + wbuf[1] = port->ch.id; + port->ch.state = SLIM_CH_STATE_ASSOCIATED; + port->state = SLIM_PORT_UNCONFIGURED; + + return slim_do_transfer(sdev->ctrl, &txn); +} + +static int slim_disconnect_port(struct slim_stream_runtime *stream, + struct slim_port *port) +{ + struct slim_device *sdev = stream->dev; + u8 wbuf[1]; + struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL}; + u8 mc = SLIM_MSG_MC_DISCONNECT_PORT; + DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg); + + wbuf[0] = port->id; + port->ch.state = SLIM_CH_STATE_DISCONNECTED; + port->state = SLIM_PORT_DISCONNECTED; + + return slim_do_transfer(sdev->ctrl, &txn); +} + +static int slim_deactivate_remove_channel(struct slim_stream_runtime *stream, + struct slim_port *port) +{ + struct slim_device *sdev = stream->dev; + u8 wbuf[1]; + struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL}; + u8 mc = SLIM_MSG_MC_NEXT_DEACTIVATE_CHANNEL; + DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg); + int ret; + + wbuf[0] = port->ch.id; + ret = slim_do_transfer(sdev->ctrl, &txn); + if (ret) + return ret; + + txn.mc = SLIM_MSG_MC_NEXT_REMOVE_CHANNEL; + port->ch.state = SLIM_CH_STATE_REMOVED; + + return slim_do_transfer(sdev->ctrl, &txn); +} + +static int slim_get_prate_code(int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(slim_presence_rate_table); i++) { + if (rate == slim_presence_rate_table[i]) + return i; + } + + return -EINVAL; +} + +/* + * slim_stream_prepare() - Prepare a SLIMbus Stream + * + * @rt: instance of slim stream runtime to configure + * @cfg: new configuration for the stream + * + * This API will configure SLIMbus stream with config parameters from cfg. + * return zero on success and error code on failure. From ASoC DPCM framework, + * this state is linked to hw_params() operation. + */ +int slim_stream_prepare(struct slim_stream_runtime *rt, + struct slim_stream_config *cfg) +{ + struct slim_controller *ctrl = rt->dev->ctrl; + struct slim_port *port; + int num_ports, i, port_id; + + if (rt->ports) { + dev_err(&rt->dev->dev, "Stream already Prepared\n"); + return -EINVAL; + } + + num_ports = hweight32(cfg->port_mask); + rt->ports = kcalloc(num_ports, sizeof(*port), GFP_KERNEL); + if (!rt->ports) + return -ENOMEM; + + rt->num_ports = num_ports; + rt->rate = cfg->rate; + rt->bps = cfg->bps; + rt->direction = cfg->direction; + + if (cfg->rate % ctrl->a_framer->superfreq) { + /* + * data rate not exactly multiple of super frame, + * use PUSH/PULL protocol + */ + if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK) + rt->prot = SLIM_PROTO_PUSH; + else + rt->prot = SLIM_PROTO_PULL; + } else { + rt->prot = SLIM_PROTO_ISO; + } + + rt->ratem = cfg->rate/ctrl->a_framer->superfreq; + + i = 0; + for_each_set_bit(port_id, &cfg->port_mask, SLIM_DEVICE_MAX_PORTS) { + port = &rt->ports[i]; + port->state = SLIM_PORT_DISCONNECTED; + port->id = port_id; + port->ch.prrate = slim_get_prate_code(cfg->rate); + port->ch.id = cfg->chs[i]; + port->ch.data_fmt = SLIM_CH_DATA_FMT_NOT_DEFINED; + port->ch.aux_fmt = SLIM_CH_AUX_FMT_NOT_APPLICABLE; + port->ch.state = SLIM_CH_STATE_ALLOCATED; + + if (cfg->direction == SNDRV_PCM_STREAM_PLAYBACK) + port->direction = SLIM_PORT_SINK; + else + port->direction = SLIM_PORT_SOURCE; + + slim_connect_port_channel(rt, port); + i++; + } + + return 0; +} +EXPORT_SYMBOL_GPL(slim_stream_prepare); + +static int slim_define_channel_content(struct slim_stream_runtime *stream, + struct slim_port *port) +{ + struct slim_device *sdev = stream->dev; + u8 wbuf[4]; + struct slim_val_inf msg = {0, 4, NULL, wbuf, NULL}; + u8 mc = SLIM_MSG_MC_NEXT_DEFINE_CONTENT; + DEFINE_SLIM_LDEST_TXN(txn, mc, 8, stream->dev->laddr, &msg); + + wbuf[0] = port->ch.id; + wbuf[1] = port->ch.prrate; + + /* Frequency Locked for ISO Protocol */ + if (stream->prot != SLIM_PROTO_ISO) + wbuf[1] |= SLIM_CHANNEL_CONTENT_FL; + + wbuf[2] = port->ch.data_fmt | (port->ch.aux_fmt << 4); + wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS; + port->ch.state = SLIM_CH_STATE_CONTENT_DEFINED; + + return slim_do_transfer(sdev->ctrl, &txn); +} + +static int slim_get_segdist_code(int ratem) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(segdist_codes); i++) { + if (segdist_codes[i].ratem == ratem) + return segdist_codes[i].segdist_code; + } + + return -EINVAL; +} + +static int slim_define_channel(struct slim_stream_runtime *stream, + struct slim_port *port) +{ + struct slim_device *sdev = stream->dev; + u8 wbuf[4]; + struct slim_val_inf msg = {0, 4, NULL, wbuf, NULL}; + u8 mc = SLIM_MSG_MC_NEXT_DEFINE_CHANNEL; + DEFINE_SLIM_LDEST_TXN(txn, mc, 8, stream->dev->laddr, &msg); + + port->ch.seg_dist = slim_get_segdist_code(stream->ratem); + + wbuf[0] = port->ch.id; + wbuf[1] = port->ch.seg_dist & 0xFF; + wbuf[2] = (stream->prot << 4) | ((port->ch.seg_dist & 0xF00) >> 8); + if (stream->prot == SLIM_PROTO_ISO) + wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS; + else + wbuf[3] = stream->bps/SLIM_SLOT_LEN_BITS + 1; + + port->ch.state = SLIM_CH_STATE_DEFINED; + + return slim_do_transfer(sdev->ctrl, &txn); +} + +static int slim_activate_channel(struct slim_stream_runtime *stream, + struct slim_port *port) +{ + struct slim_device *sdev = stream->dev; + u8 wbuf[1]; + struct slim_val_inf msg = {0, 1, NULL, wbuf, NULL}; + u8 mc = SLIM_MSG_MC_NEXT_ACTIVATE_CHANNEL; + DEFINE_SLIM_LDEST_TXN(txn, mc, 5, stream->dev->laddr, &msg); + + txn.msg->num_bytes = 1; + txn.msg->wbuf = wbuf; + wbuf[0] = port->ch.id; + port->ch.state = SLIM_CH_STATE_ACTIVE; + + return slim_do_transfer(sdev->ctrl, &txn); +} + +/* + * slim_stream_enable() - Enable a prepared SLIMbus Stream + * + * @stream: instance of slim stream runtime to enable + * + * This API will enable all the ports and channels associated with + * SLIMbus stream + * + * Return: zero on success and error code on failure. From ASoC DPCM framework, + * this state is linked to trigger() start operation. + */ +int slim_stream_enable(struct slim_stream_runtime *stream) +{ + DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION, + 3, SLIM_LA_MANAGER, NULL); + struct slim_controller *ctrl = stream->dev->ctrl; + int ret, i; + + if (ctrl->enable_stream) { + ret = ctrl->enable_stream(stream); + if (ret) + return ret; + + for (i = 0; i < stream->num_ports; i++) + stream->ports[i].ch.state = SLIM_CH_STATE_ACTIVE; + + return ret; + } + + ret = slim_do_transfer(ctrl, &txn); + if (ret) + return ret; + + /* define channels first before activating them */ + for (i = 0; i < stream->num_ports; i++) { + struct slim_port *port = &stream->ports[i]; + + slim_define_channel(stream, port); + slim_define_channel_content(stream, port); + } + + for (i = 0; i < stream->num_ports; i++) { + struct slim_port *port = &stream->ports[i]; + + slim_activate_channel(stream, port); + port->state = SLIM_PORT_CONFIGURED; + } + txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW; + + return slim_do_transfer(ctrl, &txn); +} +EXPORT_SYMBOL_GPL(slim_stream_enable); + +/* + * slim_stream_disable() - Disable a SLIMbus Stream + * + * @stream: instance of slim stream runtime to disable + * + * This API will disable all the ports and channels associated with + * SLIMbus stream + * + * Return: zero on success and error code on failure. From ASoC DPCM framework, + * this state is linked to trigger() pause operation. + */ +int slim_stream_disable(struct slim_stream_runtime *stream) +{ + DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION, + 3, SLIM_LA_MANAGER, NULL); + struct slim_controller *ctrl = stream->dev->ctrl; + int ret, i; + + if (ctrl->disable_stream) + ctrl->disable_stream(stream); + + ret = slim_do_transfer(ctrl, &txn); + if (ret) + return ret; + + for (i = 0; i < stream->num_ports; i++) + slim_deactivate_remove_channel(stream, &stream->ports[i]); + + txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW; + + return slim_do_transfer(ctrl, &txn); +} +EXPORT_SYMBOL_GPL(slim_stream_disable); + +/* + * slim_stream_unprepare() - Un-prepare a SLIMbus Stream + * + * @stream: instance of slim stream runtime to unprepare + * + * This API will un allocate all the ports and channels associated with + * SLIMbus stream + * + * Return: zero on success and error code on failure. From ASoC DPCM framework, + * this state is linked to trigger() stop operation. + */ +int slim_stream_unprepare(struct slim_stream_runtime *stream) +{ + int i; + + for (i = 0; i < stream->num_ports; i++) + slim_disconnect_port(stream, &stream->ports[i]); + + kfree(stream->ports); + stream->ports = NULL; + stream->num_ports = 0; + + return 0; +} +EXPORT_SYMBOL_GPL(slim_stream_unprepare); + +/* + * slim_stream_free() - Free a SLIMbus Stream + * + * @stream: instance of slim stream runtime to free + * + * This API will un allocate all the memory associated with + * slim stream runtime, user is not allowed to make an dereference + * to stream after this call. + * + * Return: zero on success and error code on failure. From ASoC DPCM framework, + * this state is linked to shutdown() operation. + */ +int slim_stream_free(struct slim_stream_runtime *stream) +{ + struct slim_device *sdev = stream->dev; + + spin_lock(&sdev->stream_list_lock); + list_del(&stream->node); + spin_unlock(&sdev->stream_list_lock); + + kfree(stream->name); + kfree(stream); + + return 0; +} +EXPORT_SYMBOL_GPL(slim_stream_free); diff --git a/include/linux/slimbus.h b/include/linux/slimbus.h index 63801bcc5e60..12c9719b2a55 100644 --- a/include/linux/slimbus.h +++ b/include/linux/slimbus.h @@ -48,6 +48,8 @@ struct slim_controller; * @ctrl: slim controller instance. * @laddr: 1-byte Logical address of this device. * @is_laddr_valid: indicates if the laddr is valid or not + * @stream_list: List of streams on this device + * @stream_list_lock: lock to protect the stream list * * This is the client/device handle returned when a SLIMbus * device is registered with a controller. @@ -60,6 +62,8 @@ struct slim_device { enum slim_device_status status; u8 laddr; bool is_laddr_valid; + struct list_head stream_list; + spinlock_t stream_list_lock; }; #define to_slim_device(d) container_of(d, struct slim_device, dev) @@ -108,6 +112,36 @@ struct slim_val_inf { struct completion *comp; }; +#define SLIM_DEVICE_MAX_CHANNELS 256 +/* A SLIMBus Device may have frmo 0 to 31 Ports (inclusive) */ +#define SLIM_DEVICE_MAX_PORTS 32 + +/** + * struct slim_stream_config - SLIMbus stream configuration + * Configuring a stream is done at hw_params or prepare call + * from audio drivers where they have all the required information + * regarding rate, number of channels and so on. + * There is a 1:1 mapping of channel and ports. + * + * @rate: data rate + * @bps: bits per data sample + * @ch_count: number of channels + * @chs: pointer to list of channel numbers + * @port_mask: port mask of ports to use for this stream + * @direction: direction of the stream, SNDRV_PCM_STREAM_PLAYBACK + * or SNDRV_PCM_STREAM_CAPTURE. + */ +struct slim_stream_config { + unsigned int rate; + unsigned int bps; + /* MAX 256 channels */ + unsigned int ch_count; + unsigned int *chs; + /* Max 32 ports per device */ + unsigned long port_mask; + int direction; +}; + /* * use a macro to avoid include chaining to get THIS_MODULE */ @@ -163,4 +197,16 @@ int slim_readb(struct slim_device *sdev, u32 addr); int slim_writeb(struct slim_device *sdev, u32 addr, u8 value); int slim_read(struct slim_device *sdev, u32 addr, size_t count, u8 *val); int slim_write(struct slim_device *sdev, u32 addr, size_t count, u8 *val); + +/* SLIMbus Stream apis */ +struct slim_stream_runtime; +struct slim_stream_runtime *slim_stream_allocate(struct slim_device *dev, + const char *sname); +int slim_stream_prepare(struct slim_stream_runtime *stream, + struct slim_stream_config *c); +int slim_stream_enable(struct slim_stream_runtime *stream); +int slim_stream_disable(struct slim_stream_runtime *stream); +int slim_stream_unprepare(struct slim_stream_runtime *stream); +int slim_stream_free(struct slim_stream_runtime *stream); + #endif /* _LINUX_SLIMBUS_H */