From patchwork Sat Jul 16 02:13:26 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Stultz X-Patchwork-Id: 104812 Delivered-To: patches@linaro.org Received: by 10.140.29.52 with SMTP id a49csp116845qga; Fri, 15 Jul 2016 19:13:44 -0700 (PDT) X-Received: by 10.66.86.170 with SMTP id q10mr25548186paz.105.1468635218190; Fri, 15 Jul 2016 19:13:38 -0700 (PDT) Return-Path: Received: from mail-pa0-x229.google.com (mail-pa0-x229.google.com. [2607:f8b0:400e:c03::229]) by mx.google.com with ESMTPS id d69si350736pfj.87.2016.07.15.19.13.38 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 15 Jul 2016 19:13:38 -0700 (PDT) Received-SPF: pass (google.com: domain of john.stultz@linaro.org designates 2607:f8b0:400e:c03::229 as permitted sender) client-ip=2607:f8b0:400e:c03::229; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org; spf=pass (google.com: domain of john.stultz@linaro.org designates 2607:f8b0:400e:c03::229 as permitted sender) smtp.mailfrom=john.stultz@linaro.org; dmarc=pass (p=NONE dis=NONE) header.from=linaro.org Received: by mail-pa0-x229.google.com with SMTP id dx3so44219964pab.2 for ; Fri, 15 Jul 2016 19:13:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=USgnXwCl+E+y6yUQTMvu+/7tftGr8oHDaYsIX8hG9XU=; b=FCt8lBQoEJMsp6izeyRwRHUzVTAwQ+6A1P6yBIL1QhU4z5nQJ6JetEQ3MIKKtwQR5S gwRkZCNENhuhv/Ef2K5MTvNdefaAu0rrBmX1zfuGvdQLXyDioU65k+dHC7sR7EYFwkvH dr9gGuZ6owYttjtlCuQQ8n6yxfbw4kuM4rfns= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=USgnXwCl+E+y6yUQTMvu+/7tftGr8oHDaYsIX8hG9XU=; b=PYkpKS1ZqlyTsnT1dYV2ZBHDuhoIvGE2WeDM/MENhKt1H7miH8qAdMAELHsdMj4OrU WIFWWgoURAjXx7Xy359LlTSQnDKNyg+rPOcMyhb8MujuyFW2QdCRHlFP1/nX/vc6a77x tbu9kJ0NwZF48T5c6CDFloUacvlE5csQqWJgF3gZ+M5Hhk4kNPkufOFHY7099eRxBMVV eRs0R5FxDXjKAEJ7FtwAyLFVnQnfQokmgnbV1LsV5cIn4l7ceNy9kGk3XkzuR9vKFj33 FlEa7jM0kS57E1lsnE/ZkJ3yY+oDlnZUfYqJ5VHA59csNQVs3+Ml2JmEuZGOLaxo4W1g Lw2Q== X-Gm-Message-State: ALyK8tJ/KsyJa/k4wMitdZfwzT2VgZf2TS1oUsy5M4uL4YdsBGNWIp/x8cgxGyxUnwzSxGveWb8= X-Received: by 10.66.182.13 with SMTP id ea13mr37083219pac.89.1468635217635; Fri, 15 Jul 2016 19:13:37 -0700 (PDT) Return-Path: Received: from localhost.localdomain (c-73-67-244-238.hsd1.or.comcast.net. [73.67.244.238]) by smtp.gmail.com with ESMTPSA id d1sm7352655pas.48.2016.07.15.19.13.36 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 15 Jul 2016 19:13:37 -0700 (PDT) From: John Stultz To: lkml Cc: Andy Green , Zhangfei Gao , Jingoo Han , Krzysztof Kozlowski , Maxime Ripard , Vinod Koul , Dan Williams , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai , Wei Xu , Rob Herring , Andy Green , Dave Long , Guodong Xu , John Stultz Subject: [RFC][PATCH 6/7] ASoC: hisilicon: Add hi6210 i2s audio driver for hdmi audio Date: Fri, 15 Jul 2016 19:13:26 -0700 Message-Id: <1468635207-20065-7-git-send-email-john.stultz@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1468635207-20065-1-git-send-email-john.stultz@linaro.org> References: <1468635207-20065-1-git-send-email-john.stultz@linaro.org> From: Andy Green Add driver for hi6210 i2s controller found on hi6220 boards. Cc: Zhangfei Gao Cc: Jingoo Han Cc: Krzysztof Kozlowski Cc: Maxime Ripard Cc: Vinod Koul Cc: Dan Williams Cc: Liam Girdwood Cc: Mark Brown Cc: Jaroslav Kysela Cc: Takashi Iwai Cc: Wei Xu Cc: Rob Herring Cc: Andy Green Cc: Dave Long Cc: Guodong Xu Signed-off-by: Andy Green [jstultz: Forward ported to mainline, limit rates to 48k] Signed-off-by: John Stultz --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/hisilicon/Kconfig | 5 + sound/soc/hisilicon/Makefile | 2 + sound/soc/hisilicon/hi6210-hdmi-card.c | 131 +++++++ sound/soc/hisilicon/hi6210-i2s.c | 641 +++++++++++++++++++++++++++++++++ sound/soc/hisilicon/hi6210-i2s.h | 276 ++++++++++++++ 7 files changed, 1057 insertions(+) create mode 100644 sound/soc/hisilicon/Kconfig create mode 100644 sound/soc/hisilicon/Makefile create mode 100644 sound/soc/hisilicon/hi6210-hdmi-card.c create mode 100644 sound/soc/hisilicon/hi6210-i2s.c create mode 100644 sound/soc/hisilicon/hi6210-i2s.h -- 1.9.1 diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 182d92e..9df9658 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -47,6 +47,7 @@ source "sound/soc/cirrus/Kconfig" source "sound/soc/davinci/Kconfig" source "sound/soc/dwc/Kconfig" source "sound/soc/fsl/Kconfig" +source "sound/soc/hisilicon/Kconfig" source "sound/soc/jz4740/Kconfig" source "sound/soc/nuc900/Kconfig" source "sound/soc/omap/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 9a30f21..2f6aabb 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_SND_SOC) += cirrus/ obj-$(CONFIG_SND_SOC) += davinci/ obj-$(CONFIG_SND_SOC) += dwc/ obj-$(CONFIG_SND_SOC) += fsl/ +obj-$(CONFIG_SND_SOC) += hisilicon/ obj-$(CONFIG_SND_SOC) += jz4740/ obj-$(CONFIG_SND_SOC) += img/ obj-$(CONFIG_SND_SOC) += intel/ diff --git a/sound/soc/hisilicon/Kconfig b/sound/soc/hisilicon/Kconfig new file mode 100644 index 0000000..4356d5a --- /dev/null +++ b/sound/soc/hisilicon/Kconfig @@ -0,0 +1,5 @@ +config SND_I2S_HI6210_I2S + tristate "Hisilicon I2S controller" + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Hisilicon I2S diff --git a/sound/soc/hisilicon/Makefile b/sound/soc/hisilicon/Makefile new file mode 100644 index 0000000..43a5504 --- /dev/null +++ b/sound/soc/hisilicon/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_SND_I2S_HI6210_I2S) += hi6210-i2s.o \ + hi6210-hdmi-card.o \ No newline at end of file diff --git a/sound/soc/hisilicon/hi6210-hdmi-card.c b/sound/soc/hisilicon/hi6210-hdmi-card.c new file mode 100644 index 0000000..f0995a7 --- /dev/null +++ b/sound/soc/hisilicon/hi6210-hdmi-card.c @@ -0,0 +1,131 @@ +/* + * linux/sound/soc/hisilicon/hi6210-hdmi-card.c + * + * Copyright (C) 2015 Linaro, Ltd + * + * 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, version 2 of the License. + */ + +#include +#include +#include + +static int hdmi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret; + + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret) + return ret; + + /* set i2s system clock */ + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, 24576000, SND_SOC_CLOCK_IN); + if (ret) + return ret; + + return 0; +} + +/* operations of sound device */ +static struct snd_soc_ops hdmi_ops = { + .hw_params = hdmi_hw_params, +}; + +static struct snd_soc_dai_link hi6210_hdmi_dai_link = { + .name = "hi6210-hdmi-dai-link", /* "codec name" */ + .stream_name = "hdmi", /* stream name */ + + .cpu_dai_name = "f7118000.hi6210_i2s", + .codec_name = "0.hi6210_hdmi_card", + .id = 0, + .ops = &hdmi_ops, + .codec_dai_name = "hi6210_hdmi_dai", + .platform_name = "f7118000.hi6210_i2s", +}; + +static struct snd_soc_card snd_soc_hi6210_hdmi = { + .name = "hi6210-hdmi", + .owner = THIS_MODULE, + .dai_link = &hi6210_hdmi_dai_link, + .num_links = 1, +}; + +static const struct snd_soc_dai_ops hi6210_hdmi_dai_ops = { +}; + +static struct snd_soc_dai_driver hi6210_hdmi_dai = { + .name = "hi6210_hdmi_dai", + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &hi6210_hdmi_dai_ops, +}; + +static struct snd_soc_codec_driver hi6210_hdmi_codec; + +static int hi6210_hdmi_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &snd_soc_hi6210_hdmi; + int ret; + + ret = snd_soc_register_codec(&pdev->dev, &hi6210_hdmi_codec, + &hi6210_hdmi_dai, 1); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_codec failed (%d)\n", + ret); + return ret; + } + card->dev = &pdev->dev; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + snd_soc_unregister_codec(&pdev->dev); + card->dev = NULL; + return ret; + } + return 0; +} + +static int hi6210_hdmi_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + snd_soc_unregister_codec(&pdev->dev); + card->dev = NULL; + return 0; +} + +static const struct of_device_id hi6210_hdmi_dt_ids[] = { + { .compatible = "hisilicon,hi6210-hdmi-audio-card" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, hi6210_hdmi_dt_ids); + +static struct platform_driver hi6210_hdmi_driver = { + .driver = { + .name = "hi6210-hdmi-audio", + .owner = THIS_MODULE, + .of_match_table = hi6210_hdmi_dt_ids, + }, + .probe = hi6210_hdmi_probe, + .remove = hi6210_hdmi_remove, +}; + +module_platform_driver(hi6210_hdmi_driver); + +MODULE_AUTHOR("andy.green@linaro.org"); +MODULE_DESCRIPTION("Hisilicon HDMI machine ASoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:hi6210-hdmi-audio"); diff --git a/sound/soc/hisilicon/hi6210-i2s.c b/sound/soc/hisilicon/hi6210-i2s.c new file mode 100644 index 0000000..5b7e35d --- /dev/null +++ b/sound/soc/hisilicon/hi6210-i2s.c @@ -0,0 +1,641 @@ +/* + * linux/sound/soc/m8m/hi6210_i2s.c - I2S IP driver + * + * Copyright (C) 2015 Linaro, Ltd + * Author: Andy Green + * + * 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, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This driver only deals with S2 interface (BT) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hi6210-i2s.h" + +struct hi6210_i2s { + struct device *dev; + struct reset_control *rc; + struct clk *clk[8]; + int clocks; + struct snd_soc_dai_driver dai; + void __iomem *base; + void __iomem *base_syscon; + void __iomem *base_pmctrl; + phys_addr_t base_phys; + struct snd_dmaengine_dai_dma_data dma_data[2]; + int clk_rate; + spinlock_t lock; + int rate; + int format; + u8 bits; + u8 channels; + u8 id; + u8 channel_length; + u8 use; + u32 master:1; + u32 status:1; +}; + +#define SC_PERIPH_CLKEN1 0x210 +#define SC_PERIPH_CLKDIS1 0x214 + +#define SC_PERIPH_CLKEN3 0x230 +#define SC_PERIPH_CLKDIS3 0x234 + +#define SC_PERIPH_CLKEN12 0x270 +#define SC_PERIPH_CLKDIS12 0x274 + +#define SC_PERIPH_RSTEN1 0x310 +#define SC_PERIPH_RSTDIS1 0x314 +#define SC_PERIPH_RSTSTAT1 0x318 + +#define SC_PERIPH_RSTEN2 0x320 +#define SC_PERIPH_RSTDIS2 0x324 +#define SC_PERIPH_RSTSTAT2 0x328 + +#define SOC_PMCTRL_BBPPLLALIAS 0x48 + +static void hi6210_bits(struct hi6210_i2s *i2s, u32 ofs, u32 reset, u32 set) +{ + u32 val = readl(i2s->base + ofs) & ~reset; + + writel(val | set, i2s->base + ofs); +} + +static int _hi6210_i2s_set_fmt(struct hi6210_i2s *i2s, + struct snd_pcm_substream *substream) +{ + u32 u; + + hi6210_bits(i2s, HII2S_ST_DL_FIFO_TH_CFG, + (HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_MASK << + HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_SHIFT) | + (HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_MASK << + HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_SHIFT) | + (HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_MASK << + HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_SHIFT) | + (HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_MASK << + HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_SHIFT), + (16 << HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_SHIFT) | + (30 << HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_SHIFT) | + (16 << HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_SHIFT) | + (30 << HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_SHIFT)); + + hi6210_bits(i2s, HII2S_IF_CLK_EN_CFG, 0, + BIT(19) | BIT(18) | BIT(17) | + HII2S_IF_CLK_EN_CFG__S2_IF_CLK_EN | + HII2S_IF_CLK_EN_CFG__S2_OL_MIXER_EN | + HII2S_IF_CLK_EN_CFG__S2_OL_SRC_EN | + HII2S_IF_CLK_EN_CFG__ST_DL_R_EN | + HII2S_IF_CLK_EN_CFG__ST_DL_L_EN); + + hi6210_bits(i2s, HII2S_DIG_FILTER_CLK_EN_CFG, + HII2S_DIG_FILTER_CLK_EN_CFG__DACR_SDM_EN | + HII2S_DIG_FILTER_CLK_EN_CFG__DACR_HBF2I_EN | + HII2S_DIG_FILTER_CLK_EN_CFG__DACR_AGC_EN | + HII2S_DIG_FILTER_CLK_EN_CFG__DACL_SDM_EN | + HII2S_DIG_FILTER_CLK_EN_CFG__DACL_HBF2I_EN | + HII2S_DIG_FILTER_CLK_EN_CFG__DACL_AGC_EN, + HII2S_DIG_FILTER_CLK_EN_CFG__DACR_MIXER_EN | + HII2S_DIG_FILTER_CLK_EN_CFG__DACL_MIXER_EN + ); + + hi6210_bits(i2s, HII2S_DIG_FILTER_MODULE_CFG, + HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN2_MUTE | + HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN2_MUTE, + 0 + ); + hi6210_bits(i2s, HII2S_MUX_TOP_MODULE_CFG, + HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_IN1_MUTE | + HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_IN2_MUTE | + HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_IN1_MUTE | + HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_IN2_MUTE, + 0 + ); + + switch (i2s->format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + i2s->master = false; + hi6210_bits(i2s, HII2S_I2S_CFG, 0, HII2S_I2S_CFG__S2_MST_SLV); + break; + case SND_SOC_DAIFMT_CBS_CFS: + i2s->master = true; + hi6210_bits(i2s, HII2S_I2S_CFG, HII2S_I2S_CFG__S2_MST_SLV, 0); + break; + default: + return -EINVAL; + } + + switch (i2s->format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + u = HII2S_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + u = HII2S_FORMAT_LEFT_JUST; + break; + case SND_SOC_DAIFMT_RIGHT_J: + u = HII2S_FORMAT_RIGHT_JUST; + break; + default: + return -EINVAL; + } + + /* set the i2s format */ + hi6210_bits(i2s, HII2S_I2S_CFG, + (HII2S_I2S_CFG__S2_FUNC_MODE_MASK << + HII2S_I2S_CFG__S2_FUNC_MODE_SHIFT), + u << HII2S_I2S_CFG__S2_FUNC_MODE_SHIFT); + + /* misc control */ + hi6210_bits(i2s, HII2S_CLK_SEL, + HII2S_CLK_SEL__I2S_BT_FM_SEL | /* BT gets the I2S */ + HII2S_CLK_SEL__EXT_12_288MHZ_SEL, /* internal clock src */ + 0); + + return 0; +} + +int hi6210_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev); + int ret, n; + + /* deassert reset on ABB */ + if (readl(i2s->base_syscon + SC_PERIPH_RSTSTAT2) & BIT(4)) + writel(BIT(4), i2s->base_syscon + SC_PERIPH_RSTDIS2); + + for (n = 0; n < i2s->clocks; n++) { + ret = clk_prepare_enable(i2s->clk[n]); + if (ret) + return ret; + } + + ret = clk_set_rate(i2s->clk[1], 49152000); + if (ret) { + dev_err(i2s->dev, "%s: setting 49.152MHz base rate failed %d\n", + __func__, ret); + return ret; + } + + /* enable clock before frequency division */ + writel(BIT(9), i2s->base_syscon + SC_PERIPH_CLKEN12); + + /* enable codec working clock / == "codec bus clock" */ + writel(BIT(5), i2s->base_syscon + SC_PERIPH_CLKEN1); + + /* deassert reset on codec / interface clock / working clock */ + writel(BIT(5), i2s->base_syscon + SC_PERIPH_RSTEN1); + writel(BIT(5), i2s->base_syscon + SC_PERIPH_RSTDIS1); + + /* not interested in i2s irqs */ + hi6210_bits(i2s, HII2S_CODEC_IRQ_MASK, 0, 0x3f); + + /* reset the stereo downlink fifo */ + hi6210_bits(i2s, HII2S_APB_AFIFO_CFG_1, 0, BIT(5) | BIT(4)); + hi6210_bits(i2s, HII2S_APB_AFIFO_CFG_1, BIT(5) | BIT(4), 0); + + hi6210_bits(i2s, HII2S_SW_RST_N, + (HII2S_SW_RST_N__ST_DL_WORDLEN_MASK << + HII2S_SW_RST_N__ST_DL_WORDLEN_SHIFT), + (HII2S_BITS_16 << HII2S_SW_RST_N__ST_DL_WORDLEN_SHIFT) + ); + + hi6210_bits(i2s, HII2S_MISC_CFG, + /* mux 11/12 = APB not i2s */ + HII2S_MISC_CFG__ST_DL_TEST_SEL | + /* BT R ch 0 = mixer op of DACR ch */ + HII2S_MISC_CFG__S2_DOUT_RIGHT_SEL | + HII2S_MISC_CFG__S2_DOUT_TEST_SEL, + HII2S_MISC_CFG__S2_DOUT_RIGHT_SEL | + /* BT L ch = 1 = mux 7 = "mixer output of DACL */ + HII2S_MISC_CFG__S2_DOUT_TEST_SEL + ); + + /* disable the local i2s reset */ + hi6210_bits(i2s, HII2S_SW_RST_N, 0, HII2S_SW_RST_N__SW_RST_N); + + return 0; +} +void hi6210_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev); + int n; + + for (n = 0; n < i2s->clocks; n++) + clk_disable_unprepare(i2s->clk[n]); + + writel(BIT(5), i2s->base_syscon + SC_PERIPH_RSTEN1); +} + +static void hi6210_i2s_txctrl(struct snd_soc_dai *cpu_dai, int on) +{ + struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev); + + spin_lock(&i2s->lock); + + if (on) { + /* enable S2 TX */ + hi6210_bits(i2s, HII2S_I2S_CFG, 0, HII2S_I2S_CFG__S2_IF_TX_EN); + } else + /* disable S2 TX */ + hi6210_bits(i2s, HII2S_I2S_CFG, HII2S_I2S_CFG__S2_IF_TX_EN, 0); + + spin_unlock(&i2s->lock); +} + +static void hi6210_i2s_rxctrl(struct snd_soc_dai *cpu_dai, int on) +{ + struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev); + + spin_lock(&i2s->lock); + if (on) + hi6210_bits(i2s, HII2S_I2S_CFG, 0, HII2S_I2S_CFG__S2_IF_RX_EN); + else + hi6210_bits(i2s, HII2S_I2S_CFG, HII2S_I2S_CFG__S2_IF_RX_EN, 0); + + spin_unlock(&i2s->lock); +} + +static int hi6210_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + return 0; +} + +static int hi6210_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev); + + i2s->format = fmt; + i2s->master = (i2s->format & SND_SOC_DAIFMT_MASTER_MASK) == + SND_SOC_DAIFMT_CBS_CFS; + + return 0; +} + +static int hi6210_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev); + u32 u, signed_data = 0; + struct snd_dmaengine_dai_dma_data *dma_data; + + dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); + + _hi6210_i2s_set_fmt(i2s, substream); + + dma_data->maxburst = 2; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dma_data->addr = i2s->base_phys + HII2S_ST_DL_CHANNEL; + else + dma_data->addr = i2s->base_phys + HII2S_STEREO_UPLINK_CHANNEL; + + i2s->channels = params_channels(params); + if (i2s->channels == 1) + hi6210_bits(i2s, HII2S_I2S_CFG, + 0, HII2S_I2S_CFG__S2_FRAME_MODE); + else + hi6210_bits(i2s, HII2S_I2S_CFG, + HII2S_I2S_CFG__S2_FRAME_MODE, 0); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_U16_LE: + signed_data = HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT; + /* fallthru */ + case SNDRV_PCM_FORMAT_S16_LE: + i2s->bits = 16; + dma_data->addr_width = 2; + u = HII2S_BITS_16; + break; + + case SNDRV_PCM_FORMAT_U24_LE: + signed_data = HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT; + /* fallthru */ + case SNDRV_PCM_FORMAT_S24_LE: + i2s->bits = 32; + u = HII2S_BITS_24; + dma_data->addr_width = 3; + break; + default: + dev_err(cpu_dai->dev, "Bad format\n"); + return -EINVAL; + } + + /* clear loopback, set signed type and word length */ + hi6210_bits(i2s, HII2S_I2S_CFG, + HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT | + (HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_MASK << + HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_SHIFT) | + (HII2S_I2S_CFG__S2_DIRECT_LOOP_MASK << + HII2S_I2S_CFG__S2_DIRECT_LOOP_SHIFT), + signed_data | + (u << HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_SHIFT)); + + i2s->channel_length = i2s->channels * i2s->bits; + i2s->rate = params_rate(params); + + switch (i2s->rate) { + case 8000: + u = HII2S_FS_RATE_8KHZ; + break; + case 16000: + u = HII2S_FS_RATE_16KHZ; + break; + case 32000: + u = HII2S_FS_RATE_32KHZ; + break; + case 48000: + u = HII2S_FS_RATE_48KHZ; + break; + case 96000: + u = HII2S_FS_RATE_96KHZ; + break; + case 192000: + u = HII2S_FS_RATE_192KHZ; + break; + }; + + if (!i2s->rate || !i2s->channel_length) { + dev_err(cpu_dai->dev, "channels/rate/bits on i2s bad\n"); + return -EINVAL; + } + + if (!i2s->master) + return 0; + + /* set DAC and related units to correct rate */ + hi6210_bits(i2s, HII2S_FS_CFG, + (HII2S_FS_CFG__FS_S2_MASK << + HII2S_FS_CFG__FS_S2_SHIFT) | + (HII2S_FS_CFG__FS_DACLR_MASK << + HII2S_FS_CFG__FS_DACLR_SHIFT) | + (HII2S_FS_CFG__FS_ST_DL_R_MASK << + HII2S_FS_CFG__FS_ST_DL_R_SHIFT) | + (HII2S_FS_CFG__FS_ST_DL_L_MASK << + HII2S_FS_CFG__FS_ST_DL_L_SHIFT), + (u << HII2S_FS_CFG__FS_S2_SHIFT) | + (u << HII2S_FS_CFG__FS_DACLR_SHIFT) | + (u << HII2S_FS_CFG__FS_ST_DL_R_SHIFT) | + (u << HII2S_FS_CFG__FS_ST_DL_L_SHIFT) + ); + + return 0; +} + +static int hi6210_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *cpu_dai) +{ + pr_debug("%s\n", __func__); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + hi6210_i2s_rxctrl(cpu_dai, 1); + else + hi6210_i2s_txctrl(cpu_dai, 1); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + hi6210_i2s_rxctrl(cpu_dai, 0); + else + hi6210_i2s_txctrl(cpu_dai, 0); + break; + default: + dev_err(cpu_dai->dev, "uknown cmd\n"); + return -EINVAL; + } + return 0; +} + +static int hi6210_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct hi6210_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, + &i2s->dma_data[SNDRV_PCM_STREAM_PLAYBACK], + &i2s->dma_data[SNDRV_PCM_STREAM_CAPTURE]); + + return 0; +} + + +static struct snd_soc_dai_ops hi6210_i2s_dai_ops = { + .trigger = hi6210_i2s_trigger, + .hw_params = hi6210_i2s_hw_params, + .set_fmt = hi6210_i2s_set_fmt, + .set_sysclk = hi6210_i2s_set_sysclk, + .startup = hi6210_i2s_startup, + .shutdown = hi6210_i2s_shutdown, +}; + +struct snd_soc_dai_driver hi6210_i2s_dai_init = { + .name = "hi6210_i2s", + .probe = hi6210_i2s_dai_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_U16_LE, + .rates = SNDRV_PCM_RATE_48000, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_U16_LE, + .rates = SNDRV_PCM_RATE_48000, + }, + .ops = &hi6210_i2s_dai_ops, +}; + +static const struct snd_soc_component_driver hi6210_i2s_i2s_comp = { + .name = "hi6210_i2s-i2s", +}; + +#include + +static const struct snd_pcm_hardware snd_hi6210_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_HALF_DUPLEX, + .period_bytes_min = 4096, + .period_bytes_max = 4096, + .periods_min = 4, + .periods_max = UINT_MAX, + .buffer_bytes_max = SIZE_MAX, +}; + +static const struct snd_dmaengine_pcm_config hi6210_dmaengine_pcm_config = { + .pcm_hardware = &snd_hi6210_hardware, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .prealloc_buffer_size = 64 * 1024, +}; + +static int hi6210_i2s_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct hi6210_i2s *i2s; + struct resource *res; + int ret; + + i2s = kzalloc(sizeof(*i2s), GFP_KERNEL); + if (!i2s) + return -ENOMEM; + + i2s->dev = dev; + spin_lock_init(&i2s->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + goto err2; + } + i2s->base_phys = (phys_addr_t)res->start; + + i2s->dai = hi6210_i2s_dai_init; + dev_set_drvdata(&pdev->dev, i2s); + + i2s->base = devm_ioremap_resource(dev, res); + if (i2s->base == NULL) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err2; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + ret = -ENODEV; + goto err2; + } + i2s->base_syscon = devm_ioremap_resource(dev, res); + if (i2s->base_syscon == NULL) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err2; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!res) { + ret = -ENODEV; + goto err2; + } + i2s->base_pmctrl = devm_ioremap_resource(dev, res); + if (i2s->base_pmctrl == NULL) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err2; + } + + do { + i2s->clk[i2s->clocks] = of_clk_get(pdev->dev.of_node, + i2s->clocks); + if (IS_ERR_OR_NULL(i2s->clk[i2s->clocks])) + break; + i2s->clocks++; + } while (i2s->clocks < ARRAY_SIZE(i2s->clk)); + if (!i2s->clocks) { + ret = PTR_ERR(i2s->clk[0]); + dev_err(&pdev->dev, "Failed to get clock\n"); + goto err2; + } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, + &hi6210_dmaengine_pcm_config, + 0); + if (ret) + goto err3; + + ret = snd_soc_register_component(&pdev->dev, &hi6210_i2s_i2s_comp, + &i2s->dai, 1); + if (ret) { + dev_err(&pdev->dev, "Failed to register dai\n"); + goto err3; + } + + dev_info(&pdev->dev, "Registered as %s\n", i2s->dai.name); + + return 0; + +err3: + while (--i2s->clocks) + clk_put(i2s->clk[i2s->clocks]); + +err2: + kfree(i2s); + + return ret; +} + +static int hi6210_i2s_remove(struct platform_device *pdev) +{ + struct hi6210_i2s *i2s = dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_component(&pdev->dev); + dev_set_drvdata(&pdev->dev, NULL); + iounmap(i2s->base); + + while (--i2s->clocks) + clk_put(i2s->clk[i2s->clocks]); + + kfree(i2s); + + return 0; +} + +static const struct of_device_id hi6210_i2s_dt_ids[] = { + { .compatible = "hisilicon,hi6210-i2s" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, hi6210_i2s_dt_ids); + +static struct platform_driver hi6210_i2s_driver = { + .probe = hi6210_i2s_probe, + .remove = hi6210_i2s_remove, + .driver = { + .name = "hi6210_i2s", + .owner = THIS_MODULE, + .of_match_table = hi6210_i2s_dt_ids, + }, +}; + +module_platform_driver(hi6210_i2s_driver); + +MODULE_DESCRIPTION("Hisilicon HI6210 I2S driver"); +MODULE_AUTHOR("Andy Green "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/hisilicon/hi6210-i2s.h b/sound/soc/hisilicon/hi6210-i2s.h new file mode 100644 index 0000000..85cecc4 --- /dev/null +++ b/sound/soc/hisilicon/hi6210-i2s.h @@ -0,0 +1,276 @@ +/* + * linux/sound/soc/hisilicon/hi6210-i2s.h + * + * Copyright (C) 2015 Linaro, Ltd + * Author: Andy Green + * + * 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, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Note at least on 6220, S2 == BT, S1 == Digital FM Radio IF + */ + +#ifndef _HI6210_I2S_H +#define _HI6210_I2S_H + +#define HII2S_SW_RST_N 0 + +#define HII2S_SW_RST_N__STEREO_UPLINK_WORDLEN_SHIFT 28 +#define HII2S_SW_RST_N__STEREO_UPLINK_WORDLEN_MASK 3 +#define HII2S_SW_RST_N__THIRDMD_UPLINK_WORDLEN_SHIFT 26 +#define HII2S_SW_RST_N__THIRDMD_UPLINK_WORDLEN_MASK 3 +#define HII2S_SW_RST_N__VOICE_UPLINK_WORDLEN_SHIFT 24 +#define HII2S_SW_RST_N__VOICE_UPLINK_WORDLEN_MASK 3 +#define HII2S_SW_RST_N__ST_DL_WORDLEN_SHIFT 20 +#define HII2S_SW_RST_N__ST_DL_WORDLEN_MASK 3 +#define HII2S_SW_RST_N__THIRDMD_DLINK_WORDLEN_SHIFT 18 +#define HII2S_SW_RST_N__THIRDMD_DLINK_WORDLEN_MASK 3 +#define HII2S_SW_RST_N__VOICE_DLINK_WORDLEN_SHIFT 16 +#define HII2S_SW_RST_N__VOICE_DLINK_WORDLEN_MASK 3 + +#define HII2S_SW_RST_N__SW_RST_N BIT(0) + +enum hi6210_bits { + HII2S_BITS_16, + HII2S_BITS_18, + HII2S_BITS_20, + HII2S_BITS_24, +}; + + +#define HII2S_IF_CLK_EN_CFG 4 + +#define HII2S_IF_CLK_EN_CFG__THIRDMD_UPLINK_EN BIT(25) +#define HII2S_IF_CLK_EN_CFG__THIRDMD_DLINK_EN BIT(24) +#define HII2S_IF_CLK_EN_CFG__S3_IF_CLK_EN BIT(20) +#define HII2S_IF_CLK_EN_CFG__S2_IF_CLK_EN BIT(16) +#define HII2S_IF_CLK_EN_CFG__S2_OL_MIXER_EN BIT(15) +#define HII2S_IF_CLK_EN_CFG__S2_OL_SRC_EN BIT(14) +#define HII2S_IF_CLK_EN_CFG__S2_IR_PGA_EN BIT(13) +#define HII2S_IF_CLK_EN_CFG__S2_IL_PGA_EN BIT(12) +#define HII2S_IF_CLK_EN_CFG__S1_IR_PGA_EN BIT(10) +#define HII2S_IF_CLK_EN_CFG__S1_IL_PGA_EN BIT(9) +#define HII2S_IF_CLK_EN_CFG__S1_IF_CLK_EN BIT(8) +#define HII2S_IF_CLK_EN_CFG__VOICE_DLINK_SRC_EN BIT(7) +#define HII2S_IF_CLK_EN_CFG__VOICE_DLINK_EN BIT(6) +#define HII2S_IF_CLK_EN_CFG__ST_DL_R_EN BIT(5) +#define HII2S_IF_CLK_EN_CFG__ST_DL_L_EN BIT(4) +#define HII2S_IF_CLK_EN_CFG__VOICE_UPLINK_R_EN BIT(3) +#define HII2S_IF_CLK_EN_CFG__VOICE_UPLINK_L_EN BIT(2) +#define HII2S_IF_CLK_EN_CFG__STEREO_UPLINK_R_EN BIT(1) +#define HII2S_IF_CLK_EN_CFG__STEREO_UPLINK_L_EN BIT(0) + +#define HII2S_DIG_FILTER_CLK_EN_CFG 8 +#define HII2S_DIG_FILTER_CLK_EN_CFG__DACR_SDM_EN BIT(30) +#define HII2S_DIG_FILTER_CLK_EN_CFG__DACR_HBF2I_EN BIT(28) +#define HII2S_DIG_FILTER_CLK_EN_CFG__DACR_MIXER_EN BIT(25) +#define HII2S_DIG_FILTER_CLK_EN_CFG__DACR_AGC_EN BIT(24) +#define HII2S_DIG_FILTER_CLK_EN_CFG__DACL_SDM_EN BIT(22) +#define HII2S_DIG_FILTER_CLK_EN_CFG__DACL_HBF2I_EN BIT(20) +#define HII2S_DIG_FILTER_CLK_EN_CFG__DACL_MIXER_EN BIT(17) +#define HII2S_DIG_FILTER_CLK_EN_CFG__DACL_AGC_EN BIT(16) + +#define HII2S_FS_CFG 0xc + +#define HII2S_FS_CFG__FS_S2_SHIFT 28 +#define HII2S_FS_CFG__FS_S2_MASK 7 +#define HII2S_FS_CFG__FS_S1_SHIFT 24 +#define HII2S_FS_CFG__FS_S1_MASK 7 +#define HII2S_FS_CFG__FS_ADCLR_SHIFT 20 +#define HII2S_FS_CFG__FS_ADCLR_MASK 7 +#define HII2S_FS_CFG__FS_DACLR_SHIFT 16 +#define HII2S_FS_CFG__FS_DACLR_MASK 7 +#define HII2S_FS_CFG__FS_ST_DL_R_SHIFT 8 +#define HII2S_FS_CFG__FS_ST_DL_R_MASK 7 +#define HII2S_FS_CFG__FS_ST_DL_L_SHIFT 4 +#define HII2S_FS_CFG__FS_ST_DL_L_MASK 7 +#define HII2S_FS_CFG__FS_VOICE_DLINK_SHIFT 0 +#define HII2S_FS_CFG__FS_VOICE_DLINK_MASK 7 + +enum hi6210_i2s_rates { + HII2S_FS_RATE_8KHZ = 0, + HII2S_FS_RATE_16KHZ = 1, + HII2S_FS_RATE_32KHZ = 2, + HII2S_FS_RATE_48KHZ = 4, + HII2S_FS_RATE_96KHZ = 5, + HII2S_FS_RATE_192KHZ = 6, +}; + +#define HII2S_I2S_CFG 0x10 + +#define HII2S_I2S_CFG__S2_IF_TX_EN BIT(31) +#define HII2S_I2S_CFG__S2_IF_RX_EN BIT(30) +#define HII2S_I2S_CFG__S2_FRAME_MODE BIT(29) +#define HII2S_I2S_CFG__S2_MST_SLV BIT(28) +#define HII2S_I2S_CFG__S2_LRCK_MODE BIT(27) +#define HII2S_I2S_CFG__S2_CHNNL_MODE BIT(26) +#define HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_SHIFT 24 +#define HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_MASK 3 +#define HII2S_I2S_CFG__S2_DIRECT_LOOP_SHIFT 22 +#define HII2S_I2S_CFG__S2_DIRECT_LOOP_MASK 3 +#define HII2S_I2S_CFG__S2_TX_CLK_SEL BIT(21) +#define HII2S_I2S_CFG__S2_RX_CLK_SEL BIT(20) +#define HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT BIT(19) +#define HII2S_I2S_CFG__S2_FUNC_MODE_SHIFT 16 +#define HII2S_I2S_CFG__S2_FUNC_MODE_MASK 7 +#define HII2S_I2S_CFG__S1_IF_TX_EN BIT(15) +#define HII2S_I2S_CFG__S1_IF_RX_EN BIT(14) +#define HII2S_I2S_CFG__S1_FRAME_MODE BIT(13) +#define HII2S_I2S_CFG__S1_MST_SLV BIT(12) +#define HII2S_I2S_CFG__S1_LRCK_MODE BIT(11) +#define HII2S_I2S_CFG__S1_CHNNL_MODE BIT(10) +#define HII2S_I2S_CFG__S1_CODEC_IO_WORDLENGTH_SHIFT 8 +#define HII2S_I2S_CFG__S1_CODEC_IO_WORDLENGTH_MASK 3 +#define HII2S_I2S_CFG__S1_DIRECT_LOOP_SHIFT 6 +#define HII2S_I2S_CFG__S1_DIRECT_LOOP_MASK 3 +#define HII2S_I2S_CFG__S1_TX_CLK_SEL BIT(5) +#define HII2S_I2S_CFG__S1_RX_CLK_SEL BIT(4) +#define HII2S_I2S_CFG__S1_CODEC_DATA_FORMAT BIT(3) +#define HII2S_I2S_CFG__S1_FUNC_MODE_SHIFT 0 +#define HII2S_I2S_CFG__S1_FUNC_MODE_MASK 7 + +enum hi6210_i2s_formats { + HII2S_FORMAT_I2S, + HII2S_FORMAT_PCM_STD, + HII2S_FORMAT_PCM_USER, + HII2S_FORMAT_LEFT_JUST, + HII2S_FORMAT_RIGHT_JUST, +}; + +#define HII2S_DIG_FILTER_MODULE_CFG 0x14 + +#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_GAIN_SHIFT 28 +#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_GAIN_MASK 3 +#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN4_MUTE BIT(27) +#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN3_MUTE BIT(26) +#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN2_MUTE BIT(25) +#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN1_MUTE BIT(24) +#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_GAIN_SHIFT 20 +#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_GAIN_MASK 3 +#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN4_MUTE BIT(19) +#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN3_MUTE BIT(18) +#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN2_MUTE BIT(17) +#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN1_MUTE BIT(16) +#define HII2S_DIG_FILTER_MODULE_CFG__SW_DACR_SDM_DITHER BIT(9) +#define HII2S_DIG_FILTER_MODULE_CFG__SW_DACL_SDM_DITHER BIT(8) +#define HII2S_DIG_FILTER_MODULE_CFG__LM_CODEC_DAC2ADC_SHIFT 4 +#define HII2S_DIG_FILTER_MODULE_CFG__LM_CODEC_DAC2ADC_MASK 7 +#define HII2S_DIG_FILTER_MODULE_CFG__RM_CODEC_DAC2ADC_SHIFT 0 +#define HII2S_DIG_FILTER_MODULE_CFG__RM_CODEC_DAC2ADC_MASK 7 + +enum hi6210_gains { + HII2S_GAIN_100PC, + HII2S_GAIN_50PC, + HII2S_GAIN_25PC, +}; + +#define HII2S_MUX_TOP_MODULE_CFG 0x18 + +#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_GAIN_SHIFT 14 +#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_GAIN_MASK 3 +#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_IN2_MUTE BIT(13) +#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_IN1_MUTE BIT(12) +#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_GAIN_SHIFT 10 +#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_GAIN_MASK 3 +#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_IN2_MUTE BIT(9) +#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_IN1_MUTE BIT(8) +#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_SRC_RDY BIT(6) +#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_SRC_MODE_SHIFT 4 +#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_SRC_MODE_MASK 3 +#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_SRC_RDY BIT(3) +#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_SRC_MODE_SHIFT 0 +#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_SRC_MODE_MASK 7 + +enum hi6210_s2_src_mode { + HII2S_S2_SRC_MODE_3, + HII2S_S2_SRC_MODE_12, + HII2S_S2_SRC_MODE_6, + HII2S_S2_SRC_MODE_2, +}; + +enum hi6210_voice_dlink_src_mode { + HII2S_VOICE_DL_SRC_MODE_12 = 1, + HII2S_VOICE_DL_SRC_MODE_6, + HII2S_VOICE_DL_SRC_MODE_2, + HII2S_VOICE_DL_SRC_MODE_3, +}; + +#define HII2S_ADC_PGA_CFG 0x1c +#define HII2S_S1_INPUT_PGA_CFG 0x20 +#define HII2S_S2_INPUT_PGA_CFG 0x24 +#define HII2S_ST_DL_PGA_CFG 0x28 +#define HII2S_VOICE_SIDETONE_DLINK_PGA_CFG 0x2c +#define HII2S_APB_AFIFO_CFG_1 0x30 +#define HII2S_APB_AFIFO_CFG_2 0x34 +#define HII2S_ST_DL_FIFO_TH_CFG 0x38 + +#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_SHIFT 24 +#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_MASK 0x1f +#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_SHIFT 16 +#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_MASK 0x1f +#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_SHIFT 8 +#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_MASK 0x1f +#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_SHIFT 0 +#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_MASK 0x1f + +#define HII2S_STEREO_UPLINK_FIFO_TH_CFG 0x3c +#define HII2S_VOICE_UPLINK_FIFO_TH_CFG 0x40 +#define HII2S_CODEC_IRQ_MASK 0x44 +#define HII2S_CODEC_IRQ 0x48 +#define HII2S_DACL_AGC_CFG_1 0x4c +#define HII2S_DACL_AGC_CFG_2 0x50 +#define HII2S_DACR_AGC_CFG_1 0x54 +#define HII2S_DACR_AGC_CFG_2 0x58 +#define HII2S_DMIC_SIF_CFG 0x5c +#define HII2S_MISC_CFG 0x60 + +#define HII2S_MISC_CFG__THIRDMD_DLINK_TEST_SEL BIT(17) +#define HII2S_MISC_CFG__THIRDMD_DLINK_DIN_SEL BIT(16) +#define HII2S_MISC_CFG__S3_DOUT_RIGHT_SEL BIT(14) +#define HII2S_MISC_CFG__S3_DOUT_LEFT_SEL BIT(13) +#define HII2S_MISC_CFG__S3_DIN_TEST_SEL BIT(12) +#define HII2S_MISC_CFG__VOICE_DLINK_SRC_UP_DOUT_VLD_SEL BIT(8) +#define HII2S_MISC_CFG__VOICE_DLINK_TEST_SEL BIT(7) +#define HII2S_MISC_CFG__VOICE_DLINK_DIN_SEL BIT(6) +#define HII2S_MISC_CFG__ST_DL_TEST_SEL BIT(4) +#define HII2S_MISC_CFG__S2_DOUT_RIGHT_SEL BIT(3) +#define HII2S_MISC_CFG__S2_DOUT_TEST_SEL BIT(2) +#define HII2S_MISC_CFG__S1_DOUT_TEST_SEL BIT(1) +#define HII2S_MISC_CFG__S2_DOUT_LEFT_SEL BIT(0) + +#define HII2S_S2_SRC_CFG 0x64 +#define HII2S_MEM_CFG 0x68 +#define HII2S_THIRDMD_PCM_PGA_CFG 0x6c +#define HII2S_THIRD_MODEM_FIFO_TH 0x70 +#define HII2S_S3_ANTI_FREQ_JITTER_TX_INC_CNT 0x74 +#define HII2S_S3_ANTI_FREQ_JITTER_TX_DEC_CNT 0x78 +#define HII2S_S3_ANTI_FREQ_JITTER_RX_INC_CNT 0x7c +#define HII2S_S3_ANTI_FREQ_JITTER_RX_DEC_CNT 0x80 +#define HII2S_ANTI_FREQ_JITTER_EN 0x84 +#define HII2S_CLK_SEL 0x88 + +/* 0 = BT owns the i2s */ +#define HII2S_CLK_SEL__I2S_BT_FM_SEL BIT(0) +/* 0 = internal source, 1 = ext */ +#define HII2S_CLK_SEL__EXT_12_288MHZ_SEL BIT(1) + + +#define HII2S_THIRDMD_DLINK_CHANNEL 0xe8 +#define HII2S_THIRDMD_ULINK_CHANNEL 0xec +#define HII2S_VOICE_DLINK_CHANNEL 0xf0 + +/* shovel data in here for playback */ +#define HII2S_ST_DL_CHANNEL 0xf4 +#define HII2S_STEREO_UPLINK_CHANNEL 0xf8 +#define HII2S_VOICE_UPLINK_CHANNEL 0xfc + +#endif/* _HI6210_I2S_H */