From c6aeb7de226dd08ad9b343fc6cbaf2ff36f69c84 Mon Sep 17 00:00:00 2001 From: Florian Meier Date: Fri, 22 Nov 2013 16:24:08 +0100 Subject: ASoC: Add support for BCM2835 This driver adds support for digital audio (I2S) for the BCM2835 SoC that is used by the Raspberry Pi. External audio codecs can be connected to the Raspberry Pi via P5 header. It relies on cyclic DMA engine support for BCM2835. Signed-off-by: Florian Meier Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/bcm2835-i2s.txt | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/bcm2835-i2s.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/bcm2835-i2s.txt b/Documentation/devicetree/bindings/sound/bcm2835-i2s.txt new file mode 100644 index 000000000000..65783de0aedf --- /dev/null +++ b/Documentation/devicetree/bindings/sound/bcm2835-i2s.txt @@ -0,0 +1,25 @@ +* Broadcom BCM2835 SoC I2S/PCM module + +Required properties: +- compatible: "brcm,bcm2835-i2s" +- reg: A list of base address and size entries: + * The first entry should cover the PCM registers + * The second entry should cover the PCM clock registers +- dmas: List of DMA controller phandle and DMA request line ordered pairs. +- dma-names: Identifier string for each DMA request line in the dmas property. + These strings correspond 1:1 with the ordered pairs in dmas. + + One of the DMA channels will be responsible for transmission (should be + named "tx") and one for reception (should be named "rx"). + +Example: + +bcm2835_i2s: i2s@7e203000 { + compatible = "brcm,bcm2835-i2s"; + reg = <0x7e203000 0x20>, + <0x7e101098 0x02>; + + dmas = <&dma 2>, + <&dma 3>; + dma-names = "tx", "rx"; +}; -- cgit v1.2.3 From 3a85ca9d8a06c873b7a5fb24319572926fa20e10 Mon Sep 17 00:00:00 2001 From: Brian Austin Date: Fri, 15 Nov 2013 09:35:34 -0600 Subject: ASoC: dt: binding: sound cs42l52 driver Signed-off-by: Brian Austin Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/cs42l52.txt | 46 ++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/cs42l52.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/cs42l52.txt b/Documentation/devicetree/bindings/sound/cs42l52.txt new file mode 100644 index 000000000000..bc03c9312a19 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cs42l52.txt @@ -0,0 +1,46 @@ +CS42L52 audio CODEC + +Required properties: + + - compatible : "cirrus,cs42l52" + + - reg : the I2C address of the device for I2C + +Optional properties: + + - cirrus,reset-gpio : GPIO controller's phandle and the number + of the GPIO used to reset the codec. + + - cirrus,chgfreq-divisor : Values used to set the Charge Pump Frequency. + Allowable values of 0x00 through 0x0F. These are raw values written to the + register, not the actual frequency. The frequency is determined by the following. + Frequency = (64xFs)/(N+2) + N = chgfreq_val + Fs = Sample Rate (variable) + + - cirrus,mica-differential-cfg : boolean, If present, then the MICA input is configured + as a differential input. If not present then the MICA input is configured as + Single-ended input. Single-ended mode allows for MIC1 or MIC2 muxing for input. + + - cirrus,micb-differential-cfg : boolean, If present, then the MICB input is configured + as a differential input. If not present then the MICB input is configured as + Single-ended input. Single-ended mode allows for MIC1 or MIC2 muxing for input. + + - cirrus,micbias-lvl: Set the output voltage level on the MICBIAS Pin + 0 = 0.5 x VA + 1 = 0.6 x VA + 2 = 0.7 x VA + 3 = 0.8 x VA + 4 = 0.83 x VA + 5 = 0.91 x VA + +Example: + +codec: codec@4a { + compatible = "cirrus,cs42l52"; + reg = <0x4a>; + reset-gpio = <&gpio 10 0>; + cirrus,chgfreq-divisor = <0x05>; + cirrus.mica-differential-cfg; + cirrus,micbias-lvl = <5>; +}; -- cgit v1.2.3 From fa558c2801fc173758c742d836f3cc4621851557 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 20 Nov 2013 15:25:02 +0900 Subject: ASoC: simple-card: add Device Tree support Support for loading the simple-card module via DeviceTree. It requests CPU/CODEC information. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/simple-card.txt | 68 +++++++++ sound/soc/generic/simple-card.c | 156 ++++++++++++++++++++- 2 files changed, 218 insertions(+), 6 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/simple-card.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/simple-card.txt b/Documentation/devicetree/bindings/sound/simple-card.txt new file mode 100644 index 000000000000..769a346f890c --- /dev/null +++ b/Documentation/devicetree/bindings/sound/simple-card.txt @@ -0,0 +1,68 @@ +Simple-Card: + +Simple-Card specifies audio DAI connection of SoC <-> codec. + +Required properties: + +- compatible : "simple-audio-card" + +Optional properties: + +- simple-audio-card,format : CPU/CODEC common audio format. + "i2s", "right_j", "left_j" , "dsp_a" + "dsp_b", "ac97", "pdm", "msb", "lsb" +Required subnodes: + +- simple-audio-card,cpu : CPU sub-node +- simple-audio-card,codec : CODEC sub-node + +Required CPU/CODEC subnodes properties: + +- sound-dai : phandle and port of CPU/CODEC + +Optional CPU/CODEC subnodes properties: + +- format : CPU/CODEC specific audio format if needed. + see simple-audio-card,format +- frame-master : bool property. add this if subnode is frame master +- bitclock-master : bool property. add this if subnode is bitclock master +- bitclock-inversion : bool property. add this if subnode has clock inversion +- frame-inversion : bool property. add this if subnode has frame inversion +- clocks / system-clock-frequency : specify subnode's clock if needed. + it can be specified via "clocks" if system has + clock node (= common clock), or "system-clock-frequency" + (if system doens't support common clock) + +Example: + +sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "left_j"; + + simple-audio-card,cpu { + sound-dai = <&sh_fsi2 0>; + }; + + simple-audio-card,codec { + sound-dai = <&ak4648>; + bitclock-master; + frame-master; + clocks = <&osc>; + }; +}; + +&i2c0 { + ak4648: ak4648@12 { + #sound-dai-cells = <0>; + compatible = "asahi-kasei,ak4648"; + reg = <0x12>; + }; +}; + +sh_fsi2: sh_fsi2@ec230000 { + #sound-dai-cells = <1>; + compatible = "renesas,sh_fsi2"; + reg = <0xec230000 0x400>; + interrupt-parent = <&gic>; + interrupts = <0 146 0x4>; +}; diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index b2fbb7075a6c..7a9b6b4898c0 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -8,7 +8,8 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - +#include +#include #include #include #include @@ -57,11 +58,144 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) return 0; } +static int +asoc_simple_card_sub_parse_of(struct device_node *np, + struct asoc_simple_dai *dai, + struct device_node **node) +{ + struct clk *clk; + int ret; + + /* + * get node via "sound-dai = <&phandle port>" + * it will be used as xxx_of_node on soc_bind_dai_link() + */ + *node = of_parse_phandle(np, "sound-dai", 0); + if (!*node) + return -ENODEV; + + /* get dai->name */ + ret = snd_soc_of_get_dai_name(np, &dai->name); + if (ret < 0) + goto parse_error; + + /* + * bitclock-inversion, frame-inversion + * bitclock-master, frame-master + * and specific "format" if it has + */ + dai->fmt = snd_soc_of_parse_daifmt(np, NULL); + + /* + * dai->sysclk come from + * "clocks = <&xxx>" (if system has common clock) + * or "system-clock-frequency = " + */ + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) + of_property_read_u32(np, + "system-clock-frequency", + &dai->sysclk); + else + dai->sysclk = clk_get_rate(clk); + + ret = 0; + +parse_error: + of_node_put(*node); + + return ret; +} + +static int asoc_simple_card_parse_of(struct device_node *node, + struct asoc_simple_card_info *info, + struct device *dev, + struct device_node **of_cpu, + struct device_node **of_codec, + struct device_node **of_platform) +{ + struct device_node *np; + char *name; + int ret = 0; + + /* get CPU/CODEC common format via simple-audio-card,format */ + info->daifmt = snd_soc_of_parse_daifmt(node, "simple-audio-card,") & + (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK); + + /* CPU sub-node */ + ret = -EINVAL; + np = of_get_child_by_name(node, "simple-audio-card,cpu"); + if (np) + ret = asoc_simple_card_sub_parse_of(np, + &info->cpu_dai, + of_cpu); + if (ret < 0) + return ret; + + /* CODEC sub-node */ + ret = -EINVAL; + np = of_get_child_by_name(node, "simple-audio-card,codec"); + if (np) + ret = asoc_simple_card_sub_parse_of(np, + &info->codec_dai, + of_codec); + if (ret < 0) + return ret; + + /* card name is created from CPU/CODEC dai name */ + name = devm_kzalloc(dev, + strlen(info->cpu_dai.name) + + strlen(info->codec_dai.name) + 2, + GFP_KERNEL); + sprintf(name, "%s-%s", info->cpu_dai.name, info->codec_dai.name); + info->name = info->card = name; + + /* simple-card assumes platform == cpu */ + *of_platform = *of_cpu; + + dev_dbg(dev, "card-name : %s\n", info->card); + dev_dbg(dev, "platform : %04x\n", info->daifmt); + dev_dbg(dev, "cpu : %s / %04x / %d\n", + info->cpu_dai.name, + info->cpu_dai.fmt, + info->cpu_dai.sysclk); + dev_dbg(dev, "codec : %s / %04x / %d\n", + info->codec_dai.name, + info->codec_dai.fmt, + info->codec_dai.sysclk); + + return 0; +} + static int asoc_simple_card_probe(struct platform_device *pdev) { - struct asoc_simple_card_info *cinfo = pdev->dev.platform_data; + struct asoc_simple_card_info *cinfo; + struct device_node *np = pdev->dev.of_node; + struct device_node *of_cpu, *of_codec, *of_platform; struct device *dev = &pdev->dev; + cinfo = NULL; + of_cpu = NULL; + of_codec = NULL; + of_platform = NULL; + if (np && of_device_is_available(np)) { + cinfo = devm_kzalloc(dev, sizeof(*cinfo), GFP_KERNEL); + if (cinfo) { + int ret; + ret = asoc_simple_card_parse_of(np, cinfo, dev, + &of_cpu, + &of_codec, + &of_platform); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "parse error %d\n", ret); + return ret; + } + } + } else { + cinfo = pdev->dev.platform_data; + } + if (!cinfo) { dev_err(dev, "no info for asoc-simple-card\n"); return -EINVAL; @@ -69,10 +203,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev) if (!cinfo->name || !cinfo->card || - !cinfo->codec || - !cinfo->platform || - !cinfo->cpu_dai.name || - !cinfo->codec_dai.name) { + !cinfo->codec_dai.name || + !(cinfo->codec || of_codec) || + !(cinfo->platform || of_platform) || + !(cinfo->cpu_dai.name || of_cpu)) { dev_err(dev, "insufficient asoc_simple_card_info settings\n"); return -EINVAL; } @@ -86,6 +220,9 @@ static int asoc_simple_card_probe(struct platform_device *pdev) cinfo->snd_link.platform_name = cinfo->platform; cinfo->snd_link.codec_name = cinfo->codec; cinfo->snd_link.codec_dai_name = cinfo->codec_dai.name; + cinfo->snd_link.cpu_of_node = of_cpu; + cinfo->snd_link.codec_of_node = of_codec; + cinfo->snd_link.platform_of_node = of_platform; cinfo->snd_link.init = asoc_simple_card_dai_init; /* @@ -107,10 +244,17 @@ static int asoc_simple_card_remove(struct platform_device *pdev) return snd_soc_unregister_card(&cinfo->snd_card); } +static const struct of_device_id asoc_simple_of_match[] = { + { .compatible = "simple-audio-card", }, + {}, +}; +MODULE_DEVICE_TABLE(of, asoc_simple_of_match); + static struct platform_driver asoc_simple_card = { .driver = { .name = "asoc-simple-card", .owner = THIS_MODULE, + .of_match_table = asoc_simple_of_match, }, .probe = asoc_simple_card_probe, .remove = asoc_simple_card_remove, -- cgit v1.2.3 From 7637af2e17f18bfe6264d834c6edee7706a0f15c Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Dec 2013 15:19:27 -0700 Subject: ASoC: tegra: add tegra+MAX98090 machine driver Initially, this binding and driver only describe/support playback to headphones and speakers, and capture from the external microphone, with GPIO-based jack detection for the headphone jack only. This driver is useful for the Venice2 board. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- .../bindings/sound/nvidia,tegra-audio-max98090.txt | 51 ++++ sound/soc/tegra/Kconfig | 10 + sound/soc/tegra/Makefile | 2 + sound/soc/tegra/tegra_max98090.c | 275 +++++++++++++++++++++ 4 files changed, 338 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.txt create mode 100644 sound/soc/tegra/tegra_max98090.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.txt new file mode 100644 index 000000000000..9c7c55c71370 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.txt @@ -0,0 +1,51 @@ +NVIDIA Tegra audio complex, with MAX98090 CODEC + +Required properties: +- compatible : "nvidia,tegra-audio-max98090" +- clocks : Must contain an entry for each entry in clock-names. + See ../clocks/clock-bindings.txt for details. +- clock-names : Must include the following entries: + - pll_a + - pll_a_out0 + - mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) +- nvidia,model : The user-visible name of this sound complex. +- nvidia,audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. Valid names for sources and + sinks are the MAX98090's pins (as documented in its binding), and the jacks + on the board: + + * Headphones + * Speakers + * Mic Jack + +- nvidia,i2s-controller : The phandle of the Tegra I2S controller that's + connected to the CODEC. +- nvidia,audio-codec : The phandle of the MAX98090 audio codec. + +Optional properties: +- nvidia,hp-det-gpios : The GPIO that detect headphones are plugged in + +Example: + +sound { + compatible = "nvidia,tegra-audio-max98090-venice2", + "nvidia,tegra-audio-max98090"; + nvidia,model = "NVIDIA Tegra Venice2"; + + nvidia,audio-routing = + "Headphones", "HPR", + "Headphones", "HPL", + "Speakers", "SPKR", + "Speakers", "SPKL", + "Mic Jack", "MICBIAS", + "IN34", "Mic Jack"; + + nvidia,i2s-controller = <&tegra_i2s1>; + nvidia,audio-codec = <&acodec>; + + clocks = <&tegra_car TEGRA124_CLK_PLL_A>, + <&tegra_car TEGRA124_CLK_PLL_A_OUT0>, + <&tegra_car TEGRA124_CLK_EXTERN1>; + clock-names = "pll_a", "pll_a_out0", "mclk"; +}; diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 8fc653ca3ab4..65a85f542521 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -116,3 +116,13 @@ config SND_SOC_TEGRA_ALC5632 help Say Y or M here if you want to add support for SoC audio on the Toshiba AC100 netbook. + +config SND_SOC_TEGRA_MAX98090 + tristate "SoC Audio support for Tegra boards using a MAX98090 codec" + depends on SND_SOC_TEGRA && I2C && GPIOLIB + select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC + select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC + select SND_SOC_MAX98090 + help + Say Y or M here if you want to add support for SoC audio on Tegra + boards using the MAX98090 codec, such as Venice2. diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 21d2550a08a4..5ae588cd96c4 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -24,6 +24,7 @@ snd-soc-tegra-wm8903-objs := tegra_wm8903.o snd-soc-tegra-wm9712-objs := tegra_wm9712.o snd-soc-tegra-trimslice-objs := trimslice.o snd-soc-tegra-alc5632-objs := tegra_alc5632.o +snd-soc-tegra-max98090-objs := tegra_max98090.o obj-$(CONFIG_SND_SOC_TEGRA_RT5640) += snd-soc-tegra-rt5640.o obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o @@ -31,3 +32,4 @@ obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o +obj-$(CONFIG_SND_SOC_TEGRA_MAX98090) += snd-soc-tegra-max98090.o diff --git a/sound/soc/tegra/tegra_max98090.c b/sound/soc/tegra/tegra_max98090.c new file mode 100644 index 000000000000..0283cfb7c031 --- /dev/null +++ b/sound/soc/tegra/tegra_max98090.c @@ -0,0 +1,275 @@ +/* + * Tegra machine ASoC driver for boards using a MAX90809 CODEC. + * + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + * + * Based on code copyright/by: + * + * Copyright (C) 2010-2012 - NVIDIA, Inc. + * Copyright (C) 2011 The AC100 Kernel Team + * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd. + * Copyright 2007 Wolfson Microelectronics PLC. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "tegra_asoc_utils.h" + +#define DRV_NAME "tegra-snd-max98090" + +struct tegra_max98090 { + struct tegra_asoc_utils_data util_data; + int gpio_hp_det; +}; + +static int tegra_max98090_asoc_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 *codec_dai = rtd->codec_dai; + struct snd_soc_codec *codec = codec_dai->codec; + struct snd_soc_card *card = codec->card; + struct tegra_max98090 *machine = snd_soc_card_get_drvdata(card); + int srate, mclk; + int err; + + srate = params_rate(params); + switch (srate) { + case 8000: + case 16000: + case 24000: + case 32000: + case 48000: + case 64000: + case 96000: + mclk = 12288000; + break; + case 11025: + case 22050: + case 44100: + case 88200: + mclk = 11289600; + break; + default: + mclk = 12000000; + break; + } + + err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); + if (err < 0) { + dev_err(card->dev, "Can't configure clocks\n"); + return err; + } + + err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (err < 0) { + dev_err(card->dev, "codec_dai clock not set\n"); + return err; + } + + return 0; +} + +static struct snd_soc_ops tegra_max98090_ops = { + .hw_params = tegra_max98090_asoc_hw_params, +}; + +static struct snd_soc_jack tegra_max98090_hp_jack; + +static struct snd_soc_jack_pin tegra_max98090_hp_jack_pins[] = { + { + .pin = "Headphones", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static struct snd_soc_jack_gpio tegra_max98090_hp_jack_gpio = { + .name = "Headphone detection", + .report = SND_JACK_HEADPHONE, + .debounce_time = 150, + .invert = 1, +}; + +static const struct snd_soc_dapm_widget tegra_max98090_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_SPK("Speakers", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), +}; + +static const struct snd_kcontrol_new tegra_max98090_controls[] = { + SOC_DAPM_PIN_SWITCH("Speakers"), +}; + +static int tegra_max98090_asoc_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_codec *codec = codec_dai->codec; + struct tegra_max98090 *machine = snd_soc_card_get_drvdata(codec->card); + + if (gpio_is_valid(machine->gpio_hp_det)) { + snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE, + &tegra_max98090_hp_jack); + snd_soc_jack_add_pins(&tegra_max98090_hp_jack, + ARRAY_SIZE(tegra_max98090_hp_jack_pins), + tegra_max98090_hp_jack_pins); + + tegra_max98090_hp_jack_gpio.gpio = machine->gpio_hp_det; + snd_soc_jack_add_gpios(&tegra_max98090_hp_jack, + 1, + &tegra_max98090_hp_jack_gpio); + } + + return 0; +} + +static struct snd_soc_dai_link tegra_max98090_dai = { + .name = "max98090", + .stream_name = "max98090 PCM", + .codec_dai_name = "HiFi", + .init = tegra_max98090_asoc_init, + .ops = &tegra_max98090_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, +}; + +static struct snd_soc_card snd_soc_tegra_max98090 = { + .name = "tegra-max98090", + .owner = THIS_MODULE, + .dai_link = &tegra_max98090_dai, + .num_links = 1, + .controls = tegra_max98090_controls, + .num_controls = ARRAY_SIZE(tegra_max98090_controls), + .dapm_widgets = tegra_max98090_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra_max98090_dapm_widgets), + .fully_routed = true, +}; + +static int tegra_max98090_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct snd_soc_card *card = &snd_soc_tegra_max98090; + struct tegra_max98090 *machine; + int ret; + + machine = devm_kzalloc(&pdev->dev, + sizeof(struct tegra_max98090), GFP_KERNEL); + if (!machine) { + dev_err(&pdev->dev, "Can't allocate tegra_max98090\n"); + return -ENOMEM; + } + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, machine); + + machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0); + if (machine->gpio_hp_det == -EPROBE_DEFER) + return -EPROBE_DEFER; + + ret = snd_soc_of_parse_card_name(card, "nvidia,model"); + if (ret) + goto err; + + ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); + if (ret) + goto err; + + tegra_max98090_dai.codec_of_node = of_parse_phandle(np, + "nvidia,audio-codec", 0); + if (!tegra_max98090_dai.codec_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,audio-codec' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + + tegra_max98090_dai.cpu_of_node = of_parse_phandle(np, + "nvidia,i2s-controller", 0); + if (!tegra_max98090_dai.cpu_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,i2s-controller' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + + tegra_max98090_dai.platform_of_node = tegra_max98090_dai.cpu_of_node; + + ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); + if (ret) + goto err; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err_fini_utils; + } + + return 0; + +err_fini_utils: + tegra_asoc_utils_fini(&machine->util_data); +err: + return ret; +} + +static int tegra_max98090_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct tegra_max98090 *machine = snd_soc_card_get_drvdata(card); + + snd_soc_jack_free_gpios(&tegra_max98090_hp_jack, 1, + &tegra_max98090_hp_jack_gpio); + + snd_soc_unregister_card(card); + + tegra_asoc_utils_fini(&machine->util_data); + + return 0; +} + +static const struct of_device_id tegra_max98090_of_match[] = { + { .compatible = "nvidia,tegra-audio-max98090", }, + {}, +}; + +static struct platform_driver tegra_max98090_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = tegra_max98090_of_match, + }, + .probe = tegra_max98090_probe, + .remove = tegra_max98090_remove, +}; +module_platform_driver(tegra_max98090_driver); + +MODULE_AUTHOR("Stephen Warren "); +MODULE_DESCRIPTION("Tegra max98090 machine ASoC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra_max98090_of_match); -- cgit v1.2.3 From 308a0f3f24db5e5943ef0aad722e8b3d125dbddb Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Dec 2013 15:19:26 -0700 Subject: ASoC: max98090: add DT binding document for MAX98090 CODEC This binding mainly serves to document the list of input and output pins that may be used in a sound card's audio routing table. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/max98090.txt | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/max98090.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/max98090.txt b/Documentation/devicetree/bindings/sound/max98090.txt new file mode 100644 index 000000000000..e4c8b36dcf89 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/max98090.txt @@ -0,0 +1,43 @@ +MAX98090 audio CODEC + +This device supports I2C only. + +Required properties: + +- compatible : "maxim,max98090". + +- reg : The I2C address of the device. + +- interrupts : The CODEC's interrupt output. + +Pins on the device (for linking into audio routes): + + * MIC1 + * MIC2 + * DMICL + * DMICR + * IN1 + * IN2 + * IN3 + * IN4 + * IN5 + * IN6 + * IN12 + * IN34 + * IN56 + * HPL + * HPR + * SPKL + * SPKR + * RCVL + * RCVR + * MICBIAS + +Example: + +audio-codec@10 { + compatible = "maxim,max98090"; + reg = <0x10>; + interrupt-parent = <&gpio>; + interrupts = ; +}; -- cgit v1.2.3 From 00e6cb2aed48a86e97a244c6f96ac1f934e2272c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 6 Dec 2013 11:02:49 +0100 Subject: dt: Add bindings documentation for the ADI AXI-I2S controller This patch adds the devicetree documentation for the ADI AXI-SPDIF audio controller. The controller has: * One set of memory mapped register * Two clocks, one for the memory mapped register interface, one used as the audio reference clock * One DMA interface each for the transmit and receive data Signed-off-by: Lars-Peter Clausen Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Stephen Warren Cc: Ian Campbell Cc: devicetree@vger.kernel.org Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/adi,axi-i2s.txt | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/adi,axi-i2s.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/adi,axi-i2s.txt b/Documentation/devicetree/bindings/sound/adi,axi-i2s.txt new file mode 100644 index 000000000000..5875ca459ed1 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/adi,axi-i2s.txt @@ -0,0 +1,31 @@ +ADI AXI-I2S controller + +Required properties: + - compatible : Must be "adi,axi-i2s-1.00.a" + - reg : Must contain I2S core's registers location and length + - clocks : Pairs of phandle and specifier referencing the controller's clocks. + The controller expects two clocks, the clock used for the AXI interface and + the clock used as the sampling rate reference clock sample. + - clock-names : "axi" for the clock to the AXI interface, "ref" for the sample + rate reference clock. + - dmas: Pairs of phandle and specifier for the DMA channels that are used by + the core. The core expects two dma channels, one for transmit and one for + receive. + - dma-names : "tx" for the transmit channel, "rx" for the receive channel. + +For more details on the 'dma', 'dma-names', 'clock' and 'clock-names' properties +please check: + * resource-names.txt + * clock/clock-bindings.txt + * dma/dma.txt + +Example: + + i2s: i2s@0x77600000 { + compatible = "adi,axi-i2s-1.00.a"; + reg = <0x77600000 0x1000>; + clocks = <&clk 15>, <&audio_clock>; + clock-names = "axi", "ref"; + dmas = <&ps7_dma 0>, <&ps7_dma 1>; + dma-names = "tx", "rx"; + }; -- cgit v1.2.3 From d7b528eff9277b83b315500f44ade178035ed0d1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 6 Dec 2013 11:02:51 +0100 Subject: dt: Add bindings documentation for the ADI AXI-SPDIF audio controller This patch adds the devicetree documentation for the ADI AXI-SPDIF audio controller. The controller has: * One set of memory mapped register * Two clocks, one for the memory mapped register interface, one used as the audio reference clock * A DMA interface for the transmit data Signed-off-by: Lars-Peter Clausen Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Stephen Warren Cc: Ian Campbell Cc: devicetree@vger.kernel.org Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/adi,axi-spdif-tx.txt | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/adi,axi-spdif-tx.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/adi,axi-spdif-tx.txt b/Documentation/devicetree/bindings/sound/adi,axi-spdif-tx.txt new file mode 100644 index 000000000000..46f344965313 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/adi,axi-spdif-tx.txt @@ -0,0 +1,30 @@ +ADI AXI-SPDIF controller + +Required properties: + - compatible : Must be "adi,axi-spdif-1.00.a" + - reg : Must contain SPDIF core's registers location and length + - clocks : Pairs of phandle and specifier referencing the controller's clocks. + The controller expects two clocks, the clock used for the AXI interface and + the clock used as the sampling rate reference clock sample. + - clock-names: "axi" for the clock to the AXI interface, "ref" for the sample + rate reference clock. + - dmas: Pairs of phandle and specifier for the DMA channel that is used by + the core. The core expects one dma channel for transmit. + - dma-names : Must be "tx" + +For more details on the 'dma', 'dma-names', 'clock' and 'clock-names' properties +please check: + * resource-names.txt + * clock/clock-bindings.txt + * dma/dma.txt + +Example: + + spdif: spdif@0x77400000 { + compatible = "adi,axi-spdif-tx-1.00.a"; + reg = <0x77600000 0x1000>; + clocks = <&clk 15>, <&audio_clock>; + clock-names = "axi", "ref"; + dmas = <&ps7_dma 0>; + dma-names = "tx"; + }; -- cgit v1.2.3 From a42efd97f7471c78617c6329ed39919e2f31a7cc Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 14 Nov 2013 11:35:19 +0200 Subject: ASoC: davinci: kconfig: Prepare for AM43xx support AM43xx have the same McASP IP as AM33xx and both platform uses eDMA. Modify the Kconfig so it will be possible to add audio support for AM43xx based boards later. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt | 2 +- sound/soc/davinci/Kconfig | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt index ed785b3f67be..1eed972d4719 100644 --- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt +++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt @@ -4,7 +4,7 @@ Required properties: - compatible : "ti,dm646x-mcasp-audio" : for DM646x platforms "ti,da830-mcasp-audio" : for both DA830 & DA850 platforms - "ti,am33xx-mcasp-audio" : for AM33xx platforms (AM33xx, TI81xx) + "ti,am33xx-mcasp-audio" : for AM33xx platforms (AM33xx, AM43xx, TI81xx) - reg : Should contain reg specifiers for the entries in the reg-names property. - reg-names : Should contain: diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig index be667719d44e..a8ec1fc3e4d0 100644 --- a/sound/soc/davinci/Kconfig +++ b/sound/soc/davinci/Kconfig @@ -1,6 +1,6 @@ config SND_DAVINCI_SOC - tristate "SoC Audio for the TI DAVINCI or AM33XX chip" - depends on ARCH_DAVINCI || SOC_AM33XX + tristate "SoC Audio for TI DAVINCI or AM33XX/AM43XX chips" + depends on ARCH_DAVINCI || SOC_AM33XX || SOC_AM43XX config SND_DAVINCI_SOC_I2S tristate -- cgit v1.2.3 From 453c499028bf2ecf3b31ccb7c3657fe1b0b28943 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 14 Nov 2013 11:35:34 +0200 Subject: ASoC: davinci-mcasp: Support for McASP version found in DRA7xx The IP in DRA7xx is similar to the IP found in TI81xxAM3xxx/AM4xxx type of SoCs but it is is integrated with sDMA instead of eDMA. The suitable pcm driver for DRA7xx is the omap-pcm driver which is using dmaengine. In the driver we can configure both dma related structures used for eDMA and sDMA. The only thing we need to make sure that we set the correct dma_data at startup with snd_soc_dai_set_dma_data() Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- .../bindings/sound/davinci-mcasp-audio.txt | 1 + include/linux/platform_data/davinci_asp.h | 1 + sound/soc/davinci/davinci-mcasp.c | 52 +++++++++++++++++++--- 3 files changed, 47 insertions(+), 7 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt index 1eed972d4719..990fa71ce804 100644 --- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt +++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt @@ -5,6 +5,7 @@ Required properties: "ti,dm646x-mcasp-audio" : for DM646x platforms "ti,da830-mcasp-audio" : for both DA830 & DA850 platforms "ti,am33xx-mcasp-audio" : for AM33xx platforms (AM33xx, AM43xx, TI81xx) + "ti,dra7-mcasp-audio" : for DRA7xx platforms - reg : Should contain reg specifiers for the entries in the reg-names property. - reg-names : Should contain: diff --git a/include/linux/platform_data/davinci_asp.h b/include/linux/platform_data/davinci_asp.h index 689a856b86f9..5245992b0367 100644 --- a/include/linux/platform_data/davinci_asp.h +++ b/include/linux/platform_data/davinci_asp.h @@ -92,6 +92,7 @@ enum { MCASP_VERSION_1 = 0, /* DM646x */ MCASP_VERSION_2, /* DA8xx/OMAPL1x */ MCASP_VERSION_3, /* TI81xx/AM33xx */ + MCASP_VERSION_4, /* DRA7xxx */ }; enum mcbsp_clk_input_pin { diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 93f2e294d649..fc8c13d2f31e 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -31,12 +31,14 @@ #include #include #include +#include #include "davinci-pcm.h" #include "davinci-mcasp.h" struct davinci_mcasp { struct davinci_pcm_dma_params dma_params[2]; + struct snd_dmaengine_dai_dma_data dma_data[2]; void __iomem *base; u32 fifo_base; struct device *dev; @@ -458,7 +460,9 @@ static int davinci_hw_common_param(struct davinci_mcasp *mcasp, int stream, u8 max_active_serializers = (channels + slots - 1) / slots; u32 reg; /* Default configuration */ - mcasp_set_bits(mcasp->base + DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT); + if (mcasp->version != MCASP_VERSION_4) + mcasp_set_bits(mcasp->base + DAVINCI_MCASP_PWREMUMGT_REG, + MCASP_SOFT); /* All PINS as McASP */ mcasp_set_reg(mcasp->base + DAVINCI_MCASP_PFUNC_REG, 0x00000000); @@ -605,6 +609,8 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); struct davinci_pcm_dma_params *dma_params = &mcasp->dma_params[substream->stream]; + struct snd_dmaengine_dai_dma_data *dma_data = + &mcasp->dma_data[substream->stream]; int word_length; u8 fifo_level; u8 slots = mcasp->tdm_slots; @@ -666,6 +672,8 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, dma_params->acnt = dma_params->data_type; dma_params->fifo_level = fifo_level; + dma_data->maxburst = fifo_level; + davinci_config_channel_size(mcasp, word_length); return 0; @@ -711,7 +719,12 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); - snd_soc_dai_set_dma_data(dai, substream, mcasp->dma_params); + if (mcasp->version == MCASP_VERSION_4) + snd_soc_dai_set_dma_data(dai, substream, + &mcasp->dma_data[substream->stream]); + else + snd_soc_dai_set_dma_data(dai, substream, mcasp->dma_params); + return 0; } @@ -794,6 +807,13 @@ static struct snd_platform_data omap2_mcasp_pdata = { .version = MCASP_VERSION_3, }; +static struct snd_platform_data dra7_mcasp_pdata = { + .tx_dma_offset = 0x200, + .rx_dma_offset = 0x284, + .asp_chan_q = EVENTQ_0, + .version = MCASP_VERSION_4, +}; + static const struct of_device_id mcasp_dt_ids[] = { { .compatible = "ti,dm646x-mcasp-audio", @@ -807,6 +827,10 @@ static const struct of_device_id mcasp_dt_ids[] = { .compatible = "ti,am33xx-mcasp-audio", .data = &omap2_mcasp_pdata, }, + { + .compatible = "ti,dra7-mcasp-audio", + .data = &dra7_mcasp_pdata, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mcasp_dt_ids); @@ -999,6 +1023,9 @@ static int davinci_mcasp_probe(struct platform_device *pdev) else dma_data->dma_addr = mem->start + pdata->tx_dma_offset; + /* Unconditional dmaengine stuff */ + mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = dma_data->dma_addr; + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (res) dma_data->channel = res->start; @@ -1015,6 +1042,9 @@ static int davinci_mcasp_probe(struct platform_device *pdev) else dma_data->dma_addr = mem->start + pdata->rx_dma_offset; + /* Unconditional dmaengine stuff */ + mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = dma_data->dma_addr; + if (mcasp->version < MCASP_VERSION_3) { mcasp->fifo_base = DAVINCI_MCASP_V2_AFIFO_BASE; /* dma_data->dma_addr is pointing to the data port address */ @@ -1029,6 +1059,10 @@ static int davinci_mcasp_probe(struct platform_device *pdev) else dma_data->channel = pdata->rx_dma_channel; + /* Unconditional dmaengine stuff */ + mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data = "tx"; + mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data = "rx"; + dev_set_drvdata(&pdev->dev, mcasp); ret = snd_soc_register_component(&pdev->dev, &davinci_mcasp_component, &davinci_mcasp_dai[pdata->op_mode], 1); @@ -1036,10 +1070,12 @@ static int davinci_mcasp_probe(struct platform_device *pdev) if (ret != 0) goto err_release_clk; - ret = davinci_soc_platform_register(&pdev->dev); - if (ret) { - dev_err(&pdev->dev, "register PCM failed: %d\n", ret); - goto err_unregister_component; + if (mcasp->version != MCASP_VERSION_4) { + ret = davinci_soc_platform_register(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "register PCM failed: %d\n", ret); + goto err_unregister_component; + } } return 0; @@ -1054,9 +1090,11 @@ err_release_clk: static int davinci_mcasp_remove(struct platform_device *pdev) { + struct davinci_mcasp *mcasp = dev_get_drvdata(&pdev->dev); snd_soc_unregister_component(&pdev->dev); - davinci_soc_platform_unregister(&pdev->dev); + if (mcasp->version != MCASP_VERSION_4) + davinci_soc_platform_unregister(&pdev->dev); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); -- cgit v1.2.3 From ae726e93946403b14f8cad20d5cbd22d015c9106 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 14 Nov 2013 11:35:35 +0200 Subject: ASoC: davinci-mcasp: Support for fck reparenting Optional DT property to specify the desired parent clock for the McASP fck clock. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- .../bindings/sound/davinci-mcasp-audio.txt | 3 +- sound/soc/davinci/davinci-mcasp.c | 44 ++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt index 990fa71ce804..569b26c4a81e 100644 --- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt +++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt @@ -37,7 +37,8 @@ Optional properties: - pinctrl-0: Should specify pin control group used for this controller. - pinctrl-names: Should contain only one value - "default", for more details please refer to pinctrl-bindings.txt - +- fck_parent : Should contain a valid clock name which will be used as parent + for the McASP fck Example: diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 8ec879548488..b7858bfa0295 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -823,6 +824,46 @@ static const struct of_device_id mcasp_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, mcasp_dt_ids); +static int mcasp_reparent_fck(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct clk *gfclk, *parent_clk; + const char *parent_name; + int ret; + + if (!node) + return 0; + + parent_name = of_get_property(node, "fck_parent", NULL); + if (!parent_name) + return 0; + + gfclk = clk_get(&pdev->dev, "fck"); + if (IS_ERR(gfclk)) { + dev_err(&pdev->dev, "failed to get fck\n"); + return PTR_ERR(gfclk); + } + + parent_clk = clk_get(NULL, parent_name); + if (IS_ERR(parent_clk)) { + dev_err(&pdev->dev, "failed to get parent clock\n"); + ret = PTR_ERR(parent_clk); + goto err1; + } + + ret = clk_set_parent(gfclk, parent_clk); + if (ret) { + dev_err(&pdev->dev, "failed to reparent fck\n"); + goto err2; + } + +err2: + clk_put(parent_clk); +err1: + clk_put(gfclk); + return ret; +} + static struct snd_platform_data *davinci_mcasp_set_pdata_from_of( struct platform_device *pdev) { @@ -1052,6 +1093,9 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data = "rx"; dev_set_drvdata(&pdev->dev, mcasp); + + mcasp_reparent_fck(pdev); + ret = snd_soc_register_component(&pdev->dev, &davinci_mcasp_component, &davinci_mcasp_dai[pdata->op_mode], 1); -- cgit v1.2.3 From b6344859b911990152e5ee411e62b82eb968004f Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 17 Dec 2013 11:24:41 +0800 Subject: ASoC: fsl-sai: Add device tree bindings for Freescale SAI. This adds the Document for Freescale SAI driver under Documentation/devicetree/bindings/sound/. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/fsl-sai.txt | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/fsl-sai.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/fsl-sai.txt b/Documentation/devicetree/bindings/sound/fsl-sai.txt new file mode 100644 index 000000000000..98611a6761c0 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/fsl-sai.txt @@ -0,0 +1,40 @@ +Freescale Synchronous Audio Interface (SAI). + +The SAI is based on I2S module that used communicating with audio codecs, +which provides a synchronous audio interface that supports fullduplex +serial interfaces with frame synchronization such as I2S, AC97, TDM, and +codec/DSP interfaces. + + +Required properties: +- compatible: Compatible list, contains "fsl,vf610-sai". +- reg: Offset and length of the register set for the device. +- clocks: Must contain an entry for each entry in clock-names. +- clock-names : Must include the "sai" entry. +- dmas : Generic dma devicetree binding as described in + Documentation/devicetree/bindings/dma/dma.txt. +- dma-names : Two dmas have to be defined, "tx" and "rx". +- pinctrl-names: Must contain a "default" entry. +- pinctrl-NNN: One property must exist for each entry in pinctrl-names. + See ../pinctrl/pinctrl-bindings.txt for details of the property values. +- big-endian-regs: If this property is absent, the little endian mode will + be in use as default, or the big endian mode will be in use for all the + device registers. +- big-endian-data: If this property is absent, the little endian mode will + be in use as default, or the big endian mode will be in use for all the + fifo data. + +Example: +sai2: sai@40031000 { + compatible = "fsl,vf610-sai"; + reg = <0x40031000 0x1000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sai2_1>; + clocks = <&clks VF610_CLK_SAI2>; + clock-names = "sai"; + dma-names = "tx", "rx"; + dmas = <&edma0 0 VF610_EDMA_MUXID0_SAI2_TX>, + <&edma0 0 VF610_EDMA_MUXID0_SAI2_RX>; + big-endian-regs; + big-endian-data; +}; -- cgit v1.2.3 From d4c22094b256a7327346738721b89a78d4680b08 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Mon, 23 Dec 2013 12:57:01 +0800 Subject: ASoC: simple-card: Add DAPM routes parse from device tree Parses a simple DAPM route table from device tree. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/simple-card.txt | 13 +++++++++++-- sound/soc/generic/simple-card.c | 11 +++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/simple-card.txt b/Documentation/devicetree/bindings/sound/simple-card.txt index 769a346f890c..2ee80c76ca64 100644 --- a/Documentation/devicetree/bindings/sound/simple-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-card.txt @@ -9,8 +9,13 @@ Required properties: Optional properties: - simple-audio-card,format : CPU/CODEC common audio format. - "i2s", "right_j", "left_j" , "dsp_a" - "dsp_b", "ac97", "pdm", "msb", "lsb" + "i2s", "right_j", "left_j" , "dsp_a" + "dsp_b", "ac97", "pdm", "msb", "lsb" +- simple-audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the + connection's sink, the second being the connection's + source. + Required subnodes: - simple-audio-card,cpu : CPU sub-node @@ -38,6 +43,10 @@ Example: sound { compatible = "simple-audio-card"; simple-audio-card,format = "left_j"; + simple-audio-routing = + "MIC_IN", "Mic Jack", + "Headphone Jack", "HP_OUT", + "Ext Spk", "LINE_OUT"; simple-audio-card,cpu { sound-dai = <&sh_fsi2 0>; diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 3d190d05ad4a..6230efb05fd7 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -116,12 +116,18 @@ static int asoc_simple_card_parse_of(struct device_node *node, { struct device_node *np; char *name; - int ret = 0; + int ret; /* get CPU/CODEC common format via simple-audio-card,format */ info->daifmt = snd_soc_of_parse_daifmt(node, "simple-audio-card,") & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK); + /* DAPM routes */ + ret = snd_soc_of_parse_audio_routing(&info->snd_card, + "simple-audio-routing"); + if (ret) + return ret; + /* CPU sub-node */ ret = -EINVAL; np = of_get_child_by_name(node, "simple-audio-card,cpu"); @@ -182,6 +188,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) cinfo = devm_kzalloc(dev, sizeof(*cinfo), GFP_KERNEL); if (cinfo) { int ret; + cinfo->snd_card.dev = &pdev->dev; ret = asoc_simple_card_parse_of(np, cinfo, dev, &of_cpu, &of_codec, @@ -193,6 +200,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) } } } else { + cinfo->snd_card.dev = &pdev->dev; cinfo = pdev->dev.platform_data; } @@ -232,7 +240,6 @@ static int asoc_simple_card_probe(struct platform_device *pdev) cinfo->snd_card.owner = THIS_MODULE; cinfo->snd_card.dai_link = &cinfo->snd_link; cinfo->snd_card.num_links = 1; - cinfo->snd_card.dev = &pdev->dev; return devm_snd_soc_register_card(&pdev->dev, &cinfo->snd_card); } -- cgit v1.2.3 From 0f7f3d1f17c2e4d73e449e6acb2007b13813c58e Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Fri, 20 Dec 2013 12:40:16 +0200 Subject: ASoC: hdmi-codec: Add devicetree binding with documentation Signed-off-by: Jyri Sarha cc: bcousson@baylibre.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/hdmi.txt | 17 +++++++++++++++++ sound/soc/codecs/hdmi.c | 10 ++++++++++ 2 files changed, 27 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/hdmi.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/hdmi.txt b/Documentation/devicetree/bindings/sound/hdmi.txt new file mode 100644 index 000000000000..31af7bca3099 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/hdmi.txt @@ -0,0 +1,17 @@ +Device-Tree bindings for dummy HDMI codec + +Required properties: + - compatible: should be "linux,hdmi-audio". + +CODEC output pins: + * TX + +CODEC input pins: + * RX + +Example node: + + hdmi_audio: hdmi_audio@0 { + compatible = "linux,hdmi-audio"; + status = "okay"; + }; diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c index 32797a8e4ee9..9cb1c7d3e1dc 100644 --- a/sound/soc/codecs/hdmi.c +++ b/sound/soc/codecs/hdmi.c @@ -20,6 +20,7 @@ */ #include #include +#include #define DRV_NAME "hdmi-audio-codec" @@ -60,6 +61,14 @@ static struct snd_soc_dai_driver hdmi_codec_dai = { }; +#ifdef CONFIG_OF +static const struct of_device_id hdmi_audio_codec_ids[] = { + { .compatible = "linux,hdmi-audio", }, + { } +}; +MODULE_DEVICE_TABLE(of, hdmi_audio_codec_ids); +#endif + static struct snd_soc_codec_driver hdmi_codec = { .dapm_widgets = hdmi_widgets, .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), @@ -83,6 +92,7 @@ static struct platform_driver hdmi_codec_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(hdmi_audio_codec_ids), }, .probe = hdmi_codec_probe, -- cgit v1.2.3 From 8c0b8230b2d9708eed5b50f9f8442aaa879a3c57 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 7 Jan 2014 09:15:16 +0800 Subject: ASoC: simple-card: keep the property's name the same pattern Even though we might not have rigor rule for the simple card property names, according to the existing ones, they are all in a same pattern: [simple-audio-card,]XXX; Rename simple-audio-routing to simple-audio-card,routing, and make the simple card's properties has one unified name. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/simple-card.txt | 2 +- sound/soc/generic/simple-card.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/simple-card.txt b/Documentation/devicetree/bindings/sound/simple-card.txt index 2ee80c76ca64..e9e20ec67d62 100644 --- a/Documentation/devicetree/bindings/sound/simple-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-card.txt @@ -11,7 +11,7 @@ Optional properties: - simple-audio-card,format : CPU/CODEC common audio format. "i2s", "right_j", "left_j" , "dsp_a" "dsp_b", "ac97", "pdm", "msb", "lsb" -- simple-audio-routing : A list of the connections between audio components. +- simple-audio-card,routing : A list of the connections between audio components. Each entry is a pair of strings, the first being the connection's sink, the second being the connection's source. diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index eb95beb25d43..0430be85f23c 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -134,9 +134,9 @@ static int asoc_simple_card_parse_of(struct device_node *node, (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK); /* DAPM routes */ - if (of_property_read_bool(node, "simple-audio-routing")) { + if (of_property_read_bool(node, "simple-audio-card,routing")) { ret = snd_soc_of_parse_audio_routing(&info->snd_card, - "simple-audio-routing"); + "simple-audio-card,routing"); if (ret) return ret; } -- cgit v1.2.3 From 10901e5382099a463c96f2949050283f81e365e9 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Thu, 9 Jan 2014 11:16:10 +0100 Subject: ASoC: fsl-ssi doc: Add list of supported compatibles There is no list of compatibles that are supported. This patch adds a list of compatibles to the documentation. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/fsl,ssi.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/fsl,ssi.txt b/Documentation/devicetree/bindings/sound/fsl,ssi.txt index 4303b6ab6208..b93e9a91e30e 100644 --- a/Documentation/devicetree/bindings/sound/fsl,ssi.txt +++ b/Documentation/devicetree/bindings/sound/fsl,ssi.txt @@ -4,7 +4,12 @@ The SSI is a serial device that communicates with audio codecs. It can be programmed in AC97, I2S, left-justified, or right-justified modes. Required properties: -- compatible: Compatible list, contains "fsl,ssi". +- compatible: Compatible list, should contain one of the following + compatibles: + fsl,mpc8610-ssi + fsl,imx51-ssi + fsl,imx35-ssi + fsl,imx21-ssi - cell-index: The SSI, <0> = SSI1, <1> = SSI2, and so on. - reg: Offset and length of the register set for the device. - interrupts: where a is the interrupt number and b is a -- cgit v1.2.3 From 43d24e76b69826ce32292f47060ad78cdd0197fa Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 10 Jan 2014 17:54:06 +0800 Subject: ASoC: fsl_esai: Add ESAI CPU DAI driver This patch implements a device-tree-only CPU DAI driver for Freescale ESAI controller that supports: - 12 channels playback and 8 channels record. [ Some of the inner transmitters and receivers are sharing same group of pins. So the maxmium 12 output or 8 input channels are only valid if there is no pin conflict occurring to it. ] - Independent (asynchronous mode) or shared (synchronous mode) transmit and receive sections with separate or shared internal/external clocks and frame syncs, operating in Master or Slave mode. [ Current ALSA seems not to allow CPU DAI drivers to configure DAI format separately for PLAYBACK and CAPTURE. So this first version only supports the case that uses the same DAI format for both directions. ] - Various DAI formats: I2S, Left-Justified, Right-Justified, DSP-A and DSP-B. - Programmable word length (8, 16, 20 or 24bits) - Flexible selection between system clock or external oscillator as input clock source, programmable internal clock divider and frame sync generation. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/fsl,esai.txt | 50 ++ sound/soc/fsl/Kconfig | 3 + sound/soc/fsl/Makefile | 2 + sound/soc/fsl/fsl_esai.c | 815 +++++++++++++++++++++ sound/soc/fsl/fsl_esai.h | 354 +++++++++ 5 files changed, 1224 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/fsl,esai.txt create mode 100644 sound/soc/fsl/fsl_esai.c create mode 100644 sound/soc/fsl/fsl_esai.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/fsl,esai.txt b/Documentation/devicetree/bindings/sound/fsl,esai.txt new file mode 100644 index 000000000000..d7b99fa637b5 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/fsl,esai.txt @@ -0,0 +1,50 @@ +Freescale Enhanced Serial Audio Interface (ESAI) Controller + +The Enhanced Serial Audio Interface (ESAI) provides a full-duplex serial port +for serial communication with a variety of serial devices, including industry +standard codecs, Sony/Phillips Digital Interface (S/PDIF) transceivers, and +other DSPs. It has up to six transmitters and four receivers. + +Required properties: + + - compatible : Compatible list, must contain "fsl,imx35-esai". + + - reg : Offset and length of the register set for the device. + + - interrupts : Contains the spdif interrupt. + + - dmas : Generic dma devicetree binding as described in + Documentation/devicetree/bindings/dma/dma.txt. + + - dma-names : Two dmas have to be defined, "tx" and "rx". + + - clocks: Contains an entry for each entry in clock-names. + + - clock-names : Includes the following entries: + "core" The core clock used to access registers + "extal" The esai baud clock for esai controller used to derive + HCK, SCK and FS. + "fsys" The system clock derived from ahb clock used to derive + HCK, SCK and FS. + + - fsl,fifo-depth: The number of elements in the transmit and receive FIFOs. + This number is the maximum allowed value for TFCR[TFWM] or RFCR[RFWM]. + + - fsl,esai-synchronous: This is a boolean property. If present, indicating + that ESAI would work in the synchronous mode, which means all the settings + for Receiving would be duplicated from Transmition related registers. + +Example: + +esai: esai@02024000 { + compatible = "fsl,imx35-esai"; + reg = <0x02024000 0x4000>; + interrupts = <0 51 0x04>; + clocks = <&clks 208>, <&clks 118>, <&clks 208>; + clock-names = "core", "extal", "fsys"; + dmas = <&sdma 23 21 0>, <&sdma 24 21 0>; + dma-names = "rx", "tx"; + fsl,fifo-depth = <128>; + fsl,esai-synchronous; + status = "disabled"; +}; diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index ac4fe4ea15a9..f2f39dd13bc7 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -8,6 +8,9 @@ config SND_SOC_FSL_SSI config SND_SOC_FSL_SPDIF tristate +config SND_SOC_FSL_ESAI + tristate + config SND_SOC_FSL_UTILS tristate diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index aaccbee17006..b12ad4b9b4da 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -14,11 +14,13 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o snd-soc-fsl-sai-objs := fsl_sai.o snd-soc-fsl-ssi-objs := fsl_ssi.o snd-soc-fsl-spdif-objs := fsl_spdif.o +snd-soc-fsl-esai-objs := fsl_esai.o snd-soc-fsl-utils-objs := fsl_utils.o snd-soc-fsl-dma-objs := fsl_dma.o obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o +obj-$(CONFIG_SND_SOC_FSL_ESAI) += snd-soc-fsl-esai.o obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c new file mode 100644 index 000000000000..d0c72ed261e7 --- /dev/null +++ b/sound/soc/fsl/fsl_esai.c @@ -0,0 +1,815 @@ +/* + * Freescale ESAI ALSA SoC Digital Audio Interface (DAI) driver + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "fsl_esai.h" +#include "imx-pcm.h" + +#define FSL_ESAI_RATES SNDRV_PCM_RATE_8000_192000 +#define FSL_ESAI_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +/** + * fsl_esai: ESAI private data + * + * @dma_params_rx: DMA parameters for receive channel + * @dma_params_tx: DMA parameters for transmit channel + * @pdev: platform device pointer + * @regmap: regmap handler + * @coreclk: clock source to access register + * @extalclk: esai clock source to derive HCK, SCK and FS + * @fsysclk: system clock source to derive HCK, SCK and FS + * @fifo_depth: depth of tx/rx FIFO + * @slot_width: width of each DAI slot + * @hck_rate: clock rate of desired HCKx clock + * @sck_div: if using PSR/PM dividers for SCKx clock + * @slave_mode: if fully using DAI slave mode + * @synchronous: if using tx/rx synchronous mode + * @name: driver name + */ +struct fsl_esai { + struct snd_dmaengine_dai_dma_data dma_params_rx; + struct snd_dmaengine_dai_dma_data dma_params_tx; + struct platform_device *pdev; + struct regmap *regmap; + struct clk *coreclk; + struct clk *extalclk; + struct clk *fsysclk; + u32 fifo_depth; + u32 slot_width; + u32 hck_rate[2]; + bool sck_div[2]; + bool slave_mode; + bool synchronous; + char name[32]; +}; + +static irqreturn_t esai_isr(int irq, void *devid) +{ + struct fsl_esai *esai_priv = (struct fsl_esai *)devid; + struct platform_device *pdev = esai_priv->pdev; + u32 esr; + + regmap_read(esai_priv->regmap, REG_ESAI_ESR, &esr); + + if (esr & ESAI_ESR_TINIT_MASK) + dev_dbg(&pdev->dev, "isr: Transmition Initialized\n"); + + if (esr & ESAI_ESR_RFF_MASK) + dev_warn(&pdev->dev, "isr: Receiving overrun\n"); + + if (esr & ESAI_ESR_TFE_MASK) + dev_warn(&pdev->dev, "isr: Transmition underrun\n"); + + if (esr & ESAI_ESR_TLS_MASK) + dev_dbg(&pdev->dev, "isr: Just transmitted the last slot\n"); + + if (esr & ESAI_ESR_TDE_MASK) + dev_dbg(&pdev->dev, "isr: Transmition data exception\n"); + + if (esr & ESAI_ESR_TED_MASK) + dev_dbg(&pdev->dev, "isr: Transmitting even slots\n"); + + if (esr & ESAI_ESR_TD_MASK) + dev_dbg(&pdev->dev, "isr: Transmitting data\n"); + + if (esr & ESAI_ESR_RLS_MASK) + dev_dbg(&pdev->dev, "isr: Just received the last slot\n"); + + if (esr & ESAI_ESR_RDE_MASK) + dev_dbg(&pdev->dev, "isr: Receiving data exception\n"); + + if (esr & ESAI_ESR_RED_MASK) + dev_dbg(&pdev->dev, "isr: Receiving even slots\n"); + + if (esr & ESAI_ESR_RD_MASK) + dev_dbg(&pdev->dev, "isr: Receiving data\n"); + + return IRQ_HANDLED; +} + +/** + * This function is used to calculate the divisors of psr, pm, fp and it is + * supposed to be called in set_dai_sysclk() and set_bclk(). + * + * @ratio: desired overall ratio for the paticipating dividers + * @usefp: for HCK setting, there is no need to set fp divider + * @fp: bypass other dividers by setting fp directly if fp != 0 + * @tx: current setting is for playback or capture + */ +static int fsl_esai_divisor_cal(struct snd_soc_dai *dai, bool tx, u32 ratio, + bool usefp, u32 fp) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + u32 psr, pm = 999, maxfp, prod, sub, savesub, i, j; + + maxfp = usefp ? 16 : 1; + + if (usefp && fp) + goto out_fp; + + if (ratio > 2 * 8 * 256 * maxfp || ratio < 2) { + dev_err(dai->dev, "the ratio is out of range (2 ~ %d)\n", + 2 * 8 * 256 * maxfp); + return -EINVAL; + } else if (ratio % 2) { + dev_err(dai->dev, "the raio must be even if using upper divider\n"); + return -EINVAL; + } + + ratio /= 2; + + psr = ratio <= 256 * maxfp ? ESAI_xCCR_xPSR_BYPASS : ESAI_xCCR_xPSR_DIV8; + + /* Set the max fluctuation -- 0.1% of the max devisor */ + savesub = (psr ? 1 : 8) * 256 * maxfp / 1000; + + /* Find the best value for PM */ + for (i = 1; i <= 256; i++) { + for (j = 1; j <= maxfp; j++) { + /* PSR (1 or 8) * PM (1 ~ 256) * FP (1 ~ 16) */ + prod = (psr ? 1 : 8) * i * j; + + if (prod == ratio) + sub = 0; + else if (prod / ratio == 1) + sub = prod - ratio; + else if (ratio / prod == 1) + sub = ratio - prod; + else + continue; + + /* Calculate the fraction */ + sub = sub * 1000 / ratio; + if (sub < savesub) { + savesub = sub; + pm = i; + fp = j; + } + + /* We are lucky */ + if (savesub == 0) + goto out; + } + } + + if (pm == 999) { + dev_err(dai->dev, "failed to calculate proper divisors\n"); + return -EINVAL; + } + +out: + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx), + ESAI_xCCR_xPSR_MASK | ESAI_xCCR_xPM_MASK, + psr | ESAI_xCCR_xPM(pm)); + +out_fp: + /* Bypass fp if not being required */ + if (maxfp <= 1) + return 0; + + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx), + ESAI_xCCR_xFP_MASK, ESAI_xCCR_xFP(fp)); + + return 0; +} + +/** + * This function mainly configures the clock frequency of MCLK (HCKT/HCKR) + * + * @Parameters: + * clk_id: The clock source of HCKT/HCKR + * (Input from outside; output from inside, FSYS or EXTAL) + * freq: The required clock rate of HCKT/HCKR + * dir: The clock direction of HCKT/HCKR + * + * Note: If the direction is input, we do not care about clk_id. + */ +static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + struct clk *clksrc = esai_priv->extalclk; + bool tx = clk_id <= ESAI_HCKT_EXTAL; + bool in = dir == SND_SOC_CLOCK_IN; + u32 ret, ratio, ecr = 0; + unsigned long clk_rate; + + /* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */ + esai_priv->sck_div[tx] = true; + + /* Set the direction of HCKT/HCKR pins */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx), + ESAI_xCCR_xHCKD, in ? 0 : ESAI_xCCR_xHCKD); + + if (in) + goto out; + + switch (clk_id) { + case ESAI_HCKT_FSYS: + case ESAI_HCKR_FSYS: + clksrc = esai_priv->fsysclk; + break; + case ESAI_HCKT_EXTAL: + ecr |= ESAI_ECR_ETI; + case ESAI_HCKR_EXTAL: + ecr |= ESAI_ECR_ERI; + break; + default: + return -EINVAL; + } + + if (IS_ERR(clksrc)) { + dev_err(dai->dev, "no assigned %s clock\n", + clk_id % 2 ? "extal" : "fsys"); + return PTR_ERR(clksrc); + } + clk_rate = clk_get_rate(clksrc); + + ratio = clk_rate / freq; + if (ratio * freq > clk_rate) + ret = ratio * freq - clk_rate; + else if (ratio * freq < clk_rate) + ret = clk_rate - ratio * freq; + else + ret = 0; + + /* Block if clock source can not be divided into the required rate */ + if (ret != 0 && clk_rate / ret < 1000) { + dev_err(dai->dev, "failed to derive required HCK%c rate\n", + tx ? 'T' : 'R'); + return -EINVAL; + } + + if (ratio == 1) { + /* Bypass all the dividers if not being needed */ + ecr |= tx ? ESAI_ECR_ETO : ESAI_ECR_ERO; + goto out; + } + + ret = fsl_esai_divisor_cal(dai, tx, ratio, false, 0); + if (ret) + return ret; + + esai_priv->sck_div[tx] = false; + +out: + esai_priv->hck_rate[tx] = freq; + + regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR, + tx ? ESAI_ECR_ETI | ESAI_ECR_ETO : + ESAI_ECR_ERI | ESAI_ECR_ERO, ecr); + + return 0; +} + +/** + * This function configures the related dividers according to the bclk rate + */ +static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + u32 hck_rate = esai_priv->hck_rate[tx]; + u32 sub, ratio = hck_rate / freq; + + /* Don't apply for fully slave mode*/ + if (esai_priv->slave_mode) + return 0; + + if (ratio * freq > hck_rate) + sub = ratio * freq - hck_rate; + else if (ratio * freq < hck_rate) + sub = hck_rate - ratio * freq; + else + sub = 0; + + /* Block if clock source can not be divided into the required rate */ + if (sub != 0 && hck_rate / sub < 1000) { + dev_err(dai->dev, "failed to derive required SCK%c rate\n", + tx ? 'T' : 'R'); + return -EINVAL; + } + + if (esai_priv->sck_div[tx] && (ratio > 16 || ratio == 0)) { + dev_err(dai->dev, "the ratio is out of range (1 ~ 16)\n"); + return -EINVAL; + } + + return fsl_esai_divisor_cal(dai, tx, ratio, true, + esai_priv->sck_div[tx] ? 0 : ratio); +} + +static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, + u32 rx_mask, int slots, int slot_width) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, + ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots)); + + regmap_update_bits(esai_priv->regmap, REG_ESAI_TSMA, + ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(tx_mask)); + regmap_update_bits(esai_priv->regmap, REG_ESAI_TSMB, + ESAI_xSMA_xS_MASK, ESAI_xSMB_xS(tx_mask)); + + regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, + ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots)); + + regmap_update_bits(esai_priv->regmap, REG_ESAI_RSMA, + ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(rx_mask)); + regmap_update_bits(esai_priv->regmap, REG_ESAI_RSMB, + ESAI_xSMA_xS_MASK, ESAI_xSMB_xS(rx_mask)); + + esai_priv->slot_width = slot_width; + + return 0; +} + +static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + u32 xcr = 0, xccr = 0, mask; + + /* DAI mode */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + /* Data on rising edge of bclk, frame low, 1clk before data */ + xcr |= ESAI_xCR_xFSR; + xccr |= ESAI_xCCR_xFSP | ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; + break; + case SND_SOC_DAIFMT_LEFT_J: + /* Data on rising edge of bclk, frame high */ + xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; + break; + case SND_SOC_DAIFMT_RIGHT_J: + /* Data on rising edge of bclk, frame high, right aligned */ + xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCR_xWA; + break; + case SND_SOC_DAIFMT_DSP_A: + /* Data on rising edge of bclk, frame high, 1clk before data */ + xcr |= ESAI_xCR_xFSL | ESAI_xCR_xFSR; + xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; + break; + case SND_SOC_DAIFMT_DSP_B: + /* Data on rising edge of bclk, frame high */ + xcr |= ESAI_xCR_xFSL; + xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; + break; + default: + return -EINVAL; + } + + /* DAI clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + /* Nothing to do for both normal cases */ + break; + case SND_SOC_DAIFMT_IB_NF: + /* Invert bit clock */ + xccr ^= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; + break; + case SND_SOC_DAIFMT_NB_IF: + /* Invert frame clock */ + xccr ^= ESAI_xCCR_xFSP; + break; + case SND_SOC_DAIFMT_IB_IF: + /* Invert both clocks */ + xccr ^= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP; + break; + default: + return -EINVAL; + } + + esai_priv->slave_mode = false; + + /* DAI clock master masks */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + esai_priv->slave_mode = true; + break; + case SND_SOC_DAIFMT_CBS_CFM: + xccr |= ESAI_xCCR_xCKD; + break; + case SND_SOC_DAIFMT_CBM_CFS: + xccr |= ESAI_xCCR_xFSD; + break; + case SND_SOC_DAIFMT_CBS_CFS: + xccr |= ESAI_xCCR_xFSD | ESAI_xCCR_xCKD; + break; + default: + return -EINVAL; + } + + mask = ESAI_xCR_xFSL | ESAI_xCR_xFSR; + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, mask, xcr); + regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, mask, xcr); + + mask = ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP | + ESAI_xCCR_xFSD | ESAI_xCCR_xCKD | ESAI_xCR_xWA; + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, mask, xccr); + regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, mask, xccr); + + return 0; +} + +static int fsl_esai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + + /* + * Some platforms might use the same bit to gate all three or two of + * clocks, so keep all clocks open/close at the same time for safety + */ + clk_prepare_enable(esai_priv->coreclk); + if (!IS_ERR(esai_priv->extalclk)) + clk_prepare_enable(esai_priv->extalclk); + if (!IS_ERR(esai_priv->fsysclk)) + clk_prepare_enable(esai_priv->fsysclk); + + if (!dai->active) { + /* Reset Port C */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC, + ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO)); + regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC, + ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO)); + + /* Set synchronous mode */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_SAICR, + ESAI_SAICR_SYNC, esai_priv->synchronous ? + ESAI_SAICR_SYNC : 0); + + /* Set a default slot number -- 2 */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, + ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2)); + regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, + ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2)); + } + + return 0; +} + +static int fsl_esai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + u32 width = snd_pcm_format_width(params_format(params)); + u32 channels = params_channels(params); + u32 bclk, mask, val, ret; + + bclk = params_rate(params) * esai_priv->slot_width * 2; + + ret = fsl_esai_set_bclk(dai, tx, bclk); + if (ret) + return ret; + + /* Use Normal mode to support monaural audio */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), + ESAI_xCR_xMOD_MASK, params_channels(params) > 1 ? + ESAI_xCR_xMOD_NETWORK : 0); + + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), + ESAI_xFCR_xFR_MASK, ESAI_xFCR_xFR); + + mask = ESAI_xFCR_xFR_MASK | ESAI_xFCR_xWA_MASK | ESAI_xFCR_xFWM_MASK | + (tx ? ESAI_xFCR_TE_MASK | ESAI_xFCR_TIEN : ESAI_xFCR_RE_MASK); + val = ESAI_xFCR_xWA(width) | ESAI_xFCR_xFWM(esai_priv->fifo_depth) | + (tx ? ESAI_xFCR_TE(channels) | ESAI_xFCR_TIEN : ESAI_xFCR_RE(channels)); + + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val); + + mask = ESAI_xCR_xSWS_MASK | (tx ? ESAI_xCR_PADC : 0); + val = ESAI_xCR_xSWS(esai_priv->slot_width, width) | (tx ? ESAI_xCR_PADC : 0); + + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val); + + return 0; +} + +static void fsl_esai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + + if (!IS_ERR(esai_priv->fsysclk)) + clk_disable_unprepare(esai_priv->fsysclk); + if (!IS_ERR(esai_priv->extalclk)) + clk_disable_unprepare(esai_priv->extalclk); + clk_disable_unprepare(esai_priv->coreclk); +} + +static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + u8 i, channels = substream->runtime->channels; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), + ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN); + + /* Write initial words reqiured by ESAI as normal procedure */ + for (i = 0; tx && i < channels; i++) + regmap_write(esai_priv->regmap, REG_ESAI_ETDR, 0x0); + + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), + tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, + tx ? ESAI_xCR_TE(channels) : ESAI_xCR_RE(channels)); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), + tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0); + + /* Disable and reset FIFO */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), + ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR); + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), + ESAI_xFCR_xFR, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static struct snd_soc_dai_ops fsl_esai_dai_ops = { + .startup = fsl_esai_startup, + .shutdown = fsl_esai_shutdown, + .trigger = fsl_esai_trigger, + .hw_params = fsl_esai_hw_params, + .set_sysclk = fsl_esai_set_dai_sysclk, + .set_fmt = fsl_esai_set_dai_fmt, + .set_tdm_slot = fsl_esai_set_dai_tdm_slot, +}; + +static int fsl_esai_dai_probe(struct snd_soc_dai *dai) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &esai_priv->dma_params_tx, + &esai_priv->dma_params_rx); + + return 0; +} + +static struct snd_soc_dai_driver fsl_esai_dai = { + .probe = fsl_esai_dai_probe, + .playback = { + .channels_min = 1, + .channels_max = 12, + .rates = FSL_ESAI_RATES, + .formats = FSL_ESAI_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 8, + .rates = FSL_ESAI_RATES, + .formats = FSL_ESAI_FORMATS, + }, + .ops = &fsl_esai_dai_ops, +}; + +static const struct snd_soc_component_driver fsl_esai_component = { + .name = "fsl-esai", +}; + +static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_ESAI_ERDR: + case REG_ESAI_ECR: + case REG_ESAI_ESR: + case REG_ESAI_TFCR: + case REG_ESAI_TFSR: + case REG_ESAI_RFCR: + case REG_ESAI_RFSR: + case REG_ESAI_RX0: + case REG_ESAI_RX1: + case REG_ESAI_RX2: + case REG_ESAI_RX3: + case REG_ESAI_SAISR: + case REG_ESAI_SAICR: + case REG_ESAI_TCR: + case REG_ESAI_TCCR: + case REG_ESAI_RCR: + case REG_ESAI_RCCR: + case REG_ESAI_TSMA: + case REG_ESAI_TSMB: + case REG_ESAI_RSMA: + case REG_ESAI_RSMB: + case REG_ESAI_PRRC: + case REG_ESAI_PCRC: + return true; + default: + return false; + } +} + +static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_ESAI_ETDR: + case REG_ESAI_ECR: + case REG_ESAI_TFCR: + case REG_ESAI_RFCR: + case REG_ESAI_TX0: + case REG_ESAI_TX1: + case REG_ESAI_TX2: + case REG_ESAI_TX3: + case REG_ESAI_TX4: + case REG_ESAI_TX5: + case REG_ESAI_TSR: + case REG_ESAI_SAICR: + case REG_ESAI_TCR: + case REG_ESAI_TCCR: + case REG_ESAI_RCR: + case REG_ESAI_RCCR: + case REG_ESAI_TSMA: + case REG_ESAI_TSMB: + case REG_ESAI_RSMA: + case REG_ESAI_RSMB: + case REG_ESAI_PRRC: + case REG_ESAI_PCRC: + return true; + default: + return false; + } +} + +static const struct regmap_config fsl_esai_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = REG_ESAI_PCRC, + .readable_reg = fsl_esai_readable_reg, + .writeable_reg = fsl_esai_writeable_reg, +}; + +static int fsl_esai_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct fsl_esai *esai_priv; + struct resource *res; + const uint32_t *iprop; + void __iomem *regs; + int irq, ret; + + esai_priv = devm_kzalloc(&pdev->dev, sizeof(*esai_priv), GFP_KERNEL); + if (!esai_priv) + return -ENOMEM; + + esai_priv->pdev = pdev; + strcpy(esai_priv->name, np->name); + + /* Get the addresses and IRQ */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + esai_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, + "core", regs, &fsl_esai_regmap_config); + if (IS_ERR(esai_priv->regmap)) { + dev_err(&pdev->dev, "failed to init regmap: %ld\n", + PTR_ERR(esai_priv->regmap)); + return PTR_ERR(esai_priv->regmap); + } + + esai_priv->coreclk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(esai_priv->coreclk)) { + dev_err(&pdev->dev, "failed to get core clock: %ld\n", + PTR_ERR(esai_priv->coreclk)); + return PTR_ERR(esai_priv->coreclk); + } + + esai_priv->extalclk = devm_clk_get(&pdev->dev, "extal"); + if (IS_ERR(esai_priv->extalclk)) + dev_warn(&pdev->dev, "failed to get extal clock: %ld\n", + PTR_ERR(esai_priv->extalclk)); + + esai_priv->fsysclk = devm_clk_get(&pdev->dev, "fsys"); + if (IS_ERR(esai_priv->fsysclk)) + dev_warn(&pdev->dev, "failed to get fsys clock: %ld\n", + PTR_ERR(esai_priv->fsysclk)); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, esai_isr, 0, + esai_priv->name, esai_priv); + if (ret) { + dev_err(&pdev->dev, "failed to claim irq %u\n", irq); + return ret; + } + + /* Set a default slot size */ + esai_priv->slot_width = 32; + + /* Set a default master/slave state */ + esai_priv->slave_mode = true; + + /* Determine the FIFO depth */ + iprop = of_get_property(np, "fsl,fifo-depth", NULL); + if (iprop) + esai_priv->fifo_depth = be32_to_cpup(iprop); + else + esai_priv->fifo_depth = 64; + + esai_priv->dma_params_tx.maxburst = 16; + esai_priv->dma_params_rx.maxburst = 16; + esai_priv->dma_params_tx.addr = res->start + REG_ESAI_ETDR; + esai_priv->dma_params_rx.addr = res->start + REG_ESAI_ERDR; + + esai_priv->synchronous = + of_property_read_bool(np, "fsl,esai-synchronous"); + + /* Implement full symmetry for synchronous mode */ + if (esai_priv->synchronous) { + fsl_esai_dai.symmetric_rates = 1; + fsl_esai_dai.symmetric_channels = 1; + fsl_esai_dai.symmetric_samplebits = 1; + } + + dev_set_drvdata(&pdev->dev, esai_priv); + + /* Reset ESAI unit */ + ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ERST); + if (ret) { + dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret); + return ret; + } + + /* + * We need to enable ESAI so as to access some of its registers. + * Otherwise, we would fail to dump regmap from user space. + */ + ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ESAIEN); + if (ret) { + dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(&pdev->dev, &fsl_esai_component, + &fsl_esai_dai, 1); + if (ret) { + dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); + return ret; + } + + ret = imx_pcm_dma_init(pdev); + if (ret) + dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret); + + return ret; +} + +static const struct of_device_id fsl_esai_dt_ids[] = { + { .compatible = "fsl,imx35-esai", }, + {} +}; +MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids); + +static struct platform_driver fsl_esai_driver = { + .probe = fsl_esai_probe, + .driver = { + .name = "fsl-esai-dai", + .owner = THIS_MODULE, + .of_match_table = fsl_esai_dt_ids, + }, +}; + +module_platform_driver(fsl_esai_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Freescale ESAI CPU DAI driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:fsl-esai-dai"); diff --git a/sound/soc/fsl/fsl_esai.h b/sound/soc/fsl/fsl_esai.h new file mode 100644 index 000000000000..9c9f957fcae1 --- /dev/null +++ b/sound/soc/fsl/fsl_esai.h @@ -0,0 +1,354 @@ +/* + * fsl_esai.h - ALSA ESAI interface for the Freescale i.MX SoC + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * Author: Nicolin Chen + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef _FSL_ESAI_DAI_H +#define _FSL_ESAI_DAI_H + +/* ESAI Register Map */ +#define REG_ESAI_ETDR 0x00 +#define REG_ESAI_ERDR 0x04 +#define REG_ESAI_ECR 0x08 +#define REG_ESAI_ESR 0x0C +#define REG_ESAI_TFCR 0x10 +#define REG_ESAI_TFSR 0x14 +#define REG_ESAI_RFCR 0x18 +#define REG_ESAI_RFSR 0x1C +#define REG_ESAI_xFCR(tx) (tx ? REG_ESAI_TFCR : REG_ESAI_RFCR) +#define REG_ESAI_xFSR(tx) (tx ? REG_ESAI_TFSR : REG_ESAI_RFSR) +#define REG_ESAI_TX0 0x80 +#define REG_ESAI_TX1 0x84 +#define REG_ESAI_TX2 0x88 +#define REG_ESAI_TX3 0x8C +#define REG_ESAI_TX4 0x90 +#define REG_ESAI_TX5 0x94 +#define REG_ESAI_TSR 0x98 +#define REG_ESAI_RX0 0xA0 +#define REG_ESAI_RX1 0xA4 +#define REG_ESAI_RX2 0xA8 +#define REG_ESAI_RX3 0xAC +#define REG_ESAI_SAISR 0xCC +#define REG_ESAI_SAICR 0xD0 +#define REG_ESAI_TCR 0xD4 +#define REG_ESAI_TCCR 0xD8 +#define REG_ESAI_RCR 0xDC +#define REG_ESAI_RCCR 0xE0 +#define REG_ESAI_xCR(tx) (tx ? REG_ESAI_TCR : REG_ESAI_RCR) +#define REG_ESAI_xCCR(tx) (tx ? REG_ESAI_TCCR : REG_ESAI_RCCR) +#define REG_ESAI_TSMA 0xE4 +#define REG_ESAI_TSMB 0xE8 +#define REG_ESAI_RSMA 0xEC +#define REG_ESAI_RSMB 0xF0 +#define REG_ESAI_xSMA(tx) (tx ? REG_ESAI_TSMA : REG_ESAI_RSMA) +#define REG_ESAI_xSMB(tx) (tx ? REG_ESAI_TSMB : REG_ESAI_RSMB) +#define REG_ESAI_PRRC 0xF8 +#define REG_ESAI_PCRC 0xFC + +/* ESAI Control Register -- REG_ESAI_ECR 0x8 */ +#define ESAI_ECR_ETI_SHIFT 19 +#define ESAI_ECR_ETI_MASK (1 << ESAI_ECR_ETI_SHIFT) +#define ESAI_ECR_ETI (1 << ESAI_ECR_ETI_SHIFT) +#define ESAI_ECR_ETO_SHIFT 18 +#define ESAI_ECR_ETO_MASK (1 << ESAI_ECR_ETO_SHIFT) +#define ESAI_ECR_ETO (1 << ESAI_ECR_ETO_SHIFT) +#define ESAI_ECR_ERI_SHIFT 17 +#define ESAI_ECR_ERI_MASK (1 << ESAI_ECR_ERI_SHIFT) +#define ESAI_ECR_ERI (1 << ESAI_ECR_ERI_SHIFT) +#define ESAI_ECR_ERO_SHIFT 16 +#define ESAI_ECR_ERO_MASK (1 << ESAI_ECR_ERO_SHIFT) +#define ESAI_ECR_ERO (1 << ESAI_ECR_ERO_SHIFT) +#define ESAI_ECR_ERST_SHIFT 1 +#define ESAI_ECR_ERST_MASK (1 << ESAI_ECR_ERST_SHIFT) +#define ESAI_ECR_ERST (1 << ESAI_ECR_ERST_SHIFT) +#define ESAI_ECR_ESAIEN_SHIFT 0 +#define ESAI_ECR_ESAIEN_MASK (1 << ESAI_ECR_ESAIEN_SHIFT) +#define ESAI_ECR_ESAIEN (1 << ESAI_ECR_ESAIEN_SHIFT) + +/* ESAI Status Register -- REG_ESAI_ESR 0xC */ +#define ESAI_ESR_TINIT_SHIFT 10 +#define ESAI_ESR_TINIT_MASK (1 << ESAI_ESR_TINIT_SHIFT) +#define ESAI_ESR_TINIT (1 << ESAI_ESR_TINIT_SHIFT) +#define ESAI_ESR_RFF_SHIFT 9 +#define ESAI_ESR_RFF_MASK (1 << ESAI_ESR_RFF_SHIFT) +#define ESAI_ESR_RFF (1 << ESAI_ESR_RFF_SHIFT) +#define ESAI_ESR_TFE_SHIFT 8 +#define ESAI_ESR_TFE_MASK (1 << ESAI_ESR_TFE_SHIFT) +#define ESAI_ESR_TFE (1 << ESAI_ESR_TFE_SHIFT) +#define ESAI_ESR_TLS_SHIFT 7 +#define ESAI_ESR_TLS_MASK (1 << ESAI_ESR_TLS_SHIFT) +#define ESAI_ESR_TLS (1 << ESAI_ESR_TLS_SHIFT) +#define ESAI_ESR_TDE_SHIFT 6 +#define ESAI_ESR_TDE_MASK (1 << ESAI_ESR_TDE_SHIFT) +#define ESAI_ESR_TDE (1 << ESAI_ESR_TDE_SHIFT) +#define ESAI_ESR_TED_SHIFT 5 +#define ESAI_ESR_TED_MASK (1 << ESAI_ESR_TED_SHIFT) +#define ESAI_ESR_TED (1 << ESAI_ESR_TED_SHIFT) +#define ESAI_ESR_TD_SHIFT 4 +#define ESAI_ESR_TD_MASK (1 << ESAI_ESR_TD_SHIFT) +#define ESAI_ESR_TD (1 << ESAI_ESR_TD_SHIFT) +#define ESAI_ESR_RLS_SHIFT 3 +#define ESAI_ESR_RLS_MASK (1 << ESAI_ESR_RLS_SHIFT) +#define ESAI_ESR_RLS (1 << ESAI_ESR_RLS_SHIFT) +#define ESAI_ESR_RDE_SHIFT 2 +#define ESAI_ESR_RDE_MASK (1 << ESAI_ESR_RDE_SHIFT) +#define ESAI_ESR_RDE (1 << ESAI_ESR_RDE_SHIFT) +#define ESAI_ESR_RED_SHIFT 1 +#define ESAI_ESR_RED_MASK (1 << ESAI_ESR_RED_SHIFT) +#define ESAI_ESR_RED (1 << ESAI_ESR_RED_SHIFT) +#define ESAI_ESR_RD_SHIFT 0 +#define ESAI_ESR_RD_MASK (1 << ESAI_ESR_RD_SHIFT) +#define ESAI_ESR_RD (1 << ESAI_ESR_RD_SHIFT) + +/* + * Transmit FIFO Configuration Register -- REG_ESAI_TFCR 0x10 + * Receive FIFO Configuration Register -- REG_ESAI_RFCR 0x18 + */ +#define ESAI_xFCR_TIEN_SHIFT 19 +#define ESAI_xFCR_TIEN_MASK (1 << ESAI_xFCR_TIEN_SHIFT) +#define ESAI_xFCR_TIEN (1 << ESAI_xFCR_TIEN_SHIFT) +#define ESAI_xFCR_REXT_SHIFT 19 +#define ESAI_xFCR_REXT_MASK (1 << ESAI_xFCR_REXT_SHIFT) +#define ESAI_xFCR_REXT (1 << ESAI_xFCR_REXT_SHIFT) +#define ESAI_xFCR_xWA_SHIFT 16 +#define ESAI_xFCR_xWA_WIDTH 3 +#define ESAI_xFCR_xWA_MASK (((1 << ESAI_xFCR_xWA_WIDTH) - 1) << ESAI_xFCR_xWA_SHIFT) +#define ESAI_xFCR_xWA(v) (((8 - ((v) >> 2)) << ESAI_xFCR_xWA_SHIFT) & ESAI_xFCR_xWA_MASK) +#define ESAI_xFCR_xFWM_SHIFT 8 +#define ESAI_xFCR_xFWM_WIDTH 8 +#define ESAI_xFCR_xFWM_MASK (((1 << ESAI_xFCR_xFWM_WIDTH) - 1) << ESAI_xFCR_xFWM_SHIFT) +#define ESAI_xFCR_xFWM(v) ((((v) - 1) << ESAI_xFCR_xFWM_SHIFT) & ESAI_xFCR_xFWM_MASK) +#define ESAI_xFCR_xE_SHIFT 2 +#define ESAI_xFCR_TE_WIDTH 6 +#define ESAI_xFCR_RE_WIDTH 4 +#define ESAI_xFCR_TE_MASK (((1 << ESAI_xFCR_TE_WIDTH) - 1) << ESAI_xFCR_xE_SHIFT) +#define ESAI_xFCR_RE_MASK (((1 << ESAI_xFCR_RE_WIDTH) - 1) << ESAI_xFCR_xE_SHIFT) +#define ESAI_xFCR_TE(x) ((ESAI_xFCR_TE_MASK >> (ESAI_xFCR_TE_WIDTH - ((x + 1) >> 1))) & ESAI_xFCR_TE_MASK) +#define ESAI_xFCR_RE(x) ((ESAI_xFCR_RE_MASK >> (ESAI_xFCR_RE_WIDTH - ((x + 1) >> 1))) & ESAI_xFCR_RE_MASK) +#define ESAI_xFCR_xFR_SHIFT 1 +#define ESAI_xFCR_xFR_MASK (1 << ESAI_xFCR_xFR_SHIFT) +#define ESAI_xFCR_xFR (1 << ESAI_xFCR_xFR_SHIFT) +#define ESAI_xFCR_xFEN_SHIFT 0 +#define ESAI_xFCR_xFEN_MASK (1 << ESAI_xFCR_xFEN_SHIFT) +#define ESAI_xFCR_xFEN (1 << ESAI_xFCR_xFEN_SHIFT) + +/* + * Transmit FIFO Status Register -- REG_ESAI_TFSR 0x14 + * Receive FIFO Status Register --REG_ESAI_RFSR 0x1C + */ +#define ESAI_xFSR_NTFO_SHIFT 12 +#define ESAI_xFSR_NRFI_SHIFT 12 +#define ESAI_xFSR_NTFI_SHIFT 8 +#define ESAI_xFSR_NRFO_SHIFT 8 +#define ESAI_xFSR_NTFx_WIDTH 3 +#define ESAI_xFSR_NRFx_WIDTH 2 +#define ESAI_xFSR_NTFO_MASK (((1 << ESAI_xFSR_NTFx_WIDTH) - 1) << ESAI_xFSR_NTFO_SHIFT) +#define ESAI_xFSR_NTFI_MASK (((1 << ESAI_xFSR_NTFx_WIDTH) - 1) << ESAI_xFSR_NTFI_SHIFT) +#define ESAI_xFSR_NRFO_MASK (((1 << ESAI_xFSR_NRFx_WIDTH) - 1) << ESAI_xFSR_NRFO_SHIFT) +#define ESAI_xFSR_NRFI_MASK (((1 << ESAI_xFSR_NRFx_WIDTH) - 1) << ESAI_xFSR_NRFI_SHIFT) +#define ESAI_xFSR_xFCNT_SHIFT 0 +#define ESAI_xFSR_xFCNT_WIDTH 8 +#define ESAI_xFSR_xFCNT_MASK (((1 << ESAI_xFSR_xFCNT_WIDTH) - 1) << ESAI_xFSR_xFCNT_SHIFT) + +/* ESAI Transmit Slot Register -- REG_ESAI_TSR 0x98 */ +#define ESAI_TSR_SHIFT 0 +#define ESAI_TSR_WIDTH 24 +#define ESAI_TSR_MASK (((1 << ESAI_TSR_WIDTH) - 1) << ESAI_TSR_SHIFT) + +/* Serial Audio Interface Status Register -- REG_ESAI_SAISR 0xCC */ +#define ESAI_SAISR_TODFE_SHIFT 17 +#define ESAI_SAISR_TODFE_MASK (1 << ESAI_SAISR_TODFE_SHIFT) +#define ESAI_SAISR_TODFE (1 << ESAI_SAISR_TODFE_SHIFT) +#define ESAI_SAISR_TEDE_SHIFT 16 +#define ESAI_SAISR_TEDE_MASK (1 << ESAI_SAISR_TEDE_SHIFT) +#define ESAI_SAISR_TEDE (1 << ESAI_SAISR_TEDE_SHIFT) +#define ESAI_SAISR_TDE_SHIFT 15 +#define ESAI_SAISR_TDE_MASK (1 << ESAI_SAISR_TDE_SHIFT) +#define ESAI_SAISR_TDE (1 << ESAI_SAISR_TDE_SHIFT) +#define ESAI_SAISR_TUE_SHIFT 14 +#define ESAI_SAISR_TUE_MASK (1 << ESAI_SAISR_TUE_SHIFT) +#define ESAI_SAISR_TUE (1 << ESAI_SAISR_TUE_SHIFT) +#define ESAI_SAISR_TFS_SHIFT 13 +#define ESAI_SAISR_TFS_MASK (1 << ESAI_SAISR_TFS_SHIFT) +#define ESAI_SAISR_TFS (1 << ESAI_SAISR_TFS_SHIFT) +#define ESAI_SAISR_RODF_SHIFT 10 +#define ESAI_SAISR_RODF_MASK (1 << ESAI_SAISR_RODF_SHIFT) +#define ESAI_SAISR_RODF (1 << ESAI_SAISR_RODF_SHIFT) +#define ESAI_SAISR_REDF_SHIFT 9 +#define ESAI_SAISR_REDF_MASK (1 << ESAI_SAISR_REDF_SHIFT) +#define ESAI_SAISR_REDF (1 << ESAI_SAISR_REDF_SHIFT) +#define ESAI_SAISR_RDF_SHIFT 8 +#define ESAI_SAISR_RDF_MASK (1 << ESAI_SAISR_RDF_SHIFT) +#define ESAI_SAISR_RDF (1 << ESAI_SAISR_RDF_SHIFT) +#define ESAI_SAISR_ROE_SHIFT 7 +#define ESAI_SAISR_ROE_MASK (1 << ESAI_SAISR_ROE_SHIFT) +#define ESAI_SAISR_ROE (1 << ESAI_SAISR_ROE_SHIFT) +#define ESAI_SAISR_RFS_SHIFT 6 +#define ESAI_SAISR_RFS_MASK (1 << ESAI_SAISR_RFS_SHIFT) +#define ESAI_SAISR_RFS (1 << ESAI_SAISR_RFS_SHIFT) +#define ESAI_SAISR_IF2_SHIFT 2 +#define ESAI_SAISR_IF2_MASK (1 << ESAI_SAISR_IF2_SHIFT) +#define ESAI_SAISR_IF2 (1 << ESAI_SAISR_IF2_SHIFT) +#define ESAI_SAISR_IF1_SHIFT 1 +#define ESAI_SAISR_IF1_MASK (1 << ESAI_SAISR_IF1_SHIFT) +#define ESAI_SAISR_IF1 (1 << ESAI_SAISR_IF1_SHIFT) +#define ESAI_SAISR_IF0_SHIFT 0 +#define ESAI_SAISR_IF0_MASK (1 << ESAI_SAISR_IF0_SHIFT) +#define ESAI_SAISR_IF0 (1 << ESAI_SAISR_IF0_SHIFT) + +/* Serial Audio Interface Control Register -- REG_ESAI_SAICR 0xD0 */ +#define ESAI_SAICR_ALC_SHIFT 8 +#define ESAI_SAICR_ALC_MASK (1 << ESAI_SAICR_ALC_SHIFT) +#define ESAI_SAICR_ALC (1 << ESAI_SAICR_ALC_SHIFT) +#define ESAI_SAICR_TEBE_SHIFT 7 +#define ESAI_SAICR_TEBE_MASK (1 << ESAI_SAICR_TEBE_SHIFT) +#define ESAI_SAICR_TEBE (1 << ESAI_SAICR_TEBE_SHIFT) +#define ESAI_SAICR_SYNC_SHIFT 6 +#define ESAI_SAICR_SYNC_MASK (1 << ESAI_SAICR_SYNC_SHIFT) +#define ESAI_SAICR_SYNC (1 << ESAI_SAICR_SYNC_SHIFT) +#define ESAI_SAICR_OF2_SHIFT 2 +#define ESAI_SAICR_OF2_MASK (1 << ESAI_SAICR_OF2_SHIFT) +#define ESAI_SAICR_OF2 (1 << ESAI_SAICR_OF2_SHIFT) +#define ESAI_SAICR_OF1_SHIFT 1 +#define ESAI_SAICR_OF1_MASK (1 << ESAI_SAICR_OF1_SHIFT) +#define ESAI_SAICR_OF1 (1 << ESAI_SAICR_OF1_SHIFT) +#define ESAI_SAICR_OF0_SHIFT 0 +#define ESAI_SAICR_OF0_MASK (1 << ESAI_SAICR_OF0_SHIFT) +#define ESAI_SAICR_OF0 (1 << ESAI_SAICR_OF0_SHIFT) + +/* + * Transmit Control Register -- REG_ESAI_TCR 0xD4 + * Receive Control Register -- REG_ESAI_RCR 0xDC + */ +#define ESAI_xCR_xLIE_SHIFT 23 +#define ESAI_xCR_xLIE_MASK (1 << ESAI_xCR_xLIE_SHIFT) +#define ESAI_xCR_xLIE (1 << ESAI_xCR_xLIE_SHIFT) +#define ESAI_xCR_xIE_SHIFT 22 +#define ESAI_xCR_xIE_MASK (1 << ESAI_xCR_xIE_SHIFT) +#define ESAI_xCR_xIE (1 << ESAI_xCR_xIE_SHIFT) +#define ESAI_xCR_xEDIE_SHIFT 21 +#define ESAI_xCR_xEDIE_MASK (1 << ESAI_xCR_xEDIE_SHIFT) +#define ESAI_xCR_xEDIE (1 << ESAI_xCR_xEDIE_SHIFT) +#define ESAI_xCR_xEIE_SHIFT 20 +#define ESAI_xCR_xEIE_MASK (1 << ESAI_xCR_xEIE_SHIFT) +#define ESAI_xCR_xEIE (1 << ESAI_xCR_xEIE_SHIFT) +#define ESAI_xCR_xPR_SHIFT 19 +#define ESAI_xCR_xPR_MASK (1 << ESAI_xCR_xPR_SHIFT) +#define ESAI_xCR_xPR (1 << ESAI_xCR_xPR_SHIFT) +#define ESAI_xCR_PADC_SHIFT 17 +#define ESAI_xCR_PADC_MASK (1 << ESAI_xCR_PADC_SHIFT) +#define ESAI_xCR_PADC (1 << ESAI_xCR_PADC_SHIFT) +#define ESAI_xCR_xFSR_SHIFT 16 +#define ESAI_xCR_xFSR_MASK (1 << ESAI_xCR_xFSR_SHIFT) +#define ESAI_xCR_xFSR (1 << ESAI_xCR_xFSR_SHIFT) +#define ESAI_xCR_xFSL_SHIFT 15 +#define ESAI_xCR_xFSL_MASK (1 << ESAI_xCR_xFSL_SHIFT) +#define ESAI_xCR_xFSL (1 << ESAI_xCR_xFSL_SHIFT) +#define ESAI_xCR_xSWS_SHIFT 10 +#define ESAI_xCR_xSWS_WIDTH 5 +#define ESAI_xCR_xSWS_MASK (((1 << ESAI_xCR_xSWS_WIDTH) - 1) << ESAI_xCR_xSWS_SHIFT) +#define ESAI_xCR_xSWS(s, w) ((w < 24 ? (s - w + ((w - 8) >> 2)) : (s < 32 ? 0x1e : 0x1f)) << ESAI_xCR_xSWS_SHIFT) +#define ESAI_xCR_xMOD_SHIFT 8 +#define ESAI_xCR_xMOD_WIDTH 2 +#define ESAI_xCR_xMOD_MASK (((1 << ESAI_xCR_xMOD_WIDTH) - 1) << ESAI_xCR_xMOD_SHIFT) +#define ESAI_xCR_xMOD_ONDEMAND (0x1 << ESAI_xCR_xMOD_SHIFT) +#define ESAI_xCR_xMOD_NETWORK (0x1 << ESAI_xCR_xMOD_SHIFT) +#define ESAI_xCR_xMOD_AC97 (0x3 << ESAI_xCR_xMOD_SHIFT) +#define ESAI_xCR_xWA_SHIFT 7 +#define ESAI_xCR_xWA_MASK (1 << ESAI_xCR_xWA_SHIFT) +#define ESAI_xCR_xWA (1 << ESAI_xCR_xWA_SHIFT) +#define ESAI_xCR_xSHFD_SHIFT 6 +#define ESAI_xCR_xSHFD_MASK (1 << ESAI_xCR_xSHFD_SHIFT) +#define ESAI_xCR_xSHFD (1 << ESAI_xCR_xSHFD_SHIFT) +#define ESAI_xCR_xE_SHIFT 0 +#define ESAI_xCR_TE_WIDTH 6 +#define ESAI_xCR_RE_WIDTH 4 +#define ESAI_xCR_TE_MASK (((1 << ESAI_xCR_TE_WIDTH) - 1) << ESAI_xCR_xE_SHIFT) +#define ESAI_xCR_RE_MASK (((1 << ESAI_xCR_RE_WIDTH) - 1) << ESAI_xCR_xE_SHIFT) +#define ESAI_xCR_TE(x) ((ESAI_xCR_TE_MASK >> (ESAI_xCR_TE_WIDTH - ((x + 1) >> 1))) & ESAI_xCR_TE_MASK) +#define ESAI_xCR_RE(x) ((ESAI_xCR_RE_MASK >> (ESAI_xCR_RE_WIDTH - ((x + 1) >> 1))) & ESAI_xCR_RE_MASK) + +/* + * Transmit Clock Control Register -- REG_ESAI_TCCR 0xD8 + * Receive Clock Control Register -- REG_ESAI_RCCR 0xE0 + */ +#define ESAI_xCCR_xHCKD_SHIFT 23 +#define ESAI_xCCR_xHCKD_MASK (1 << ESAI_xCCR_xHCKD_SHIFT) +#define ESAI_xCCR_xHCKD (1 << ESAI_xCCR_xHCKD_SHIFT) +#define ESAI_xCCR_xFSD_SHIFT 22 +#define ESAI_xCCR_xFSD_MASK (1 << ESAI_xCCR_xFSD_SHIFT) +#define ESAI_xCCR_xFSD (1 << ESAI_xCCR_xFSD_SHIFT) +#define ESAI_xCCR_xCKD_SHIFT 21 +#define ESAI_xCCR_xCKD_MASK (1 << ESAI_xCCR_xCKD_SHIFT) +#define ESAI_xCCR_xCKD (1 << ESAI_xCCR_xCKD_SHIFT) +#define ESAI_xCCR_xHCKP_SHIFT 20 +#define ESAI_xCCR_xHCKP_MASK (1 << ESAI_xCCR_xHCKP_SHIFT) +#define ESAI_xCCR_xHCKP (1 << ESAI_xCCR_xHCKP_SHIFT) +#define ESAI_xCCR_xFSP_SHIFT 19 +#define ESAI_xCCR_xFSP_MASK (1 << ESAI_xCCR_xFSP_SHIFT) +#define ESAI_xCCR_xFSP (1 << ESAI_xCCR_xFSP_SHIFT) +#define ESAI_xCCR_xCKP_SHIFT 18 +#define ESAI_xCCR_xCKP_MASK (1 << ESAI_xCCR_xCKP_SHIFT) +#define ESAI_xCCR_xCKP (1 << ESAI_xCCR_xCKP_SHIFT) +#define ESAI_xCCR_xFP_SHIFT 14 +#define ESAI_xCCR_xFP_WIDTH 4 +#define ESAI_xCCR_xFP_MASK (((1 << ESAI_xCCR_xFP_WIDTH) - 1) << ESAI_xCCR_xFP_SHIFT) +#define ESAI_xCCR_xFP(v) ((((v) - 1) << ESAI_xCCR_xFP_SHIFT) & ESAI_xCCR_xFP_MASK) +#define ESAI_xCCR_xDC_SHIFT 9 +#define ESAI_xCCR_xDC_WIDTH 4 +#define ESAI_xCCR_xDC_MASK (((1 << ESAI_xCCR_xDC_WIDTH) - 1) << ESAI_xCCR_xDC_SHIFT) +#define ESAI_xCCR_xDC(v) ((((v) - 1) << ESAI_xCCR_xDC_SHIFT) & ESAI_xCCR_xDC_MASK) +#define ESAI_xCCR_xPSR_SHIFT 8 +#define ESAI_xCCR_xPSR_MASK (1 << ESAI_xCCR_xPSR_SHIFT) +#define ESAI_xCCR_xPSR_BYPASS (1 << ESAI_xCCR_xPSR_SHIFT) +#define ESAI_xCCR_xPSR_DIV8 (0 << ESAI_xCCR_xPSR_SHIFT) +#define ESAI_xCCR_xPM_SHIFT 0 +#define ESAI_xCCR_xPM_WIDTH 8 +#define ESAI_xCCR_xPM_MASK (((1 << ESAI_xCCR_xPM_WIDTH) - 1) << ESAI_xCCR_xPM_SHIFT) +#define ESAI_xCCR_xPM(v) ((((v) - 1) << ESAI_xCCR_xPM_SHIFT) & ESAI_xCCR_xPM_MASK) + +/* Transmit Slot Mask Register A/B -- REG_ESAI_TSMA/B 0xE4 ~ 0xF0 */ +#define ESAI_xSMA_xS_SHIFT 0 +#define ESAI_xSMA_xS_WIDTH 16 +#define ESAI_xSMA_xS_MASK (((1 << ESAI_xSMA_xS_WIDTH) - 1) << ESAI_xSMA_xS_SHIFT) +#define ESAI_xSMA_xS(v) ((v) & ESAI_xSMA_xS_MASK) +#define ESAI_xSMB_xS_SHIFT 0 +#define ESAI_xSMB_xS_WIDTH 16 +#define ESAI_xSMB_xS_MASK (((1 << ESAI_xSMB_xS_WIDTH) - 1) << ESAI_xSMB_xS_SHIFT) +#define ESAI_xSMB_xS(v) (((v) >> ESAI_xSMA_xS_WIDTH) & ESAI_xSMA_xS_MASK) + +/* Port C Direction Register -- REG_ESAI_PRRC 0xF8 */ +#define ESAI_PRRC_PDC_SHIFT 0 +#define ESAI_PRRC_PDC_WIDTH 12 +#define ESAI_PRRC_PDC_MASK (((1 << ESAI_PRRC_PDC_WIDTH) - 1) << ESAI_PRRC_PDC_SHIFT) +#define ESAI_PRRC_PDC(v) ((v) & ESAI_PRRC_PDC_MASK) + +/* Port C Control Register -- REG_ESAI_PCRC 0xFC */ +#define ESAI_PCRC_PC_SHIFT 0 +#define ESAI_PCRC_PC_WIDTH 12 +#define ESAI_PCRC_PC_MASK (((1 << ESAI_PCRC_PC_WIDTH) - 1) << ESAI_PCRC_PC_SHIFT) +#define ESAI_PCRC_PC(v) ((v) & ESAI_PCRC_PC_MASK) + +#define ESAI_GPIO 0xfff + +/* ESAI clock source */ +#define ESAI_HCKT_FSYS 0 +#define ESAI_HCKT_EXTAL 1 +#define ESAI_HCKR_FSYS 2 +#define ESAI_HCKR_EXTAL 3 + +/* ESAI clock divider */ +#define ESAI_TX_DIV_PSR 0 +#define ESAI_TX_DIV_PM 1 +#define ESAI_TX_DIV_FP 2 +#define ESAI_RX_DIV_PSR 3 +#define ESAI_RX_DIV_PM 4 +#define ESAI_RX_DIV_FP 5 +#endif /* _FSL_ESAI_DAI_H */ -- cgit v1.2.3 From c892ecab0a7068c6d3ad0ba93c4b5e9bbbed1468 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Sat, 11 Jan 2014 14:48:31 +0100 Subject: ASoC: tlv320aic3x: Add tlv320aic32x4 as compatible Add tlv320aic32x4 to the compatible list in the binding documentation. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/tlv320aic3x.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt index 5e6040c2c2e9..9d8ea14db490 100644 --- a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt +++ b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt @@ -6,6 +6,7 @@ Required properties: - compatible - "string" - One of: "ti,tlv320aic3x" - Generic TLV320AIC3x device + "ti,tlv320aic32x4" - TLV320AIC32x4 "ti,tlv320aic33" - TLV320AIC33 "ti,tlv320aic3007" - TLV320AIC3007 "ti,tlv320aic3106" - TLV320AIC3106 -- cgit v1.2.3