brcm2708: add 3.14 support
[openwrt/svn-archive/archive.git] / target / linux / brcm2708 / patches-3.14 / 0049-Add-IQaudIO-Sound-Card-support-for-Raspberry-Pi.patch
diff --git a/target/linux/brcm2708/patches-3.14/0049-Add-IQaudIO-Sound-Card-support-for-Raspberry-Pi.patch b/target/linux/brcm2708/patches-3.14/0049-Add-IQaudIO-Sound-Card-support-for-Raspberry-Pi.patch
new file mode 100644 (file)
index 0000000..ee21862
--- /dev/null
@@ -0,0 +1,1094 @@
+From deaa651db204a6add9cb0189350922625fd1a311 Mon Sep 17 00:00:00 2001
+From: Gordon Garrity <gordon@iqaudio.com>
+Date: Sat, 8 Mar 2014 16:56:57 +0000
+Subject: [PATCH 49/54] Add IQaudIO Sound Card support for Raspberry Pi
+
+---
+ arch/arm/configs/bcmrpi_defconfig |   1 +
+ arch/arm/mach-bcm2708/bcm2708.c   |  22 ++
+ sound/soc/bcm/Kconfig             |   7 +
+ sound/soc/bcm/Makefile            |   2 +
+ sound/soc/bcm/iqaudio-dac.c       | 111 +++++++
+ sound/soc/codecs/Kconfig          |   4 +
+ sound/soc/codecs/Makefile         |   2 +
+ sound/soc/codecs/pcm512x.c        | 677 ++++++++++++++++++++++++++++++++++++++
+ sound/soc/codecs/pcm512x.h        | 142 ++++++++
+ 9 files changed, 968 insertions(+)
+ create mode 100644 sound/soc/bcm/iqaudio-dac.c
+ create mode 100644 sound/soc/codecs/pcm512x.c
+ create mode 100644 sound/soc/codecs/pcm512x.h
+
+diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig
+index 3d3c8ab..a024670 100644
+--- a/arch/arm/configs/bcmrpi_defconfig
++++ b/arch/arm/configs/bcmrpi_defconfig
+@@ -750,6 +750,7 @@ CONFIG_SND_BCM2708_SOC_RPI_DAC=m
+ CONFIG_SND_SOC_I2C_AND_SPI=m
+ CONFIG_SND_SOC_PCM5102A=m
+ CONFIG_SND_SOC_PCM1794A=m
++CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m
+ CONFIG_SOUND_PRIME=m
+ CONFIG_HIDRAW=y
+ CONFIG_HID_A4TECH=m
+diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c
+index beb2eca..95a47fa 100644
+--- a/arch/arm/mach-bcm2708/bcm2708.c
++++ b/arch/arm/mach-bcm2708/bcm2708.c
+@@ -680,6 +680,22 @@ static struct platform_device snd_pcm1794a_codec_device = {
+ };
+ #endif
++
++#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE)
++static struct platform_device snd_rpi_iqaudio_dac_device = {
++        .name = "snd-rpi-iqaudio-dac",
++        .id = 0,
++        .num_resources = 0,
++};
++
++// Use the actual device name rather than generic driver name
++static struct i2c_board_info __initdata snd_pcm512x_i2c_devices[] = {
++      {
++              I2C_BOARD_INFO("pcm5122", 0x4c)
++      },
++};
++#endif
++
+ int __init bcm_register_device(struct platform_device *pdev)
+ {
+       int ret;
+@@ -822,6 +838,12 @@ void __init bcm2708_init(void)
+         bcm_register_device(&snd_pcm1794a_codec_device);
+ #endif
++#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE)
++        bcm_register_device(&snd_rpi_iqaudio_dac_device);
++        i2c_register_board_info(1, snd_pcm512x_i2c_devices, ARRAY_SIZE(snd_pcm512x_i2c_devices));
++#endif
++
++
+       for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
+               struct amba_device *d = amba_devs[i];
+               amba_device_register(d, &iomem_resource);
+diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig
+index e563dbc..84e4f27 100644
+--- a/sound/soc/bcm/Kconfig
++++ b/sound/soc/bcm/Kconfig
+@@ -39,3 +39,10 @@ config SND_BCM2708_SOC_RPI_DAC
+         select SND_SOC_PCM1794A
+         help
+          Say Y or M if you want to add support for RPi-DAC.
++
++config SND_BCM2708_SOC_IQAUDIO_DAC
++      tristate "Support for IQaudIO-DAC"
++      depends on SND_BCM2708_SOC_I2S
++      select SND_SOC_PCM512x
++      help
++        Say Y or M if you want to add support for IQaudIO-DAC.
+diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile
+index 826df7d..d597fb0 100644
+--- a/sound/soc/bcm/Makefile
++++ b/sound/soc/bcm/Makefile
+@@ -12,7 +12,9 @@ obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o
+ snd-soc-hifiberry-dac-objs := hifiberry_dac.o
+ snd-soc-hifiberry-digi-objs := hifiberry_digi.o
+ snd-soc-rpi-dac-objs := rpi-dac.o
++snd-soc-iqaudio-dac-objs := iqaudio-dac.o
+ obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o
+ obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) += snd-soc-hifiberry-digi.o
+ obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o
++obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) += snd-soc-iqaudio-dac.o
+diff --git a/sound/soc/bcm/iqaudio-dac.c b/sound/soc/bcm/iqaudio-dac.c
+new file mode 100644
+index 0000000..515f044
+--- /dev/null
++++ b/sound/soc/bcm/iqaudio-dac.c
+@@ -0,0 +1,111 @@
++/*
++ * ASoC Driver for IQaudIO DAC
++ *
++ * Author:    Florian Meier <florian.meier@koalo.de>
++ *            Copyright 2013
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ */
++
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/jack.h>
++
++static int snd_rpi_iqaudio_dac_init(struct snd_soc_pcm_runtime *rtd)
++{
++// NOT USED   struct snd_soc_codec *codec = rtd->codec;
++
++      return 0;
++}
++
++static int snd_rpi_iqaudio_dac_hw_params(struct snd_pcm_substream *substream,
++                                     struct snd_pcm_hw_params *params)
++{
++      struct snd_soc_pcm_runtime *rtd = substream->private_data;
++// NOT USED   struct snd_soc_dai *codec_dai = rtd->codec_dai;
++// NOT USED   struct snd_soc_codec *codec = rtd->codec;
++      struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
++
++      unsigned int sample_bits =
++              snd_pcm_format_physical_width(params_format(params));
++
++      return snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2);
++}
++
++/* machine stream operations */
++static struct snd_soc_ops snd_rpi_iqaudio_dac_ops = {
++      .hw_params = snd_rpi_iqaudio_dac_hw_params,
++};
++
++static struct snd_soc_dai_link snd_rpi_iqaudio_dac_dai[] = {
++{
++      .name           = "IQaudIO DAC",
++      .stream_name    = "IQaudIO DAC HiFi",
++      .cpu_dai_name   = "bcm2708-i2s.0",
++      .codec_dai_name = "pcm512x-hifi",
++      .platform_name  = "bcm2708-i2s.0",
++      .codec_name     = "pcm512x.1-004c",
++      .dai_fmt        = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
++                              SND_SOC_DAIFMT_CBS_CFS,
++      .ops            = &snd_rpi_iqaudio_dac_ops,
++      .init           = snd_rpi_iqaudio_dac_init,
++},
++};
++
++/* audio machine driver */
++static struct snd_soc_card snd_rpi_iqaudio_dac = {
++      .name         = "snd_rpi_iqaudio_dac",
++      .dai_link     = snd_rpi_iqaudio_dac_dai,
++      .num_links    = ARRAY_SIZE(snd_rpi_iqaudio_dac_dai),
++};
++
++static int snd_rpi_iqaudio_dac_probe(struct platform_device *pdev)
++{
++      int ret = 0;
++
++      snd_rpi_iqaudio_dac.dev = &pdev->dev;
++      ret = snd_soc_register_card(&snd_rpi_iqaudio_dac);
++      if (ret)
++              dev_err(&pdev->dev,
++                      "snd_soc_register_card() failed: %d\n", ret);
++
++      return ret;
++}
++
++static int snd_rpi_iqaudio_dac_remove(struct platform_device *pdev)
++{
++      return snd_soc_unregister_card(&snd_rpi_iqaudio_dac);
++}
++
++static const struct of_device_id iqaudio_of_match[] = {
++      { .compatible = "iqaudio,iqaudio-dac", },
++      {},
++};
++
++static struct platform_driver snd_rpi_iqaudio_dac_driver = {
++      .driver = {
++              .name   = "snd-rpi-iqaudio-dac",
++              .owner  = THIS_MODULE,
++              .of_match_table = iqaudio_of_match,
++      },
++      .probe          = snd_rpi_iqaudio_dac_probe,
++      .remove         = snd_rpi_iqaudio_dac_remove,
++};
++
++module_platform_driver(snd_rpi_iqaudio_dac_driver);
++
++MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>");
++MODULE_DESCRIPTION("ASoC Driver for IQAudio DAC");
++MODULE_LICENSE("GPL v2");
+diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
+index 4d2569e..ac28805 100644
+--- a/sound/soc/codecs/Kconfig
++++ b/sound/soc/codecs/Kconfig
+@@ -61,6 +61,7 @@ config SND_SOC_ALL_CODECS
+       select SND_SOC_PCM3008
+       select SND_SOC_PCM1794A
+       select SND_SOC_PCM5102A
++      select SND_SOC_PCM512x if SND_SOC_I2C_AND_SPI
+       select SND_SOC_RT5631 if I2C
+       select SND_SOC_RT5640 if I2C
+       select SND_SOC_SGTL5000 if I2C
+@@ -321,6 +322,9 @@ config SND_SOC_PCM1794A
+ config SND_SOC_PCM5102A
+       tristate
++config SND_SOC_PCM512x
++       tristate
++
+ config SND_SOC_RT5631
+       tristate
+diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
+index 9b806a2..61461c1 100644
+--- a/sound/soc/codecs/Makefile
++++ b/sound/soc/codecs/Makefile
+@@ -48,6 +48,7 @@ snd-soc-pcm1792a-codec-objs := pcm1792a.o
+ snd-soc-pcm3008-objs := pcm3008.o
+ snd-soc-pcm1794a-objs := pcm1794a.o
+ snd-soc-pcm5102a-objs := pcm5102a.o
++snd-soc-pcm512x-objs := pcm512x.o
+ snd-soc-rt5631-objs := rt5631.o
+ snd-soc-rt5640-objs := rt5640.o
+ snd-soc-sgtl5000-objs := sgtl5000.o
+@@ -183,6 +184,7 @@ obj-$(CONFIG_SND_SOC_PCM1792A)     += snd-soc-pcm1792a-codec.o
+ obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
+ obj-$(CONFIG_SND_SOC_PCM1794A)        += snd-soc-pcm1794a.o
+ obj-$(CONFIG_SND_SOC_PCM5102A)        += snd-soc-pcm5102a.o
++obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
+ obj-$(CONFIG_SND_SOC_RT5631)  += snd-soc-rt5631.o
+ obj-$(CONFIG_SND_SOC_RT5640)  += snd-soc-rt5640.o
+ obj-$(CONFIG_SND_SOC_SGTL5000)  += snd-soc-sgtl5000.o
+diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c
+new file mode 100644
+index 0000000..55b6200
+--- /dev/null
++++ b/sound/soc/codecs/pcm512x.c
+@@ -0,0 +1,677 @@
++/*
++ * Driver for the PCM512x CODECs
++ *
++ * Author:    Mark Brown <broonie@linaro.org>
++ *            Copyright 2014 Linaro Ltd
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ */
++
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/clk.h>
++#include <linux/i2c.h>
++#include <linux/pm_runtime.h>
++#include <linux/regmap.h>
++#include <linux/regulator/consumer.h>
++#include <linux/spi/spi.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/tlv.h>
++
++#include "pcm512x.h"
++
++#define PCM512x_NUM_SUPPLIES 3
++static const char *pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = {
++      "AVDD",
++      "DVDD",
++      "CPVDD",
++};
++
++struct pcm512x_priv {
++      struct regmap *regmap;
++      struct clk *sclk;
++      struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES];
++      struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES];
++};
++
++/*
++ * We can't use the same notifier block for more than one supply and
++ * there's no way I can see to get from a callback to the caller
++ * except container_of().
++ */
++#define PCM512x_REGULATOR_EVENT(n) \
++static int pcm512x_regulator_event_##n(struct notifier_block *nb, \
++                                    unsigned long event, void *data)    \
++{ \
++      struct pcm512x_priv *pcm512x = container_of(nb, struct pcm512x_priv, \
++                                                  supply_nb[n]); \
++      if (event & REGULATOR_EVENT_DISABLE) { \
++              regcache_mark_dirty(pcm512x->regmap);   \
++              regcache_cache_only(pcm512x->regmap, true);     \
++      } \
++      return 0; \
++}
++
++PCM512x_REGULATOR_EVENT(0)
++PCM512x_REGULATOR_EVENT(1)
++PCM512x_REGULATOR_EVENT(2)
++
++static const struct reg_default pcm512x_reg_defaults[] = {
++      { PCM512x_RESET,            0x00 },
++      { PCM512x_POWER,            0x00 },
++      { PCM512x_MUTE,             0x00 },
++      { PCM512x_DSP,              0x00 },
++      { PCM512x_PLL_REF,          0x00 },
++      { PCM512x_DAC_ROUTING,      0x11 },
++      { PCM512x_DSP_PROGRAM,      0x01 },
++      { PCM512x_CLKDET,           0x00 },
++      { PCM512x_AUTO_MUTE,        0x00 },
++      { PCM512x_ERROR_DETECT,     0x00 },
++      { PCM512x_DIGITAL_VOLUME_1, 0x00 },
++      { PCM512x_DIGITAL_VOLUME_2, 0x30 },
++      { PCM512x_DIGITAL_VOLUME_3, 0x30 },
++      { PCM512x_DIGITAL_MUTE_1,   0x22 },
++      { PCM512x_DIGITAL_MUTE_2,   0x00 },
++      { PCM512x_DIGITAL_MUTE_3,   0x07 },
++};
++
++static bool pcm512x_readable(struct device *dev, unsigned int reg)
++{
++      switch (reg) {
++      case PCM512x_RESET:
++      case PCM512x_POWER:
++      case PCM512x_MUTE:
++      case PCM512x_PLL_EN:
++      case PCM512x_SPI_MISO_FUNCTION:
++      case PCM512x_DSP:
++      case PCM512x_GPIO_EN:
++      case PCM512x_BCLK_LRCLK_CFG:
++      case PCM512x_DSP_GPIO_INPUT:
++      case PCM512x_MASTER_MODE:
++      case PCM512x_PLL_REF:
++      case PCM512x_PLL_COEFF_0:
++      case PCM512x_PLL_COEFF_1:
++      case PCM512x_PLL_COEFF_2:
++      case PCM512x_PLL_COEFF_3:
++      case PCM512x_PLL_COEFF_4:
++      case PCM512x_DSP_CLKDIV:
++      case PCM512x_DAC_CLKDIV:
++      case PCM512x_NCP_CLKDIV:
++      case PCM512x_OSR_CLKDIV:
++      case PCM512x_MASTER_CLKDIV_1:
++      case PCM512x_MASTER_CLKDIV_2:
++      case PCM512x_FS_SPEED_MODE:
++      case PCM512x_IDAC_1:
++      case PCM512x_IDAC_2:
++      case PCM512x_ERROR_DETECT:
++      case PCM512x_I2S_1:
++      case PCM512x_I2S_2:
++      case PCM512x_DAC_ROUTING:
++      case PCM512x_DSP_PROGRAM:
++      case PCM512x_CLKDET:
++      case PCM512x_AUTO_MUTE:
++      case PCM512x_DIGITAL_VOLUME_1:
++      case PCM512x_DIGITAL_VOLUME_2:
++      case PCM512x_DIGITAL_VOLUME_3:
++      case PCM512x_DIGITAL_MUTE_1:
++      case PCM512x_DIGITAL_MUTE_2:
++      case PCM512x_DIGITAL_MUTE_3:
++      case PCM512x_GPIO_OUTPUT_1:
++      case PCM512x_GPIO_OUTPUT_2:
++      case PCM512x_GPIO_OUTPUT_3:
++      case PCM512x_GPIO_OUTPUT_4:
++      case PCM512x_GPIO_OUTPUT_5:
++      case PCM512x_GPIO_OUTPUT_6:
++      case PCM512x_GPIO_CONTROL_1:
++      case PCM512x_GPIO_CONTROL_2:
++      case PCM512x_OVERFLOW:
++      case PCM512x_RATE_DET_1:
++      case PCM512x_RATE_DET_2:
++      case PCM512x_RATE_DET_3:
++      case PCM512x_RATE_DET_4:
++      case PCM512x_ANALOG_MUTE_DET:
++      case PCM512x_GPIN:
++      case PCM512x_DIGITAL_MUTE_DET:
++              return true;
++      default:
++              return false;
++      }
++}
++
++static bool pcm512x_volatile(struct device *dev, unsigned int reg)
++{
++      switch (reg) {
++      case PCM512x_PLL_EN:
++      case PCM512x_OVERFLOW:
++      case PCM512x_RATE_DET_1:
++      case PCM512x_RATE_DET_2:
++      case PCM512x_RATE_DET_3:
++      case PCM512x_RATE_DET_4:
++      case PCM512x_ANALOG_MUTE_DET:
++      case PCM512x_GPIN:
++      case PCM512x_DIGITAL_MUTE_DET:
++              return true;
++      default:
++              return false;
++      }
++}
++
++static const DECLARE_TLV_DB_SCALE(digital_tlv, -10350, 50, 1);
++
++static const char *pcm512x_dsp_program_texts[] = {
++      "FIR interpolation with de-emphasis",
++      "Low latency IIR with de-emphasis",
++      "High attenuation with de-emphasis",
++      "Ringing-less low latency FIR",
++};
++
++static const unsigned int pcm512x_dsp_program_values[] = {
++      1,
++      2,
++      3,
++      5,
++      7,
++};
++
++static const SOC_VALUE_ENUM_SINGLE_DECL(pcm512x_dsp_program,
++                                      PCM512x_DSP_PROGRAM, 0, 0x1f,
++                                      pcm512x_dsp_program_texts,
++                                      pcm512x_dsp_program_values);
++
++static const char *pcm512x_clk_missing_text[] = {
++      "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s"
++};
++
++static const struct soc_enum pcm512x_clk_missing =
++      SOC_ENUM_SINGLE(PCM512x_CLKDET, 0,  7, pcm512x_clk_missing_text);
++
++static const char *pcm512x_autom_text[] = {
++      "21ms", "106ms", "213ms", "533ms", "1.07s", "2.13s", "5.33s", "10.66s"
++};
++
++static const struct soc_enum pcm512x_autom_l =
++      SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATML_SHIFT, 7,
++                      pcm512x_autom_text);
++
++static const struct soc_enum pcm512x_autom_r =
++      SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATMR_SHIFT, 7,
++                      pcm512x_autom_text);
++
++static const char *pcm512x_ramp_rate_text[] = {
++      "1 sample/update", "2 samples/update", "4 samples/update",
++      "Immediate"
++};
++
++static const struct soc_enum pcm512x_vndf =
++      SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDF_SHIFT, 4,
++                      pcm512x_ramp_rate_text);
++
++static const struct soc_enum pcm512x_vnuf =
++      SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUF_SHIFT, 4,
++                      pcm512x_ramp_rate_text);
++
++static const struct soc_enum pcm512x_vedf =
++      SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDF_SHIFT, 4,
++                      pcm512x_ramp_rate_text);
++
++static const char *pcm512x_ramp_step_text[] = {
++      "4dB/step", "2dB/step", "1dB/step", "0.5dB/step"
++};
++
++static const struct soc_enum pcm512x_vnds =
++      SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDS_SHIFT, 4,
++                      pcm512x_ramp_step_text);
++
++static const struct soc_enum pcm512x_vnus =
++      SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUS_SHIFT, 4,
++                      pcm512x_ramp_step_text);
++
++static const struct soc_enum pcm512x_veds =
++      SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDS_SHIFT, 4,
++                      pcm512x_ramp_step_text);
++
++static const struct snd_kcontrol_new pcm512x_controls[] = {
++SOC_DOUBLE_R_TLV("Playback Digital Volume", PCM512x_DIGITAL_VOLUME_2,
++               PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv),
++SOC_DOUBLE("Playback Digital Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT,
++         PCM512x_RQMR_SHIFT, 1, 1),
++
++SOC_SINGLE("Deemphasis Switch", PCM512x_DSP, PCM512x_DEMP_SHIFT, 1, 1),
++SOC_VALUE_ENUM("DSP Program", pcm512x_dsp_program),
++
++SOC_ENUM("Clock Missing Period", pcm512x_clk_missing),
++SOC_ENUM("Auto Mute Time Left", pcm512x_autom_l),
++SOC_ENUM("Auto Mute Time Right", pcm512x_autom_r),
++SOC_SINGLE("Auto Mute Mono Switch", PCM512x_DIGITAL_MUTE_3,
++         PCM512x_ACTL_SHIFT, 1, 0),
++SOC_DOUBLE("Auto Mute Switch", PCM512x_DIGITAL_MUTE_3, PCM512x_AMLE_SHIFT,
++         PCM512x_AMLR_SHIFT, 1, 0),
++
++SOC_ENUM("Volume Ramp Down Rate", pcm512x_vndf),
++SOC_ENUM("Volume Ramp Down Step", pcm512x_vnds),
++SOC_ENUM("Volume Ramp Up Rate", pcm512x_vnuf),
++SOC_ENUM("Volume Ramp Up Step", pcm512x_vnus),
++SOC_ENUM("Volume Ramp Down Emergency Rate", pcm512x_vedf),
++SOC_ENUM("Volume Ramp Down Emergency Step", pcm512x_veds),
++};
++
++static const struct snd_soc_dapm_widget pcm512x_dapm_widgets[] = {
++SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
++SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
++
++SND_SOC_DAPM_OUTPUT("OUTL"),
++SND_SOC_DAPM_OUTPUT("OUTR"),
++};
++
++static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = {
++      { "DACL", NULL, "Playback" },
++      { "DACR", NULL, "Playback" },
++
++      { "OUTL", NULL, "DACL" },
++      { "OUTR", NULL, "DACR" },
++};
++
++static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
++                                enum snd_soc_bias_level level)
++{
++      struct pcm512x_priv *pcm512x = dev_get_drvdata(codec->dev);
++      int ret;
++
++      switch (level) {
++      case SND_SOC_BIAS_ON:
++      case SND_SOC_BIAS_PREPARE:
++              break;
++
++      case SND_SOC_BIAS_STANDBY:
++              ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
++                                       PCM512x_RQST, 0);
++              if (ret != 0) {
++                      dev_err(codec->dev, "Failed to remove standby: %d\n",
++                              ret);
++                      return ret;
++              }
++              break;
++
++      case SND_SOC_BIAS_OFF:
++              ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
++                                       PCM512x_RQST, PCM512x_RQST);
++              if (ret != 0) {
++                      dev_err(codec->dev, "Failed to request standby: %d\n",
++                              ret);
++                      return ret;
++              }
++              break;
++      }
++
++      codec->dapm.bias_level = level;
++
++      return 0;
++}
++
++static struct snd_soc_dai_driver pcm512x_dai = {
++      .name = "pcm512x-hifi",
++      .playback = {
++              .stream_name = "Playback",
++              .channels_min = 2,
++              .channels_max = 2,
++              .rates = SNDRV_PCM_RATE_8000_192000,
++              .formats = SNDRV_PCM_FMTBIT_S16_LE |
++                         SNDRV_PCM_FMTBIT_S24_LE |
++                         SNDRV_PCM_FMTBIT_S32_LE
++      },
++};
++
++static struct snd_soc_codec_driver pcm512x_codec_driver = {
++      .set_bias_level = pcm512x_set_bias_level,
++      .idle_bias_off = true,
++
++      .controls = pcm512x_controls,
++      .num_controls = ARRAY_SIZE(pcm512x_controls),
++      .dapm_widgets = pcm512x_dapm_widgets,
++      .num_dapm_widgets = ARRAY_SIZE(pcm512x_dapm_widgets),
++      .dapm_routes = pcm512x_dapm_routes,
++      .num_dapm_routes = ARRAY_SIZE(pcm512x_dapm_routes),
++};
++
++static const struct regmap_config pcm512x_regmap = {
++      .reg_bits = 8,
++      .val_bits = 8,
++
++      .readable_reg = pcm512x_readable,
++      .volatile_reg = pcm512x_volatile,
++
++      .max_register = PCM512x_MAX_REGISTER,
++      .reg_defaults = pcm512x_reg_defaults,
++      .num_reg_defaults = ARRAY_SIZE(pcm512x_reg_defaults),
++      .cache_type = REGCACHE_RBTREE,
++};
++
++static const struct of_device_id pcm512x_of_match[] = {
++      { .compatible = "ti,pcm5121", },
++      { .compatible = "ti,pcm5122", },
++      { }
++};
++MODULE_DEVICE_TABLE(of, pcm512x_of_match);
++
++static int pcm512x_probe(struct device *dev, struct regmap *regmap)
++{
++      struct pcm512x_priv *pcm512x;
++      int i, ret;
++
++      pcm512x = devm_kzalloc(dev, sizeof(struct pcm512x_priv), GFP_KERNEL);
++      if (!pcm512x)
++              return -ENOMEM;
++
++      dev_set_drvdata(dev, pcm512x);
++      pcm512x->regmap = regmap;
++
++      for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++)
++              pcm512x->supplies[i].supply = pcm512x_supply_names[i];
++
++      ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(pcm512x->supplies),
++                                    pcm512x->supplies);
++      if (ret != 0) {
++              dev_err(dev, "Failed to get supplies: %d\n", ret);
++              return ret;
++      }
++
++      pcm512x->supply_nb[0].notifier_call = pcm512x_regulator_event_0;
++      pcm512x->supply_nb[1].notifier_call = pcm512x_regulator_event_1;
++      pcm512x->supply_nb[2].notifier_call = pcm512x_regulator_event_2;
++
++      for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) {
++              ret = regulator_register_notifier(pcm512x->supplies[i].consumer,
++                                                &pcm512x->supply_nb[i]);
++              if (ret != 0) {
++                      dev_err(dev,
++                              "Failed to register regulator notifier: %d\n",
++                              ret);
++              }
++      }
++
++      ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies),
++                                  pcm512x->supplies);
++      if (ret != 0) {
++              dev_err(dev, "Failed to enable supplies: %d\n", ret);
++              return ret;
++      }
++
++      /* Reset the device, verifying I/O in the process for I2C */
++      ret = regmap_write(regmap, PCM512x_RESET,
++                         PCM512x_RSTM | PCM512x_RSTR);
++      if (ret != 0) {
++              dev_err(dev, "Failed to reset device: %d\n", ret);
++              goto err;
++      }
++
++      ret = regmap_write(regmap, PCM512x_RESET, 0);
++      if (ret != 0) {
++              dev_err(dev, "Failed to reset device: %d\n", ret);
++              goto err;
++      }
++
++      pcm512x->sclk = devm_clk_get(dev, NULL);
++      if (IS_ERR(pcm512x->sclk)) {
++              if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER)
++                      return -EPROBE_DEFER;
++
++              dev_info(dev, "No SCLK, using BCLK: %ld\n",
++                       PTR_ERR(pcm512x->sclk));
++
++              /* Disable reporting of missing SCLK as an error */
++              regmap_update_bits(regmap, PCM512x_ERROR_DETECT,
++                                 PCM512x_IDCH, PCM512x_IDCH);
++
++              /* Switch PLL input to BCLK */
++              regmap_update_bits(regmap, PCM512x_PLL_REF,
++                                 PCM512x_SREF, PCM512x_SREF);
++      } else {
++              ret = clk_prepare_enable(pcm512x->sclk);
++              if (ret != 0) {
++                      dev_err(dev, "Failed to enable SCLK: %d\n", ret);
++                      return ret;
++              }
++      }
++
++      /* Default to standby mode */
++      ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
++                               PCM512x_RQST, PCM512x_RQST);
++      if (ret != 0) {
++              dev_err(dev, "Failed to request standby: %d\n",
++                      ret);
++              goto err_clk;
++      }
++
++      pm_runtime_set_active(dev);
++      pm_runtime_enable(dev);
++      pm_runtime_idle(dev);
++
++      ret = snd_soc_register_codec(dev, &pcm512x_codec_driver,
++                                  &pcm512x_dai, 1);
++      if (ret != 0) {
++              dev_err(dev, "Failed to register CODEC: %d\n", ret);
++              goto err_pm;
++      }
++
++      dev_info(dev, "Completed initialisation - pcm512x_probe");
++
++      return 0;
++
++err_pm:
++      pm_runtime_disable(dev);
++err_clk:
++      if (!IS_ERR(pcm512x->sclk))
++              clk_disable_unprepare(pcm512x->sclk);
++err:
++      regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
++                                   pcm512x->supplies);
++      return ret;
++}
++
++static void pcm512x_remove(struct device *dev)
++{
++      struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
++
++      snd_soc_unregister_codec(dev);
++      pm_runtime_disable(dev);
++      if (!IS_ERR(pcm512x->sclk))
++              clk_disable_unprepare(pcm512x->sclk);
++      regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
++                             pcm512x->supplies);
++}
++
++/* TODO
++static int pcm512x_suspend(struct device *dev)
++{
++      struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
++      int ret;
++
++      ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
++                               PCM512x_RQPD, PCM512x_RQPD);
++      if (ret != 0) {
++              dev_err(dev, "Failed to request power down: %d\n", ret);
++              return ret;
++      }
++
++      ret = regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
++                                   pcm512x->supplies);
++      if (ret != 0) {
++              dev_err(dev, "Failed to disable supplies: %d\n", ret);
++              return ret;
++      }
++
++      if (!IS_ERR(pcm512x->sclk))
++              clk_disable_unprepare(pcm512x->sclk);
++
++      return 0;
++}
++
++static int pcm512x_resume(struct device *dev)
++{
++      struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
++      int ret;
++
++      if (!IS_ERR(pcm512x->sclk)) {
++              ret = clk_prepare_enable(pcm512x->sclk);
++              if (ret != 0) {
++                      dev_err(dev, "Failed to enable SCLK: %d\n", ret);
++                      return ret;
++              }
++      }
++
++      ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies),
++                                  pcm512x->supplies);
++      if (ret != 0) {
++              dev_err(dev, "Failed to enable supplies: %d\n", ret);
++              return ret;
++      }
++
++      regcache_cache_only(pcm512x->regmap, false);
++      ret = regcache_sync(pcm512x->regmap);
++      if (ret != 0) {
++              dev_err(dev, "Failed to sync cache: %d\n", ret);
++              return ret;
++      }
++
++      ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
++                               PCM512x_RQPD, 0);
++      if (ret != 0) {
++              dev_err(dev, "Failed to remove power down: %d\n", ret);
++              return ret;
++      }
++
++      return 0;
++}
++
++// END OF PCM512x_suspend and resume calls TODO
++*/
++
++static const struct dev_pm_ops pcm512x_pm_ops = {
++      SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL)
++};
++
++#if IS_ENABLED(CONFIG_I2C)
++static int pcm512x_i2c_probe(struct i2c_client *i2c,
++                           const struct i2c_device_id *id)
++{
++      struct regmap *regmap;
++
++      regmap = devm_regmap_init_i2c(i2c, &pcm512x_regmap);
++      if (IS_ERR(regmap))
++              return PTR_ERR(regmap);
++
++      return pcm512x_probe(&i2c->dev, regmap);
++}
++
++static int pcm512x_i2c_remove(struct i2c_client *i2c)
++{
++      pcm512x_remove(&i2c->dev);
++      return 0;
++}
++
++static const struct i2c_device_id pcm512x_i2c_id[] = {
++      { "pcm5121", },
++      { "pcm5122", },
++      { }
++};
++MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id);
++
++static struct i2c_driver pcm512x_i2c_driver = {
++      .probe          = pcm512x_i2c_probe,
++      .remove         = pcm512x_i2c_remove,
++      .id_table       = pcm512x_i2c_id,
++      .driver         = {
++              .name   = "pcm512x",
++              .owner  = THIS_MODULE,
++              .of_match_table = pcm512x_of_match,
++              .pm     = &pcm512x_pm_ops,
++      },
++};
++#endif
++
++#if defined(CONFIG_SPI_MASTER)
++static int pcm512x_spi_probe(struct spi_device *spi)
++{
++      struct regmap *regmap;
++      int ret;
++
++      regmap = devm_regmap_init_spi(spi, &pcm512x_regmap);
++      if (IS_ERR(regmap)) {
++              ret = PTR_ERR(regmap);
++              return ret;
++      }
++
++      return pcm512x_probe(&spi->dev, regmap);
++}
++
++static int pcm512x_spi_remove(struct spi_device *spi)
++{
++      pcm512x_remove(&spi->dev);
++      return 0;
++}
++
++static const struct spi_device_id pcm512x_spi_id[] = {
++      { "pcm5121", },
++      { "pcm5122", },
++      { },
++};
++MODULE_DEVICE_TABLE(spi, pcm512x_spi_id);
++
++static struct spi_driver pcm512x_spi_driver = {
++      .probe          = pcm512x_spi_probe,
++      .remove         = pcm512x_spi_remove,
++      .id_table       = pcm512x_spi_id,
++      .driver = {
++              .name   = "pcm512x",
++              .owner  = THIS_MODULE,
++              .of_match_table = pcm512x_of_match,
++              .pm     = &pcm512x_pm_ops,
++      },
++};
++#endif
++
++static int __init pcm512x_modinit(void)
++{
++      int ret = 0;
++
++#if IS_ENABLED(CONFIG_I2C)
++      ret = i2c_add_driver(&pcm512x_i2c_driver);
++      if (ret) {
++              printk(KERN_ERR "Failed to register pcm512x I2C driver: %d\n",
++                     ret);
++      }
++#endif
++#if defined(CONFIG_SPI_MASTER)
++      ret = spi_register_driver(&pcm512x_spi_driver);
++      if (ret != 0) {
++              printk(KERN_ERR "Failed to register pcm512x SPI driver: %d\n",
++                     ret);
++      }
++#endif
++      return ret;
++}
++module_init(pcm512x_modinit);
++
++static void __exit pcm512x_exit(void)
++{
++#if IS_ENABLED(CONFIG_I2C)
++      i2c_del_driver(&pcm512x_i2c_driver);
++#endif
++#if defined(CONFIG_SPI_MASTER)
++      spi_unregister_driver(&pcm512x_spi_driver);
++#endif
++}
++module_exit(pcm512x_exit);
++
++MODULE_DESCRIPTION("ASoC PCM512x codec driver");
++MODULE_AUTHOR("Mark Brown <broonie@linaro.org>");
++MODULE_LICENSE("GPL v2");
+diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h
+new file mode 100644
+index 0000000..b2f518e
+--- /dev/null
++++ b/sound/soc/codecs/pcm512x.h
+@@ -0,0 +1,142 @@
++/*
++ * Driver for the PCM512x CODECs
++ *
++ * Author:    Mark Brown <broonie@linaro.org>
++ *            Copyright 2014 Linaro Ltd
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ */
++
++#ifndef _SND_SOC_PCM512X
++#define _SND_SOC_PCM512X
++
++#define PCM512x_PAGE_0_BASE 0
++
++#define PCM512x_PAGE              0
++
++#define PCM512x_RESET             (PCM512x_PAGE_0_BASE +   1)
++#define PCM512x_POWER             (PCM512x_PAGE_0_BASE +   2)
++#define PCM512x_MUTE              (PCM512x_PAGE_0_BASE +   3)
++#define PCM512x_PLL_EN            (PCM512x_PAGE_0_BASE +   4)
++#define PCM512x_SPI_MISO_FUNCTION (PCM512x_PAGE_0_BASE +   6)
++#define PCM512x_DSP               (PCM512x_PAGE_0_BASE +   7)
++#define PCM512x_GPIO_EN           (PCM512x_PAGE_0_BASE +   8)
++#define PCM512x_BCLK_LRCLK_CFG    (PCM512x_PAGE_0_BASE +   9)
++#define PCM512x_DSP_GPIO_INPUT    (PCM512x_PAGE_0_BASE +  10)
++#define PCM512x_MASTER_MODE       (PCM512x_PAGE_0_BASE +  12)
++#define PCM512x_PLL_REF           (PCM512x_PAGE_0_BASE +  13)
++#define PCM512x_PLL_COEFF_0       (PCM512x_PAGE_0_BASE +  20)
++#define PCM512x_PLL_COEFF_1       (PCM512x_PAGE_0_BASE +  21)
++#define PCM512x_PLL_COEFF_2       (PCM512x_PAGE_0_BASE +  22)
++#define PCM512x_PLL_COEFF_3       (PCM512x_PAGE_0_BASE +  23)
++#define PCM512x_PLL_COEFF_4       (PCM512x_PAGE_0_BASE +  24)
++#define PCM512x_DSP_CLKDIV        (PCM512x_PAGE_0_BASE +  27)
++#define PCM512x_DAC_CLKDIV        (PCM512x_PAGE_0_BASE +  28)
++#define PCM512x_NCP_CLKDIV        (PCM512x_PAGE_0_BASE +  29)
++#define PCM512x_OSR_CLKDIV        (PCM512x_PAGE_0_BASE +  30)
++#define PCM512x_MASTER_CLKDIV_1   (PCM512x_PAGE_0_BASE +  32)
++#define PCM512x_MASTER_CLKDIV_2   (PCM512x_PAGE_0_BASE +  33)
++#define PCM512x_FS_SPEED_MODE     (PCM512x_PAGE_0_BASE +  34)
++#define PCM512x_IDAC_1            (PCM512x_PAGE_0_BASE +  35)
++#define PCM512x_IDAC_2            (PCM512x_PAGE_0_BASE +  36)
++#define PCM512x_ERROR_DETECT      (PCM512x_PAGE_0_BASE +  37)
++#define PCM512x_I2S_1             (PCM512x_PAGE_0_BASE +  40)
++#define PCM512x_I2S_2             (PCM512x_PAGE_0_BASE +  41)
++#define PCM512x_DAC_ROUTING       (PCM512x_PAGE_0_BASE +  42)
++#define PCM512x_DSP_PROGRAM       (PCM512x_PAGE_0_BASE +  43)
++#define PCM512x_CLKDET            (PCM512x_PAGE_0_BASE +  44)
++#define PCM512x_AUTO_MUTE         (PCM512x_PAGE_0_BASE +  59)
++#define PCM512x_DIGITAL_VOLUME_1  (PCM512x_PAGE_0_BASE +  60)
++#define PCM512x_DIGITAL_VOLUME_2  (PCM512x_PAGE_0_BASE +  61)
++#define PCM512x_DIGITAL_VOLUME_3  (PCM512x_PAGE_0_BASE +  62)
++#define PCM512x_DIGITAL_MUTE_1    (PCM512x_PAGE_0_BASE +  63)
++#define PCM512x_DIGITAL_MUTE_2    (PCM512x_PAGE_0_BASE +  64)
++#define PCM512x_DIGITAL_MUTE_3    (PCM512x_PAGE_0_BASE +  65)
++#define PCM512x_GPIO_OUTPUT_1     (PCM512x_PAGE_0_BASE +  80)
++#define PCM512x_GPIO_OUTPUT_2     (PCM512x_PAGE_0_BASE +  81)
++#define PCM512x_GPIO_OUTPUT_3     (PCM512x_PAGE_0_BASE +  82)
++#define PCM512x_GPIO_OUTPUT_4     (PCM512x_PAGE_0_BASE +  83)
++#define PCM512x_GPIO_OUTPUT_5     (PCM512x_PAGE_0_BASE +  84)
++#define PCM512x_GPIO_OUTPUT_6     (PCM512x_PAGE_0_BASE +  85)
++#define PCM512x_GPIO_CONTROL_1    (PCM512x_PAGE_0_BASE +  86)
++#define PCM512x_GPIO_CONTROL_2    (PCM512x_PAGE_0_BASE +  87)
++#define PCM512x_OVERFLOW          (PCM512x_PAGE_0_BASE +  90)
++#define PCM512x_RATE_DET_1        (PCM512x_PAGE_0_BASE +  91)
++#define PCM512x_RATE_DET_2        (PCM512x_PAGE_0_BASE +  92)
++#define PCM512x_RATE_DET_3        (PCM512x_PAGE_0_BASE +  93)
++#define PCM512x_RATE_DET_4        (PCM512x_PAGE_0_BASE +  94)
++#define PCM512x_ANALOG_MUTE_DET   (PCM512x_PAGE_0_BASE + 108)
++#define PCM512x_GPIN              (PCM512x_PAGE_0_BASE + 119)
++#define PCM512x_DIGITAL_MUTE_DET  (PCM512x_PAGE_0_BASE + 120)
++
++#define PCM512x_MAX_REGISTER      (PCM512x_PAGE_0_BASE + 120)
++
++/* Page 0, Register 1 - reset */
++#define PCM512x_RSTR (1 << 0)
++#define PCM512x_RSTM (1 << 4)
++
++/* Page 0, Register 2 - power */
++#define PCM512x_RQPD       (1 << 0)
++#define PCM512x_RQPD_SHIFT 0
++#define PCM512x_RQST       (1 << 4)
++#define PCM512x_RQST_SHIFT 4
++
++/* Page 0, Register 3 - mute */
++#define PCM512x_RQMR_SHIFT 0
++#define PCM512x_RQML_SHIFT 4
++
++/* Page 0, Register 4 - PLL */
++#define PCM512x_PLCE       (1 << 0)
++#define PCM512x_RLCE_SHIFT 0
++#define PCM512x_PLCK       (1 << 4)
++#define PCM512x_PLCK_SHIFT 4
++
++/* Page 0, Register 7 - DSP */
++#define PCM512x_SDSL       (1 << 0)
++#define PCM512x_SDSL_SHIFT 0
++#define PCM512x_DEMP       (1 << 4)
++#define PCM512x_DEMP_SHIFT 4
++
++/* Page 0, Register 13 - PLL reference */
++#define PCM512x_SREF (1 << 4)
++
++/* Page 0, Register 37 - Error detection */
++#define PCM512x_IPLK (1 << 0)
++#define PCM512x_DCAS (1 << 1)
++#define PCM512x_IDCM (1 << 2)
++#define PCM512x_IDCH (1 << 3)
++#define PCM512x_IDSK (1 << 4)
++#define PCM512x_IDBK (1 << 5)
++#define PCM512x_IDFS (1 << 6)
++
++/* Page 0, Register 42 - DAC routing */
++#define PCM512x_AUPR_SHIFT 0
++#define PCM512x_AUPL_SHIFT 4
++
++/* Page 0, Register 59 - auto mute */
++#define PCM512x_ATMR_SHIFT 0
++#define PCM512x_ATML_SHIFT 4
++
++/* Page 0, Register 63 - ramp rates */
++#define PCM512x_VNDF_SHIFT 6
++#define PCM512x_VNDS_SHIFT 4
++#define PCM512x_VNUF_SHIFT 2
++#define PCM512x_VNUS_SHIFT 0
++
++/* Page 0, Register 64 - emergency ramp rates */
++#define PCM512x_VEDF_SHIFT 6
++#define PCM512x_VEDS_SHIFT 4
++
++/* Page 0, Register 65 - Digital mute enables */
++#define PCM512x_ACTL_SHIFT 2
++#define PCM512x_AMLE_SHIFT 1
++#define PCM512x_AMLR_SHIFT 0
++
++#endif
+-- 
+1.9.1
+