MLK-15070-1: ASoC: codecs: Add support for AK4497 DAC
authorDaniel Baluta <daniel.baluta@nxp.com>
Mon, 31 Jul 2017 10:56:30 +0000 (13:56 +0300)
committerNitin Garg <nitin.garg@nxp.com>
Tue, 20 Mar 2018 19:53:04 +0000 (14:53 -0500)
AK4497 is a 32-bit 2ch DAC, supporting up to 6 types
of digital filters and accepts up to 768kHz PCM data
and 22.4MHz DSD data.

This is based on original code received from Asahi Kasei
with the following modifications:

* there is now a .component_driver inside snd_soc_codec_driver holding
  the controls, dapm_widgets and dap_routes.
* Remove akdbgprt
* Use module_i2c_driver
* Add NXP copyright
* Use symbolic names for registers
* Fix coding style issues
* fix function parameters indentation
* remove multiple empty newlines
* convert C++ style comments to C style comments
* fix spaces and tabs at the end of the line.
* remove braces {} for single block statements
* Make pointers const
* Fix lines over 80 chars
* Don't initialize statics to 0
* Use usleep_range instead of msleep
* Fx vendor prefix
* Add DT bindings documentation for ak4497 codec
* Remove regmap default functions
* Use devm_kzalloc
* Fix MAX_REGISTERS value
* Add $self as module author
* Remove .owner field
* Make ak4497_init_reg return void

Signed-off-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Documentation/devicetree/bindings/sound/ak4497.txt [new file with mode: 0644]
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ak4497.c [new file with mode: 0644]
sound/soc/codecs/ak4497.h [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/sound/ak4497.txt b/Documentation/devicetree/bindings/sound/ak4497.txt
new file mode 100644 (file)
index 0000000..f1fd59f
--- /dev/null
@@ -0,0 +1,23 @@
+AK4497 audio codec
+
+This device supports I2C mode only.
+
+Required properties:
+
+- compatible : "asahi-kasei,ak4497"
+- reg : The I2C address of the device.
+- asahi-kasei,pdn-gpio: A GPIO specifier for the GPIO controlling
+       the power down & reset pin.
+- asahi-kasei,mute-gpio: A GPIO specifier for the GPIO controlling
+       the soft mute pin.
+
+Example:
+
+&i2c {
+       ak4458: ak4458@0x10 {
+               compatible = "asahi-kasei,ak4458";
+               reg = <0x10>;
+               asahi-kasei,pdn-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>
+               asahi-kasei,mute-gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>
+       };
+};
index 0c72a2c..0d6f568 100644 (file)
@@ -38,6 +38,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_AK4458
        select SND_SOC_AK4458_I2C if I2C
        select SND_SOC_AK4458_SPI if SPI_MASTER
+       select SND_SOC_AK4497 if I2C
        select SND_SOC_AK4535 if I2C
        select SND_SOC_AK4554
        select SND_SOC_AK4613 if I2C
@@ -375,6 +376,11 @@ config SND_SOC_AK4458_SPI
        select SND_SOC_AK4458
        select REGMAP_SPI
 
+config SND_SOC_AK4497
+       tristate "AKM AK4497 CODEC"
+       depends on I2C
+       select REGMAP_I2C
+
 config SND_SOC_AK4535
        tristate
 
index ccff316..bd1385e 100644 (file)
@@ -29,6 +29,7 @@ snd-soc-ak4104-objs := ak4104.o
 snd-soc-ak4458-objs := ak4458.o
 snd-soc-ak4458-i2c-objs := ak4458-i2c.o
 snd-soc-ak4458-spi-objs := ak4458-spi.o
+snd-soc-ak4497-objs := ak4497.o
 snd-soc-ak4535-objs := ak4535.o
 snd-soc-ak4554-objs := ak4554.o
 snd-soc-ak4613-objs := ak4613.o
@@ -258,6 +259,7 @@ obj-$(CONFIG_SND_SOC_AK4104)        += snd-soc-ak4104.o
 obj-$(CONFIG_SND_SOC_AK4458)   += snd-soc-ak4458.o
 obj-$(CONFIG_SND_SOC_AK4458_I2C)       += snd-soc-ak4458-i2c.o
 obj-$(CONFIG_SND_SOC_AK4458_SPI)       += snd-soc-ak4458-spi.o
+obj-$(CONFIG_SND_SOC_AK4497)   += snd-soc-ak4497.o
 obj-$(CONFIG_SND_SOC_AK4535)   += snd-soc-ak4535.o
 obj-$(CONFIG_SND_SOC_AK4554)   += snd-soc-ak4554.o
 obj-$(CONFIG_SND_SOC_AK4613)   += snd-soc-ak4613.o
diff --git a/sound/soc/codecs/ak4497.c b/sound/soc/codecs/ak4497.c
new file mode 100644 (file)
index 0000000..49be4fa
--- /dev/null
@@ -0,0 +1,897 @@
+/*
+ * ak4497.c  --  audio driver for AK4497
+ *
+ * Copyright (C) 2016 Asahi Kasei Microdevices Corporation
+ * Copyright (C) 2017, NXP
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+
+#include "ak4497.h"
+
+//#define AK4497_DEBUG   //used at debug mode
+
+/* AK4497 Codec Private Data */
+struct ak4497_priv {
+       struct i2c_client *i2c;
+       int fs1;        /* Sampling Frequency */
+       int nBickFreq;  /* 0: 48fs for 24bit,  1: 64fs or more for 32bit */
+       int nDSDSel;
+       int nTdmSds;
+       int pdn_gpio;
+       int mute_gpio;
+};
+
+/* ak4497 register cache & default register settings */
+static const struct reg_default ak4497_reg[] = {
+       { AK4497_00_CONTROL1, 0x0C},
+       { AK4497_01_CONTROL2, 0x22},
+       { AK4497_02_CONTROL3, 0x00},
+       { AK4497_03_LCHATT, 0xFF},
+       { AK4497_04_RCHATT, 0xFF},
+       { AK4497_05_CONTROL4, 0x00},
+       { AK4497_06_DSD1, 0x00},
+       { AK4497_07_CONTROL5, 0x00},
+       { AK4497_08_SOUNDCONTROL, 0x00},
+       { AK4497_09_DSD2, 0x00},
+       { AK4497_0A_CONTROL7, 0x04},
+       { AK4497_0B_CONTROL8, 0x00},
+       { AK4497_0C_RESERVED, 0x00},
+       { AK4497_0D_RESERVED, 0x00},
+       { AK4497_0E_RESERVED, 0x00},
+       { AK4497_0F_RESERVED, 0x00},
+       { AK4497_10_RESERVED, 0x00},
+       { AK4497_11_RESERVED, 0x00},
+       { AK4497_12_RESERVED, 0x00},
+       { AK4497_13_RESERVED, 0x00},
+       { AK4497_14_RESERVED, 0x00},
+       { AK4497_15_DFSREAD, 0x00},
+};
+
+/* Volume control:
+ * from -127 to 0 dB in 0.5 dB steps (mute instead of -127.5 dB)
+ */
+static DECLARE_TLV_DB_SCALE(latt_tlv, -12750, 50, 0);
+static DECLARE_TLV_DB_SCALE(ratt_tlv, -12750, 50, 0);
+
+static const char * const ak4497_ecs_select_texts[] = {"768kHz", "384kHz"};
+
+static const char * const ak4497_dem_select_texts[] = {
+       "44.1kHz", "OFF", "48kHz", "32kHz"};
+static const char * const ak4497_dzfm_select_texts[] = {
+       "Separated", "ANDed"};
+
+static const char * const ak4497_sellr_select_texts[] = {
+       "Rch", "Lch"};
+static const char * const ak4497_dckb_select_texts[] = {
+       "Falling", "Rising"};
+static const char * const ak4497_dcks_select_texts[] = {
+       "512fs", "768fs"};
+
+static const char * const ak4497_dsdd_select_texts[] = {
+       "Normal", "Volume Bypass"};
+
+static const char * const ak4497_sc_select_texts[] = {
+       "Setting 1", "Setting 2", "Setting 3"};
+static const char * const ak4497_dsdf_select_texts[] = {
+       "50kHz", "150kHz"};
+static const char * const ak4497_dsd_input_path_select[] = {
+       "16_17_19pin", "3_4_5pin"};
+static const char * const ak4497_ats_select_texts[] = {
+       "4080/fs", "2040/fs", "510/fs", "255/fs"};
+
+static const struct soc_enum ak4497_dac_enum[] = {
+       SOC_ENUM_SINGLE(AK4497_00_CONTROL1, 5,
+                       ARRAY_SIZE(ak4497_ecs_select_texts),
+                       ak4497_ecs_select_texts),
+       SOC_ENUM_SINGLE(AK4497_01_CONTROL2, 1,
+                       ARRAY_SIZE(ak4497_dem_select_texts),
+                       ak4497_dem_select_texts),
+       SOC_ENUM_SINGLE(AK4497_01_CONTROL2, 6,
+                       ARRAY_SIZE(ak4497_dzfm_select_texts),
+                       ak4497_dzfm_select_texts),
+       SOC_ENUM_SINGLE(AK4497_02_CONTROL3, 1,
+                       ARRAY_SIZE(ak4497_sellr_select_texts),
+                       ak4497_sellr_select_texts),
+       SOC_ENUM_SINGLE(AK4497_02_CONTROL3, 4,
+                       ARRAY_SIZE(ak4497_dckb_select_texts),
+                       ak4497_dckb_select_texts),
+       SOC_ENUM_SINGLE(AK4497_02_CONTROL3, 5,
+                       ARRAY_SIZE(ak4497_dcks_select_texts),
+                       ak4497_dcks_select_texts),
+       SOC_ENUM_SINGLE(AK4497_06_DSD1, 1,
+                       ARRAY_SIZE(ak4497_dsdd_select_texts),
+                       ak4497_dsdd_select_texts),
+       SOC_ENUM_SINGLE(AK4497_08_SOUNDCONTROL, 0,
+                       ARRAY_SIZE(ak4497_sc_select_texts),
+                       ak4497_sc_select_texts),
+       SOC_ENUM_SINGLE(AK4497_09_DSD2, 1,
+                       ARRAY_SIZE(ak4497_dsdf_select_texts),
+                       ak4497_dsdf_select_texts),
+       SOC_ENUM_SINGLE(AK4497_09_DSD2, 2,
+                       ARRAY_SIZE(ak4497_dsd_input_path_select),
+                       ak4497_dsd_input_path_select),
+       SOC_ENUM_SINGLE(AK4497_0B_CONTROL8, 6,
+                       ARRAY_SIZE(ak4497_ats_select_texts),
+                       ak4497_ats_select_texts),
+};
+
+static const char * const ak4497_dsdsel_select_texts[] = {
+       "64fs", "128fs", "256fs", "512fs"};
+static const char * const ak4497_bickfreq_select[] = {"48fs", "64fs"};
+
+static const char * const ak4497_tdm_sds_select[] = {
+       "L1R1", "TDM128_L1R1", "TDM128_L2R2",
+       "TDM256_L1R1", "TDM256_L2R2",  "TDM256_L3R3", "TDM256_L4R4",
+       "TDM512_L1R1", "TDM512_L2R2",  "TDM512_L3R3", "TDM512_L4R4",
+       "TDM512_L5R5", "TDM512_L6R6",  "TDM512_L7R7", "TDM512_L8R8",
+};
+
+static const char * const ak4497_adfs_select[] = {
+       "Normal Speed Mode", "Double Speed Mode", "Quad Speed Mode",
+       "Quad Speed Mode", "Oct Speed Mode", "Hex Speed Mode", "Oct Speed Mode",
+       "Hex Speed Mode"
+};
+
+static const struct soc_enum ak4497_dac_enum2[] = {
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ak4497_dsdsel_select_texts),
+                           ak4497_dsdsel_select_texts),
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ak4497_bickfreq_select),
+                           ak4497_bickfreq_select),
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ak4497_tdm_sds_select),
+                           ak4497_tdm_sds_select),
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ak4497_adfs_select),
+                           ak4497_adfs_select)
+};
+
+static int ak4497_get_dsdsel(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value  *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.enumerated.item[0] = ak4497->nDSDSel;
+
+       return 0;
+}
+
+static int ak4497_set_dsdsel(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value  *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec);
+
+       ak4497->nDSDSel = ucontrol->value.enumerated.item[0];
+
+       if (ak4497->nDSDSel == 0) { /* 2.8224MHz */
+               snd_soc_update_bits(codec, AK4497_06_DSD1, 0x01, 0x00);
+               snd_soc_update_bits(codec, AK4497_09_DSD2, 0x01, 0x00);
+       } else if (ak4497->nDSDSel == 1) { /* 5.6448MHz */
+               snd_soc_update_bits(codec, AK4497_06_DSD1, 0x01, 0x01);
+               snd_soc_update_bits(codec, AK4497_09_DSD2, 0x01, 0x00);
+       } else { /* 11.2896MHz */
+               snd_soc_update_bits(codec, AK4497_06_DSD1, 0x01, 0x00);
+               snd_soc_update_bits(codec, AK4497_09_DSD2, 0x01, 0x01);
+       }
+
+       return 0;
+}
+
+static int ak4497_get_bickfs(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value  *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.enumerated.item[0] = ak4497->nBickFreq;
+
+       return 0;
+}
+
+static int ak4497_set_bickfs(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value  *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec);
+
+       ak4497->nBickFreq = ucontrol->value.enumerated.item[0];
+
+       return 0;
+}
+
+static int ak4497_get_tdmsds(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value  *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.enumerated.item[0] = ak4497->nTdmSds;
+
+       return 0;
+}
+
+static int ak4497_set_tdmsds(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value  *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec);
+       int regA, regB;
+
+       ak4497->nTdmSds = ucontrol->value.enumerated.item[0];
+
+       if (ak4497->nTdmSds == 0)
+               regB = 0; /* SDS0 bit = 0 */
+       else
+               regB = (1 & (ak4497->nTdmSds - 1)); /* SDS0 bit = 1 */
+
+       switch (ak4497->nTdmSds) {
+       case 0:
+               regA = 0; /* Normal */
+               break;
+       case 1:
+       case 2:
+               regA = 4; /* TDM128 TDM1-0bits = 1 */
+               break;
+       case 3:
+       case 4:
+               regA = 8;  /* TDM128 TDM1-0bits = 2 */
+               break;
+       case 5:
+       case 6:
+               regA = 9;  /* TDM128 TDM1-0bits = 2 */
+               break;
+       case 7:
+       case 8:
+               regA = 0xC;  /* TDM128 TDM1-0bits = 3 */
+               break;
+       case 9:
+       case 10:
+               regA = 0xD;  /* TDM128 TDM1-0bits = 3 */
+               break;
+       case 11:
+       case 12:
+               regA = 0xE;  /* TDM128 TDM1-0bits = 3 */
+               break;
+       case 13:
+       case 14:
+               regA = 0xF;  /* TDM128 TDM1-0bits = 3 */
+               break;
+       default:
+               regA = 0;
+               regB = 0;
+               break;
+       }
+
+       regA <<= 4;
+       regB <<= 4;
+
+       snd_soc_update_bits(codec, AK4497_0A_CONTROL7, 0xF0, regA);
+       snd_soc_update_bits(codec, AK4497_0B_CONTROL8, 0x10, regB);
+
+       return 0;
+}
+
+static int ak4497_get_adfs(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       int nADFSbit;
+
+       nADFSbit = snd_soc_read(codec, AK4497_15_DFSREAD);
+       nADFSbit &= 0x7;
+
+       ucontrol->value.enumerated.item[0] = nADFSbit;
+
+       return 0;
+}
+
+static int ak4497_set_adfs(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_value *ucontrol)
+{
+       pr_debug("AK4497 : ADFS is read only\n");
+
+       return 0;
+}
+
+static const char * const gain_control_texts[] = {
+       "2.8_2.8Vpp", "2.8_2.5Vpp", "2.5_2.5Vpp", "3.75_3.75Vpp", "3.75_2.5Vpp"
+};
+
+static const unsigned int gain_control_values[] = {
+       0, 1, 2, 4, 5
+};
+
+static const struct soc_enum ak4497_gain_control_enum =
+       SOC_VALUE_ENUM_SINGLE(AK4497_07_CONTROL5, 1, 7,
+                             ARRAY_SIZE(gain_control_texts),
+                             gain_control_texts,
+                             gain_control_values);
+
+#ifdef AK4497_DEBUG
+
+static const char * const test_reg_select[] = {
+       "read AK4497 Reg 00:0B",
+       "read AK4497 Reg 15"
+};
+
+static const struct soc_enum ak4497_enum[] = {
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(test_reg_select), test_reg_select),
+};
+
+static int nTestRegNo;
+
+static int get_test_reg(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value  *ucontrol)
+{
+       /* Get the current output routing */
+       ucontrol->value.enumerated.item[0] = nTestRegNo;
+
+       return 0;
+}
+
+static int set_test_reg(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value  *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       u32 currMode = ucontrol->value.enumerated.item[0];
+       int i, value;
+       int regs, rege;
+
+       nTestRegNo = currMode;
+
+       if (nTestRegNo == 0) {
+               regs = 0x00;
+               rege = 0x0B;
+       } else {
+               regs = 0x15;
+               rege = 0x15;
+       }
+
+       for (i = regs; i <= rege; i++) {
+               value = snd_soc_read(codec, i);
+               pr_debug("***AK4497 Addr,Reg=(%x, %x)\n", i, value);
+       }
+
+       return 0;
+}
+#endif
+
+static const struct snd_kcontrol_new ak4497_snd_controls[] = {
+       SOC_SINGLE_TLV("AK4497 Lch Digital Volume",
+                       AK4497_03_LCHATT, 0, 0xFF, 0, latt_tlv),
+       SOC_SINGLE_TLV("AK4497 Rch Digital Volume",
+                       AK4497_04_RCHATT, 0, 0xFF, 0, ratt_tlv),
+
+       SOC_ENUM("AK4497 EX DF I/F clock", ak4497_dac_enum[0]),
+       SOC_ENUM("AK4497 De-emphasis Response", ak4497_dac_enum[1]),
+       SOC_ENUM("AK4497 Data Zero Detect Mode", ak4497_dac_enum[2]),
+       SOC_ENUM("AK4497 Data Selection at Mono Mode", ak4497_dac_enum[3]),
+
+       SOC_ENUM("AK4497 Polarity of DCLK", ak4497_dac_enum[4]),
+       SOC_ENUM("AK4497 DCKL Frequency", ak4497_dac_enum[5]),
+
+       SOC_ENUM("AK4497 DDSD Play Back Path", ak4497_dac_enum[6]),
+       SOC_ENUM("AK4497 Sound control", ak4497_dac_enum[7]),
+       SOC_ENUM("AK4497 Cut Off of DSD Filter", ak4497_dac_enum[8]),
+
+       SOC_ENUM_EXT("AK4497 DSD Data Stream", ak4497_dac_enum2[0],
+                    ak4497_get_dsdsel, ak4497_set_dsdsel),
+       SOC_ENUM_EXT("AK4497 BICK Frequency Select", ak4497_dac_enum2[1],
+                    ak4497_get_bickfs, ak4497_set_bickfs),
+       SOC_ENUM_EXT("AK4497 TDM Data Select", ak4497_dac_enum2[2],
+                    ak4497_get_tdmsds, ak4497_set_tdmsds),
+
+       SOC_SINGLE("AK4497 External Digital Filter", AK4497_00_CONTROL1,
+                  6, 1, 0),
+       SOC_SINGLE("AK4497 MCLK Frequency Auto Setting", AK4497_00_CONTROL1,
+                  7, 1, 0),
+       SOC_SINGLE("AK4497 MCLK FS Auto Detect", AK4497_00_CONTROL1, 4, 1, 0),
+
+       SOC_SINGLE("AK4497 Soft Mute Control", AK4497_01_CONTROL2, 0, 1, 0),
+       SOC_SINGLE("AK4497 Short delay filter", AK4497_01_CONTROL2, 5, 1, 0),
+       SOC_SINGLE("AK4497 Data Zero Detect Enable", AK4497_01_CONTROL2,
+                  7, 1, 0),
+       SOC_SINGLE("AK4497 Slow Roll-off Filter", AK4497_02_CONTROL3, 0, 1, 0),
+       SOC_SINGLE("AK4497 Invering Enable of DZF", AK4497_02_CONTROL3,
+                  4, 1, 0),
+       SOC_SINGLE("AK4497 Mono Mode", AK4497_02_CONTROL3, 3, 1, 0),
+       SOC_SINGLE("AK4497 Super Slow Roll-off Filter", AK4497_05_CONTROL4,
+                  0, 1, 0),
+       SOC_SINGLE("AK4497 AOUTR Phase Inverting", AK4497_05_CONTROL4,
+                  6, 1, 0),
+       SOC_SINGLE("AK4497 AOUTL Phase Inverting", AK4497_05_CONTROL4,
+                  7, 1, 0),
+       SOC_SINGLE("AK4497 DSD Mute Release", AK4497_06_DSD1, 3, 1, 0),
+       SOC_SINGLE("AK4497 DSD Mute Control Hold", AK4497_06_DSD1, 4, 1, 0),
+       SOC_SINGLE("AK4497 DSDR is detected", AK4497_06_DSD1, 5, 1, 0),
+       SOC_SINGLE("AK4497 DSDL is detected", AK4497_06_DSD1, 6, 1, 0),
+       SOC_SINGLE("AK4497 DSD Data Mute", AK4497_06_DSD1, 7, 1, 0),
+       SOC_SINGLE("AK4497 Synchronization Control", AK4497_07_CONTROL5,
+                  0, 1, 0),
+
+       SOC_ENUM("AK4497 Output Level", ak4497_gain_control_enum),
+       SOC_SINGLE("AK4497 High Sonud Quality Mode", AK4497_08_SOUNDCONTROL,
+                  2, 1, 0),
+       SOC_SINGLE("AK4497 Heavy Load Mode", AK4497_08_SOUNDCONTROL, 3, 1, 0),
+       SOC_ENUM("AK4497 DSD Data Input Pin", ak4497_dac_enum[9]),
+       SOC_SINGLE("AK4497 Daisy Chain", AK4497_0B_CONTROL8, 1, 1, 0),
+       SOC_ENUM("AK4497 ATT Transit Time", ak4497_dac_enum[10]),
+
+       SOC_ENUM_EXT("AK4497 Read FS Auto Detect Mode", ak4497_dac_enum2[3],
+                    ak4497_get_adfs, ak4497_set_adfs),
+
+#ifdef AK4497_DEBUG
+       SOC_ENUM_EXT("Reg Read", ak4497_enum[0], get_test_reg, set_test_reg),
+#endif
+
+};
+
+static const char * const ak4497_dac_enable_texts[] = {"Off", "On"};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(ak4497_dac_enable_enum,
+                                ak4497_dac_enable_texts);
+
+static const struct snd_kcontrol_new ak4497_dac_enable_control =
+       SOC_DAPM_ENUM("DAC Switch", ak4497_dac_enable_enum);
+
+/* ak4497 dapm widgets */
+static const struct snd_soc_dapm_widget ak4497_dapm_widgets[] = {
+       SND_SOC_DAPM_AIF_IN("AK4497 SDTI", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+       SND_SOC_DAPM_DAC("AK4497 DAC", NULL, AK4497_0A_CONTROL7, 2, 0),
+
+       SND_SOC_DAPM_MUX("AK4497 DAC Enable", SND_SOC_NOPM,
+                        0, 0, &ak4497_dac_enable_control),
+
+       SND_SOC_DAPM_OUTPUT("AK4497 AOUT"),
+
+};
+
+static const struct snd_soc_dapm_route ak4497_intercon[] = {
+       {"AK4497 DAC", NULL, "AK4497 SDTI"},
+       {"AK4497 DAC Enable", "On", "AK4497 DAC"},
+       {"AK4497 AOUT", NULL, "AK4497 DAC Enable"},
+};
+
+static int ak4497_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 ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec);
+
+       u8 dfs;
+       u8 dfs2;
+       int nfs1;
+
+       nfs1 = params_rate(params);
+       ak4497->fs1 = nfs1;
+
+       dfs = snd_soc_read(codec, AK4497_01_CONTROL2);
+       dfs &= ~AK4497_DFS;
+
+       dfs2 = snd_soc_read(codec, AK4497_05_CONTROL4);
+       dfs2 &= ~AK4497_DFS2;
+
+       switch (nfs1) {
+       case 32000:
+       case 44100:
+       case 48000:
+               dfs |= AK4497_DFS_48KHZ;
+               dfs2 |= AK4497_DFS2_48KHZ;
+               break;
+       case 88200:
+       case 96000:
+               dfs |= AK4497_DFS_96KHZ;
+               dfs2 |= AK4497_DFS2_48KHZ;
+               break;
+
+       case 176400:
+       case 192000:
+               dfs |= AK4497_DFS_192KHZ;
+               dfs2 |= AK4497_DFS2_48KHZ;
+               break;
+       case 384000:
+               dfs |= AK4497_DFS_384KHZ;
+               dfs2 |= AK4497_DFS2_384KHZ;
+               break;
+       case 768000:
+               dfs |= AK4497_DFS_768KHZ;
+               dfs2 |= AK4497_DFS2_384KHZ;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_write(codec, AK4497_01_CONTROL2, dfs);
+       snd_soc_write(codec, AK4497_05_CONTROL4, dfs2);
+
+       return 0;
+}
+
+static int ak4497_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+                                unsigned int freq, int dir)
+{
+//     struct snd_soc_codec *codec = dai->codec;
+
+       return 0;
+}
+
+static int ak4497_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec);
+       u8 format;
+       u8 format2;
+
+       /* set master/slave audio interface */
+       format = snd_soc_read(codec, AK4497_00_CONTROL1);
+       format &= ~AK4497_DIF;
+
+       format2 = snd_soc_read(codec, AK4497_02_CONTROL3);
+       format2 &= ~AK4497_DIF_DSD;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+       case SND_SOC_DAIFMT_CBS_CFM:
+       case SND_SOC_DAIFMT_CBM_CFS:
+       default:
+               dev_err(codec->dev, "Clock mode unsupported");
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               format |= AK4497_DIF_I2S_MODE;
+               if (ak4497->nBickFreq == 1)
+                       format |= AK4497_DIF_32BIT_MODE;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               format |= AK4497_DIF_MSB_MODE;
+               if (ak4497->nBickFreq == 1)
+                       format |= AK4497_DIF_32BIT_MODE;
+               break;
+       case SND_SOC_DAIFMT_DSD:
+               format2 |= AK4497_DIF_DSD_MODE;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* set format */
+       snd_soc_write(codec, AK4497_00_CONTROL1, format);
+       snd_soc_write(codec, AK4497_02_CONTROL3, format2);
+
+       return 0;
+}
+
+static bool ak4497_volatile(struct device *dev, unsigned int reg)
+{
+       int ret;
+
+#ifdef AK4497_DEBUG
+       ret = 1;
+#else
+       switch (reg) {
+       case AK4497_15_DFSREAD:
+               ret = 1;
+               break;
+       default:
+               ret = 0;
+               break;
+       }
+#endif
+       return ret;
+}
+
+static int ak4497_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+       case SND_SOC_BIAS_PREPARE:
+       case SND_SOC_BIAS_STANDBY:
+               /* RSTN bit = 1 */
+               snd_soc_update_bits(codec, AK4497_00_CONTROL1, 0x01, 0x01);
+               break;
+       case SND_SOC_BIAS_OFF:
+               /* RSTN bit = 0 */
+               snd_soc_update_bits(codec, AK4497_00_CONTROL1, 0x01, 0x00);
+               break;
+       }
+
+       return 0;
+}
+
+static int ak4497_set_dai_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec);
+       int nfs, ndt;
+
+       nfs = ak4497->fs1;
+
+       if (mute) { /* SMUTE: 1 , MUTE */
+               snd_soc_update_bits(codec, AK4497_01_CONTROL2, 0x01, 0x01);
+               ndt = 7424000 / nfs;
+               mdelay(ndt);
+
+               /* External Mute ON */
+               if (ak4497->mute_gpio != -1)
+                       gpio_set_value(ak4497->mute_gpio, 1);
+       } else { /* SMUTE: 0, NORMAL operation */
+
+               /* External Mute OFF */
+               if (ak4497->mute_gpio != -1)
+                       gpio_set_value(ak4497->mute_gpio, 0);
+               snd_soc_update_bits(codec, AK4497_01_CONTROL2, 0x01, 0x00);
+       }
+
+       return 0;
+}
+
+#define AK4497_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+                     SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+                     SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+                     SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
+                     SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
+                     SNDRV_PCM_RATE_192000)
+
+#define AK4497_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
+                       SNDRV_PCM_FMTBIT_S32_LE)
+
+
+static struct snd_soc_dai_ops ak4497_dai_ops = {
+       .hw_params      = ak4497_hw_params,
+       .set_sysclk     = ak4497_set_dai_sysclk,
+       .set_fmt        = ak4497_set_dai_fmt,
+       .digital_mute = ak4497_set_dai_mute,
+};
+
+struct snd_soc_dai_driver ak4497_dai[] = {
+       {
+               .name = "ak4497-aif",
+               .playback = {
+                      .stream_name = "Playback",
+                      .channels_min = 1,
+                      .channels_max = 2,
+                      .rates = AK4497_RATES,
+                      .formats = AK4497_FORMATS,
+               },
+               .ops = &ak4497_dai_ops,
+       },
+};
+
+static void ak4497_init_reg(struct snd_soc_codec *codec)
+{
+       struct ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec);
+
+       if (ak4497->mute_gpio > 0)
+               gpio_set_value(ak4497->mute_gpio, 1); /* External Mute ON */
+
+       if (ak4497->pdn_gpio > 0) {
+               gpio_set_value(ak4497->pdn_gpio, 0);
+               usleep_range(1000, 2000);
+               gpio_set_value(ak4497->pdn_gpio, 1);
+               usleep_range(1000, 2000);
+       }
+
+       /* ak4497_set_bias_level(codec, SND_SOC_BIAS_STANDBY); */
+
+       /* SYNCE bit = 1 */
+       snd_soc_update_bits(codec, AK4497_07_CONTROL5, 0x01, 0x01);
+
+       /*  HLOAD bit = 1, SC2 bit = 1 */
+       snd_soc_update_bits(codec, AK4497_08_SOUNDCONTROL, 0x0F, 0x0C);
+}
+
+static int ak4497_parse_dt(struct ak4497_priv *ak4497)
+{
+       struct device *dev;
+       struct device_node *np;
+
+       dev = &(ak4497->i2c->dev);
+       np = dev->of_node;
+
+       ak4497->pdn_gpio = -1;
+       ak4497->mute_gpio = -1;
+
+       if (!np)
+               return -1;
+
+       ak4497->pdn_gpio = of_get_named_gpio(np, "ak4497,pdn-gpio", 0);
+       if (ak4497->pdn_gpio < 0)
+               ak4497->pdn_gpio = -1;
+
+       if (!gpio_is_valid(ak4497->pdn_gpio)) {
+               dev_err(dev, "ak4497 pdn pin(%u) is invalid\n",
+                      ak4497->pdn_gpio);
+               ak4497->pdn_gpio = -1;
+       }
+
+       ak4497->mute_gpio = of_get_named_gpio(np, "ak4497,mute-gpio", 0);
+       if (ak4497->mute_gpio < 0)
+               ak4497->mute_gpio = -1;
+
+       if (!gpio_is_valid(ak4497->mute_gpio)) {
+               dev_err(dev, "ak4497 mute_gpio(%u) is invalid\n",
+                      ak4497->mute_gpio);
+               ak4497->mute_gpio = -1;
+       }
+
+       return 0;
+}
+
+static int ak4497_probe(struct snd_soc_codec *codec)
+{
+       struct ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+
+       ret = ak4497_parse_dt(ak4497);
+       if (ak4497->pdn_gpio > 0) {
+               ret = gpio_request(ak4497->pdn_gpio, "ak4497 pdn");
+               gpio_direction_output(ak4497->pdn_gpio, 0);
+       }
+       if (ak4497->mute_gpio > 0) {
+               ret = gpio_request(ak4497->mute_gpio, "ak4497 mute");
+               gpio_direction_output(ak4497->mute_gpio, 0);
+       }
+
+       ak4497_init_reg(codec);
+
+       ak4497->fs1 = 48000;
+       ak4497->nBickFreq = 1;
+       ak4497->nDSDSel = 0;
+       ak4497->nTdmSds = 0;
+
+       return ret;
+}
+
+static int ak4497_remove(struct snd_soc_codec *codec)
+{
+       struct ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec);
+
+       ak4497_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       if (ak4497->pdn_gpio > 0) {
+               gpio_set_value(ak4497->pdn_gpio, 0);
+               gpio_free(ak4497->pdn_gpio);
+       }
+       if (ak4497->mute_gpio > 0)
+               gpio_free(ak4497->mute_gpio);
+
+       return 0;
+}
+
+static int ak4497_suspend(struct snd_soc_codec *codec)
+{
+       struct ak4497_priv *ak4497 = snd_soc_codec_get_drvdata(codec);
+
+       ak4497_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       if (ak4497->pdn_gpio > 0)
+               gpio_set_value(ak4497->pdn_gpio, 0);
+
+       if (ak4497->mute_gpio > 0)
+               gpio_free(ak4497->mute_gpio);
+
+       return 0;
+}
+
+static int ak4497_resume(struct snd_soc_codec *codec)
+{
+       ak4497_init_reg(codec);
+
+       return 0;
+}
+
+struct snd_soc_codec_driver soc_codec_dev_ak4497 = {
+       .probe = ak4497_probe,
+       .remove = ak4497_remove,
+       .suspend = ak4497_suspend,
+       .resume = ak4497_resume,
+
+       .idle_bias_off = true,
+       .set_bias_level = ak4497_set_bias_level,
+
+       .component_driver = {
+               .controls = ak4497_snd_controls,
+               .num_controls = ARRAY_SIZE(ak4497_snd_controls),
+               .dapm_widgets = ak4497_dapm_widgets,
+               .num_dapm_widgets = ARRAY_SIZE(ak4497_dapm_widgets),
+               .dapm_routes = ak4497_intercon,
+               .num_dapm_routes = ARRAY_SIZE(ak4497_intercon),
+       },
+};
+
+static const struct regmap_config ak4497_regmap = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = AK4497_MAX_REGISTERS,
+       .volatile_reg = ak4497_volatile,
+
+       .reg_defaults = ak4497_reg,
+       .num_reg_defaults = ARRAY_SIZE(ak4497_reg),
+       .cache_type = REGCACHE_RBTREE,
+};
+
+static int ak4497_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct ak4497_priv *ak4497;
+       struct regmap *regmap;
+       int ret = 0;
+
+       ak4497 = devm_kzalloc(&i2c->dev,
+                             sizeof(struct ak4497_priv), GFP_KERNEL);
+       if (ak4497 == NULL)
+               return -ENOMEM;
+
+       regmap = devm_regmap_init_i2c(i2c, &ak4497_regmap);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       i2c_set_clientdata(i2c, ak4497);
+       ak4497->i2c = i2c;
+
+       ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_ak4497,
+                                    &ak4497_dai[0], ARRAY_SIZE(ak4497_dai));
+       if (ret < 0)
+               kfree(ak4497);
+
+       return ret;
+}
+
+static int ak4497_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+
+       return 0;
+}
+
+static const struct of_device_id ak4497_i2c_dt_ids[] = {
+       { .compatible = "asahi-kasei,ak4497"},
+       { }
+};
+
+static const struct i2c_device_id ak4497_i2c_id[] = {
+       { "ak4497", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4497_i2c_id);
+
+static struct i2c_driver ak4497_i2c_driver = {
+       .driver = {
+               .name = "ak4497",
+               .of_match_table = of_match_ptr(ak4497_i2c_dt_ids),
+       },
+       .probe = ak4497_i2c_probe,
+       .remove = ak4497_i2c_remove,
+       .id_table = ak4497_i2c_id,
+};
+
+module_i2c_driver(ak4497_i2c_driver);
+
+MODULE_AUTHOR("Junichi Wakasugi <wakasugi.jb@om.asahi-kasei.co.jp>");
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>");
+MODULE_DESCRIPTION("ASoC ak4497 codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4497.h b/sound/soc/codecs/ak4497.h
new file mode 100644 (file)
index 0000000..49eb9d5
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * ak4497.h  --  audio driver for ak4497
+ *
+ * Copyright (C) 2016 Asahi Kasei Microdevices Corporation
+ * Copyright (C) 2017, NXP
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+
+#define SND_SOC_DAIFMT_DSD                0x101
+
+#ifndef _AK4497_H
+#define _AK4497_H
+
+#define AK4497_00_CONTROL1          0x00
+#define AK4497_01_CONTROL2          0x01
+#define AK4497_02_CONTROL3          0x02
+#define AK4497_03_LCHATT            0x03
+#define AK4497_04_RCHATT            0x04
+#define AK4497_05_CONTROL4          0x05
+#define AK4497_06_DSD1              0x06
+#define AK4497_07_CONTROL5          0x07
+#define AK4497_08_SOUNDCONTROL      0x08
+#define AK4497_09_DSD2              0x09
+#define AK4497_0A_CONTROL7          0x0A
+#define AK4497_0B_CONTROL8          0x0B
+#define AK4497_0C_RESERVED          0x0C
+#define AK4497_0D_RESERVED          0x0D
+#define AK4497_0E_RESERVED          0x0E
+#define AK4497_0F_RESERVED          0x0F
+#define AK4497_10_RESERVED          0x10
+#define AK4497_11_RESERVED          0x11
+#define AK4497_12_RESERVED          0x12
+#define AK4497_13_RESERVED          0x13
+#define AK4497_14_RESERVED          0x14
+#define AK4497_15_DFSREAD           0x15
+
+
+#define AK4497_MAX_REGISTERS   (AK4497_15_DFSREAD)
+
+/* Bitfield Definitions */
+
+/* AK4497_00_CONTROL1 (0x00) Fields */
+#define AK4497_DIF                                     0x0E
+#define AK4497_DIF_MSB_MODE        (2 << 1)
+#define AK4497_DIF_I2S_MODE     (3 << 1)
+#define AK4497_DIF_32BIT_MODE  (4 << 1)
+
+/* AK4497_02_CONTROL3 (0x02) Fields */
+#define AK4497_DIF_DSD                         0x80
+#define AK4497_DIF_DSD_MODE        (1 << 7)
+
+
+/* AK4497_01_CONTROL2 (0x01) Fields */
+/* AK4497_05_CONTROL4 (0x05) Fields */
+#define AK4497_DFS                             0x18
+#define AK4497_DFS_48KHZ               (0x0 << 3)  //  30kHz to 54kHz
+#define AK4497_DFS_96KHZ               (0x1 << 3)  //  54kHz to 108kHz
+#define AK4497_DFS_192KHZ              (0x2 << 3)  //  120kHz  to 216kHz
+#define AK4497_DFS_384KHZ              (0x0 << 3)
+#define AK4497_DFS_768KHZ              (0x1 << 3)
+
+#define AK4497_DFS2                            0x2
+#define AK4497_DFS2_48KHZ              (0x0 << 1)  // 30kHz to 216kHz
+#define AK4497_DFS2_384KHZ             (0x1 << 1)  // 384kHz, 768kHz to 108kHz
+
+#endif