From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH, MAILING_LIST_MULTI,NICE_REPLY_A,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS, URIBL_BLOCKED,USER_AGENT_SANE_1 autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8B73DC433E0 for ; Mon, 3 Aug 2020 13:09:28 +0000 (UTC) Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 481D520678 for ; Mon, 3 Aug 2020 13:09:28 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="w303k0TZ"; dkim=fail reason="signature verification failed" (2048-bit key) header.d=microchip.com header.i=@microchip.com header.b="zMD5uDFt"; dkim=fail reason="signature verification failed" (1024-bit key) header.d=microchiptechnology.onmicrosoft.com header.i=@microchiptechnology.onmicrosoft.com header.b="QZ8hmZBK" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 481D520678 Authentication-Results: mail.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=microchip.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=merlin.20170209; h=Sender:Content-Transfer-Encoding: Content-Type:Cc:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:Content-ID:In-Reply-To:References: Message-ID:Date:Subject:To:From:Reply-To:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=fHQn0zh4FKmsrkgRfG0nu9hjtHaidP99/n5MzoQ1spA=; b=w303k0TZCxfOAfJzVOVnCWc9O dn37YSM/ZYa1yyv8SNjTFGvNxBftnio0lSmJT7hvMUzo1MZ6UAAdyrKs5PBtMEnxVJOnZUNPACqFN 7GblSUpNB23EVDEYNMqTEs4ELiXDMbp/o9tHObNvl9n+2jr464A8v9fgXY+NzvprNdAtl4f5/w8MD b88cQEg1KUlsoutuqG20tXzcOh3H9w94dYTwwfsxRemc0kOpzRv4mdEtX7yergoGMPQ03d8tj8a4m CMUPNDyzKwcv7V7g+9lEuc2pVJDczmCCcxLlD5ASMD7/lewpDUBbt4sgyGU9TKTQeK+HXevxMWQjx bjfkXNZYQ==; Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1k2aBH-0001TX-VL; Mon, 03 Aug 2020 13:07:08 +0000 Received: from esa2.microchip.iphmx.com ([68.232.149.84]) by merlin.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1k2aBE-0001St-6l for linux-arm-kernel@lists.infradead.org; Mon, 03 Aug 2020 13:07:06 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1596460024; x=1627996024; h=from:to:cc:subject:date:message-id:references: in-reply-to:content-id:content-transfer-encoding: mime-version; bh=lFYmmCJ+kwPwVQIfcB1u2ydoBgfra08OW8XBd6VYAMk=; b=zMD5uDFt1SQ9RK2FnMWxajoKa7IorpO2Mg05s/zZRWw2Tu13vf1hJHXy P+zlLNS7B8cCnMyehDUx2DMzOd2GHIaQPuECGUPi14g9KPFlYC+k3Ilvz WW/caItNBbuTqD1hLdyVjjiZ+ByuQPhEvn2esjFe0k/4LwdDai9g3hBjv dVZoHI4kbtgu2WJY9UziE6Decaduz2/4hNa+7x5D5sfsJIYDRZVNx2zwa 6I3h8nF5frvQn9QMftg5BajGnhB3mbuc1lY/F/5P+itgIlKqREDxhm609 knEpvS/ZzhitQ0cyLirxWi/LmKSV+20GlM3FpixXe5GXhESA2co6QqHd8 g==; IronPort-SDR: 77EH4yeVazX8KszrURS6XcVNCKQvnodAH5f4cAcEJ7b6zCEl0oK8v4p7W/vfc6K2g4Tc3UwqYq wZl3bT+U0NS4vDtQ3Izsz/noh/dztlvEwyli+9jQEEouz9R86j+eP0fSuFBeQuKNmchwhc8LoY dADU8XE0U8k3E1l5YUIDC0Dg3kZKKDGpm3hXINy36jnTbQiz+9A/kCMH68uirpyRZorBFANBqy DB8WEf/5A591tBjYDm9TFqteD801vELqybXW43KKLOBAgd8ywlyJMxy6KGbuYyiOTjntZRbHhC xrE= X-IronPort-AV: E=Sophos;i="5.75,430,1589266800"; d="scan'208";a="84235213" Received: from smtpout.microchip.com (HELO email.microchip.com) ([198.175.253.82]) by esa2.microchip.iphmx.com with ESMTP/TLS/AES256-SHA256; 03 Aug 2020 06:07:01 -0700 Received: from chn-vm-ex02.mchp-main.com (10.10.85.144) by chn-vm-ex04.mchp-main.com (10.10.85.152) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1979.3; Mon, 3 Aug 2020 06:07:00 -0700 Received: from NAM11-DM6-obe.outbound.protection.outlook.com (10.10.215.89) by email.microchip.com (10.10.87.72) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1979.3 via Frontend Transport; Mon, 3 Aug 2020 06:06:57 -0700 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=KBFacZXk1H9O9mz+qT+zb/YDsXKjJORpUCndVJhmpimuOpItjVq4PqHJ+GfBJ45DiBs67I9j5/qYbE46ATz9N3tkFGZi7K/KiTuYTZImzCQh0AHV6vuBGs2+CeXFYzyLX6ybIQCflmx5P6Dq912u08SUcN1RaFf5w939psf2phn27F95xgHbFSROXENZAsRScrhjJf3W4yxOULxCwWoqSfVi4zAZhqcvQhEaEYjYJIs4Sn5eaQbaoFNKF9o6YOmeMLm+6slAaPC0E9tb3Z4nh26kQgFqBrWITZPFSUw3+LPXUuekfv2B27AbVMALHOaq0OeIMAgsMtFeS/dsS+NFbA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=lFYmmCJ+kwPwVQIfcB1u2ydoBgfra08OW8XBd6VYAMk=; b=KTb5gqlYrsDZB+Pm6LOxsTig1G4xmNToiqUfV6C/laubwoDbgw07vgfv4UlXb3Q4nTbwG6y0DGC3oI+mQ22CN8N+Wy5heGgfQV92SEv4W7ajHeAwuLeLYeJD/9KAeNwnCECs7VnT1t/xb01eNPQoNkZVKVbnCSFXkPY3oUQ4zuf1S49715ZRw8UUVMLJ/Jn0WJqpi2chSLhPrfGsJTzr4Qe1oMkHKycT375mLs6BLJa19oLi+cXXQetv5oSa8Iu6ffiPuABmLzPROxthajjlBDqJjlhEazy1CuEUnYCh/8oT/QZRc7l9V6RSJ3fWrCt7S3i3O90X7FCjXbdZCH/mtA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=microchip.com; dmarc=pass action=none header.from=microchip.com; dkim=pass header.d=microchip.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=microchiptechnology.onmicrosoft.com; s=selector2-microchiptechnology-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=lFYmmCJ+kwPwVQIfcB1u2ydoBgfra08OW8XBd6VYAMk=; b=QZ8hmZBKJKejKM3ln+4SAY+wDKevBfEjnIO5STaaNpknqLHpk6+r5ArnPmwa7CO5cY6Od6yiW4kwdb+u8KhxtwiIKTg7AC1MCwQ1vtccjvndv5AgtE90PxvJNFw1PtJIDbTMBgpCVqG9pGKC8pWU2O3rAMJOoulTtPvI3wugcKI= Received: from DM6PR11MB3420.namprd11.prod.outlook.com (2603:10b6:5:69::31) by DM5PR11MB1930.namprd11.prod.outlook.com (2603:10b6:3:106::20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3239.20; Mon, 3 Aug 2020 13:06:57 +0000 Received: from DM6PR11MB3420.namprd11.prod.outlook.com ([fe80::e8b2:1d82:49d9:f4b]) by DM6PR11MB3420.namprd11.prod.outlook.com ([fe80::e8b2:1d82:49d9:f4b%6]) with mapi id 15.20.3239.021; Mon, 3 Aug 2020 13:06:57 +0000 From: To: , , , , Subject: Re: [PATCH v3 2/2] ASoC: mchp-spdiftx: add driver for S/PDIF TX Controller Thread-Topic: [PATCH v3 2/2] ASoC: mchp-spdiftx: add driver for S/PDIF TX Controller Thread-Index: AQHWaZb2Xv4RISU33E+0BkyOzrgC0g== Date: Mon, 3 Aug 2020 13:06:56 +0000 Message-ID: <4d009743-733a-9578-71b2-d320fdca9331@microchip.com> References: <20200803081851.102570-1-codrin.ciubotariu@microchip.com> <20200803081851.102570-2-codrin.ciubotariu@microchip.com> In-Reply-To: <20200803081851.102570-2-codrin.ciubotariu@microchip.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: user-agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.10.0 authentication-results: microchip.com; dkim=none (message not signed) header.d=none;microchip.com; dmarc=none action=none header.from=microchip.com; x-originating-ip: [86.124.22.126] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 26f72aa7-d6a3-47e0-b6c5-08d837ae19e4 x-ms-traffictypediagnostic: DM5PR11MB1930: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-bypassexternaltag: True x-ms-oob-tlc-oobclassifiers: OLM:47; x-ms-exchange-senderadcheck: 1 x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: IqhWajqZAcLAF/se6/NI8rIYlmT55uVL/le6GBuZDf3L+5T0DHgJUhEDiuZQGFHEziiX8tp9WuwbCNJhKfrgmOjnq7VEAZI1Q280eS7CcYVGJdPu6zal0r8PxPi61AD+LFVobsR5+5T+bljTF/ifEg0+GJVYFYfi8xdK6rCowfnZw5wg3y/OgRpkTg0KhNHQms12uOp7T2lG0XfPOCi0S0a3m2Zl6AiZHf3U26STKliaDeFlv7JWtgVjqA9CvYpCE9rNBVXRonnQ8AzjCzHiA3BpbGzZgoUcqLTQ4VUsYFKFXO2l01KnbOAO7sT1oP0k7BrMoFo3qelAQGrrAStp6YLZVFLjimiavcUEujNj4dSw4SStkWQfmWLKTrXqGUAN x-forefront-antispam-report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:DM6PR11MB3420.namprd11.prod.outlook.com; PTR:; CAT:NONE; SFTY:; SFS:(39860400002)(346002)(366004)(376002)(136003)(396003)(71200400001)(186003)(83380400001)(66946007)(2906002)(478600001)(86362001)(66446008)(64756008)(91956017)(76116006)(7416002)(31696002)(110136005)(31686004)(54906003)(53546011)(6506007)(66556008)(26005)(36756003)(66476007)(316002)(6512007)(30864003)(8676002)(6486002)(8936002)(4326008)(2616005)(5660300002)(43740500002)(579004)(559001); DIR:OUT; SFP:1101; x-ms-exchange-antispam-messagedata: yjnEvKxnfTZoydQst8YZNsAkheLwjl06evum8PdvhImoYtqY5m5/Z470cAOgOica1cj2h3rxfFBEoItJK9hPZt90rAqu28MI0Bk9Cfomm4fnAVN0E1FiWKYsxuLi2H0cEN4mjdjNbP1Hdlrv8u4BgKcAFmgr9a6p2LZ/oqLvm3OPokX59UNb/y4pucAFVK4fXLSboHIZ6uIBYfUaRjWY5dodWA/fYqoMXcbjwFXj0BvRFi4EJzDUTwwD0INsEljeS7o09TfmA1OSpK9pHTLCxiuoDlKhZC2b+QXvh+ZxPS8mC3EQkakfMkpaTe7HCOcss44MqgmZwER9eH/mXfooOsSfVvic36c2pCD59HnHnWgMI7kNnKx8MZ7HolWZsxvWt6/fHbShUp40GEkJoXy2EpMHo6ME4GvekyvVtPFXvjOAwRKEUERgDSkwaKjQY05yjlpsshGdVo6oLBjb2NsEpGGzoSBFWOEXUK+V1FL8kDUv4MnuPcN4TtTA0Y0GRh9abNTdSRjb9LtG1VnFDTI2Go++BV9L9YytFWtJi0po9EjdOi4qYNapGXd1AvqNTVoq+CIMRPBTnqaOQYj1irXYMgng8GxmVHch5zcFVFhDbQVcC6QG/AVhpU7FZRIUG4KFAAnyfrN4wWN9ccRqb11vlg== Content-ID: MIME-Version: 1.0 X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM6PR11MB3420.namprd11.prod.outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 26f72aa7-d6a3-47e0-b6c5-08d837ae19e4 X-MS-Exchange-CrossTenant-originalarrivaltime: 03 Aug 2020 13:06:56.9642 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 3f4057f3-b418-4d4e-ba84-d55b4e897d88 X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: k3afn8mp5+vevxtbh9HwxO8q4zNDj7yQHqMQO1yaxXNJMvMXipi1x8jsJiPXrpHX871k1y1YmP9vAyHaw+HwrhgObK3Xf30BCrbWBsQe1BY= X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM5PR11MB1930 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200803_090704_562251_67597641 X-CRM114-Status: GOOD ( 24.63 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: alexandre.belloni@bootlin.com, broonie@kernel.org, lgirdwood@gmail.com, tiwai@suse.com, Ludovic.Desroches@microchip.com, robh+dt@kernel.org, perex@perex.cz Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org On 03.08.2020 11:18, Codrin Ciubotariu wrote: > The new SPDIF TX controller is a serial port compliant with the IEC- > 60958 standard. It also supports programmable User Data and Channel > Status fields. > > This IP is embedded in Microchip's sama7g5 SoC. > > Signed-off-by: Codrin Ciubotariu > --- > > Changes in v2, v3: > - none; > > sound/soc/atmel/Kconfig | 12 + > sound/soc/atmel/Makefile | 2 + > sound/soc/atmel/mchp-spdiftx.c | 864 +++++++++++++++++++++++++++++++++ > 3 files changed, 878 insertions(+) > create mode 100644 sound/soc/atmel/mchp-spdiftx.c > > diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig > index 71f2d42188c4..93beb7d670a3 100644 > --- a/sound/soc/atmel/Kconfig > +++ b/sound/soc/atmel/Kconfig > @@ -132,4 +132,16 @@ config SND_MCHP_SOC_I2S_MCC > and supports a Time Division Multiplexed (TDM) interface with > external multi-channel audio codecs. > > +config SND_MCHP_SOC_SPDIFTX > + tristate "Microchip ASoC driver for boards using S/PDIF TX" > + depends on OF && (ARCH_AT91 || COMPILE_TEST) > + select SND_SOC_GENERIC_DMAENGINE_PCM > + select REGMAP_MMIO > + help > + Say Y or M if you want to add support for Microchip S/PDIF TX ASoc > + driver on the following Microchip platforms: > + - sama7g5 > + > + This S/PDIF TX driver is compliant with IEC-60958 standard and > + includes programable User Data and Channel Status fields. > endif > diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile > index c7d2989791be..3fd89a0063df 100644 > --- a/sound/soc/atmel/Makefile > +++ b/sound/soc/atmel/Makefile > @@ -5,6 +5,7 @@ snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o > snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o > snd-soc-atmel-i2s-objs := atmel-i2s.o > snd-soc-mchp-i2s-mcc-objs := mchp-i2s-mcc.o > +snd-soc-mchp-spdiftx-objs := mchp-spdiftx.o > > # pdc and dma need to both be built-in if any user of > # ssc is built-in. > @@ -17,6 +18,7 @@ endif > obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o > obj-$(CONFIG_SND_ATMEL_SOC_I2S) += snd-soc-atmel-i2s.o > obj-$(CONFIG_SND_MCHP_SOC_I2S_MCC) += snd-soc-mchp-i2s-mcc.o > +obj-$(CONFIG_SND_MCHP_SOC_SPDIFTX) += snd-soc-mchp-spdiftx.o > > # AT91 Machine Support > snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o > diff --git a/sound/soc/atmel/mchp-spdiftx.c b/sound/soc/atmel/mchp-spdiftx.c > new file mode 100644 > index 000000000000..738f6788212e > --- /dev/null > +++ b/sound/soc/atmel/mchp-spdiftx.c > @@ -0,0 +1,864 @@ > +// SPDX-License-Identifier: GPL-2.0 > +// > +// Driver for Microchip S/PDIF TX Controller > +// > +// Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries > +// > +// Author: Codrin Ciubotariu > + > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > + > +/* > + * ---- S/PDIF Transmitter Controller Register map ---- > + */ > +#define SPDIFTX_CR 0x00 /* Control Register */ > +#define SPDIFTX_MR 0x04 /* Mode Register */ This register is read/write either in atomic and non-atomic contextes but not protected everywhere the same way. > +#define SPDIFTX_CDR 0x0C /* Common Data Register */ > + > +#define SPDIFTX_IER 0x14 /* Interrupt Enable Register */ > +#define SPDIFTX_IDR 0x18 /* Interrupt Disable Register */ > +#define SPDIFTX_IMR 0x1C /* Interrupt Mask Register */ > +#define SPDIFTX_ISR 0x20 /* Interrupt Status Register */ > + > +#define SPDIFTX_CH1UD(reg) (0x50 + (reg) * 4) /* User Data 1 Register x */ > +#define SPDIFTX_CH1S(reg) (0x80 + (reg) * 4) /* Channel Status 1 Register x */ > + > +#define SPDIFTX_VERSION 0xF0 > + > +/* > + * ---- Control Register (Write-only) ---- > + */ > +#define SPDIFTX_CR_SWRST BIT(0) /* Software Reset */ > +#define SPDIFTX_CR_FCLR BIT(1) /* FIFO clear */ > + > +/* > + * ---- Mode Register (Read/Write) ---- > + */ > +/* Transmit Enable */ > +#define SPDIFTX_MR_TXEN_MASK GENMASK(0, 0) > +#define SPDIFTX_MR_TXEN_DISABLE (0 << 0) > +#define SPDIFTX_MR_TXEN_ENABLE (1 << 0) > + > +/* Multichannel Transfer */ > +#define SPDIFTX_MR_MULTICH_MASK GENAMSK(1, 1) > +#define SPDIFTX_MR_MULTICH_MONO (0 << 1) > +#define SPDIFTX_MR_MULTICH_DUAL (1 << 1) > + > +/* Data Word Endian Mode */ > +#define SPDIFTX_MR_ENDIAN_MASK GENMASK(2, 2) > +#define SPDIFTX_MR_ENDIAN_LITTLE (0 << 2) > +#define SPDIFTX_MR_ENDIAN_BIG (1 << 2) > + > +/* Data Justification */ > +#define SPDIFTX_MR_JUSTIFY_MASK GENMASK(3, 3) > +#define SPDIFTX_MR_JUSTIFY_LSB (0 << 3) > +#define SPDIFTX_MR_JUSTIFY_MSB (1 << 3) > + > +/* Common Audio Register Transfer Mode */ > +#define SPDIFTX_MR_CMODE_MASK GENMASK(5, 4) > +#define SPDIFTX_MR_CMODE_INDEX_ACCESS (0 << 4) > +#define SPDIFTX_MR_CMODE_TOGGLE_ACCESS (1 << 4) > +#define SPDIFTX_MR_CMODE_INTERLVD_ACCESS (2 << 4) > + > +/* Valid Bits per Sample */ > +#define SPDIFTX_MR_VBPS_MASK GENMASK(13, 8) > +#define SPDIFTX_MR_VBPS(bps) (((bps) << 8) & SPDIFTX_MR_VBPS_MASK) > + > +/* Chunk Size */ > +#define SPDIFTX_MR_CHUNK_MASK GENMASK(19, 16) > +#define SPDIFTX_MR_CHUNK(size) (((size) << 16) & SPDIFTX_MR_CHUNK_MASK) > + > +/* Validity Bits for Channels 1 and 2 */ > +#define SPDIFTX_MR_VALID1 BIT(24) > +#define SPDIFTX_MR_VALID2 BIT(25) > + > +/* Disable Null Frame on underrrun */ > +#define SPDIFTX_MR_DNFR_MASK GENMASK(27, 27) > +#define SPDIFTX_MR_DNFR_INVALID (0 << 27) > +#define SPDIFTX_MR_DNFR_VALID (1 << 27) > + > +/* Bytes per Sample */ > +#define SPDIFTX_MR_BPS_MASK GENMASK(29, 28) > +#define SPDIFTX_MR_BPS(bytes) \ > + ((((bytes) - 1) << 28) & SPDIFTX_MR_BPS_MASK) > + > +/* > + * ---- Interrupt Enable/Disable/Mask/Status Register (Write/Read-only) ---- > + */ > +#define SPDIFTX_IR_TXRDY BIT(0) > +#define SPDIFTX_IR_TXEMPTY BIT(1) > +#define SPDIFTX_IR_TXFULL BIT(2) > +#define SPDIFTX_IR_TXCHUNK BIT(3) > +#define SPDIFTX_IR_TXUDR BIT(4) > +#define SPDIFTX_IR_TXOVR BIT(5) > +#define SPDIFTX_IR_CSRDY BIT(6) > +#define SPDIFTX_IR_UDRDY BIT(7) > +#define SPDIFTX_IR_TXRDYCH(ch) BIT((ch) + 8) > +#define SPDIFTX_IR_SECE BIT(10) > +#define SPDIFTX_IR_TXUDRCH(ch) BIT((ch) + 11) > +#define SPDIFTX_IR_BEND BIT(13) > + > +static bool mchp_spdiftx_readable_reg(struct device *dev, unsigned int reg) > +{ > + switch (reg) { > + case SPDIFTX_MR: > + case SPDIFTX_IMR: > + case SPDIFTX_ISR: > + case SPDIFTX_CH1UD(0): > + case SPDIFTX_CH1UD(1): > + case SPDIFTX_CH1UD(2): > + case SPDIFTX_CH1UD(3): > + case SPDIFTX_CH1UD(4): > + case SPDIFTX_CH1UD(5): > + case SPDIFTX_CH1S(0): > + case SPDIFTX_CH1S(1): > + case SPDIFTX_CH1S(2): > + case SPDIFTX_CH1S(3): > + case SPDIFTX_CH1S(4): > + case SPDIFTX_CH1S(5): > + return true; > + default: > + return false; > + } > +} > + > +static bool mchp_spdiftx_writeable_reg(struct device *dev, unsigned int reg) > +{ > + switch (reg) { > + case SPDIFTX_CR: > + case SPDIFTX_MR: > + case SPDIFTX_CDR: > + case SPDIFTX_IER: > + case SPDIFTX_IDR: > + case SPDIFTX_CH1UD(0): > + case SPDIFTX_CH1UD(1): > + case SPDIFTX_CH1UD(2): > + case SPDIFTX_CH1UD(3): > + case SPDIFTX_CH1UD(4): > + case SPDIFTX_CH1UD(5): > + case SPDIFTX_CH1S(0): > + case SPDIFTX_CH1S(1): > + case SPDIFTX_CH1S(2): > + case SPDIFTX_CH1S(3): > + case SPDIFTX_CH1S(4): > + case SPDIFTX_CH1S(5): > + return true; > + default: > + return false; > + } > +} > + > +static bool mchp_spdiftx_precious_reg(struct device *dev, unsigned int reg) > +{ > + switch (reg) { > + case SPDIFTX_CDR: > + case SPDIFTX_ISR: > + return true; > + default: > + return false; > + } > +} > + > +static const struct regmap_config mchp_spdiftx_regmap_config = { > + .reg_bits = 32, > + .reg_stride = 4, > + .val_bits = 32, > + .max_register = SPDIFTX_VERSION, > + .readable_reg = mchp_spdiftx_readable_reg, > + .writeable_reg = mchp_spdiftx_writeable_reg, > + .precious_reg = mchp_spdiftx_precious_reg, > +}; > + > +#define SPDIFTX_GCLK_RATIO 128 > + > +#define SPDIFTX_CS_BITS 192 > +#define SPDIFTX_UD_BITS 192 > + > +struct mchp_spdiftx_mixer_control { > + unsigned char ch_stat[SPDIFTX_CS_BITS / 8]; > + unsigned char user_data[SPDIFTX_UD_BITS / 8]; > + spinlock_t lock; > +}; > + > +struct mchp_spdiftx_dev { > + struct mchp_spdiftx_mixer_control control; > + struct snd_dmaengine_dai_dma_data playback; > + struct device *dev; > + struct regmap *regmap; > + struct clk *pclk; > + struct clk *gclk; > + unsigned int fmt; > + const struct mchp_i2s_caps *caps; > + int gclk_enabled:1; > +}; > + > +static inline int mchp_spdiftx_is_running(struct mchp_spdiftx_dev *dev) > +{ > + u32 mr; > + > + regmap_read(dev->regmap, SPDIFTX_MR, &mr); > + return !!(mr & SPDIFTX_MR_TXEN_ENABLE); > +} > + > +static void mchp_spdiftx_channel_status_write(struct mchp_spdiftx_dev *dev) > +{ > + struct mchp_spdiftx_mixer_control *ctrl = &dev->control; > + u32 val; > + int i; > + > + for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat) / 4; i++) { > + val = (ctrl->ch_stat[(i * 4) + 0] << 0) | > + (ctrl->ch_stat[(i * 4) + 1] << 8) | > + (ctrl->ch_stat[(i * 4) + 2] << 16) | > + (ctrl->ch_stat[(i * 4) + 3] << 24); > + > + regmap_write(dev->regmap, SPDIFTX_CH1S(i), val); > + } > +} > + > +static void mchp_spdiftx_user_data_write(struct mchp_spdiftx_dev *dev) > +{ > + struct mchp_spdiftx_mixer_control *ctrl = &dev->control; > + u32 val; > + int i; > + > + for (i = 0; i < ARRAY_SIZE(ctrl->user_data) / 4; i++) { > + val = (ctrl->user_data[(i * 4) + 0] << 0) | > + (ctrl->user_data[(i * 4) + 1] << 8) | > + (ctrl->user_data[(i * 4) + 2] << 16) | > + (ctrl->user_data[(i * 4) + 3] << 24); > + > + regmap_write(dev->regmap, SPDIFTX_CH1UD(i), val); > + } > +} > + > +static irqreturn_t mchp_spdiftx_interrupt(int irq, void *dev_id) > +{ > + struct mchp_spdiftx_dev *dev = dev_id; > + struct mchp_spdiftx_mixer_control *ctrl = &dev->control; > + u32 sr, imr, pending, idr = 0; > + > + regmap_read(dev->regmap, SPDIFTX_ISR, &sr); > + regmap_read(dev->regmap, SPDIFTX_IMR, &imr); > + pending = sr & imr; > + > + if (!pending) > + return IRQ_NONE; > + > + if (pending & SPDIFTX_IR_TXUDR) { > + dev_warn(dev->dev, "underflow detected\n"); > + idr |= SPDIFTX_IR_TXUDR; > + } > + > + if (pending & SPDIFTX_IR_TXOVR) { > + dev_warn(dev->dev, "overflow detected\n"); > + idr |= SPDIFTX_IR_TXOVR; > + } > + > + if (pending & SPDIFTX_IR_UDRDY) { > + spin_lock(&ctrl->lock); > + mchp_spdiftx_user_data_write(dev); > + spin_unlock(&ctrl->lock); > + idr |= SPDIFTX_IR_UDRDY; > + } > + > + if (pending & SPDIFTX_IR_CSRDY) { > + spin_lock(&ctrl->lock); > + mchp_spdiftx_channel_status_write(dev); > + spin_unlock(&ctrl->lock); > + idr |= SPDIFTX_IR_CSRDY; > + } > + > + regmap_write(dev->regmap, SPDIFTX_IDR, idr); > + > + return IRQ_HANDLED; > +} > + > +static int mchp_spdiftx_dai_startup(struct snd_pcm_substream *substream, > + struct snd_soc_dai *dai) > +{ > + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); > + int err; > + > + err = clk_prepare_enable(dev->pclk); > + if (err) { > + dev_err(dev->dev, > + "failed to enable the peripheral clock: %d\n", err); > + return err; > + } > + > + /* Software reset the IP */ > + regmap_write(dev->regmap, SPDIFTX_CR, > + SPDIFTX_CR_SWRST | SPDIFTX_CR_FCLR); > + > + return 0; > +} > + > +static void mchp_spdiftx_dai_shutdown(struct snd_pcm_substream *substream, > + struct snd_soc_dai *dai) > +{ > + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); > + > + /* Disable interrupts */ > + regmap_write(dev->regmap, SPDIFTX_IDR, 0xffffffff); > + > + clk_disable_unprepare(dev->pclk); > +} > + > +static int mchp_spdiftx_trigger(struct snd_pcm_substream *substream, int cmd, > + struct snd_soc_dai *dai) > +{ > + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); > + struct mchp_spdiftx_mixer_control *ctrl = &dev->control; > + u32 mr; > + int running; > + int ret; > + > + /* do not start/stop while channel status or user data is updated */ > + spin_lock(&ctrl->lock); > + regmap_read(dev->regmap, SPDIFTX_MR, &mr); Here, atomic, for instance. > + running = !!(mr & SPDIFTX_MR_TXEN_ENABLE); > + > + switch (cmd) { > + case SNDRV_PCM_TRIGGER_START: > + case SNDRV_PCM_TRIGGER_RESUME: > + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: > + if (!running) { > + mr &= ~SPDIFTX_MR_TXEN_MASK; > + mr |= SPDIFTX_MR_TXEN_ENABLE; > + } > + break; > + case SNDRV_PCM_TRIGGER_STOP: > + case SNDRV_PCM_TRIGGER_SUSPEND: > + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: > + if (running) { > + mr &= ~SPDIFTX_MR_TXEN_MASK; > + mr |= SPDIFTX_MR_TXEN_DISABLE; > + } > + break; > + default: > + spin_unlock(&ctrl->lock); > + return -EINVAL; > + } > + > + ret = regmap_write(dev->regmap, SPDIFTX_MR, mr); > + spin_unlock(&ctrl->lock); > + if (ret) { > + dev_err(dev->dev, "unable to disable TX: %d\n", ret); > + return ret; > + } > + > + return 0; > +} > + > +static int mchp_spdiftx_hw_params(struct snd_pcm_substream *substream, > + struct snd_pcm_hw_params *params, > + struct snd_soc_dai *dai) > +{ > + unsigned long flags; > + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); > + struct mchp_spdiftx_mixer_control *ctrl = &dev->control; > + u32 mr; > + unsigned int bps = params_physical_width(params) / 8; > + int ret; > + > + dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n", > + __func__, params_rate(params), params_format(params), > + params_width(params), params_channels(params)); > + > + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { > + dev_err(dev->dev, "Capture is not supported\n"); > + return -EINVAL; > + } > + > + regmap_read(dev->regmap, SPDIFTX_MR, &mr); Here non-atomic. > + > + if (mr & SPDIFTX_MR_TXEN_ENABLE) { > + dev_err(dev->dev, "PCM already running\n"); > + return -EBUSY; > + } > + > + /* Defaults: Toggle mode, justify to LSB, chunksize 1 */ > + mr = SPDIFTX_MR_CMODE_TOGGLE_ACCESS | SPDIFTX_MR_JUSTIFY_LSB; > + dev->playback.maxburst = 1; > + switch (params_channels(params)) { > + case 1: > + mr |= SPDIFTX_MR_MULTICH_MONO; > + break; > + case 2: > + mr |= SPDIFTX_MR_MULTICH_DUAL; > + if (bps > 2) > + dev->playback.maxburst = 2; > + break; > + default: > + dev_err(dev->dev, "unsupported number of channels: %d\n", > + params_channels(params)); > + return -EINVAL; > + } > + mr |= SPDIFTX_MR_CHUNK(dev->playback.maxburst); > + > + switch (params_format(params)) { > + case SNDRV_PCM_FORMAT_S8: > + mr |= SPDIFTX_MR_VBPS(8); > + break; > + case SNDRV_PCM_FORMAT_S16_BE: > + mr |= SPDIFTX_MR_ENDIAN_BIG; > + fallthrough; > + case SNDRV_PCM_FORMAT_S16_LE: > + mr |= SPDIFTX_MR_VBPS(16); > + break; > + case SNDRV_PCM_FORMAT_S18_3BE: > + mr |= SPDIFTX_MR_ENDIAN_BIG; > + fallthrough; > + case SNDRV_PCM_FORMAT_S18_3LE: > + mr |= SPDIFTX_MR_VBPS(18); > + break; > + case SNDRV_PCM_FORMAT_S20_3BE: > + mr |= SPDIFTX_MR_ENDIAN_BIG; > + fallthrough; > + case SNDRV_PCM_FORMAT_S20_3LE: > + mr |= SPDIFTX_MR_VBPS(20); > + break; > + case SNDRV_PCM_FORMAT_S24_3BE: > + mr |= SPDIFTX_MR_ENDIAN_BIG; > + fallthrough; > + case SNDRV_PCM_FORMAT_S24_3LE: > + mr |= SPDIFTX_MR_VBPS(24); > + break; > + case SNDRV_PCM_FORMAT_S24_BE: > + mr |= SPDIFTX_MR_ENDIAN_BIG; > + fallthrough; > + case SNDRV_PCM_FORMAT_S24_LE: > + mr |= SPDIFTX_MR_VBPS(24); > + break; > + case SNDRV_PCM_FORMAT_S32_BE: > + mr |= SPDIFTX_MR_ENDIAN_BIG; > + fallthrough; > + case SNDRV_PCM_FORMAT_S32_LE: > + mr |= SPDIFTX_MR_VBPS(32); > + break; > + default: > + dev_err(dev->dev, "unsupported PCM format: %d\n", > + params_format(params)); > + return -EINVAL; > + } > + > + mr |= SPDIFTX_MR_BPS(bps); > + > + spin_lock_irqsave(&ctrl->lock, flags); > + ctrl->ch_stat[3] &= ~IEC958_AES3_CON_FS; > + switch (params_rate(params)) { > + case 22050: > + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_22050; > + break; > + case 24000: > + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_24000; > + break; > + case 32000: > + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_32000; > + break; > + case 44100: > + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_44100; > + break; > + case 48000: > + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_48000; > + break; > + case 88200: > + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_88200; > + break; > + case 96000: > + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_96000; > + break; > + case 176400: > + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_176400; > + break; > + case 192000: > + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_192000; > + break; > + case 8000: > + case 11025: > + case 16000: > + case 64000: > + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_NOTID; > + break; > + default: > + dev_err(dev->dev, "unsupported sample frequency: %u\n", > + params_rate(params)); > + spin_unlock_irqrestore(&ctrl->lock, flags); > + return -EINVAL; > + } > + mchp_spdiftx_channel_status_write(dev); > + spin_unlock_irqrestore(&ctrl->lock, flags); > + mr |= SPDIFTX_MR_VALID1 | SPDIFTX_MR_VALID2; > + > + if (dev->gclk_enabled) { > + clk_disable_unprepare(dev->gclk); > + dev->gclk_enabled = 0; > + } > + ret = clk_set_rate(dev->gclk, params_rate(params) * > + SPDIFTX_GCLK_RATIO); > + if (ret) { > + dev_err(dev->dev, > + "unable to change gclk rate to: rate %u * ratio %u\n", > + params_rate(params), SPDIFTX_GCLK_RATIO); > + return ret; > + } > + ret = clk_prepare_enable(dev->gclk); > + if (ret) { > + dev_err(dev->dev, "unable to enable gclk: %d\n", ret); > + return ret; > + } > + dev->gclk_enabled = 1; > + dev_dbg(dev->dev, "%s(): GCLK set to %d\n", __func__, > + params_rate(params) * SPDIFTX_GCLK_RATIO); > + > + /* Enable interrupts */ > + regmap_write(dev->regmap, SPDIFTX_IER, > + SPDIFTX_IR_TXUDR | SPDIFTX_IR_TXOVR); > + > + regmap_write(dev->regmap, SPDIFTX_MR, mr); Same here. > + > + return 0; > +} > + > +static int mchp_spdiftx_hw_free(struct snd_pcm_substream *substream, > + struct snd_soc_dai *dai) > +{ > + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); > + > + regmap_write(dev->regmap, SPDIFTX_IDR, > + SPDIFTX_IR_TXUDR | SPDIFTX_IR_TXOVR); > + if (dev->gclk_enabled) { > + clk_disable_unprepare(dev->gclk); > + dev->gclk_enabled = 0; > + } > + > + return regmap_write(dev->regmap, SPDIFTX_CR, > + SPDIFTX_CR_SWRST | SPDIFTX_CR_FCLR); > +} > + > + > +static const struct snd_soc_dai_ops mchp_spdiftx_dai_ops = { > + .startup = mchp_spdiftx_dai_startup, > + .shutdown = mchp_spdiftx_dai_shutdown, > + .trigger = mchp_spdiftx_trigger, > + .hw_params = mchp_spdiftx_hw_params, > + .hw_free = mchp_spdiftx_hw_free, > +}; > + > +#define MCHP_SPDIFTX_RATES SNDRV_PCM_RATE_8000_192000 > + > +#define MCHP_SPDIFTX_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ > + SNDRV_PCM_FMTBIT_S16_LE | \ > + SNDRV_PCM_FMTBIT_U16_BE | \ > + SNDRV_PCM_FMTBIT_S18_3LE | \ > + SNDRV_PCM_FMTBIT_S18_3BE | \ > + SNDRV_PCM_FMTBIT_S20_3LE | \ > + SNDRV_PCM_FMTBIT_S20_3BE | \ > + SNDRV_PCM_FMTBIT_S24_3LE | \ > + SNDRV_PCM_FMTBIT_S24_3BE | \ > + SNDRV_PCM_FMTBIT_S24_LE | \ > + SNDRV_PCM_FMTBIT_S24_BE | \ > + SNDRV_PCM_FMTBIT_S32_LE | \ > + SNDRV_PCM_FMTBIT_S32_BE \ > + ) > + > +static int mchp_spdiftx_info(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_info *uinfo) > +{ > + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; > + uinfo->count = 1; > + > + return 0; > +} > + > +static int mchp_spdiftx_cs_get(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *uvalue) > +{ > + unsigned long flags; > + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); > + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); > + struct mchp_spdiftx_mixer_control *ctrl = &dev->control; > + > + spin_lock_irqsave(&ctrl->lock, flags); > + memcpy(uvalue->value.iec958.status, ctrl->ch_stat, > + sizeof(ctrl->ch_stat)); > + spin_unlock_irqrestore(&ctrl->lock, flags); > + > + return 0; > +} > + > +static int mchp_spdiftx_cs_put(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *uvalue) > +{ > + unsigned long flags; > + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); > + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); > + struct mchp_spdiftx_mixer_control *ctrl = &dev->control; > + int changed = 0; > + int i; > + > + spin_lock_irqsave(&ctrl->lock, flags); > + for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat); i++) { > + if (ctrl->ch_stat[i] != uvalue->value.iec958.status[i]) > + changed = 1; > + ctrl->ch_stat[i] = uvalue->value.iec958.status[i]; > + } > + > + if (changed) { > + /* don't enable IP while we copy the channel status */ > + if (mchp_spdiftx_is_running(dev)) { > + /* > + * if SPDIF is running, wait for interrupt to write > + * channel status > + */ > + regmap_write(dev->regmap, SPDIFTX_IER, > + SPDIFTX_IR_CSRDY); > + } else { > + mchp_spdiftx_channel_status_write(dev); > + } > + } > + spin_unlock_irqrestore(&ctrl->lock, flags); > + > + return changed; > +} > + > +static int mchp_spdiftx_cs_mask(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *uvalue) > +{ > + memset(uvalue->value.iec958.status, 0xff, > + sizeof(uvalue->value.iec958.status)); > + > + return 0; > +} > + > +static int mchp_spdiftx_subcode_get(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *uvalue) > +{ > + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); > + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); > + struct mchp_spdiftx_mixer_control *ctrl = &dev->control; > + unsigned long flags; > + > + spin_lock_irqsave(&ctrl->lock, flags); > + memcpy(uvalue->value.iec958.subcode, ctrl->user_data, > + sizeof(ctrl->user_data)); > + spin_unlock_irqrestore(&ctrl->lock, flags); > + > + return 0; > +} > + > +static int mchp_spdiftx_subcode_put(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *uvalue) > +{ > + unsigned long flags; > + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); > + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); > + struct mchp_spdiftx_mixer_control *ctrl = &dev->control; > + int changed = 0; > + int i; > + > + spin_lock_irqsave(&ctrl->lock, flags); > + for (i = 0; i < ARRAY_SIZE(ctrl->user_data); i++) { > + if (ctrl->user_data[i] != uvalue->value.iec958.subcode[i]) > + changed = 1; > + > + ctrl->user_data[i] = uvalue->value.iec958.subcode[i]; > + } > + if (changed) { > + if (mchp_spdiftx_is_running(dev)) { > + /* > + * if SPDIF is running, wait for interrupt to write > + * user data > + */ > + regmap_write(dev->regmap, SPDIFTX_IER, > + SPDIFTX_IR_UDRDY); > + } else { > + mchp_spdiftx_user_data_write(dev); > + } > + } > + spin_unlock_irqrestore(&ctrl->lock, flags); > + > + return changed; > +} > + > +static struct snd_kcontrol_new mchp_spdiftx_ctrls[] = { > + /* Channel status controller */ > + { > + .iface = SNDRV_CTL_ELEM_IFACE_PCM, > + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), > + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | > + SNDRV_CTL_ELEM_ACCESS_VOLATILE, > + .info = mchp_spdiftx_info, > + .get = mchp_spdiftx_cs_get, > + .put = mchp_spdiftx_cs_put, > + }, > + { > + .iface = SNDRV_CTL_ELEM_IFACE_PCM, > + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), > + .access = SNDRV_CTL_ELEM_ACCESS_READ, > + SNDRV_CTL_ELEM_ACCESS_VOLATILE, > + .info = mchp_spdiftx_info, > + .get = mchp_spdiftx_cs_mask, > + }, > + /* User bits controller */ > + { > + .iface = SNDRV_CTL_ELEM_IFACE_PCM, > + .name = "IEC958 Subcode Playback Default", > + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, > + .info = mchp_spdiftx_info, > + .get = mchp_spdiftx_subcode_get, > + .put = mchp_spdiftx_subcode_put, > + }, > +}; > + > +static int mchp_spdiftx_dai_probe(struct snd_soc_dai *dai) > +{ > + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); > + > + snd_soc_dai_init_dma_data(dai, &dev->playback, NULL); > + > + /* Add controls */ > + snd_soc_add_dai_controls(dai, mchp_spdiftx_ctrls, > + ARRAY_SIZE(mchp_spdiftx_ctrls)); > + > + return 0; > +} > + > +static struct snd_soc_dai_driver mchp_spdiftx_dai = { > + .name = "mchp-spdiftx", > + .probe = mchp_spdiftx_dai_probe, > + .playback = { > + .stream_name = "S/PDIF TX Playback", > + .channels_min = 1, > + .channels_max = 2, > + .rates = MCHP_SPDIFTX_RATES, > + .formats = MCHP_SPDIFTX_FORMATS, > + }, > + .ops = &mchp_spdiftx_dai_ops, > +}; > + > +static const struct snd_soc_component_driver mchp_spdiftx_component = { > + .name = "mchp-spdiftx", > +}; > + > +static const struct of_device_id mchp_spdiftx_dt_ids[] = { > + { > + .compatible = "microchip,sama7g5-spdiftx", > + }, > + { /* sentinel */ } > +}; > + > +MODULE_DEVICE_TABLE(of, mchp_spdiftx_dt_ids); > +static int mchp_spdiftx_probe(struct platform_device *pdev) > +{ > + struct device_node *np = pdev->dev.of_node; > + const struct of_device_id *match; > + struct mchp_spdiftx_dev *dev; > + struct resource *mem; > + struct regmap *regmap; > + void __iomem *base; > + struct mchp_spdiftx_mixer_control *ctrl; > + int irq; > + int err; > + > + /* Get memory for driver data. */ > + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); > + if (!dev) > + return -ENOMEM; > + > + /* Get hardware capabilities. */ > + match = of_match_node(mchp_spdiftx_dt_ids, np); > + if (match) > + dev->caps = match->data; > + > + /* Map I/O registers. */ > + base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); > + if (IS_ERR(base)) > + return PTR_ERR(base); > + > + regmap = devm_regmap_init_mmio(&pdev->dev, base, > + &mchp_spdiftx_regmap_config); > + if (IS_ERR(regmap)) > + return PTR_ERR(regmap); > + > + /* Request IRQ */ > + irq = platform_get_irq(pdev, 0); > + if (irq < 0) > + return irq; > + > + err = devm_request_irq(&pdev->dev, irq, mchp_spdiftx_interrupt, 0, > + dev_name(&pdev->dev), dev); > + if (err) > + return err; > + > + /* Get the peripheral clock */ > + dev->pclk = devm_clk_get(&pdev->dev, "pclk"); > + if (IS_ERR(dev->pclk)) { > + err = PTR_ERR(dev->pclk); > + dev_err(&pdev->dev, > + "failed to get the peripheral clock: %d\n", err); > + return err; > + } > + > + /* Get the generic clock */ > + dev->gclk = devm_clk_get(&pdev->dev, "gclk"); > + if (IS_ERR(dev->gclk)) { > + err = PTR_ERR(dev->gclk); > + dev_err(&pdev->dev, > + "failed to get the PMC generic clock: %d\n", err); > + return err; > + } > + > + ctrl = &dev->control; > + spin_lock_init(&ctrl->lock); > + > + /* Init channel status */ > + ctrl->ch_stat[0] = IEC958_AES0_CON_NOT_COPYRIGHT | > + IEC958_AES0_CON_EMPHASIS_NONE; > + > + dev->dev = &pdev->dev; > + dev->regmap = regmap; > + platform_set_drvdata(pdev, dev); > + > + dev->playback.addr = (dma_addr_t)mem->start + SPDIFTX_CDR; > + dev->playback.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; > + > + err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); > + if (err) { > + dev_err(&pdev->dev, "failed to register PMC: %d\n", err); > + return err; > + } > + > + err = devm_snd_soc_register_component(&pdev->dev, > + &mchp_spdiftx_component, > + &mchp_spdiftx_dai, 1); > + if (err) { > + dev_err(&pdev->dev, "failed to register component: %d\n", err); > + return err; > + } > + > + return 0; > +} > + > +static struct platform_driver mchp_spdiftx_driver = { > + .probe = mchp_spdiftx_probe, > + .driver = { > + .name = "mchp_spdiftx", > + .of_match_table = of_match_ptr(mchp_spdiftx_dt_ids), > + }, > +}; > + > +module_platform_driver(mchp_spdiftx_driver); > + > +MODULE_AUTHOR("Codrin Ciubotariu "); > +MODULE_DESCRIPTION("Microchip S/PDIF TX Controller Driver"); > +MODULE_LICENSE("GPL v2"); > _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel