Hi Puyou,
On Fri, Jul 03, 2020 at 09:51:37PM +0800, puyou...@gmail.com wrote: > From: Puyou Lu <puyou...@gmail.com> > > Only tested on mt7621 currently. > > Signed-off-by: Puyou Lu <puyou...@gmail.com> > --- > target/linux/ramips/dts/AP-MT7621A-V60.dts | 13 + This file was renamed into mt7621_mediatek_ap-mt7621a-v60.dts. Please rebase your patch on top of the master branch of https://git.openwrt.org/openwrt/openwrt.git Cheers Daniel > 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 = <GIC_SHARED 10 IRQ_TYPE_LEVEL_HIGH>; > + > + 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 <linux/module.h> > ++#include <linux/platform_device.h> > ++#include <linux/clk.h> > ++#include <linux/regmap.h> > ++#include <linux/reset.h> > ++#include <linux/debugfs.h> > ++#include <linux/of_device.h> > ++#include <sound/pcm_params.h> > ++#include <sound/dmaengine_pcm.h> > ++ > ++#include <asm/mach-ralink/ralink_regs.h> > ++ > ++#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 <puyou...@gmail.com>"); > ++MODULE_DESCRIPTION("Ralink/MediaTek PCM driver"); > ++MODULE_LICENSE("GPL"); > ++MODULE_ALIAS("platform:" DRV_NAME); > -- > 2.7.4 > > > _______________________________________________ > openwrt-devel mailing list > openwrt-devel@lists.openwrt.org > https://lists.openwrt.org/mailman/listinfo/openwrt-devel _______________________________________________ openwrt-devel mailing list openwrt-devel@lists.openwrt.org https://lists.openwrt.org/mailman/listinfo/openwrt-devel