Merge remote-tracking branches 'asoc/topic/fsl-ssi', 'asoc/topic/hi6220' and 'asoc/topic/imx' into asoc-next

This commit is contained in:
Mark Brown 2017-04-30 22:16:01 +09:00
commit 50946b2aec
11 changed files with 997 additions and 108 deletions

View File

@ -20,24 +20,8 @@ Required properties:
have. have.
- interrupt-parent: The phandle for the interrupt controller that - interrupt-parent: The phandle for the interrupt controller that
services interrupts for this device. services interrupts for this device.
- fsl,playback-dma: Phandle to a node for the DMA channel to use for
playback of audio. This is typically dictated by SOC
design. See the notes below.
- fsl,capture-dma: Phandle to a node for the DMA channel to use for
capture (recording) of audio. This is typically dictated
by SOC design. See the notes below.
- fsl,fifo-depth: The number of elements in the transmit and receive FIFOs. - fsl,fifo-depth: The number of elements in the transmit and receive FIFOs.
This number is the maximum allowed value for SFCSR[TFWM0]. This number is the maximum allowed value for SFCSR[TFWM0].
- fsl,ssi-asynchronous:
If specified, the SSI is to be programmed in asynchronous
mode. In this mode, pins SRCK, STCK, SRFS, and STFS must
all be connected to valid signals. In synchronous mode,
SRCK and SRFS are ignored. Asynchronous mode allows
playback and capture to use different sample sizes and
sample rates. Some drivers may require that SRCK and STCK
be connected together, and SRFS and STFS be connected
together. This would still allow different sample sizes,
but not different sample rates.
- clocks: "ipg" - Required clock for the SSI unit - clocks: "ipg" - Required clock for the SSI unit
"baud" - Required clock for SSI master mode. Otherwise this "baud" - Required clock for SSI master mode. Otherwise this
clock is not used clock is not used
@ -61,6 +45,24 @@ Optional properties:
- fsl,mode: The operating mode for the AC97 interface only. - fsl,mode: The operating mode for the AC97 interface only.
"ac97-slave" - AC97 mode, SSI is clock slave "ac97-slave" - AC97 mode, SSI is clock slave
"ac97-master" - AC97 mode, SSI is clock master "ac97-master" - AC97 mode, SSI is clock master
- fsl,ssi-asynchronous:
If specified, the SSI is to be programmed in asynchronous
mode. In this mode, pins SRCK, STCK, SRFS, and STFS must
all be connected to valid signals. In synchronous mode,
SRCK and SRFS are ignored. Asynchronous mode allows
playback and capture to use different sample sizes and
sample rates. Some drivers may require that SRCK and STCK
be connected together, and SRFS and STFS be connected
together. This would still allow different sample sizes,
but not different sample rates.
- fsl,playback-dma: Phandle to a node for the DMA channel to use for
playback of audio. This is typically dictated by SOC
design. See the notes below.
Only used on Power Architecture.
- fsl,capture-dma: Phandle to a node for the DMA channel to use for
capture (recording) of audio. This is typically dictated
by SOC design. See the notes below.
Only used on Power Architecture.
Child 'codec' node required properties: Child 'codec' node required properties:
- compatible: Compatible list, contains the name of the codec - compatible: Compatible list, contains the name of the codec

View File

@ -0,0 +1,42 @@
* Hisilicon 6210 i2s controller
Required properties:
- compatible: should be one of the following:
- "hisilicon,hi6210-i2s"
- reg: physical base address of the i2s controller unit and length of
memory mapped region.
- interrupts: should contain the i2s interrupt.
- clocks: a list of phandle + clock-specifier pairs, one for each entry
in clock-names.
- clock-names: should contain following:
- "dacodec"
- "i2s-base"
- dmas: DMA specifiers for tx dma. See the DMA client binding,
Documentation/devicetree/bindings/dma/dma.txt
- dma-names: should be "tx" and "rx"
- hisilicon,sysctrl-syscon: phandle to sysctrl syscon
- #sound-dai-cells: Should be set to 1 (for multi-dai)
- The dai cell indexes reference the following interfaces:
0: S2 interface
(Currently that is the only one available, but more may be
supported in the future)
Example for the hi6210 i2s controller:
i2s0: i2s@f7118000{
compatible = "hisilicon,hi6210-i2s";
reg = <0x0 0xf7118000 0x0 0x8000>; /* i2s unit */
interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>; /* 155 "DigACodec_intr"-32 */
clocks = <&sys_ctrl HI6220_DACODEC_PCLK>,
<&sys_ctrl HI6220_BBPPLL0_DIV>;
clock-names = "dacodec", "i2s-base";
dmas = <&dma0 15 &dma0 14>;
dma-names = "rx", "tx";
hisilicon,sysctrl-syscon = <&sys_ctrl>;
#sound-dai-cells = <1>;
};
Then when referencing the i2s controller:
sound-dai = <&i2s0 0>; /* index 0 => S2 interface */

View File

@ -47,6 +47,7 @@ source "sound/soc/cirrus/Kconfig"
source "sound/soc/davinci/Kconfig" source "sound/soc/davinci/Kconfig"
source "sound/soc/dwc/Kconfig" source "sound/soc/dwc/Kconfig"
source "sound/soc/fsl/Kconfig" source "sound/soc/fsl/Kconfig"
source "sound/soc/hisilicon/Kconfig"
source "sound/soc/jz4740/Kconfig" source "sound/soc/jz4740/Kconfig"
source "sound/soc/nuc900/Kconfig" source "sound/soc/nuc900/Kconfig"
source "sound/soc/omap/Kconfig" source "sound/soc/omap/Kconfig"

View File

@ -27,6 +27,7 @@ obj-$(CONFIG_SND_SOC) += cirrus/
obj-$(CONFIG_SND_SOC) += davinci/ obj-$(CONFIG_SND_SOC) += davinci/
obj-$(CONFIG_SND_SOC) += dwc/ obj-$(CONFIG_SND_SOC) += dwc/
obj-$(CONFIG_SND_SOC) += fsl/ obj-$(CONFIG_SND_SOC) += fsl/
obj-$(CONFIG_SND_SOC) += hisilicon/
obj-$(CONFIG_SND_SOC) += jz4740/ obj-$(CONFIG_SND_SOC) += jz4740/
obj-$(CONFIG_SND_SOC) += img/ obj-$(CONFIG_SND_SOC) += img/
obj-$(CONFIG_SND_SOC) += intel/ obj-$(CONFIG_SND_SOC) += intel/

View File

@ -35,6 +35,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/ctype.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -54,16 +55,6 @@
#include "fsl_ssi.h" #include "fsl_ssi.h"
#include "imx-pcm.h" #include "imx-pcm.h"
/**
* FSLSSI_I2S_RATES: sample rates supported by the I2S
*
* This driver currently only supports the SSI running in I2S slave mode,
* which means the codec determines the sample rate. Therefore, we tell
* ALSA that we support all rates and let the codec driver decide what rates
* are really supported.
*/
#define FSLSSI_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS
/** /**
* FSLSSI_I2S_FORMATS: audio formats supported by the SSI * FSLSSI_I2S_FORMATS: audio formats supported by the SSI
* *
@ -1212,14 +1203,14 @@ static struct snd_soc_dai_driver fsl_ssi_dai_template = {
.stream_name = "CPU-Playback", .stream_name = "CPU-Playback",
.channels_min = 1, .channels_min = 1,
.channels_max = 32, .channels_max = 32,
.rates = FSLSSI_I2S_RATES, .rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = FSLSSI_I2S_FORMATS, .formats = FSLSSI_I2S_FORMATS,
}, },
.capture = { .capture = {
.stream_name = "CPU-Capture", .stream_name = "CPU-Capture",
.channels_min = 1, .channels_min = 1,
.channels_max = 32, .channels_max = 32,
.rates = FSLSSI_I2S_RATES, .rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = FSLSSI_I2S_FORMATS, .formats = FSLSSI_I2S_FORMATS,
}, },
.ops = &fsl_ssi_dai_ops, .ops = &fsl_ssi_dai_ops,
@ -1325,14 +1316,10 @@ static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = {
*/ */
static void make_lowercase(char *s) static void make_lowercase(char *s)
{ {
char *p = s; if (!s)
char c; return;
for (; *s; s++)
while ((c = *p)) { *s = tolower(*s);
if ((c >= 'A') && (c <= 'Z'))
*p = c + ('a' - 'A');
p++;
}
} }
static int fsl_ssi_imx_probe(struct platform_device *pdev, static int fsl_ssi_imx_probe(struct platform_device *pdev,

View File

@ -33,48 +33,20 @@ static bool filter(struct dma_chan *chan, void *param)
return true; return true;
} }
static const struct snd_pcm_hardware imx_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME,
.buffer_bytes_max = IMX_DEFAULT_DMABUF_SIZE,
.period_bytes_min = 128,
.period_bytes_max = 65535, /* Limited by SDMA engine */
.periods_min = 2,
.periods_max = 255,
.fifo_size = 0,
};
static const struct snd_dmaengine_pcm_config imx_dmaengine_pcm_config = { static const struct snd_dmaengine_pcm_config imx_dmaengine_pcm_config = {
.pcm_hardware = &imx_pcm_hardware,
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
.compat_filter_fn = filter, .compat_filter_fn = filter,
.prealloc_buffer_size = IMX_DEFAULT_DMABUF_SIZE,
}; };
int imx_pcm_dma_init(struct platform_device *pdev, size_t size) int imx_pcm_dma_init(struct platform_device *pdev, size_t size)
{ {
struct snd_dmaengine_pcm_config *config; struct snd_dmaengine_pcm_config *config;
struct snd_pcm_hardware *pcm_hardware;
config = devm_kzalloc(&pdev->dev, config = devm_kzalloc(&pdev->dev,
sizeof(struct snd_dmaengine_pcm_config), GFP_KERNEL); sizeof(struct snd_dmaengine_pcm_config), GFP_KERNEL);
if (!config) if (!config)
return -ENOMEM; return -ENOMEM;
*config = imx_dmaengine_pcm_config; *config = imx_dmaengine_pcm_config;
if (size)
config->prealloc_buffer_size = size;
pcm_hardware = devm_kzalloc(&pdev->dev,
sizeof(struct snd_pcm_hardware), GFP_KERNEL);
*pcm_hardware = imx_pcm_hardware;
if (size)
pcm_hardware->buffer_bytes_max = size;
config->pcm_hardware = pcm_hardware;
return devm_snd_dmaengine_pcm_register(&pdev->dev, return devm_snd_dmaengine_pcm_register(&pdev->dev,
config, config,

View File

@ -33,14 +33,14 @@ struct imx_wm8962_data {
struct snd_soc_card card; struct snd_soc_card card;
char codec_dai_name[DAI_NAME_SIZE]; char codec_dai_name[DAI_NAME_SIZE];
char platform_name[DAI_NAME_SIZE]; char platform_name[DAI_NAME_SIZE];
struct clk *codec_clk;
unsigned int clk_frequency; unsigned int clk_frequency;
}; };
struct imx_priv { struct imx_priv {
struct platform_device *pdev; struct platform_device *pdev;
int sample_rate;
snd_pcm_format_t sample_format;
}; };
static struct imx_priv card_priv;
static const struct snd_soc_dapm_widget imx_wm8962_dapm_widgets[] = { static const struct snd_soc_dapm_widget imx_wm8962_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_HP("Headphone Jack", NULL),
@ -49,14 +49,14 @@ static const struct snd_soc_dapm_widget imx_wm8962_dapm_widgets[] = {
SND_SOC_DAPM_MIC("DMIC", NULL), SND_SOC_DAPM_MIC("DMIC", NULL),
}; };
static int sample_rate = 44100;
static snd_pcm_format_t sample_format = SNDRV_PCM_FORMAT_S16_LE;
static int imx_hifi_hw_params(struct snd_pcm_substream *substream, static int imx_hifi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params) struct snd_pcm_hw_params *params)
{ {
sample_rate = params_rate(params); struct snd_soc_pcm_runtime *rtd = substream->private_data;
sample_format = params_format(params); struct imx_priv *priv = snd_soc_card_get_drvdata(rtd->card);
priv->sample_rate = params_rate(params);
priv->sample_format = params_format(params);
return 0; return 0;
} }
@ -71,7 +71,7 @@ static int imx_wm8962_set_bias_level(struct snd_soc_card *card,
{ {
struct snd_soc_pcm_runtime *rtd; struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai *codec_dai; struct snd_soc_dai *codec_dai;
struct imx_priv *priv = &card_priv; struct imx_priv *priv = snd_soc_card_get_drvdata(card);
struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card); struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card);
struct device *dev = &priv->pdev->dev; struct device *dev = &priv->pdev->dev;
unsigned int pll_out; unsigned int pll_out;
@ -85,10 +85,10 @@ static int imx_wm8962_set_bias_level(struct snd_soc_card *card,
switch (level) { switch (level) {
case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_PREPARE:
if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
if (sample_format == SNDRV_PCM_FORMAT_S24_LE) if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE)
pll_out = sample_rate * 384; pll_out = priv->sample_rate * 384;
else else
pll_out = sample_rate * 256; pll_out = priv->sample_rate * 256;
ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL, ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
WM8962_FLL_MCLK, data->clk_frequency, WM8962_FLL_MCLK, data->clk_frequency,
@ -140,7 +140,7 @@ static int imx_wm8962_late_probe(struct snd_soc_card *card)
{ {
struct snd_soc_pcm_runtime *rtd; struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai *codec_dai; struct snd_soc_dai *codec_dai;
struct imx_priv *priv = &card_priv; struct imx_priv *priv = snd_soc_card_get_drvdata(card);
struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card); struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card);
struct device *dev = &priv->pdev->dev; struct device *dev = &priv->pdev->dev;
int ret; int ret;
@ -160,13 +160,20 @@ static int imx_wm8962_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
struct device_node *ssi_np, *codec_np; struct device_node *ssi_np, *codec_np;
struct platform_device *ssi_pdev; struct platform_device *ssi_pdev;
struct imx_priv *priv = &card_priv;
struct i2c_client *codec_dev; struct i2c_client *codec_dev;
struct imx_wm8962_data *data; struct imx_wm8962_data *data;
struct imx_priv *priv;
struct clk *codec_clk;
int int_port, ext_port; int int_port, ext_port;
int ret; int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->pdev = pdev; priv->pdev = pdev;
priv->sample_rate = 44100;
priv->sample_format = SNDRV_PCM_FORMAT_S16_LE;
ret = of_property_read_u32(np, "mux-int-port", &int_port); ret = of_property_read_u32(np, "mux-int-port", &int_port);
if (ret) { if (ret) {
@ -231,19 +238,15 @@ static int imx_wm8962_probe(struct platform_device *pdev)
goto fail; goto fail;
} }
data->codec_clk = devm_clk_get(&codec_dev->dev, NULL); codec_clk = clk_get(&codec_dev->dev, NULL);
if (IS_ERR(data->codec_clk)) { if (IS_ERR(codec_clk)) {
ret = PTR_ERR(data->codec_clk); ret = PTR_ERR(codec_clk);
dev_err(&codec_dev->dev, "failed to get codec clk: %d\n", ret); dev_err(&codec_dev->dev, "failed to get codec clk: %d\n", ret);
goto fail; goto fail;
} }
data->clk_frequency = clk_get_rate(data->codec_clk); data->clk_frequency = clk_get_rate(codec_clk);
ret = clk_prepare_enable(data->codec_clk); clk_put(codec_clk);
if (ret) {
dev_err(&codec_dev->dev, "failed to enable codec clk: %d\n", ret);
goto fail;
}
data->dai.name = "HiFi"; data->dai.name = "HiFi";
data->dai.stream_name = "HiFi"; data->dai.stream_name = "HiFi";
@ -258,10 +261,10 @@ static int imx_wm8962_probe(struct platform_device *pdev)
data->card.dev = &pdev->dev; data->card.dev = &pdev->dev;
ret = snd_soc_of_parse_card_name(&data->card, "model"); ret = snd_soc_of_parse_card_name(&data->card, "model");
if (ret) if (ret)
goto clk_fail; goto fail;
ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
if (ret) if (ret)
goto clk_fail; goto fail;
data->card.num_links = 1; data->card.num_links = 1;
data->card.owner = THIS_MODULE; data->card.owner = THIS_MODULE;
data->card.dai_link = &data->dai; data->card.dai_link = &data->dai;
@ -277,16 +280,9 @@ static int imx_wm8962_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, &data->card); ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
if (ret) { if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
goto clk_fail; goto fail;
} }
of_node_put(ssi_np);
of_node_put(codec_np);
return 0;
clk_fail:
clk_disable_unprepare(data->codec_clk);
fail: fail:
of_node_put(ssi_np); of_node_put(ssi_np);
of_node_put(codec_np); of_node_put(codec_np);
@ -294,17 +290,6 @@ fail:
return ret; return ret;
} }
static int imx_wm8962_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card);
if (!IS_ERR(data->codec_clk))
clk_disable_unprepare(data->codec_clk);
return 0;
}
static const struct of_device_id imx_wm8962_dt_ids[] = { static const struct of_device_id imx_wm8962_dt_ids[] = {
{ .compatible = "fsl,imx-audio-wm8962", }, { .compatible = "fsl,imx-audio-wm8962", },
{ /* sentinel */ } { /* sentinel */ }
@ -318,7 +303,6 @@ static struct platform_driver imx_wm8962_driver = {
.of_match_table = imx_wm8962_dt_ids, .of_match_table = imx_wm8962_dt_ids,
}, },
.probe = imx_wm8962_probe, .probe = imx_wm8962_probe,
.remove = imx_wm8962_remove,
}; };
module_platform_driver(imx_wm8962_driver); module_platform_driver(imx_wm8962_driver);

View File

@ -0,0 +1,5 @@
config SND_I2S_HI6210_I2S
tristate "Hisilicon I2S controller"
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Hisilicon I2S

View File

@ -0,0 +1 @@
obj-$(CONFIG_SND_I2S_HI6210_I2S) += hi6210-i2s.o

View File

@ -0,0 +1,618 @@
/*
* linux/sound/soc/m8m/hi6210_i2s.c - I2S IP driver
*
* Copyright (C) 2015 Linaro, Ltd
* Author: Andy Green <andy.green@linaro.org>
*
* 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 <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/jiffies.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/dmaengine_pcm.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <linux/interrupt.h>
#include <linux/reset.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/mfd/syscon.h>
#include <linux/reset-controller.h>
#include <linux/clk.h>
#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;
struct regmap *sysctrl;
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
enum {
CLK_DACODEC,
CLK_I2S_BASE,
};
static inline void hi6210_write_reg(struct hi6210_i2s *i2s, int reg, u32 val)
{
writel(val, i2s->base + reg);
}
static inline u32 hi6210_read_reg(struct hi6210_i2s *i2s, int reg)
{
return readl(i2s->base + reg);
}
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;
u32 val;
/* deassert reset on ABB */
regmap_read(i2s->sysctrl, SC_PERIPH_RSTSTAT2, &val);
if (val & BIT(4))
regmap_write(i2s->sysctrl, SC_PERIPH_RSTDIS2, BIT(4));
for (n = 0; n < i2s->clocks; n++) {
ret = clk_prepare_enable(i2s->clk[n]);
if (ret) {
while (n--)
clk_disable_unprepare(i2s->clk[n]);
return ret;
}
}
ret = clk_set_rate(i2s->clk[CLK_I2S_BASE], 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 */
regmap_write(i2s->sysctrl, SC_PERIPH_CLKEN12, BIT(9));
/* enable codec working clock / == "codec bus clock" */
regmap_write(i2s->sysctrl, SC_PERIPH_CLKEN1, BIT(5));
/* deassert reset on codec / interface clock / working clock */
regmap_write(i2s->sysctrl, SC_PERIPH_RSTEN1, BIT(5));
regmap_write(i2s->sysctrl, SC_PERIPH_RSTDIS1, BIT(5));
/* not interested in i2s irqs */
val = hi6210_read_reg(i2s, HII2S_CODEC_IRQ_MASK);
val |= 0x3f;
hi6210_write_reg(i2s, HII2S_CODEC_IRQ_MASK, val);
/* reset the stereo downlink fifo */
val = hi6210_read_reg(i2s, HII2S_APB_AFIFO_CFG_1);
val |= (BIT(5) | BIT(4));
hi6210_write_reg(i2s, HII2S_APB_AFIFO_CFG_1, val);
val = hi6210_read_reg(i2s, HII2S_APB_AFIFO_CFG_1);
val &= ~(BIT(5) | BIT(4));
hi6210_write_reg(i2s, HII2S_APB_AFIFO_CFG_1, val);
val = hi6210_read_reg(i2s, HII2S_SW_RST_N);
val &= ~(HII2S_SW_RST_N__ST_DL_WORDLEN_MASK <<
HII2S_SW_RST_N__ST_DL_WORDLEN_SHIFT);
val |= (HII2S_BITS_16 << HII2S_SW_RST_N__ST_DL_WORDLEN_SHIFT);
hi6210_write_reg(i2s, HII2S_SW_RST_N, val);
val = hi6210_read_reg(i2s, HII2S_MISC_CFG);
/* mux 11/12 = APB not i2s */
val &= ~HII2S_MISC_CFG__ST_DL_TEST_SEL;
/* BT R ch 0 = mixer op of DACR ch */
val &= ~HII2S_MISC_CFG__S2_DOUT_RIGHT_SEL;
val &= ~HII2S_MISC_CFG__S2_DOUT_TEST_SEL;
val |= HII2S_MISC_CFG__S2_DOUT_RIGHT_SEL;
/* BT L ch = 1 = mux 7 = "mixer output of DACL */
val |= HII2S_MISC_CFG__S2_DOUT_TEST_SEL;
hi6210_write_reg(i2s, HII2S_MISC_CFG, val);
val = hi6210_read_reg(i2s, HII2S_SW_RST_N);
val |= HII2S_SW_RST_N__SW_RST_N;
hi6210_write_reg(i2s, HII2S_SW_RST_N, val);
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]);
regmap_write(i2s->sysctrl, SC_PERIPH_RSTEN1, BIT(5));
}
static void hi6210_i2s_txctrl(struct snd_soc_dai *cpu_dai, int on)
{
struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
u32 val;
spin_lock(&i2s->lock);
if (on) {
/* enable S2 TX */
val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
val |= HII2S_I2S_CFG__S2_IF_TX_EN;
hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
} else {
/* disable S2 TX */
val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
val &= ~HII2S_I2S_CFG__S2_IF_TX_EN;
hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
}
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);
u32 val;
spin_lock(&i2s->lock);
if (on) {
val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
val |= HII2S_I2S_CFG__S2_IF_RX_EN;
hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
} else {
val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
val &= ~HII2S_I2S_CFG__S2_IF_RX_EN;
hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
}
spin_unlock(&i2s->lock);
}
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);
/*
* We don't actually set the hardware until the hw_params
* call, but we need to validate the user input here.
*/
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
case SND_SOC_DAIFMT_CBS_CFS:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
case SND_SOC_DAIFMT_LEFT_J:
case SND_SOC_DAIFMT_RIGHT_J:
break;
default:
return -EINVAL;
}
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 bits = 0, rate = 0, signed_data = 0, fmt = 0;
u32 val;
struct snd_dmaengine_dai_dma_data *dma_data;
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:
bits = 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:
bits = HII2S_BITS_24;
break;
default:
dev_err(cpu_dai->dev, "Bad format\n");
return -EINVAL;
}
switch (params_rate(params)) {
case 8000:
rate = HII2S_FS_RATE_8KHZ;
break;
case 16000:
rate = HII2S_FS_RATE_16KHZ;
break;
case 32000:
rate = HII2S_FS_RATE_32KHZ;
break;
case 48000:
rate = HII2S_FS_RATE_48KHZ;
break;
case 96000:
rate = HII2S_FS_RATE_96KHZ;
break;
case 192000:
rate = HII2S_FS_RATE_192KHZ;
break;
default:
dev_err(cpu_dai->dev, "Bad rate: %d\n", params_rate(params));
return -EINVAL;
}
if (!(params_channels(params))) {
dev_err(cpu_dai->dev, "Bad channels\n");
return -EINVAL;
}
dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
switch (bits) {
case HII2S_BITS_24:
i2s->bits = 32;
dma_data->addr_width = 3;
break;
default:
i2s->bits = 16;
dma_data->addr_width = 2;
break;
}
i2s->rate = params_rate(params);
i2s->channels = params_channels(params);
i2s->channel_length = i2s->channels * i2s->bits;
val = hi6210_read_reg(i2s, HII2S_ST_DL_FIFO_TH_CFG);
val &= ~((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));
val |= ((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_write_reg(i2s, HII2S_ST_DL_FIFO_TH_CFG, val);
val = hi6210_read_reg(i2s, HII2S_IF_CLK_EN_CFG);
val |= (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_write_reg(i2s, HII2S_IF_CLK_EN_CFG, val);
val = hi6210_read_reg(i2s, HII2S_DIG_FILTER_CLK_EN_CFG);
val &= ~(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);
val |= (HII2S_DIG_FILTER_CLK_EN_CFG__DACR_MIXER_EN |
HII2S_DIG_FILTER_CLK_EN_CFG__DACL_MIXER_EN);
hi6210_write_reg(i2s, HII2S_DIG_FILTER_CLK_EN_CFG, val);
val = hi6210_read_reg(i2s, HII2S_DIG_FILTER_MODULE_CFG);
val &= ~(HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN2_MUTE |
HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN2_MUTE);
hi6210_write_reg(i2s, HII2S_DIG_FILTER_MODULE_CFG, val);
val = hi6210_read_reg(i2s, HII2S_MUX_TOP_MODULE_CFG);
val &= ~(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);
hi6210_write_reg(i2s, HII2S_MUX_TOP_MODULE_CFG, val);
switch (i2s->format & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
i2s->master = false;
val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
val |= HII2S_I2S_CFG__S2_MST_SLV;
hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
break;
case SND_SOC_DAIFMT_CBS_CFS:
i2s->master = true;
val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
val &= ~HII2S_I2S_CFG__S2_MST_SLV;
hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
break;
default:
WARN_ONCE(1, "Invalid i2s->fmt MASTER_MASK. This shouldn't happen\n");
return -EINVAL;
}
switch (i2s->format & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
fmt = HII2S_FORMAT_I2S;
break;
case SND_SOC_DAIFMT_LEFT_J:
fmt = HII2S_FORMAT_LEFT_JUST;
break;
case SND_SOC_DAIFMT_RIGHT_J:
fmt = HII2S_FORMAT_RIGHT_JUST;
break;
default:
WARN_ONCE(1, "Invalid i2s->fmt FORMAT_MASK. This shouldn't happen\n");
return -EINVAL;
}
val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
val &= ~(HII2S_I2S_CFG__S2_FUNC_MODE_MASK <<
HII2S_I2S_CFG__S2_FUNC_MODE_SHIFT);
val |= fmt << HII2S_I2S_CFG__S2_FUNC_MODE_SHIFT;
hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
val = hi6210_read_reg(i2s, HII2S_CLK_SEL);
val &= ~(HII2S_CLK_SEL__I2S_BT_FM_SEL | /* BT gets the I2S */
HII2S_CLK_SEL__EXT_12_288MHZ_SEL);
hi6210_write_reg(i2s, HII2S_CLK_SEL, val);
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;
switch (i2s->channels) {
case 1:
val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
val |= HII2S_I2S_CFG__S2_FRAME_MODE;
hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
break;
default:
val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
val &= ~HII2S_I2S_CFG__S2_FRAME_MODE;
hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
break;
}
/* clear loopback, set signed type and word length */
val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
val &= ~HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT;
val &= ~(HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_MASK <<
HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_SHIFT);
val &= ~(HII2S_I2S_CFG__S2_DIRECT_LOOP_MASK <<
HII2S_I2S_CFG__S2_DIRECT_LOOP_SHIFT);
val |= signed_data;
val |= (bits << HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_SHIFT);
hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
if (!i2s->master)
return 0;
/* set DAC and related units to correct rate */
val = hi6210_read_reg(i2s, HII2S_FS_CFG);
val &= ~(HII2S_FS_CFG__FS_S2_MASK << HII2S_FS_CFG__FS_S2_SHIFT);
val &= ~(HII2S_FS_CFG__FS_DACLR_MASK << HII2S_FS_CFG__FS_DACLR_SHIFT);
val &= ~(HII2S_FS_CFG__FS_ST_DL_R_MASK <<
HII2S_FS_CFG__FS_ST_DL_R_SHIFT);
val &= ~(HII2S_FS_CFG__FS_ST_DL_L_MASK <<
HII2S_FS_CFG__FS_ST_DL_L_SHIFT);
val |= (rate << HII2S_FS_CFG__FS_S2_SHIFT);
val |= (rate << HII2S_FS_CFG__FS_DACLR_SHIFT);
val |= (rate << HII2S_FS_CFG__FS_ST_DL_R_SHIFT);
val |= (rate << HII2S_FS_CFG__FS_ST_DL_L_SHIFT);
hi6210_write_reg(i2s, HII2S_FS_CFG, val);
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,
.startup = hi6210_i2s_startup,
.shutdown = hi6210_i2s_shutdown,
};
struct snd_soc_dai_driver hi6210_i2s_dai_init = {
.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",
};
static int hi6210_i2s_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct hi6210_i2s *i2s;
struct resource *res;
int ret;
i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
if (!i2s)
return -ENOMEM;
i2s->dev = dev;
spin_lock_init(&i2s->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
i2s->base = devm_ioremap_resource(dev, res);
if (IS_ERR(i2s->base))
return PTR_ERR(i2s->base);
i2s->base_phys = (phys_addr_t)res->start;
i2s->dai = hi6210_i2s_dai_init;
dev_set_drvdata(&pdev->dev, i2s);
i2s->sysctrl = syscon_regmap_lookup_by_phandle(node,
"hisilicon,sysctrl-syscon");
if (IS_ERR(i2s->sysctrl))
return PTR_ERR(i2s->sysctrl);
i2s->clk[CLK_DACODEC] = devm_clk_get(&pdev->dev, "dacodec");
if (IS_ERR_OR_NULL(i2s->clk[CLK_DACODEC]))
return PTR_ERR(i2s->clk[CLK_DACODEC]);
i2s->clocks++;
i2s->clk[CLK_I2S_BASE] = devm_clk_get(&pdev->dev, "i2s-base");
if (IS_ERR_OR_NULL(i2s->clk[CLK_I2S_BASE]))
return PTR_ERR(i2s->clk[CLK_I2S_BASE]);
i2s->clocks++;
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret)
return ret;
ret = devm_snd_soc_register_component(&pdev->dev, &hi6210_i2s_i2s_comp,
&i2s->dai, 1);
return ret;
}
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,
.driver = {
.name = "hi6210_i2s",
.of_match_table = hi6210_i2s_dt_ids,
},
};
module_platform_driver(hi6210_i2s_driver);
MODULE_DESCRIPTION("Hisilicon HI6210 I2S driver");
MODULE_AUTHOR("Andy Green <andy.green@linaro.org>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,276 @@
/*
* linux/sound/soc/hisilicon/hi6210-i2s.h
*
* Copyright (C) 2015 Linaro, Ltd
* Author: Andy Green <andy.green@linaro.org>
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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 */