ASoC: pcm3168a: Add driver for pcm3168a codec
authorDamien.Horsley <Damien.Horsley@imgtec.com>
Tue, 8 Dec 2015 15:59:00 +0000 (15:59 +0000)
committerMark Brown <broonie@kernel.org>
Sat, 12 Dec 2015 22:39:59 +0000 (22:39 +0000)
Add driver for Texas Instruments pcm3168a codec

Signed-off-by: Damien.Horsley <Damien.Horsley@imgtec.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/pcm3168a-i2c.c [new file with mode: 0644]
sound/soc/codecs/pcm3168a-spi.c [new file with mode: 0644]
sound/soc/codecs/pcm3168a.c [new file with mode: 0644]
sound/soc/codecs/pcm3168a.h [new file with mode: 0644]

index cfdafc4c11ea9a64c465eef6d9ac4ed3520c159a..012bdcf6d175a571fab7d0251734b60729e0e888 100644 (file)
@@ -85,6 +85,8 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_PCM1681 if I2C
        select SND_SOC_PCM1792A if SPI_MASTER
        select SND_SOC_PCM3008
+       select SND_SOC_PCM3168A_I2C if I2C
+       select SND_SOC_PCM3168A_SPI if SPI_MASTER
        select SND_SOC_PCM512x_I2C if I2C
        select SND_SOC_PCM512x_SPI if SPI_MASTER
        select SND_SOC_RT286 if I2C
@@ -506,6 +508,21 @@ config SND_SOC_PCM1792A
 config SND_SOC_PCM3008
        tristate
 
+config SND_SOC_PCM3168A
+       tristate
+
+config SND_SOC_PCM3168A_I2C
+       tristate "Texas Instruments PCM3168A CODEC - I2C"
+       depends on I2C
+       select SND_SOC_PCM3168A
+       select REGMAP_I2C
+
+config SND_SOC_PCM3168A_SPI
+       tristate "Texas Instruments PCM3168A CODEC - SPI"
+       depends on SPI_MASTER
+       select SND_SOC_PCM3168A
+       select REGMAP_SPI
+
 config SND_SOC_PCM512x
        tristate
 
index f632fc42f59f08d9d6c72f6aac045d9fe28176a1..890ae5571a00948b6b4602206a6950664e6003a6 100644 (file)
@@ -78,6 +78,9 @@ snd-soc-nau8825-objs := nau8825.o
 snd-soc-pcm1681-objs := pcm1681.o
 snd-soc-pcm1792a-codec-objs := pcm1792a.o
 snd-soc-pcm3008-objs := pcm3008.o
+snd-soc-pcm3168a-objs := pcm3168a.o
+snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o
+snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o
 snd-soc-pcm512x-objs := pcm512x.o
 snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
 snd-soc-pcm512x-spi-objs := pcm512x-spi.o
@@ -273,6 +276,9 @@ obj-$(CONFIG_SND_SOC_NAU8825)   += snd-soc-nau8825.o
 obj-$(CONFIG_SND_SOC_PCM1681)  += snd-soc-pcm1681.o
 obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
 obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
+obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o
+obj-$(CONFIG_SND_SOC_PCM3168A_I2C)     += snd-soc-pcm3168a-i2c.o
+obj-$(CONFIG_SND_SOC_PCM3168A_SPI)     += snd-soc-pcm3168a-spi.o
 obj-$(CONFIG_SND_SOC_PCM512x)  += snd-soc-pcm512x.o
 obj-$(CONFIG_SND_SOC_PCM512x_I2C)      += snd-soc-pcm512x-i2c.o
 obj-$(CONFIG_SND_SOC_PCM512x_SPI)      += snd-soc-pcm512x-spi.o
diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c
new file mode 100644 (file)
index 0000000..6feb090
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * PCM3168A codec i2c driver
+ *
+ * Copyright (C) 2015 Imagination Technologies Ltd.
+ *
+ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
+ *
+ * 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.
+ */
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <sound/soc.h>
+
+#include "pcm3168a.h"
+
+static int pcm3168a_i2c_probe(struct i2c_client *i2c,
+                            const struct i2c_device_id *id)
+{
+       struct regmap *regmap;
+
+       regmap = devm_regmap_init_i2c(i2c, &pcm3168a_regmap);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       return pcm3168a_probe(&i2c->dev, regmap);
+}
+
+static int pcm3168a_i2c_remove(struct i2c_client *i2c)
+{
+       pcm3168a_remove(&i2c->dev);
+
+       return 0;
+}
+
+static const struct i2c_device_id pcm3168a_i2c_id[] = {
+       { "pcm3168a", },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, pcm3168a_i2c_id);
+
+static const struct of_device_id pcm3168a_of_match[] = {
+       { .compatible = "ti,pcm3168a", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, pcm3168a_of_match);
+
+static struct i2c_driver pcm3168a_i2c_driver = {
+       .probe          = pcm3168a_i2c_probe,
+       .remove         = pcm3168a_i2c_remove,
+       .id_table       = pcm3168a_i2c_id,
+       .driver         = {
+               .name   = "pcm3168a",
+               .of_match_table = pcm3168a_of_match,
+               .pm             = &pcm3168a_pm_ops,
+       },
+};
+module_i2c_driver(pcm3168a_i2c_driver);
+
+MODULE_DESCRIPTION("PCM3168A I2C codec driver");
+MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3168a-spi.c b/sound/soc/codecs/pcm3168a-spi.c
new file mode 100644 (file)
index 0000000..03945a2
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * PCM3168A codec spi driver
+ *
+ * Copyright (C) 2015 Imagination Technologies Ltd.
+ *
+ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include <sound/soc.h>
+
+#include "pcm3168a.h"
+
+static int pcm3168a_spi_probe(struct spi_device *spi)
+{
+       struct regmap *regmap;
+
+       regmap = devm_regmap_init_spi(spi, &pcm3168a_regmap);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       return pcm3168a_probe(&spi->dev, regmap);
+}
+
+static int pcm3168a_spi_remove(struct spi_device *spi)
+{
+       pcm3168a_remove(&spi->dev);
+
+       return 0;
+}
+
+static const struct spi_device_id pcm3168a_spi_id[] = {
+       { "pcm3168a", },
+       { },
+};
+MODULE_DEVICE_TABLE(spi, pcm3168a_spi_id);
+
+static const struct of_device_id pcm3168a_of_match[] = {
+       { .compatible = "ti,pcm3168a", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, pcm3168a_of_match);
+
+static struct spi_driver pcm3168a_spi_driver = {
+       .probe          = pcm3168a_spi_probe,
+       .remove         = pcm3168a_spi_remove,
+       .id_table       = pcm3168a_spi_id,
+       .driver = {
+               .name   = "pcm3168a",
+               .of_match_table = pcm3168a_of_match,
+               .pm             = &pcm3168a_pm_ops,
+       },
+};
+module_spi_driver(pcm3168a_spi_driver);
+
+MODULE_DESCRIPTION("PCM3168A SPI codec driver");
+MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
new file mode 100644 (file)
index 0000000..44b268a
--- /dev/null
@@ -0,0 +1,767 @@
+/*
+ * PCM3168A codec driver
+ *
+ * Copyright (C) 2015 Imagination Technologies Ltd.
+ *
+ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "pcm3168a.h"
+
+#define PCM3168A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+                        SNDRV_PCM_FMTBIT_S24_3LE | \
+                        SNDRV_PCM_FMTBIT_S24_LE | \
+                        SNDRV_PCM_FMTBIT_S32_LE)
+
+#define PCM3168A_FMT_I2S               0x0
+#define PCM3168A_FMT_LEFT_J            0x1
+#define PCM3168A_FMT_RIGHT_J           0x2
+#define PCM3168A_FMT_RIGHT_J_16                0x3
+#define PCM3168A_FMT_DSP_A             0x4
+#define PCM3168A_FMT_DSP_B             0x5
+#define PCM3168A_FMT_DSP_MASK          0x4
+
+#define PCM3168A_NUM_SUPPLIES 6
+static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = {
+       "VDD1",
+       "VDD2",
+       "VCCAD1",
+       "VCCAD2",
+       "VCCDA1",
+       "VCCDA2"
+};
+
+struct pcm3168a_priv {
+       struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES];
+       struct regmap *regmap;
+       struct clk *scki;
+       bool adc_master_mode;
+       bool dac_master_mode;
+       unsigned long sysclk;
+       unsigned int adc_fmt;
+       unsigned int dac_fmt;
+};
+
+static const char *const pcm3168a_roll_off[] = { "Sharp", "Slow" };
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_d1_roll_off, PCM3168A_DAC_OP_FLT,
+               PCM3168A_DAC_FLT_SHIFT, pcm3168a_roll_off);
+static SOC_ENUM_SINGLE_DECL(pcm3168a_d2_roll_off, PCM3168A_DAC_OP_FLT,
+               PCM3168A_DAC_FLT_SHIFT + 1, pcm3168a_roll_off);
+static SOC_ENUM_SINGLE_DECL(pcm3168a_d3_roll_off, PCM3168A_DAC_OP_FLT,
+               PCM3168A_DAC_FLT_SHIFT + 2, pcm3168a_roll_off);
+static SOC_ENUM_SINGLE_DECL(pcm3168a_d4_roll_off, PCM3168A_DAC_OP_FLT,
+               PCM3168A_DAC_FLT_SHIFT + 3, pcm3168a_roll_off);
+
+static const char *const pcm3168a_volume_type[] = {
+               "Individual", "Master + Individual" };
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_volume_type, PCM3168A_DAC_ATT_DEMP_ZF,
+               PCM3168A_DAC_ATMDDA_SHIFT, pcm3168a_volume_type);
+
+static const char *const pcm3168a_att_speed_mult[] = { "2048", "4096" };
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_att_mult, PCM3168A_DAC_ATT_DEMP_ZF,
+               PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_att_speed_mult);
+
+static const char *const pcm3168a_demp[] = {
+               "Disabled", "48khz", "44.1khz", "32khz" };
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_demp, PCM3168A_DAC_ATT_DEMP_ZF,
+               PCM3168A_DAC_DEMP_SHIFT, pcm3168a_demp);
+
+static const char *const pcm3168a_zf_func[] = {
+               "DAC 1/2/3/4 AND", "DAC 1/2/3/4 OR", "DAC 1/2/3 AND",
+               "DAC 1/2/3 OR", "DAC 4 AND", "DAC 4 OR" };
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_func, PCM3168A_DAC_ATT_DEMP_ZF,
+               PCM3168A_DAC_AZRO_SHIFT, pcm3168a_zf_func);
+
+static const char *const pcm3168a_pol[] = { "Active High", "Active Low" };
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_pol, PCM3168A_DAC_ATT_DEMP_ZF,
+               PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_pol);
+
+static const char *const pcm3168a_con[] = { "Differential", "Single-Ended" };
+
+static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc1_con, PCM3168A_ADC_SEAD,
+                               0, 1, pcm3168a_con);
+static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc2_con, PCM3168A_ADC_SEAD,
+                               2, 3, pcm3168a_con);
+static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc3_con, PCM3168A_ADC_SEAD,
+                               4, 5, pcm3168a_con);
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_volume_type, PCM3168A_ADC_ATT_OVF,
+               PCM3168A_ADC_ATMDAD_SHIFT, pcm3168a_volume_type);
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_att_mult, PCM3168A_ADC_ATT_OVF,
+               PCM3168A_ADC_ATSPAD_SHIFT, pcm3168a_att_speed_mult);
+
+static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_ov_pol, PCM3168A_ADC_ATT_OVF,
+               PCM3168A_ADC_OVFP_SHIFT, pcm3168a_pol);
+
+/* -100db to 0db, register values 0-54 cause mute */
+static const DECLARE_TLV_DB_SCALE(pcm3168a_dac_tlv, -10050, 50, 1);
+
+/* -100db to 20db, register values 0-14 cause mute */
+static const DECLARE_TLV_DB_SCALE(pcm3168a_adc_tlv, -10050, 50, 1);
+
+static const struct snd_kcontrol_new pcm3168a_snd_controls[] = {
+       SOC_SINGLE("DAC Power-Save Switch", PCM3168A_DAC_PWR_MST_FMT,
+                       PCM3168A_DAC_PSMDA_SHIFT, 1, 1),
+       SOC_ENUM("DAC1 Digital Filter roll-off", pcm3168a_d1_roll_off),
+       SOC_ENUM("DAC2 Digital Filter roll-off", pcm3168a_d2_roll_off),
+       SOC_ENUM("DAC3 Digital Filter roll-off", pcm3168a_d3_roll_off),
+       SOC_ENUM("DAC4 Digital Filter roll-off", pcm3168a_d4_roll_off),
+       SOC_DOUBLE("DAC1 Invert Switch", PCM3168A_DAC_INV, 0, 1, 1, 0),
+       SOC_DOUBLE("DAC2 Invert Switch", PCM3168A_DAC_INV, 2, 3, 1, 0),
+       SOC_DOUBLE("DAC3 Invert Switch", PCM3168A_DAC_INV, 4, 5, 1, 0),
+       SOC_DOUBLE("DAC4 Invert Switch", PCM3168A_DAC_INV, 6, 7, 1, 0),
+       SOC_DOUBLE_STS("DAC1 Zero Flag", PCM3168A_DAC_ZERO, 0, 1, 1, 0),
+       SOC_DOUBLE_STS("DAC2 Zero Flag", PCM3168A_DAC_ZERO, 2, 3, 1, 0),
+       SOC_DOUBLE_STS("DAC3 Zero Flag", PCM3168A_DAC_ZERO, 4, 5, 1, 0),
+       SOC_DOUBLE_STS("DAC4 Zero Flag", PCM3168A_DAC_ZERO, 6, 7, 1, 0),
+       SOC_ENUM("DAC Volume Control Type", pcm3168a_dac_volume_type),
+       SOC_ENUM("DAC Volume Rate Multiplier", pcm3168a_dac_att_mult),
+       SOC_ENUM("DAC De-Emphasis", pcm3168a_dac_demp),
+       SOC_ENUM("DAC Zero Flag Function", pcm3168a_dac_zf_func),
+       SOC_ENUM("DAC Zero Flag Polarity", pcm3168a_dac_zf_pol),
+       SOC_SINGLE_RANGE_TLV("Master Playback Volume",
+                       PCM3168A_DAC_VOL_MASTER, 0, 54, 255, 0,
+                       pcm3168a_dac_tlv),
+       SOC_DOUBLE_R_RANGE_TLV("DAC1 Playback Volume",
+                       PCM3168A_DAC_VOL_CHAN_START,
+                       PCM3168A_DAC_VOL_CHAN_START + 1,
+                       0, 54, 255, 0, pcm3168a_dac_tlv),
+       SOC_DOUBLE_R_RANGE_TLV("DAC2 Playback Volume",
+                       PCM3168A_DAC_VOL_CHAN_START + 2,
+                       PCM3168A_DAC_VOL_CHAN_START + 3,
+                       0, 54, 255, 0, pcm3168a_dac_tlv),
+       SOC_DOUBLE_R_RANGE_TLV("DAC3 Playback Volume",
+                       PCM3168A_DAC_VOL_CHAN_START + 4,
+                       PCM3168A_DAC_VOL_CHAN_START + 5,
+                       0, 54, 255, 0, pcm3168a_dac_tlv),
+       SOC_DOUBLE_R_RANGE_TLV("DAC4 Playback Volume",
+                       PCM3168A_DAC_VOL_CHAN_START + 6,
+                       PCM3168A_DAC_VOL_CHAN_START + 7,
+                       0, 54, 255, 0, pcm3168a_dac_tlv),
+       SOC_SINGLE("ADC1 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
+                       PCM3168A_ADC_BYP_SHIFT, 1, 1),
+       SOC_SINGLE("ADC2 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
+                       PCM3168A_ADC_BYP_SHIFT + 1, 1, 1),
+       SOC_SINGLE("ADC3 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
+                       PCM3168A_ADC_BYP_SHIFT + 2, 1, 1),
+       SOC_ENUM("ADC1 Connection Type", pcm3168a_adc1_con),
+       SOC_ENUM("ADC2 Connection Type", pcm3168a_adc2_con),
+       SOC_ENUM("ADC3 Connection Type", pcm3168a_adc3_con),
+       SOC_DOUBLE("ADC1 Invert Switch", PCM3168A_ADC_INV, 0, 1, 1, 0),
+       SOC_DOUBLE("ADC2 Invert Switch", PCM3168A_ADC_INV, 2, 3, 1, 0),
+       SOC_DOUBLE("ADC3 Invert Switch", PCM3168A_ADC_INV, 4, 5, 1, 0),
+       SOC_DOUBLE("ADC1 Mute Switch", PCM3168A_ADC_MUTE, 0, 1, 1, 0),
+       SOC_DOUBLE("ADC2 Mute Switch", PCM3168A_ADC_MUTE, 2, 3, 1, 0),
+       SOC_DOUBLE("ADC3 Mute Switch", PCM3168A_ADC_MUTE, 4, 5, 1, 0),
+       SOC_DOUBLE_STS("ADC1 Overflow Flag", PCM3168A_ADC_OV, 0, 1, 1, 0),
+       SOC_DOUBLE_STS("ADC2 Overflow Flag", PCM3168A_ADC_OV, 2, 3, 1, 0),
+       SOC_DOUBLE_STS("ADC3 Overflow Flag", PCM3168A_ADC_OV, 4, 5, 1, 0),
+       SOC_ENUM("ADC Volume Control Type", pcm3168a_adc_volume_type),
+       SOC_ENUM("ADC Volume Rate Multiplier", pcm3168a_adc_att_mult),
+       SOC_ENUM("ADC Overflow Flag Polarity", pcm3168a_adc_ov_pol),
+       SOC_SINGLE_RANGE_TLV("Master Capture Volume",
+                       PCM3168A_ADC_VOL_MASTER, 0, 14, 255, 0,
+                       pcm3168a_adc_tlv),
+       SOC_DOUBLE_R_RANGE_TLV("ADC1 Capture Volume",
+                       PCM3168A_ADC_VOL_CHAN_START,
+                       PCM3168A_ADC_VOL_CHAN_START + 1,
+                       0, 14, 255, 0, pcm3168a_adc_tlv),
+       SOC_DOUBLE_R_RANGE_TLV("ADC2 Capture Volume",
+                       PCM3168A_ADC_VOL_CHAN_START + 2,
+                       PCM3168A_ADC_VOL_CHAN_START + 3,
+                       0, 14, 255, 0, pcm3168a_adc_tlv),
+       SOC_DOUBLE_R_RANGE_TLV("ADC3 Capture Volume",
+                       PCM3168A_ADC_VOL_CHAN_START + 4,
+                       PCM3168A_ADC_VOL_CHAN_START + 5,
+                       0, 14, 255, 0, pcm3168a_adc_tlv)
+};
+
+static const struct snd_soc_dapm_widget pcm3168a_dapm_widgets[] = {
+       SND_SOC_DAPM_DAC("DAC1", "Playback", PCM3168A_DAC_OP_FLT,
+                       PCM3168A_DAC_OPEDA_SHIFT, 1),
+       SND_SOC_DAPM_DAC("DAC2", "Playback", PCM3168A_DAC_OP_FLT,
+                       PCM3168A_DAC_OPEDA_SHIFT + 1, 1),
+       SND_SOC_DAPM_DAC("DAC3", "Playback", PCM3168A_DAC_OP_FLT,
+                       PCM3168A_DAC_OPEDA_SHIFT + 2, 1),
+       SND_SOC_DAPM_DAC("DAC4", "Playback", PCM3168A_DAC_OP_FLT,
+                       PCM3168A_DAC_OPEDA_SHIFT + 3, 1),
+
+       SND_SOC_DAPM_OUTPUT("AOUT1L"),
+       SND_SOC_DAPM_OUTPUT("AOUT1R"),
+       SND_SOC_DAPM_OUTPUT("AOUT2L"),
+       SND_SOC_DAPM_OUTPUT("AOUT2R"),
+       SND_SOC_DAPM_OUTPUT("AOUT3L"),
+       SND_SOC_DAPM_OUTPUT("AOUT3R"),
+       SND_SOC_DAPM_OUTPUT("AOUT4L"),
+       SND_SOC_DAPM_OUTPUT("AOUT4R"),
+
+       SND_SOC_DAPM_ADC("ADC1", "Capture", PCM3168A_ADC_PWR_HPFB,
+                       PCM3168A_ADC_PSVAD_SHIFT, 1),
+       SND_SOC_DAPM_ADC("ADC2", "Capture", PCM3168A_ADC_PWR_HPFB,
+                       PCM3168A_ADC_PSVAD_SHIFT + 1, 1),
+       SND_SOC_DAPM_ADC("ADC3", "Capture", PCM3168A_ADC_PWR_HPFB,
+                       PCM3168A_ADC_PSVAD_SHIFT + 2, 1),
+
+       SND_SOC_DAPM_INPUT("AIN1L"),
+       SND_SOC_DAPM_INPUT("AIN1R"),
+       SND_SOC_DAPM_INPUT("AIN2L"),
+       SND_SOC_DAPM_INPUT("AIN2R"),
+       SND_SOC_DAPM_INPUT("AIN3L"),
+       SND_SOC_DAPM_INPUT("AIN3R")
+};
+
+static const struct snd_soc_dapm_route pcm3168a_dapm_routes[] = {
+       /* Playback */
+       { "AOUT1L", NULL, "DAC1" },
+       { "AOUT1R", NULL, "DAC1" },
+
+       { "AOUT2L", NULL, "DAC2" },
+       { "AOUT2R", NULL, "DAC2" },
+
+       { "AOUT3L", NULL, "DAC3" },
+       { "AOUT3R", NULL, "DAC3" },
+
+       { "AOUT4L", NULL, "DAC4" },
+       { "AOUT4R", NULL, "DAC4" },
+
+       /* Capture */
+       { "ADC1", NULL, "AIN1L" },
+       { "ADC1", NULL, "AIN1R" },
+
+       { "ADC2", NULL, "AIN2L" },
+       { "ADC2", NULL, "AIN2R" },
+
+       { "ADC3", NULL, "AIN3L" },
+       { "ADC3", NULL, "AIN3R" }
+};
+
+static unsigned int pcm3168a_scki_ratios[] = {
+       768,
+       512,
+       384,
+       256,
+       192,
+       128
+};
+
+#define PCM3168A_NUM_SCKI_RATIOS_DAC   ARRAY_SIZE(pcm3168a_scki_ratios)
+#define PCM3168A_NUM_SCKI_RATIOS_ADC   (ARRAY_SIZE(pcm3168a_scki_ratios) - 2)
+
+#define PCM1368A_MAX_SYSCLK            36864000
+
+static int pcm3168a_reset(struct pcm3168a_priv *pcm3168a)
+{
+       int ret;
+
+       ret = regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE, 0);
+       if (ret)
+               return ret;
+
+       /* Internal reset is de-asserted after 3846 SCKI cycles */
+       msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk));
+
+       return regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE,
+                       PCM3168A_MRST_MASK | PCM3168A_SRST_MASK);
+}
+
+static int pcm3168a_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
+
+       regmap_write(pcm3168a->regmap, PCM3168A_DAC_MUTE, mute ? 0xff : 0);
+
+       return 0;
+}
+
+static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai,
+                                 int clk_id, unsigned int freq, int dir)
+{
+       struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(dai->codec);
+
+       if (freq > PCM1368A_MAX_SYSCLK)
+               return -EINVAL;
+
+       pcm3168a->sysclk = freq;
+
+       return 0;
+}
+
+static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
+                              unsigned int format, bool dac)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
+       u32 fmt, reg, mask, shift;
+       bool master_mode;
+
+       switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_LEFT_J:
+               fmt = PCM3168A_FMT_LEFT_J;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               fmt = PCM3168A_FMT_I2S;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               fmt = PCM3168A_FMT_RIGHT_J;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               fmt = PCM3168A_FMT_DSP_A;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               fmt = PCM3168A_FMT_DSP_B;
+               break;
+       default:
+               dev_err(codec->dev, "unsupported dai format\n");
+               return -EINVAL;
+       }
+
+       switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               master_mode = false;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               master_mode = true;
+               break;
+       default:
+               dev_err(codec->dev, "unsupported master/slave mode\n");
+               return -EINVAL;
+       }
+
+       switch (format & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (dac) {
+               reg = PCM3168A_DAC_PWR_MST_FMT;
+               mask = PCM3168A_DAC_FMT_MASK;
+               shift = PCM3168A_DAC_FMT_SHIFT;
+               pcm3168a->dac_master_mode = master_mode;
+               pcm3168a->dac_fmt = fmt;
+       } else {
+               reg = PCM3168A_ADC_MST_FMT;
+               mask = PCM3168A_ADC_FMTAD_MASK;
+               shift = PCM3168A_ADC_FMTAD_SHIFT;
+               pcm3168a->adc_master_mode = master_mode;
+               pcm3168a->adc_fmt = fmt;
+       }
+
+       regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
+
+       return 0;
+}
+
+static int pcm3168a_set_dai_fmt_dac(struct snd_soc_dai *dai,
+                              unsigned int format)
+{
+       return pcm3168a_set_dai_fmt(dai, format, true);
+}
+
+static int pcm3168a_set_dai_fmt_adc(struct snd_soc_dai *dai,
+                              unsigned int format)
+{
+       return pcm3168a_set_dai_fmt(dai, format, false);
+}
+
+static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
+                            struct snd_pcm_hw_params *params,
+                            struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
+       bool tx, master_mode;
+       u32 val, mask, shift, reg;
+       unsigned int rate, channels, fmt, ratio, max_ratio;
+       int i, min_frame_size;
+       snd_pcm_format_t format;
+
+       rate = params_rate(params);
+       format = params_format(params);
+       channels = params_channels(params);
+
+       ratio = pcm3168a->sysclk / rate;
+
+       tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+       if (tx) {
+               max_ratio = PCM3168A_NUM_SCKI_RATIOS_DAC;
+               reg = PCM3168A_DAC_PWR_MST_FMT;
+               mask = PCM3168A_DAC_MSDA_MASK;
+               shift = PCM3168A_DAC_MSDA_SHIFT;
+               master_mode = pcm3168a->dac_master_mode;
+               fmt = pcm3168a->dac_fmt;
+       } else {
+               max_ratio = PCM3168A_NUM_SCKI_RATIOS_ADC;
+               reg = PCM3168A_ADC_MST_FMT;
+               mask = PCM3168A_ADC_MSAD_MASK;
+               shift = PCM3168A_ADC_MSAD_SHIFT;
+               master_mode = pcm3168a->adc_master_mode;
+               fmt = pcm3168a->adc_fmt;
+       }
+
+       for (i = 0; i < max_ratio; i++) {
+               if (pcm3168a_scki_ratios[i] == ratio)
+                       break;
+       }
+
+       if (i == max_ratio) {
+               dev_err(codec->dev, "unsupported sysclk ratio\n");
+               return -EINVAL;
+       }
+
+       min_frame_size = params_width(params) * 2;
+       switch (min_frame_size) {
+       case 32:
+               if (master_mode || (fmt != PCM3168A_FMT_RIGHT_J)) {
+                       dev_err(codec->dev, "32-bit frames are supported only for slave mode using right justified\n");
+                       return -EINVAL;
+               }
+               fmt = PCM3168A_FMT_RIGHT_J_16;
+               break;
+       case 48:
+               if (master_mode || (fmt & PCM3168A_FMT_DSP_MASK)) {
+                       dev_err(codec->dev, "48-bit frames not supported in master mode, or slave mode using DSP\n");
+                       return -EINVAL;
+               }
+               break;
+       case 64:
+               break;
+       default:
+               dev_err(codec->dev, "unsupported frame size: %d\n", min_frame_size);
+               return -EINVAL;
+       }
+
+       if (master_mode)
+               val = ((i + 1) << shift);
+       else
+               val = 0;
+
+       regmap_update_bits(pcm3168a->regmap, reg, mask, val);
+
+       if (tx) {
+               mask = PCM3168A_DAC_FMT_MASK;
+               shift = PCM3168A_DAC_FMT_SHIFT;
+       } else {
+               mask = PCM3168A_ADC_FMTAD_MASK;
+               shift = PCM3168A_ADC_FMTAD_SHIFT;
+       }
+
+       regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = {
+       .set_fmt        = pcm3168a_set_dai_fmt_dac,
+       .set_sysclk     = pcm3168a_set_dai_sysclk,
+       .hw_params      = pcm3168a_hw_params,
+       .digital_mute   = pcm3168a_digital_mute
+};
+
+static const struct snd_soc_dai_ops pcm3168a_adc_dai_ops = {
+       .set_fmt        = pcm3168a_set_dai_fmt_adc,
+       .set_sysclk     = pcm3168a_set_dai_sysclk,
+       .hw_params      = pcm3168a_hw_params
+};
+
+static struct snd_soc_dai_driver pcm3168a_dais[] = {
+       {
+               .name = "pcm3168a-dac",
+               .playback = {
+                       .stream_name = "Playback",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = SNDRV_PCM_RATE_8000_192000,
+                       .formats = PCM3168A_FORMATS
+               },
+               .ops = &pcm3168a_dac_dai_ops
+       },
+       {
+               .name = "pcm3168a-adc",
+               .capture = {
+                       .stream_name = "Capture",
+                       .channels_min = 1,
+                       .channels_max = 6,
+                       .rates = SNDRV_PCM_RATE_8000_96000,
+                       .formats = PCM3168A_FORMATS
+               },
+               .ops = &pcm3168a_adc_dai_ops
+       },
+};
+
+static const struct reg_default pcm3168a_reg_default[] = {
+       { PCM3168A_RST_SMODE, PCM3168A_MRST_MASK | PCM3168A_SRST_MASK },
+       { PCM3168A_DAC_PWR_MST_FMT, 0x00 },
+       { PCM3168A_DAC_OP_FLT, 0x00 },
+       { PCM3168A_DAC_INV, 0x00 },
+       { PCM3168A_DAC_MUTE, 0x00 },
+       { PCM3168A_DAC_ZERO, 0x00 },
+       { PCM3168A_DAC_ATT_DEMP_ZF, 0x00 },
+       { PCM3168A_DAC_VOL_MASTER, 0xff },
+       { PCM3168A_DAC_VOL_CHAN_START, 0xff },
+       { PCM3168A_DAC_VOL_CHAN_START + 1, 0xff },
+       { PCM3168A_DAC_VOL_CHAN_START + 2, 0xff },
+       { PCM3168A_DAC_VOL_CHAN_START + 3, 0xff },
+       { PCM3168A_DAC_VOL_CHAN_START + 4, 0xff },
+       { PCM3168A_DAC_VOL_CHAN_START + 5, 0xff },
+       { PCM3168A_DAC_VOL_CHAN_START + 6, 0xff },
+       { PCM3168A_DAC_VOL_CHAN_START + 7, 0xff },
+       { PCM3168A_ADC_SMODE, 0x00 },
+       { PCM3168A_ADC_MST_FMT, 0x00 },
+       { PCM3168A_ADC_PWR_HPFB, 0x00 },
+       { PCM3168A_ADC_SEAD, 0x00 },
+       { PCM3168A_ADC_INV, 0x00 },
+       { PCM3168A_ADC_MUTE, 0x00 },
+       { PCM3168A_ADC_OV, 0x00 },
+       { PCM3168A_ADC_ATT_OVF, 0x00 },
+       { PCM3168A_ADC_VOL_MASTER, 0xd3 },
+       { PCM3168A_ADC_VOL_CHAN_START, 0xd3 },
+       { PCM3168A_ADC_VOL_CHAN_START + 1, 0xd3 },
+       { PCM3168A_ADC_VOL_CHAN_START + 2, 0xd3 },
+       { PCM3168A_ADC_VOL_CHAN_START + 3, 0xd3 },
+       { PCM3168A_ADC_VOL_CHAN_START + 4, 0xd3 },
+       { PCM3168A_ADC_VOL_CHAN_START + 5, 0xd3 }
+};
+
+static bool pcm3168a_readable_register(struct device *dev, unsigned int reg)
+{
+       if (reg >= PCM3168A_RST_SMODE)
+               return true;
+       else
+               return false;
+}
+
+static bool pcm3168a_volatile_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case PCM3168A_DAC_ZERO:
+       case PCM3168A_ADC_OV:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool pcm3168a_writeable_register(struct device *dev, unsigned int reg)
+{
+       if (reg < PCM3168A_RST_SMODE)
+               return false;
+
+       switch (reg) {
+       case PCM3168A_DAC_ZERO:
+       case PCM3168A_ADC_OV:
+               return false;
+       default:
+               return true;
+       }
+}
+
+const struct regmap_config pcm3168a_regmap = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = PCM3168A_ADC_VOL_CHAN_START + 5,
+       .reg_defaults = pcm3168a_reg_default,
+       .num_reg_defaults = ARRAY_SIZE(pcm3168a_reg_default),
+       .readable_reg = pcm3168a_readable_register,
+       .volatile_reg = pcm3168a_volatile_register,
+       .writeable_reg = pcm3168a_writeable_register,
+       .cache_type = REGCACHE_FLAT
+};
+EXPORT_SYMBOL_GPL(pcm3168a_regmap);
+
+static const struct snd_soc_codec_driver pcm3168a_driver = {
+       .idle_bias_off = true,
+       .controls = pcm3168a_snd_controls,
+       .num_controls = ARRAY_SIZE(pcm3168a_snd_controls),
+       .dapm_widgets = pcm3168a_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(pcm3168a_dapm_widgets),
+       .dapm_routes = pcm3168a_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(pcm3168a_dapm_routes)
+};
+
+int pcm3168a_probe(struct device *dev, struct regmap *regmap)
+{
+       struct pcm3168a_priv *pcm3168a;
+       int ret, i;
+
+       pcm3168a = devm_kzalloc(dev, sizeof(*pcm3168a), GFP_KERNEL);
+       if (pcm3168a == NULL)
+               return -ENOMEM;
+
+       dev_set_drvdata(dev, pcm3168a);
+
+       pcm3168a->scki = devm_clk_get(dev, "scki");
+       if (IS_ERR(pcm3168a->scki)) {
+               ret = PTR_ERR(pcm3168a->scki);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "failed to acquire clock 'scki': %d\n", ret);
+               return ret;
+       }
+
+       ret = clk_prepare_enable(pcm3168a->scki);
+       if (ret) {
+               dev_err(dev, "Failed to enable mclk: %d\n", ret);
+               return ret;
+       }
+
+       pcm3168a->sysclk = clk_get_rate(pcm3168a->scki);
+
+       for (i = 0; i < ARRAY_SIZE(pcm3168a->supplies); i++)
+               pcm3168a->supplies[i].supply = pcm3168a_supply_names[i];
+
+       ret = devm_regulator_bulk_get(dev,
+                       ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies);
+       if (ret) {
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "failed to request supplies: %d\n", ret);
+               goto err_clk;
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies),
+                                   pcm3168a->supplies);
+       if (ret) {
+               dev_err(dev, "failed to enable supplies: %d\n", ret);
+               goto err_clk;
+       }
+
+       pcm3168a->regmap = regmap;
+       if (IS_ERR(pcm3168a->regmap)) {
+               ret = PTR_ERR(pcm3168a->regmap);
+               dev_err(dev, "failed to allocate regmap: %d\n", ret);
+               goto err_regulator;
+       }
+
+       ret = pcm3168a_reset(pcm3168a);
+       if (ret) {
+               dev_err(dev, "Failed to reset device: %d\n", ret);
+               goto err_regulator;
+       }
+
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+       pm_runtime_idle(dev);
+
+       ret = snd_soc_register_codec(dev, &pcm3168a_driver, pcm3168a_dais,
+                       ARRAY_SIZE(pcm3168a_dais));
+       if (ret) {
+               dev_err(dev, "failed to register codec: %d\n", ret);
+               goto err_regulator;
+       }
+
+       return 0;
+
+err_regulator:
+       regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
+                       pcm3168a->supplies);
+err_clk:
+       clk_disable_unprepare(pcm3168a->scki);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(pcm3168a_probe);
+
+void pcm3168a_remove(struct device *dev)
+{
+       struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
+
+       snd_soc_unregister_codec(dev);
+       pm_runtime_disable(dev);
+       regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
+                               pcm3168a->supplies);
+       clk_disable_unprepare(pcm3168a->scki);
+}
+EXPORT_SYMBOL_GPL(pcm3168a_remove);
+
+#ifdef CONFIG_PM
+static int pcm3168a_rt_resume(struct device *dev)
+{
+       struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_prepare_enable(pcm3168a->scki);
+       if (ret) {
+               dev_err(dev, "Failed to enable mclk: %d\n", ret);
+               return ret;
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies),
+                                   pcm3168a->supplies);
+       if (ret) {
+               dev_err(dev, "Failed to enable supplies: %d\n", ret);
+               goto err_clk;
+       }
+
+       ret = pcm3168a_reset(pcm3168a);
+       if (ret) {
+               dev_err(dev, "Failed to reset device: %d\n", ret);
+               goto err_regulator;
+       }
+
+       regcache_cache_only(pcm3168a->regmap, false);
+
+       regcache_mark_dirty(pcm3168a->regmap);
+
+       ret = regcache_sync(pcm3168a->regmap);
+       if (ret) {
+               dev_err(dev, "Failed to sync regmap: %d\n", ret);
+               goto err_regulator;
+       }
+
+       return 0;
+
+err_regulator:
+       regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
+                              pcm3168a->supplies);
+err_clk:
+       clk_disable_unprepare(pcm3168a->scki);
+
+       return ret;
+}
+
+static int pcm3168a_rt_suspend(struct device *dev)
+{
+       struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
+
+       regcache_cache_only(pcm3168a->regmap, true);
+
+       regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
+                              pcm3168a->supplies);
+
+       clk_disable_unprepare(pcm3168a->scki);
+
+       return 0;
+}
+#endif
+
+const struct dev_pm_ops pcm3168a_pm_ops = {
+       SET_RUNTIME_PM_OPS(pcm3168a_rt_suspend, pcm3168a_rt_resume, NULL)
+};
+EXPORT_SYMBOL_GPL(pcm3168a_pm_ops);
+
+MODULE_DESCRIPTION("PCM3168A codec driver");
+MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3168a.h b/sound/soc/codecs/pcm3168a.h
new file mode 100644 (file)
index 0000000..56c8332
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * PCM3168A codec driver header
+ *
+ * Copyright (C) 2015 Imagination Technologies Ltd.
+ *
+ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
+ *
+ * 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.
+ */
+
+#ifndef __PCM3168A_H__
+#define __PCM3168A_H__
+
+extern const struct dev_pm_ops pcm3168a_pm_ops;
+extern const struct regmap_config pcm3168a_regmap;
+
+extern int pcm3168a_probe(struct device *dev, struct regmap *regmap);
+extern void pcm3168a_remove(struct device *dev);
+
+#define PCM3168A_RST_SMODE                     0x40
+#define PCM3168A_MRST_MASK                     0x80
+#define PCM3168A_SRST_MASK                     0x40
+#define PCM3168A_DAC_SRDA_SHIFT                        0
+#define PCM3168A_DAC_SRDA_MASK                 0x3
+
+#define PCM3168A_DAC_PWR_MST_FMT               0x41
+#define PCM3168A_DAC_PSMDA_SHIFT               7
+#define PCM3168A_DAC_PSMDA_MASK                        0x80
+#define PCM3168A_DAC_MSDA_SHIFT                        4
+#define PCM3168A_DAC_MSDA_MASK                 0x70
+#define PCM3168A_DAC_FMT_SHIFT                 0
+#define PCM3168A_DAC_FMT_MASK                  0xf
+
+#define PCM3168A_DAC_OP_FLT                    0x42
+#define PCM3168A_DAC_OPEDA_SHIFT               4
+#define PCM3168A_DAC_OPEDA_MASK                        0xf0
+#define PCM3168A_DAC_FLT_SHIFT                 0
+#define PCM3168A_DAC_FLT_MASK                  0xf
+
+#define PCM3168A_DAC_INV                       0x43
+
+#define PCM3168A_DAC_MUTE                      0x44
+
+#define PCM3168A_DAC_ZERO                      0x45
+
+#define PCM3168A_DAC_ATT_DEMP_ZF               0x46
+#define PCM3168A_DAC_ATMDDA_MASK               0x80
+#define PCM3168A_DAC_ATMDDA_SHIFT              7
+#define PCM3168A_DAC_ATSPDA_MASK               0x40
+#define PCM3168A_DAC_ATSPDA_SHIFT              6
+#define PCM3168A_DAC_DEMP_SHIFT                        4
+#define PCM3168A_DAC_DEMP_MASK                 0x30
+#define PCM3168A_DAC_AZRO_SHIFT                        1
+#define PCM3168A_DAC_AZRO_MASK                 0xe
+#define PCM3168A_DAC_ZREV_MASK                 0x1
+#define PCM3168A_DAC_ZREV_SHIFT                        0
+
+#define PCM3168A_DAC_VOL_MASTER                        0x47
+
+#define PCM3168A_DAC_VOL_CHAN_START            0x48
+
+#define PCM3168A_ADC_SMODE                     0x50
+#define PCM3168A_ADC_SRAD_SHIFT                        0
+#define PCM3168A_ADC_SRAD_MASK                 0x3
+
+#define PCM3168A_ADC_MST_FMT                   0x51
+#define PCM3168A_ADC_MSAD_SHIFT                        4
+#define PCM3168A_ADC_MSAD_MASK                 0x70
+#define PCM3168A_ADC_FMTAD_SHIFT               0
+#define PCM3168A_ADC_FMTAD_MASK                        0x7
+
+#define PCM3168A_ADC_PWR_HPFB                  0x52
+#define PCM3168A_ADC_PSVAD_SHIFT               4
+#define PCM3168A_ADC_PSVAD_MASK                        0x70
+#define PCM3168A_ADC_BYP_SHIFT                 0
+#define PCM3168A_ADC_BYP_MASK                  0x7
+
+#define PCM3168A_ADC_SEAD                      0x53
+
+#define PCM3168A_ADC_INV                       0x54
+
+#define PCM3168A_ADC_MUTE                      0x55
+
+#define PCM3168A_ADC_OV                                0x56
+
+#define PCM3168A_ADC_ATT_OVF                   0x57
+#define PCM3168A_ADC_ATMDAD_MASK               0x80
+#define PCM3168A_ADC_ATMDAD_SHIFT              7
+#define PCM3168A_ADC_ATSPAD_MASK               0x40
+#define PCM3168A_ADC_ATSPAD_SHIFT              6
+#define PCM3168A_ADC_OVFP_MASK                 0x1
+#define PCM3168A_ADC_OVFP_SHIFT                        0
+
+#define PCM3168A_ADC_VOL_MASTER                        0x58
+
+#define PCM3168A_ADC_VOL_CHAN_START            0x59
+
+#endif