From patchwork Fri Jul 3 13:51:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Puyou Lu X-Patchwork-Id: 1322386 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.openwrt.org (client-ip=2001:8b0:10b:1231::1; helo=merlin.infradead.org; envelope-from=openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; secure) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=merlin.20170209 header.b=daVkYbw1; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=WD4ZP/5k; dkim-atps=neutral Received: from merlin.infradead.org (merlin.infradead.org [IPv6:2001:8b0:10b:1231::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 49yxK26f6Vz9sRW for ; Fri, 3 Jul 2020 23:54:14 +1000 (AEST) 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:MIME-Version:Cc:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:References:In-Reply-To:Message-Id:Date:Subject:To: From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=rMAPAVItO9VV5RRyPtWjHM+tSLpwarNyLFnzDl/gPBM=; b=daVkYbw12XFr+J59HPr65EcSOp 4ofZK1Wf5Qqgg9DCgnifrxQUIuG6asbMu00V991CzUFFxPd4Tjga1Kw9nwlGtxF4kdNINBXaA9Xjk eXeZ82gp3wCrjKomxy7vPDh1EMmJVIlm6byxZGcq+Gr7u/lC5OooOEWeIyz/6bRaNWNZWIQYkwwwo Qk/o9WLuoCraHHLhBGoeCuC5nUGjgx240QyawIbrj9zu/cx5MV7kOGwIc41FR5jLGCxP9gBIER9Mx gwqKWV5MgrUhJpSQmQ1mVLbVDkugwAI7f1wGVWKjMq1udSCyrJE3ZC7NU0ML2YcOdvdpnntW4VC6e ymMcoqYg==; Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jrM6p-0005Xs-ES; Fri, 03 Jul 2020 13:52:07 +0000 Received: from mail-pj1-x1043.google.com ([2607:f8b0:4864:20::1043]) by merlin.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1jrM6m-0005Wy-H9 for openwrt-devel@lists.openwrt.org; Fri, 03 Jul 2020 13:52:06 +0000 Received: by mail-pj1-x1043.google.com with SMTP id mn17so618398pjb.4 for ; Fri, 03 Jul 2020 06:52:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=YTnhTAqHZBagS9lLNpMRFZ531sefr1//9Jq+/GBWzmg=; b=WD4ZP/5koUQcqEyITw93ZKZfMd/4uIkL/9g1f5C+/f/1UuII9RzgVX0m5Rgqw0tFdl kEMJmRyPj0edJWWnOsOPp6vriEcvgxHD/uPfBBANBe68p3sekFSZomUCgVkz/0ilsO8Z Ee1oZ9OGq2rjC1riTqiVMP5PNfj8l3m3yAkw8bqledjVN5PPI85HdNw3pAc0ik8a4iY9 5JJbT2bxMCPNQRV9hiMGh8G1Vqo9WiXpmsrbxByy1M69hM63h7McrMn3KsoqFPtoUoYd EWf0AfI2DIbCSQRncpI9+zRI9Xp8VhAF2kKz1+bhTOjADNHFaTN9Qx2fD0kvifFkTtpl jGgQ== 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=YTnhTAqHZBagS9lLNpMRFZ531sefr1//9Jq+/GBWzmg=; b=a07xbjaOXu/WiyYm6B8oYpkSm9CqQ14vbZnVebtygGl4NOAnbFAFL7bAU/ExCYka4U 4F2u5mzr7uEbBQ1GI6fbF7N/RpKu0O2FL7ilqtoSV8e5mUzr3yC37yDPlxcwxvvJE+em L0qKGM0GYcIFp//G0UOhwrWNTSUVi4TV7GxITEg+MGU3uvR+DfLkoOAGLe8LG+HNuN1M URO8wdqsJVYdUX8dmwdDl4oQIalxdlk5Q+rcePS8Cq2qju2cfG30D+HGeAEHy/jOvGSW WWIGSmK0vTBRvjFIwKa8s/cKi+BwFcDkMAo5UWzM71PHrInDRe/1+YdBXIazISBOeC4J 4cDQ== X-Gm-Message-State: AOAM532+g0jBifXbJ4w5/8ckv2+oCue0FED8t9u8mjhBIJhOPNb8PiZZ /SMEE9LSGqYepU7hQgFR2DrdGoqlLZ4NzHRc X-Google-Smtp-Source: ABdhPJzcYxxGUpVDafe0TOu/MciNXdy83kb3zaLGq8M/sEhU0IJltx33Tk1mVFcIQgfB5O1lpZLWDQ== X-Received: by 2002:a17:902:a606:: with SMTP id u6mr30168527plq.94.1593784321317; Fri, 03 Jul 2020 06:52:01 -0700 (PDT) Received: from localhost.localdomain ([223.104.64.157]) by smtp.gmail.com with ESMTPSA id g4sm11431447pfi.68.2020.07.03.06.51.58 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Fri, 03 Jul 2020 06:52:00 -0700 (PDT) From: puyou.lu@gmail.com To: openwrt-devel@lists.openwrt.org Subject: [PATCH 1/1] ramips: mt7621: add ralink pcm controller driver Date: Fri, 3 Jul 2020 21:51:37 +0800 Message-Id: <1593784297-31453-2-git-send-email-puyou.lu@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1593784297-31453-1-git-send-email-puyou.lu@gmail.com> References: <1593784297-31453-1-git-send-email-puyou.lu@gmail.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200703_095204_641549_506C924E X-CRM114-Status: GOOD ( 19.33 ) X-Spam-Score: -0.2 (/) X-Spam-Report: SpamAssassin version 3.4.4 on merlin.infradead.org summary: Content analysis details: (-0.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider [puyou.lu[at]gmail.com] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:1043 listed in] [list.dnswl.org] 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain X-BeenThere: openwrt-devel@lists.openwrt.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: OpenWrt Development List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Puyou Lu MIME-Version: 1.0 Sender: "openwrt-devel" Errors-To: openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org From: Puyou Lu Only tested on mt7621 currently. Signed-off-by: Puyou Lu --- target/linux/ramips/dts/AP-MT7621A-V60.dts | 13 + target/linux/ramips/dts/mt7621.dtsi | 18 + target/linux/ramips/modules.mk | 5 +- .../0049-asoc-add-mt7620-pcm-support.patch | 920 +++++++++++++++++++++ 4 files changed, 955 insertions(+), 1 deletion(-) create mode 100644 target/linux/ramips/patches-4.14/0049-asoc-add-mt7620-pcm-support.patch diff --git a/target/linux/ramips/dts/AP-MT7621A-V60.dts b/target/linux/ramips/dts/AP-MT7621A-V60.dts index b613c9c..c4db011 100644 --- a/target/linux/ramips/dts/AP-MT7621A-V60.dts +++ b/target/linux/ramips/dts/AP-MT7621A-V60.dts @@ -54,6 +54,12 @@ ralink,function = "i2s"; }; }; + pcm_pins: pcm { + pcm { + ralink,group = "uart2"; + ralink,function = "pcm"; + }; + }; }; &i2c { @@ -78,6 +84,13 @@ pinctrl-0 = <&i2s_pins>; }; +&pcm { + #sound-dai-cells = <0>; + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pcm_pins>; +}; + &spi0 { status = "okay"; diff --git a/target/linux/ramips/dts/mt7621.dtsi b/target/linux/ramips/dts/mt7621.dtsi index 4f69e09..759a928 100644 --- a/target/linux/ramips/dts/mt7621.dtsi +++ b/target/linux/ramips/dts/mt7621.dtsi @@ -141,6 +141,24 @@ status = "disabled"; }; + pcm: pcm@2000 { + compatible = "mediatek,mt7621-pcm"; + reg = <0x2000 0x800>; + + clocks = <&sysclock>; + + resets = <&rstctrl 11>; + reset-names = "pcm"; + + interrupt-parent = <&gic>; + interrupts = ; + + txdma-req = <6>; + rxdma-req = <5>; + + status = "disabled"; + }; + systick: systick@500 { compatible = "ralink,mt7621-systick", "ralink,cevt-systick"; reg = <0x500 0x10>; diff --git a/target/linux/ramips/modules.mk b/target/linux/ramips/modules.mk index b604110..5976a68 100644 --- a/target/linux/ramips/modules.mk +++ b/target/linux/ramips/modules.mk @@ -119,20 +119,23 @@ define KernelPackage/sound-mt7620 DEPENDS:=@TARGET_ramips +kmod-sound-soc-core +kmod-regmap-i2c +kmod-dma-ralink @!TARGET_ramips_rt288x KCONFIG:= \ CONFIG_SND_RALINK_SOC_I2S \ + CONFIG_SND_RALINK_SOC_PCM \ CONFIG_SND_SIMPLE_CARD \ CONFIG_SND_SIMPLE_CARD_UTILS \ CONFIG_SND_SOC_WM8960 FILES:= \ $(LINUX_DIR)/sound/soc/ralink/snd-soc-ralink-i2s.ko \ + $(LINUX_DIR)/sound/soc/ralink/snd-soc-ralink-pcm.ko \ $(LINUX_DIR)/sound/soc/generic/snd-soc-simple-card.ko \ $(LINUX_DIR)/sound/soc/generic/snd-soc-simple-card-utils.ko \ $(LINUX_DIR)/sound/soc/codecs/snd-soc-wm8960.ko AUTOLOAD:=$(call AutoLoad,90,snd-soc-wm8960 snd-soc-ralink-i2s snd-soc-simple-card) + AUTOLOAD:=$(call AutoLoad,90,snd-soc-wm8960 snd-soc-ralink-pcm snd-soc-simple-card) $(call AddDepends/sound) endef define KernelPackage/sound-mt7620/description - Alsa modules for ralink i2s controller. + Alsa modules for ralink i2s/pcm controller. endef $(eval $(call KernelPackage,sound-mt7620)) diff --git a/target/linux/ramips/patches-4.14/0049-asoc-add-mt7620-pcm-support.patch b/target/linux/ramips/patches-4.14/0049-asoc-add-mt7620-pcm-support.patch new file mode 100644 index 0000000..57c65d1 --- /dev/null +++ b/target/linux/ramips/patches-4.14/0049-asoc-add-mt7620-pcm-support.patch @@ -0,0 +1,920 @@ +--- a/sound/soc/ralink/Kconfig ++++ b/sound/soc/ralink/Kconfig +@@ -6,3 +6,12 @@ config SND_RALINK_SOC_I2S + help + Say Y if you want to use I2S protocol and I2S codec on Ralink/MediaTek + based boards. ++ ++config SND_RALINK_SOC_PCM ++ depends on RALINK && SND_SOC && !SOC_RT288X ++ select SND_SOC_GENERIC_DMAENGINE_PCM ++ select REGMAP_MMIO ++ tristate "SoC Audio (PCM protocol) for Ralink SoC" ++ help ++ Say Y if you want to use PCM protocol and PCM codec on Ralink/MediaTek ++ based boards. +--- a/sound/soc/ralink/Makefile ++++ b/sound/soc/ralink/Makefile +@@ -2,5 +2,7 @@ + # Ralink/MediaTek Platform Support + # + snd-soc-ralink-i2s-objs := ralink-i2s.o ++snd-soc-ralink-pcm-objs := ralink-pcm.o + + obj-$(CONFIG_SND_RALINK_SOC_I2S) += snd-soc-ralink-i2s.o ++obj-$(CONFIG_SND_RALINK_SOC_PCM) += snd-soc-ralink-pcm.o +--- /dev/null ++++ b/sound/soc/ralink/ralink-pcm.c +@@ -0,0 +1,892 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define DRV_NAME "ralink-pcm" ++ ++#define PCM_REG_GLB_CFG 0x000 ++#define PCM_REG_PCM_CFG 0x004 ++#define PCM_REG_INT_STATUS 0x008 ++#define PCM_REG_INT_EN 0x00C ++#define PCM_REG_CHA0_FF_STATUS 0x010 ++#define PCM_REG_CHB0_FF_STATUS 0x014 ++#define PCM_REG_CHA0_CFG 0x020 ++#define PCM_REG_CHB0_CFG 0x024 ++#define PCM_REG_FSYNC_CFG 0x030 ++#define PCM_REG_CHA0_CFG2 0x034 ++#define PCM_REG_CHB0_CFG2 0x038 ++#define PCM_REG_IP_INFO 0x040 ++#define PCM_REG_RSV_REG16 0x044 ++#define PCM_REG_DIVCOMP_CFG 0x050 ++#define PCM_REG_DIVINT_CFG 0x054 ++#define PCM_REG_DIGDELAY_CFG 0x060 ++#define PCM_REG_CH0_FIFO 0x080 ++#define PCM_REG_CH1_FIFO 0x084 ++#define PCM_REG_CH2_FIFO 0x088 ++#define PCM_REG_CH3_FIFO 0x08C ++#define PCM_REG_CHA1_FF_STATUS 0x110 ++#define PCM_REG_CHB1_FF_STATUS 0x114 ++#define PCM_REG_CHA1_CFG 0x120 ++#define PCM_REG_CHB1_CFG 0x124 ++#define PCM_REG_CHA1_CFG2 0x134 ++#define PCM_REG_CHB1_CFG2 0x138 ++ ++/* PCM_REG_GLB_CFG */ ++#define PCM_REG_GLB_CFG_EN BIT(31) ++#define PCM_REG_GLB_CFG_DMA_EN BIT(30) ++#define PCM_REG_GLB_CFG_RX_THRES 20 ++#define PCM_REG_GLB_CFG_TX_THRES 16 ++#define PCM_REG_GLB_CFG_THRES_MASK (7 << PCM_REG_GLB_CFG_RX_THRES) | \ ++ (7 << PCM_REG_GLB_CFG_TX_THRES) ++#define PCM_REG_GLB_CFG_DFT_THRES (4 << PCM_REG_GLB_CFG_RX_THRES) | \ ++ (4 << PCM_REG_GLB_CFG_TX_THRES) ++#define PCM_REG_GLB_CFG_CH 0 ++#define PCM_REG_GLB_CFG_CH_MASK (0xF << PCM_REG_GLB_CFG_CH) ++#define PCM_REG_GLB_CFG_CH0 (BIT(0) << PCM_REG_GLB_CFG_CH) ++#define PCM_REG_GLB_CFG_CH1 (BIT(1) << PCM_REG_GLB_CFG_CH) ++#define PCM_REG_GLB_CFG_CH2 (BIT(2) << PCM_REG_GLB_CFG_CH) ++#define PCM_REG_GLB_CFG_CH3 (BIT(3) << PCM_REG_GLB_CFG_CH) ++ ++/* PCM_REG_PCM_CFG */ ++#define PCM_REG_PCM_CFG_CLKOUT_EN BIT(30) ++#define PCM_REG_PCM_CFG_EXT_FSYNC BIT(27) ++#define PCM_REG_PCM_CFG_FSYNC_POL BIT(25) ++#define PCM_REG_PCM_CFG_DTX_TRI BIT(24) ++#define PCM_REG_PCM_CFG_SLOT_MODE_MASK 0x7 ++#define PCM_REG_PCM_CFG_SLOT_MODE_4 0x0 // FS = BCLK / 8 / 4 ++#define PCM_REG_PCM_CFG_SLOT_MODE_8 0x1 // FS = BCLK / 8 / 8 ++#define PCM_REG_PCM_CFG_SLOT_MODE_16 0x2 // FS = BCLK / 8 / 16 ++#define PCM_REG_PCM_CFG_SLOT_MODE_32 0x3 // FS = BCLK / 8 / 32 ++#define PCM_REG_PCM_CFG_SLOT_MODE_64 0x4 // FS = BCLK / 8 / 64 ++#define PCM_REG_PCM_CFG_SLOT_MODE_128 0x5 // FS = BCLK / 8 / 128 ++ ++/* PCM_REG_INT_STATUS */ ++#define PCM_REG_INT_TX_FAULT BIT(7) ++#define PCM_REG_INT_TX_OVRUN BIT(6) ++#define PCM_REG_INT_TX_UNRUN BIT(5) ++#define PCM_REG_INT_TX_THRES BIT(4) ++#define PCM_REG_INT_RX_FAULT BIT(3) ++#define PCM_REG_INT_RX_OVRUN BIT(2) ++#define PCM_REG_INT_RX_UNRUN BIT(1) ++#define PCM_REG_INT_RX_THRES BIT(0) ++#define PCM_REG_INT_TX_MASK 0xF0 ++#define PCM_REG_INT_RX_MASK 0x0F ++ ++/* PCM_REG_CHA0_CFG */ ++#define PCM_REG_CHA0_CFG_TS_START 0 ++#define PCM_REG_CHA0_CFG_TS_START_MASK (0x3FF << PCM_REG_CHA0_CFG_TS_START) ++ ++/* PCM_REG_FSYNC_CFG */ ++#define PCM_REG_FSYNC_CFG_EN BIT(31) ++#define PCM_REG_FSYNC_CFG_CAP_DT BIT(30) ++#define PCM_REG_FSYNC_CFG_DRV_DT BIT(29) ++#define PCM_REG_FSYNC_CFG_CAP_FSYNC BIT(28) ++#define PCM_REG_FSYNC_CFG_DRV_FSYNC BIT(27) ++#define PCM_REG_FSYNC_CFG_FSYNC_INTV 0 ++#define PCM_REG_FSYNC_CFG_FSYNC_INTV_MASK \ ++ (0x3F << PCM_REG_FSYNC_CFG_FSYNC_INTV) ++ ++/* PCM_REG_DIVCOMP_CFG */ ++#define PCM_REG_DIVCOMP_CFG_CLKEN BIT(31) ++#define PCM_REG_DIVCOMP_CFG_MASK 0xFF ++ ++/* PCM_REG_DIVINT_CFG */ ++#define PCM_REG_DIVINT_CFG_MASK 0x3FF ++ ++/* FIFO */ ++#define RALINK_PCM_FIFO_SIZE 32 ++ ++#define RALINK_PCM_INT_EN 1 ++ ++struct ralink_pcm_stats { ++ u32 dmafault; ++ u32 overrun; ++ u32 underrun; ++ u32 belowthres; ++}; ++ ++struct ralink_pcm { ++ struct device *dev; ++ void __iomem *regs; ++ struct clk *clk; ++ struct regmap *regmap; ++ u32 flags; ++ unsigned int fmt; ++ u16 txdma_req; ++ u16 rxdma_req; ++ ++ struct snd_dmaengine_dai_dma_data playback_dma_data; ++ struct snd_dmaengine_dai_dma_data capture_dma_data; ++ ++ struct dentry *dbg_dir; ++ struct dentry *dbg_stats; ++ struct ralink_pcm_stats txstats; ++ struct ralink_pcm_stats rxstats; ++}; ++ ++#define PRINT_REG(reg) \ ++ regmap_read(pcm->regmap, PCM_REG_##reg, &buf); \ ++ printk(KERN_DEBUG "%3x: %08x " #reg "\n", PCM_REG_##reg, buf); ++ ++static void ralink_pcm_dump_regs(struct ralink_pcm *pcm) ++{ ++ u32 buf; ++ ++ printk(KERN_DEBUG "mt7621 pcm regs:\n"); ++ PRINT_REG(GLB_CFG); ++ PRINT_REG(PCM_CFG); ++ PRINT_REG(INT_STATUS); ++ PRINT_REG(INT_EN); ++ PRINT_REG(CHA0_FF_STATUS); ++ PRINT_REG(CHB0_FF_STATUS); ++ PRINT_REG(CHA0_CFG); ++ PRINT_REG(CHB0_CFG); ++ PRINT_REG(FSYNC_CFG); ++ PRINT_REG(CHA0_CFG2); ++ PRINT_REG(CHB0_CFG2); ++ PRINT_REG(IP_INFO); ++ PRINT_REG(RSV_REG16); ++ PRINT_REG(DIVCOMP_CFG); ++ PRINT_REG(DIVINT_CFG); ++ PRINT_REG(DIGDELAY_CFG); ++ PRINT_REG(CH0_FIFO); ++ PRINT_REG(CH1_FIFO); ++ PRINT_REG(CH2_FIFO); ++ PRINT_REG(CH3_FIFO); ++ PRINT_REG(CHA1_FF_STATUS); ++ PRINT_REG(CHB1_FF_STATUS); ++ PRINT_REG(CHA1_CFG); ++ PRINT_REG(CHB1_CFG); ++ PRINT_REG(CHA1_CFG2); ++ PRINT_REG(CHB1_CFG2); ++} ++ ++static int ralink_pcm_set_sysclk(struct snd_soc_dai *dai, ++ int clk_id, unsigned int freq, int dir) ++{ ++ return 0; ++} ++ ++static int ralink_pcm_set_bclk(struct snd_soc_dai *dai, int width, int rate) ++{ ++ struct ralink_pcm *pcm = snd_soc_dai_get_drvdata(dai); ++ unsigned long clk; ++ unsigned long freqin = 294000000; ++ int divint, divcomp; ++ ++ /* now use fixed PCM_REG_PCM_CFG_SLOT_MODE_8 */ ++ if (width > 64) ++ return -EINVAL; ++ width = 64; ++ ++ /* disable clock at slave mode */ ++ if ((pcm->fmt & SND_SOC_DAIFMT_MASTER_MASK) == ++ SND_SOC_DAIFMT_CBM_CFM) { ++ regmap_update_bits(pcm->regmap, PCM_REG_DIVCOMP_CFG, ++ PCM_REG_DIVCOMP_CFG_CLKEN, 0); ++ return 0; ++ } ++ ++ /* FREQOUT = FREQIN * (1/2) * (1/(DIVINT + DIVCOMP/256)) */ ++ clk = freqin / (2 * 1 * width); ++ divint = clk / rate; ++ divcomp = ((clk % rate) * 256) / rate; ++ ++ if ((divint > PCM_REG_DIVINT_CFG_MASK) || ++ (divcomp > PCM_REG_DIVCOMP_CFG_MASK)) ++ return -EINVAL; ++ ++ regmap_update_bits(pcm->regmap, PCM_REG_DIVINT_CFG, ++ PCM_REG_DIVINT_CFG_MASK, ++ divint); ++ regmap_update_bits(pcm->regmap, PCM_REG_DIVCOMP_CFG, ++ PCM_REG_DIVCOMP_CFG_MASK, ++ divcomp); ++ ++ /* enable clock */ ++ regmap_update_bits(pcm->regmap, PCM_REG_DIVCOMP_CFG, ++ PCM_REG_DIVCOMP_CFG_CLKEN, ++ PCM_REG_DIVCOMP_CFG_CLKEN); ++ ++ dev_dbg(pcm->dev, "freqin: %lu, rate: %u, width: %u, int: %d, comp: %d\n", ++ freqin, rate, width, divint, divcomp); ++ ++ return 0; ++} ++ ++static int ralink_pcm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ++{ ++ struct ralink_pcm *pcm = snd_soc_dai_get_drvdata(dai); ++ ++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBM_CFM: ++ regmap_update_bits(pcm->regmap, PCM_REG_PCM_CFG, ++ PCM_REG_PCM_CFG_CLKOUT_EN | ++ PCM_REG_PCM_CFG_EXT_FSYNC, ++ 0 | ++ PCM_REG_PCM_CFG_EXT_FSYNC); ++ break; ++ case SND_SOC_DAIFMT_CBS_CFS: ++ regmap_update_bits(pcm->regmap, PCM_REG_PCM_CFG, ++ PCM_REG_PCM_CFG_CLKOUT_EN | ++ PCM_REG_PCM_CFG_EXT_FSYNC, ++ PCM_REG_PCM_CFG_CLKOUT_EN | ++ 0); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* interface format */ ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_DSP_A: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* clock inversion */ ++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { ++ case SND_SOC_DAIFMT_IB_NF: ++ regmap_update_bits(pcm->regmap, PCM_REG_FSYNC_CFG, ++ PCM_REG_FSYNC_CFG_CAP_DT | ++ PCM_REG_FSYNC_CFG_DRV_DT | ++ PCM_REG_FSYNC_CFG_CAP_FSYNC | ++ PCM_REG_FSYNC_CFG_DRV_FSYNC, ++ 0 | ++ PCM_REG_FSYNC_CFG_DRV_DT | ++ 0 | ++ PCM_REG_FSYNC_CFG_DRV_FSYNC); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ pcm->fmt = fmt; ++ ++ return 0; ++} ++ ++static int ralink_pcm_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct ralink_pcm *pcm = snd_soc_dai_get_drvdata(dai); ++ ++ if (dai->active) ++ return 0; ++ ++ /* setup status interrupt */ ++#if (RALINK_PCM_INT_EN) ++ regmap_write(pcm->regmap, PCM_REG_INT_EN, 0xff); ++#else ++ regmap_write(pcm->regmap, PCM_REG_INT_EN, 0x0); ++#endif ++ ++ /* enable */ ++ regmap_update_bits(pcm->regmap, PCM_REG_GLB_CFG, ++ PCM_REG_GLB_CFG_EN | ++ PCM_REG_GLB_CFG_CH_MASK, ++ PCM_REG_GLB_CFG_EN | ++ PCM_REG_GLB_CFG_CH0 | PCM_REG_GLB_CFG_CH1); ++ ++ return 0; ++} ++ ++static void ralink_pcm_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct ralink_pcm *pcm = snd_soc_dai_get_drvdata(dai); ++ ++ /* If both streams are stopped, disable module and clock */ ++ if (dai->active) ++ return; ++ ++ /* ++ * datasheet mention when disable all control regs are cleared ++ * to initial values. need reinit at startup. ++ */ ++ regmap_update_bits(pcm->regmap, PCM_REG_GLB_CFG, ++ PCM_REG_GLB_CFG_EN | ++ PCM_REG_GLB_CFG_CH_MASK, ++ 0 | ++ 0); ++} ++ ++static int ralink_pcm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) ++{ ++ int width; ++ int ret; ++ ++ width = params_width(params); ++ switch (width) { ++ case 16: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (params_channels(params)) { ++ case 1: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* setup bclk rate */ ++ ret = ralink_pcm_set_bclk(dai, width, params_rate(params)); ++ ++ return ret; ++} ++ ++static int ralink_pcm_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct ralink_pcm *pcm = snd_soc_dai_get_drvdata(dai); ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ regmap_update_bits(pcm->regmap, PCM_REG_GLB_CFG, ++ PCM_REG_GLB_CFG_DMA_EN, ++ PCM_REG_GLB_CFG_DMA_EN); ++ break; ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ regmap_update_bits(pcm->regmap, PCM_REG_GLB_CFG, ++ PCM_REG_GLB_CFG_DMA_EN, ++ 0); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void ralink_pcm_init_dma_data(struct ralink_pcm *pcm, ++ struct resource *res) ++{ ++ struct snd_dmaengine_dai_dma_data *dma_data; ++ ++ /* channel 0 Playback */ ++ dma_data = &pcm->playback_dma_data; ++ dma_data->addr = res->start + PCM_REG_CH0_FIFO; ++ dma_data->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ dma_data->maxburst = 1; ++ dma_data->slave_id = pcm->txdma_req; ++ ++ /* channel 1 Capture */ ++ dma_data = &pcm->capture_dma_data; ++ dma_data->addr = res->start + PCM_REG_CH1_FIFO; ++ dma_data->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ dma_data->maxburst = 1; ++ dma_data->slave_id = pcm->rxdma_req; ++} ++ ++static int ralink_pcm_dai_probe(struct snd_soc_dai *dai) ++{ ++ struct ralink_pcm *pcm = snd_soc_dai_get_drvdata(dai); ++ ++ snd_soc_dai_init_dma_data(dai, &pcm->playback_dma_data, ++ &pcm->capture_dma_data); ++ ++ regmap_update_bits(pcm->regmap, PCM_REG_PCM_CFG, ++ PCM_REG_PCM_CFG_FSYNC_POL | ++ PCM_REG_PCM_CFG_DTX_TRI | ++ PCM_REG_PCM_CFG_SLOT_MODE_MASK, ++ PCM_REG_PCM_CFG_FSYNC_POL | ++ 0 | ++ PCM_REG_PCM_CFG_SLOT_MODE_8); ++ ++ regmap_update_bits(pcm->regmap, PCM_REG_CHA0_CFG, ++ PCM_REG_CHA0_CFG_TS_START_MASK, ++ 1); ++ ++ regmap_update_bits(pcm->regmap, PCM_REG_GLB_CFG, ++ PCM_REG_GLB_CFG_THRES_MASK, ++ PCM_REG_GLB_CFG_DFT_THRES); ++ ++ regmap_update_bits(pcm->regmap, PCM_REG_FSYNC_CFG, ++ PCM_REG_FSYNC_CFG_EN | ++ PCM_REG_FSYNC_CFG_FSYNC_INTV_MASK, ++ PCM_REG_FSYNC_CFG_EN | ++ 1); ++ ++ return 0; ++} ++ ++static int ralink_pcm_dai_remove(struct snd_soc_dai *dai) ++{ ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops ralink_pcm_dai_ops = { ++ .set_sysclk = ralink_pcm_set_sysclk, ++ .set_fmt = ralink_pcm_set_fmt, ++ .startup = ralink_pcm_startup, ++ .shutdown = ralink_pcm_shutdown, ++ .hw_params = ralink_pcm_hw_params, ++ .trigger = ralink_pcm_trigger, ++}; ++ ++static struct snd_soc_dai_driver ralink_pcm_dai = { ++ .name = DRV_NAME, ++ .probe = ralink_pcm_dai_probe, ++ .remove = ralink_pcm_dai_remove, ++ .ops = &ralink_pcm_dai_ops, ++ .capture = { ++ .stream_name = "PCM Capture", ++ .channels_min = 1, ++ .channels_max = 1, ++ .rate_min = 5512, ++ .rate_max = 192000, ++ .rates = SNDRV_PCM_RATE_CONTINUOUS, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .playback = { ++ .stream_name = "PCM Playback", ++ .channels_min = 1, ++ .channels_max = 1, ++ .rate_min = 5512, ++ .rate_max = 192000, ++ .rates = SNDRV_PCM_RATE_CONTINUOUS, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .symmetric_rates = 1, ++}; ++ ++static struct snd_pcm_hardware ralink_pcm_hardware = { ++ .info = SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_MMAP_VALID | ++ SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ .channels_min = 2, ++ .channels_max = 2, ++ .period_bytes_min = PAGE_SIZE, ++ .period_bytes_max = PAGE_SIZE * 2, ++ .periods_min = 2, ++ .periods_max = 128, ++ .buffer_bytes_max = 128 * 1024, ++ .fifo_size = RALINK_PCM_FIFO_SIZE, ++}; ++ ++static const struct snd_dmaengine_pcm_config ralink_dmaengine_pcm_config = { ++ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, ++ .pcm_hardware = &ralink_pcm_hardware, ++ .prealloc_buffer_size = 256 * PAGE_SIZE, ++}; ++ ++static const struct snd_soc_component_driver ralink_pcm_component = { ++ .name = DRV_NAME, ++}; ++ ++static bool ralink_pcm_writeable_reg(struct device *dev, unsigned int reg) ++{ ++ return true; ++} ++ ++static bool ralink_pcm_readable_reg(struct device *dev, unsigned int reg) ++{ ++ return true; ++} ++ ++static bool ralink_pcm_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case PCM_REG_INT_STATUS: ++ case PCM_REG_CHA0_FF_STATUS: ++ case PCM_REG_CHB0_FF_STATUS: ++ case PCM_REG_CHA1_FF_STATUS: ++ case PCM_REG_CHB1_FF_STATUS: ++ return true; ++ } ++ return false; ++} ++ ++static const struct regmap_config ralink_pcm_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .writeable_reg = ralink_pcm_writeable_reg, ++ .readable_reg = ralink_pcm_readable_reg, ++ .volatile_reg = ralink_pcm_volatile_reg, ++ .max_register = PCM_REG_CHB1_CFG2, ++}; ++ ++#if (RALINK_PCM_INT_EN) ++static irqreturn_t ralink_pcm_irq(int irq, void *devid) ++{ ++ struct ralink_pcm *pcm = devid; ++ u32 status; ++ ++ regmap_read(pcm->regmap, PCM_REG_INT_STATUS, &status); ++ if (unlikely(!status)) ++ return IRQ_NONE; ++ ++ /* tx stats */ ++ if (status & PCM_REG_INT_TX_MASK) { ++ if (status & PCM_REG_INT_TX_THRES) ++ pcm->txstats.belowthres++; ++ if (status & PCM_REG_INT_TX_UNRUN) ++ pcm->txstats.underrun++; ++ if (status & PCM_REG_INT_TX_OVRUN) ++ pcm->txstats.overrun++; ++ if (status & PCM_REG_INT_TX_FAULT) ++ pcm->txstats.dmafault++; ++ } ++ ++ /* rx stats */ ++ if (status & PCM_REG_INT_RX_MASK) { ++ if (status & PCM_REG_INT_RX_THRES) ++ pcm->rxstats.belowthres++; ++ if (status & PCM_REG_INT_RX_UNRUN) ++ pcm->rxstats.underrun++; ++ if (status & PCM_REG_INT_RX_OVRUN) ++ pcm->rxstats.overrun++; ++ if (status & PCM_REG_INT_RX_FAULT) ++ pcm->rxstats.dmafault++; ++ } ++ ++ /* clean status bits */ ++ regmap_write(pcm->regmap, PCM_REG_INT_STATUS, status); ++ ++ return IRQ_HANDLED; ++} ++#endif ++ ++#if IS_ENABLED(CONFIG_DEBUG_FS) ++static int ralink_pcm_stats_show(struct seq_file *s, void *unused) ++{ ++ struct ralink_pcm *pcm = s->private; ++ ++ seq_printf(s, "tx stats\n"); ++ seq_printf(s, "\tbelow threshold\t%u\n", pcm->txstats.belowthres); ++ seq_printf(s, "\tunder run\t%u\n", pcm->txstats.underrun); ++ seq_printf(s, "\tover run\t%u\n", pcm->txstats.overrun); ++ seq_printf(s, "\tdma fault\t%u\n", pcm->txstats.dmafault); ++ ++ seq_printf(s, "rx stats\n"); ++ seq_printf(s, "\tbelow threshold\t%u\n", pcm->rxstats.belowthres); ++ seq_printf(s, "\tunder run\t%u\n", pcm->rxstats.underrun); ++ seq_printf(s, "\tover run\t%u\n", pcm->rxstats.overrun); ++ seq_printf(s, "\tdma fault\t%u\n", pcm->rxstats.dmafault); ++ ++ ralink_pcm_dump_regs(pcm); ++ ++ return 0; ++} ++ ++static int ralink_pcm_stats_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, ralink_pcm_stats_show, inode->i_private); ++} ++ ++static const struct file_operations ralink_pcm_stats_ops = { ++ .open = ralink_pcm_stats_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static inline int ralink_pcm_debugfs_create(struct ralink_pcm *pcm) ++{ ++ pcm->dbg_dir = debugfs_create_dir(dev_name(pcm->dev), NULL); ++ if (!pcm->dbg_dir) ++ return -ENOMEM; ++ ++ pcm->dbg_stats = debugfs_create_file("stats", S_IRUGO, ++ pcm->dbg_dir, pcm, &ralink_pcm_stats_ops); ++ if (!pcm->dbg_stats) { ++ debugfs_remove(pcm->dbg_dir); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static inline void ralink_pcm_debugfs_remove(struct ralink_pcm *pcm) ++{ ++ debugfs_remove(pcm->dbg_stats); ++ debugfs_remove(pcm->dbg_dir); ++} ++#else ++static inline int ralink_pcm_debugfs_create(struct ralink_pcm *pcm) ++{ ++ return 0; ++} ++ ++static inline void ralink_pcm_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg) ++{ ++} ++#endif ++ ++#define RALINK_SYSCTL_BASE 0xBE000000 ++#define RALINK_ANA_CTRL_BASE 0xBE000F00 ++#define RALINK_REG_RD(addr) (*(volatile u32 *)(addr)) ++#define RALINK_REG_WR(addr, val) (*(volatile u32 *)(addr) = (val)) ++ ++static void mt7621_refclk_setup(void) ++{ ++ bool clk_20mhz = 0, clk_40mhz = 0; ++ u32 reg; ++ ++ reg = RALINK_REG_RD(RALINK_SYSCTL_BASE + 0x10); ++ reg = (reg >> 6) & 0x7; ++ if (reg <= 2) { ++ /* 20MHz Xtal */ ++ clk_20mhz = true; ++ } else if (reg >= 3 && reg <= 5) { ++ /* 40MHz Xtal */ ++ clk_40mhz = true; ++ } else { ++ /* 25MHz Xtal */ ++ } ++ ++ /* reset required registers to default */ ++ RALINK_REG_WR(RALINK_ANA_CTRL_BASE + 0x0000, 0x00008000); ++ RALINK_REG_WR(RALINK_ANA_CTRL_BASE + 0x0014, 0x01401d61); ++ RALINK_REG_WR(RALINK_ANA_CTRL_BASE + 0x0018, 0x38233d0e); ++ RALINK_REG_WR(RALINK_ANA_CTRL_BASE + 0x001c, 0x80120004); ++ RALINK_REG_WR(RALINK_ANA_CTRL_BASE + 0x0020, 0x1c7dbf48); ++ ++ /* toggle RG_XPTL_CHG */ ++ RALINK_REG_WR(RALINK_ANA_CTRL_BASE + 0x0000, 0x00008800); ++ RALINK_REG_WR(RALINK_ANA_CTRL_BASE + 0x0000, 0x00008c00); ++ ++ reg = RALINK_REG_RD(RALINK_ANA_CTRL_BASE + 0x0014); ++ reg &= ~0x0000ffc0; ++ if (clk_20mhz || clk_40mhz) { ++ reg |= 0x1d << 8; ++ } else { ++ reg |= 0x17 << 8; ++ } ++ reg |= 0x1 << 6; ++ RALINK_REG_WR(RALINK_ANA_CTRL_BASE + 0x0014, reg); ++ ++ reg = RALINK_REG_RD(RALINK_ANA_CTRL_BASE + 0x0018); ++ reg &= ~0xf0773f00; ++ reg |= 0x3 << 28; ++ reg |= 0x2 << 20; ++ if (clk_20mhz || clk_40mhz) { ++ reg |= 0x3 << 16; ++ } else { ++ reg |= 0x2 << 16; ++ } ++ reg |= 0x3 << 12; ++ if (clk_20mhz || clk_40mhz) { ++ reg |= 0xd << 8; ++ } else { ++ reg |= 0x7 << 8; ++ } ++ RALINK_REG_WR(RALINK_ANA_CTRL_BASE + 0x0018, reg); ++ ++ if (clk_20mhz || clk_40mhz) { ++ reg = 0x1c7dbf48; ++ } else { ++ reg = 0x1697cc39; ++ } ++ RALINK_REG_WR(RALINK_ANA_CTRL_BASE + 0x0020, reg); ++ ++ /* Common setting - Set PLLGP_CTRL_4 */ ++ reg = RALINK_REG_RD(RALINK_ANA_CTRL_BASE + 0x001c); ++ reg &= ~(0x1 << 31); ++ RALINK_REG_WR(RALINK_ANA_CTRL_BASE + 0x001c, reg); ++ ++ reg = RALINK_REG_RD(RALINK_ANA_CTRL_BASE + 0x001c); ++ reg |= 0x1 << 0; ++ RALINK_REG_WR(RALINK_ANA_CTRL_BASE + 0x001c, reg); ++ ++ reg = RALINK_REG_RD(RALINK_ANA_CTRL_BASE + 0x001c); ++ reg |= 0x1 << 3; ++ RALINK_REG_WR(RALINK_ANA_CTRL_BASE + 0x001c, reg); ++ ++ reg = RALINK_REG_RD(RALINK_ANA_CTRL_BASE + 0x001c); ++ reg |= 0x1 << 8; ++ RALINK_REG_WR(RALINK_ANA_CTRL_BASE + 0x001c, reg); ++ ++ reg = RALINK_REG_RD(RALINK_ANA_CTRL_BASE + 0x001c); ++ reg |= 0x1 << 6; ++ RALINK_REG_WR(RALINK_ANA_CTRL_BASE + 0x001c, reg); ++ ++ reg = RALINK_REG_RD(RALINK_ANA_CTRL_BASE + 0x001c); ++ reg |= 0x1 << 5; ++ RALINK_REG_WR(RALINK_ANA_CTRL_BASE + 0x001c, reg); ++ ++ reg = RALINK_REG_RD(RALINK_ANA_CTRL_BASE + 0x001c); ++ reg |= 0x1 << 7; ++ RALINK_REG_WR(RALINK_ANA_CTRL_BASE + 0x001c, reg); ++ ++ reg = RALINK_REG_RD(RALINK_ANA_CTRL_BASE + 0x001c); ++ reg |= 0x1 << 17; ++ RALINK_REG_WR(RALINK_ANA_CTRL_BASE + 0x001c, reg); ++} ++ ++struct rt_pcm_data { ++ u32 flags; ++ void (*refclk_setup)(void); ++}; ++ ++static struct rt_pcm_data mt7621_pcm_data = { ++ .refclk_setup = mt7621_refclk_setup ++}; ++ ++static const struct of_device_id ralink_pcm_match_table[] = { ++ { .compatible = "mediatek,mt7621-pcm", ++ .data = (void *)&mt7621_pcm_data }, ++}; ++MODULE_DEVICE_TABLE(of, ralink_pcm_match_table); ++ ++static int ralink_pcm_probe(struct platform_device *pdev) ++{ ++ const struct of_device_id *match; ++ struct device_node *np = pdev->dev.of_node; ++ struct ralink_pcm *pcm; ++ struct resource *res; ++ int irq, ret; ++ u32 dma_req; ++ struct rt_pcm_data *data; ++ ++ pcm = devm_kzalloc(&pdev->dev, sizeof(*pcm), GFP_KERNEL); ++ if (!pcm) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, pcm); ++ pcm->dev = &pdev->dev; ++ ++ match = of_match_device(ralink_pcm_match_table, &pdev->dev); ++ if (!match) ++ return -EINVAL; ++ data = (struct rt_pcm_data *)match->data; ++ pcm->flags = data->flags; ++ if (data->refclk_setup) ++ data->refclk_setup(); ++ ++ if (of_property_read_u32(np, "txdma-req", &dma_req)) { ++ dev_err(&pdev->dev, "no txdma-req define\n"); ++ return -EINVAL; ++ } ++ pcm->txdma_req = (u16)dma_req; ++ if (of_property_read_u32(np, "rxdma-req", &dma_req)) { ++ dev_err(&pdev->dev, "no rxdma-req define\n"); ++ return -EINVAL; ++ } ++ pcm->rxdma_req = (u16)dma_req; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ pcm->regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(pcm->regs)) ++ return PTR_ERR(pcm->regs); ++ ++ pcm->regmap = devm_regmap_init_mmio(&pdev->dev, pcm->regs, ++ &ralink_pcm_regmap_config); ++ if (IS_ERR(pcm->regmap)) { ++ dev_err(&pdev->dev, "regmap init failed\n"); ++ return PTR_ERR(pcm->regmap); ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "failed to get irq\n"); ++ return -EINVAL; ++ } ++ ++#if (RALINK_PCM_INT_EN) ++ ret = devm_request_irq(&pdev->dev, irq, ralink_pcm_irq, ++ 0, dev_name(&pdev->dev), pcm); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to request irq\n"); ++ return ret; ++ } ++#endif ++ ++ pcm->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(pcm->clk)) { ++ dev_err(&pdev->dev, "no clock defined\n"); ++ return PTR_ERR(pcm->clk); ++ } ++ ++ ret = clk_prepare_enable(pcm->clk); ++ if (ret) ++ return ret; ++ ++ ralink_pcm_init_dma_data(pcm, res); ++ ++ device_reset(&pdev->dev); ++ ++ ret = ralink_pcm_debugfs_create(pcm); ++ if (ret) { ++ dev_err(&pdev->dev, "create debugfs failed\n"); ++ goto err_clk_disable; ++ } ++ ++ ++ ret = devm_snd_soc_register_component(&pdev->dev, &ralink_pcm_component, ++ &ralink_pcm_dai, 1); ++ if (ret) ++ goto err_debugfs; ++ ++ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, ++ &ralink_dmaengine_pcm_config, ++ SND_DMAENGINE_PCM_FLAG_COMPAT); ++ if (ret) ++ goto err_debugfs; ++ ++ return 0; ++ ++err_debugfs: ++ ralink_pcm_debugfs_remove(pcm); ++ ++err_clk_disable: ++ clk_disable_unprepare(pcm->clk); ++ ++ return ret; ++} ++ ++static int ralink_pcm_remove(struct platform_device *pdev) ++{ ++ struct ralink_pcm *pcm = platform_get_drvdata(pdev); ++ ++ ralink_pcm_debugfs_remove(pcm); ++ clk_disable_unprepare(pcm->clk); ++ ++ return 0; ++} ++ ++static struct platform_driver ralink_pcm_driver = { ++ .probe = ralink_pcm_probe, ++ .remove = ralink_pcm_remove, ++ .driver = { ++ .name = DRV_NAME, ++ .of_match_table = ralink_pcm_match_table, ++ }, ++}; ++module_platform_driver(ralink_pcm_driver); ++ ++MODULE_AUTHOR("Puyou Lu "); ++MODULE_DESCRIPTION("Ralink/MediaTek PCM driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:" DRV_NAME);