summaryrefslogtreecommitdiff
path: root/sound/soc/codecs/ak4458.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/ak4458.c')
-rw-r--r--sound/soc/codecs/ak4458.c1196
1 files changed, 1196 insertions, 0 deletions
diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c
new file mode 100644
index 000000000000..752fc5706b20
--- /dev/null
+++ b/sound/soc/codecs/ak4458.c
@@ -0,0 +1,1196 @@
+/*
+ * ak4458.c -- audio driver for AK4458 DAC
+ *
+ * Copyright (C) 2016 Asahi Kasei Microdevices Corporation
+ * Copyright 2017 NXP
+ *
+ * Authors:
+ * Tsuyoshi Mutsuro
+ * Junichi Wakasugi <wakasugi.jb@om.asahi-kasei.co.jp>
+ * Mihai Serban <mihai.serban@nxp.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+
+#include "ak4458.h"
+
+#define AK4458_NUM_SUPPLIES 2
+static const char *ak4458_supply_names[AK4458_NUM_SUPPLIES] = {
+ "DVDD",
+ "AVDD",
+};
+
+/* AK4458 Codec Private Data */
+struct ak4458_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ int pdn_gpio;
+ int mute_gpio;
+ int sds; /* SDS2-0 bits */
+ int digfil; /* SSLOW, SD, SLOW bits */
+ int fs; /* sampling rate */
+ int lr[4]; /* (MONO, INVL, INVR, SELLR) x4ch */
+ int fmt;
+ int slots;
+ int slot_width;
+ struct regulator_bulk_data supplies[AK4458_NUM_SUPPLIES];
+};
+
+static const struct reg_default ak4458_reg_defaults[] = {
+ { 0x00, 0x0C }, /* 0x00 AK4458_00_CONTROL1 */
+ { 0x01, 0x22 }, /* 0x01 AK4458_01_CONTROL2 */
+ { 0x02, 0x00 }, /* 0x02 AK4458_02_CONTROL3 */
+ { 0x03, 0xFF }, /* 0x03 AK4458_03_LCHATT */
+ { 0x04, 0xFF }, /* 0x04 AK4458_04_RCHATT */
+ { 0x05, 0x00 }, /* 0x05 AK4458_05_CONTROL4 */
+ { 0x06, 0x00 }, /* 0x06 AK4458_06_DSD1 */
+ { 0x07, 0x03 }, /* 0x07 AK4458_07_CONTROL5 */
+ { 0x08, 0x00 }, /* 0x08 AK4458_08_SOUND_CONTROL */
+ { 0x09, 0x00 }, /* 0x09 AK4458_09_DSD2 */
+ { 0x0A, 0x0D }, /* 0x0A AK4458_0A_CONTROL6 */
+ { 0x0B, 0x0C }, /* 0x0B AK4458_0B_CONTROL7 */
+ { 0x0C, 0x00 }, /* 0x0C AK4458_0C_CONTROL8 */
+ { 0x0D, 0x00 }, /* 0x0D AK4458_0D_CONTROL9 */
+ { 0x0E, 0x50 }, /* 0x0E AK4458_0E_CONTROL10 */
+ { 0x0F, 0xFF }, /* 0x0F AK4458_0F_L2CHATT */
+ { 0x10, 0xFF }, /* 0x10 AK4458_10_R2CHATT */
+ { 0x11, 0xFF }, /* 0x11 AK4458_11_L3CHATT */
+ { 0x12, 0xFF }, /* 0x12 AK4458_12_R3CHATT */
+ { 0x13, 0xFF }, /* 0x13 AK4458_13_L4CHATT */
+ { 0x14, 0xFF }, /* 0x14 AK4458_14_R4CHATT */
+};
+
+static const struct regmap_range ak4458_spi_non_readable_reg_ranges[] = {
+ regmap_reg_range(AK4458_00_CONTROL1, AK4458_14_R4CHATT),
+};
+
+static const struct regmap_access_table ak4458_spi_readable_regs = {
+ .no_ranges = ak4458_spi_non_readable_reg_ranges,
+ .n_no_ranges = ARRAY_SIZE(ak4458_spi_non_readable_reg_ranges),
+};
+
+/*
+ * 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, 1);
+static DECLARE_TLV_DB_SCALE(ratt_tlv, -12750, 50, 1);
+
+/*
+ * DEM1 bit DEM0 bit Mode
+ * 0 0 44.1kHz
+ * 0 1 OFF (default)
+ * 1 0 48kHz
+ * 1 1 32kHz
+ */
+static const char * const ak4458_dem_select_texts[] = {
+ "44.1kHz", "OFF", "48kHz", "32kHz"
+};
+
+/*
+ * SSLOW, SD, SLOW bits Digital Filter Setting
+ * 0, 0, 0 : Sharp Roll-Off Filter
+ * 0, 0, 1 : Slow Roll-Off Filter
+ * 0, 1, 0 : Short delay Sharp Roll-Off Filter
+ * 0, 1, 1 : Short delay Slow Roll-Off Filter
+ * 1, *, * : Super Slow Roll-Off Filter
+ */
+static const char * const ak4458_digfil_select_texts[] = {
+ "Sharp Roll-Off Filter",
+ "Slow Roll-Off Filter",
+ "Short delay Sharp Roll-Off Filter",
+ "Short delay Slow Roll-Off Filter",
+ "Super Slow Roll-Off Filter"
+};
+
+/*
+ * DZFB: Inverting Enable of DZF
+ * 0: DZF goes H at Zero Detection
+ * 1: DZF goes L at Zero Detection
+ */
+static const char * const ak4458_dzfb_select_texts[] = {"H", "L"};
+
+/*
+ * SC1-0 bits: Sound Mode Setting
+ * 0 0 : Sound Mode 0
+ * 0 1 : Sound Mode 1
+ * 1 0 : Sound Mode 2
+ * 1 1 : Reserved
+ */
+static const char * const ak4458_sc_select_texts[] = {
+ "Sound Mode 0", "Sound Mode 1", "Sound Mode 2"
+};
+
+/*
+ * SDS2-0 bits: Output Data Select
+ * Refer to Data Sheet
+ */
+static const char * const ak4458_sds_select_texts[] = {
+ "Setting 0", "Setting 1", "Setting 2", "Setting 3",
+ "Setting 4", "Setting 5", "Setting 6", "Setting 7",
+};
+
+/*
+ * TDM1-0 bits: TDM Mode Setting
+ * 0 0 : Normal Mode
+ * 0 1 : TDM128 Mode
+ * 1 0 : TDM256 Mode
+ * 1 1 : TDM512 Mode
+ */
+static const char * const ak4458_tdm_select_texts[] = {
+ "Normal Mode", "TDM128 Mode", "TDM256 Mode", "TDM512 Mode"
+};
+
+/* FIR2-0 bits: FIR Filter Mode Setting */
+static const char * const ak4458_fir_select_texts[] = {
+ "Mode 0", "Mode 1", "Mode 2", "Mode 3",
+ "Mode 4", "Mode 5", "Mode 6", "Mode 7",
+};
+
+/* Mono and SELLR bit Setting (1~4) */
+static const char * const ak4458_dac_LR_select_texts[] = {
+ "Lch In, Rch In",
+ "Lch In, Rch In Invert",
+ "Lch In Invert, Rch In",
+ "Lch In Invert, Rch In Invert",
+ "Rch In, Lch In",
+ "Rch In, Lch In Invert",
+ "Rch In Invert, Lch In",
+ "Rch In Invert, Lch In Invert",
+ "Lch In, Lch In",
+ "Lch In, Lch In Invert",
+ "Lch In Invert, Lch In",
+ "Lch In Invert, Lch In Invert",
+ "Rch In, Rch In",
+ "Rch In, Rch In Invert",
+ "Rch In Invert, Rch In",
+ "Rch In Invert, Rch In Invert",
+};
+
+/* ATS1-0 bits Attenuation Speed */
+static const char * const ak4458_ats_select_texts[] = {
+ "4080/fs", "2040/fs", "510/fs", "255/fs",
+};
+
+/* DIF2 bit Audio Interface Format Setting(BICK fs) */
+static const char * const ak4458_dif_select_texts[] = {"32fs,48fs", "64fs",};
+
+static int get_DAC1_LR(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ak4458_priv *ak4458 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = ak4458->lr[0];
+
+ return 0;
+};
+
+static int get_DAC2_LR(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ak4458_priv *ak4458 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = ak4458->lr[1];
+
+ return 0;
+};
+
+static int get_DAC3_LR(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ak4458_priv *ak4458 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = ak4458->lr[2];
+
+ return 0;
+};
+
+static int get_DAC4_LR(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ak4458_priv *ak4458 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = ak4458->lr[3];
+
+ return 0;
+};
+
+static int get_digfil(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ak4458_priv *ak4458 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = ak4458->digfil;
+
+ return 0;
+};
+
+static int get_sds(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ak4458_priv *ak4458 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = ak4458->sds;
+
+ return 0;
+};
+
+static int set_digfil(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ak4458_priv *ak4458 = snd_soc_codec_get_drvdata(codec);
+
+ int reg_01, reg_02, reg_05, num;
+
+ num = ucontrol->value.enumerated.item[0];
+ if (num > 4)
+ return -EINVAL;
+
+ ak4458->digfil = num;
+
+ /* write SD bit */
+ reg_01 = snd_soc_read(codec, AK4458_01_CONTROL2);
+ reg_01 &= ~AK4458_SD_MASK;
+
+ reg_01 |= ((ak4458->digfil & 0x02) << 4);
+ snd_soc_write(codec, AK4458_01_CONTROL2, reg_01);
+
+ /* write SLOW bit */
+ reg_02 = snd_soc_read(codec, AK4458_02_CONTROL3);
+ reg_02 &= ~AK4458_SLOW_MASK;
+
+ reg_02 |= (ak4458->digfil & 0x01);
+ snd_soc_write(codec, AK4458_02_CONTROL3, reg_02);
+
+ /* write SSLOW bit */
+ reg_05 = snd_soc_read(codec, AK4458_05_CONTROL4);
+ reg_05 &= ~AK4458_SSLOW_MASK;
+
+ reg_05 |= ((ak4458->digfil & 0x04) >> 2);
+ snd_soc_write(codec, AK4458_05_CONTROL4, reg_05);
+
+ return 0;
+};
+
+static int set_sds(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ak4458_priv *ak4458 = snd_soc_codec_get_drvdata(codec);
+
+ int reg_0b, reg_0a;
+
+ if (ucontrol->value.enumerated.item[0] > 7)
+ return -EINVAL;
+
+ ak4458->sds = ucontrol->value.enumerated.item[0];
+
+ /* write SDS0 bit */
+ reg_0b = snd_soc_read(codec, AK4458_0B_CONTROL7);
+ reg_0b &= ~AK4458_SDS0__MASK;
+
+ reg_0b |= ((ak4458->sds & 0x01) << 4);
+ snd_soc_write(codec, AK4458_0B_CONTROL7, reg_0b);
+
+ /* write SDS1,2 bits */
+ reg_0a = snd_soc_read(codec, AK4458_0A_CONTROL6);
+ reg_0a &= ~AK4458_SDS12_MASK;
+
+ reg_0a |= ((ak4458->sds & 0x02) << 4);
+ reg_0a |= ((ak4458->sds & 0x04) << 2);
+ snd_soc_write(codec, AK4458_0A_CONTROL6, reg_0a);
+
+ return 0;
+
+};
+
+static int set_DAC1_LR(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ak4458_priv *ak4458 = snd_soc_codec_get_drvdata(codec);
+
+ int reg_02, reg_05;
+
+ if (ucontrol->value.enumerated.item[0] > 15)
+ return -EINVAL;
+
+ ak4458->lr[0] = ucontrol->value.enumerated.item[0];
+
+ /* write MONO1 and SELLR1 bits */
+ reg_02 = snd_soc_read(codec, AK4458_02_CONTROL3);
+ reg_02 &= ~AK4458_DAC1_LR_MASK;
+
+
+ reg_02 |= (ak4458->lr[0] & 0x08) << 0;
+ reg_02 |= (ak4458->lr[0] & 0x04) >> 1;
+ snd_soc_write(codec, AK4458_02_CONTROL3, reg_02);
+
+ /* write INVL1 and INVR1 bits */
+ reg_05 = snd_soc_read(codec, AK4458_05_CONTROL4);
+ reg_05 &= ~AK4458_DAC1_INV_MASK;
+
+ reg_05 |= (ak4458->lr[0] & 0x02) << 6;
+ reg_05 |= (ak4458->lr[0] & 0x01) << 6;
+ snd_soc_write(codec, AK4458_05_CONTROL4, reg_05);
+
+ return 0;
+
+};
+
+static int set_DAC2_LR(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ak4458_priv *ak4458 = snd_soc_codec_get_drvdata(codec);
+
+ int reg_0D, reg_05;
+
+ if (ucontrol->value.enumerated.item[0] > 15)
+ return -EINVAL;
+
+
+ ak4458->lr[1] = ucontrol->value.enumerated.item[0];
+
+ /* write MONO2 bit */
+ reg_0D = snd_soc_read(codec, AK4458_0D_CONTROL9);
+ reg_0D &= ~AK4458_DAC2_MASK1;
+
+ reg_0D |= (ak4458->lr[1] & 0x08) << 2;
+ snd_soc_write(codec, AK4458_0D_CONTROL9, reg_0D);
+
+ /* write SELLR2 and INVL1 and INVR1 bits */
+ reg_05 = snd_soc_read(codec, AK4458_05_CONTROL4);
+ reg_05 &= ~AK4458_DAC2_MASK2;
+
+ reg_05 |= (ak4458->lr[1] & 0x04) << 1;
+ reg_05 |= (ak4458->lr[1] & 0x02) << 4;
+ reg_05 |= (ak4458->lr[1] & 0x01) << 4;
+ snd_soc_write(codec, AK4458_05_CONTROL4, reg_05);
+
+ return 0;
+
+};
+
+static int set_DAC3_LR(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ak4458_priv *ak4458 = snd_soc_codec_get_drvdata(codec);
+
+ int reg_0C, reg_0D;
+
+ if (ucontrol->value.enumerated.item[0] > 15)
+ return -EINVAL;
+
+
+ ak4458->lr[2] = ucontrol->value.enumerated.item[0];
+
+ /* write MONO3 and SELLR3 bits */
+ reg_0D = snd_soc_read(codec, AK4458_0D_CONTROL9);
+ reg_0D &= ~AK4458_DAC3_LR_MASK;
+
+
+ reg_0D |= (ak4458->lr[2] & 0x08) << 3;
+ reg_0D |= (ak4458->lr[2] & 0x04) << 0;
+ snd_soc_write(codec, AK4458_0D_CONTROL9, reg_0D);
+
+ /* write INVL3 and INVR3 bits */
+ reg_0C = snd_soc_read(codec, AK4458_0C_CONTROL8);
+ reg_0C &= ~AK4458_DAC3_INV_MASK;
+
+ reg_0C |= (ak4458->lr[2] & 0x02) << 3;
+ reg_0C |= (ak4458->lr[2] & 0x01) << 5;
+ snd_soc_write(codec, AK4458_0C_CONTROL8, reg_0C);
+
+ return 0;
+
+};
+
+static int set_DAC4_LR(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ak4458_priv *ak4458 = snd_soc_codec_get_drvdata(codec);
+
+ int reg_0C, reg_0D;
+
+ if (ucontrol->value.enumerated.item[0] > 15)
+ return -EINVAL;
+
+
+ ak4458->lr[3] = ucontrol->value.enumerated.item[0];
+
+ /* write MONO4 and SELLR4 bits */
+ reg_0D = snd_soc_read(codec, AK4458_0D_CONTROL9);
+ reg_0D &= ~AK4458_DAC4_LR_MASK;
+
+
+ reg_0D |= (ak4458->lr[3] & 0x08) << 4;
+ reg_0D |= (ak4458->lr[3] & 0x04) << 1;
+ snd_soc_write(codec, AK4458_0D_CONTROL9, reg_0D);
+
+ /* write INVL4 and INVR4 bits */
+ reg_0C = snd_soc_read(codec, AK4458_0C_CONTROL8);
+ reg_0C &= ~AK4458_DAC4_INV_MASK;
+
+ reg_0C |= (ak4458->lr[3] & 0x02) << 5;
+ reg_0C |= (ak4458->lr[3] & 0x01) << 7;
+ snd_soc_write(codec, AK4458_0C_CONTROL8, reg_0C);
+
+ return 0;
+
+};
+
+static const struct soc_enum ak4458_dac_enum[] = {
+/*0*/ SOC_ENUM_SINGLE(AK4458_01_CONTROL2, 1,
+ ARRAY_SIZE(ak4458_dem_select_texts),
+ ak4458_dem_select_texts),
+/*1*/ SOC_ENUM_SINGLE(AK4458_0A_CONTROL6, 0,
+ ARRAY_SIZE(ak4458_dem_select_texts),
+ ak4458_dem_select_texts),
+/*2*/ SOC_ENUM_SINGLE(AK4458_0E_CONTROL10, 4,
+ ARRAY_SIZE(ak4458_dem_select_texts),
+ ak4458_dem_select_texts),
+/*3*/ SOC_ENUM_SINGLE(AK4458_0E_CONTROL10, 6,
+ ARRAY_SIZE(ak4458_dem_select_texts),
+ ak4458_dem_select_texts),
+/*4*/ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ak4458_digfil_select_texts),
+ ak4458_digfil_select_texts),
+/*5*/ SOC_ENUM_SINGLE(AK4458_02_CONTROL3, 2,
+ ARRAY_SIZE(ak4458_dzfb_select_texts),
+ ak4458_dzfb_select_texts),
+/*6*/ SOC_ENUM_SINGLE(AK4458_08_SOUND_CONTROL, 0,
+ ARRAY_SIZE(ak4458_sc_select_texts),
+ ak4458_sc_select_texts),
+/*7*/ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ak4458_sds_select_texts),
+ ak4458_sds_select_texts),
+/*8*/ SOC_ENUM_SINGLE(AK4458_0C_CONTROL8, 0,
+ ARRAY_SIZE(ak4458_fir_select_texts),
+ ak4458_fir_select_texts),
+/*9*/ SOC_ENUM_SINGLE(AK4458_0A_CONTROL6, 6,
+ ARRAY_SIZE(ak4458_tdm_select_texts),
+ ak4458_tdm_select_texts),
+/*10*/ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ak4458_dac_LR_select_texts),
+ ak4458_dac_LR_select_texts),
+/*11*/ SOC_ENUM_SINGLE(AK4458_0B_CONTROL7, 6,
+ ARRAY_SIZE(ak4458_ats_select_texts),
+ ak4458_ats_select_texts),
+/*12*/ SOC_ENUM_SINGLE(AK4458_00_CONTROL1, 3,
+ ARRAY_SIZE(ak4458_dif_select_texts),
+ ak4458_dif_select_texts),
+};
+
+static const struct snd_kcontrol_new ak4458_snd_controls[] = {
+ SOC_SINGLE_TLV("AK4458 L1ch Digital Volume",
+ AK4458_03_LCHATT, 0/*shift*/, 0xFF/*max value*/,
+ 0/*invert*/, latt_tlv),
+ SOC_SINGLE_TLV("AK4458 R1ch Digital Volume",
+ AK4458_04_RCHATT, 0, 0xFF, 0, ratt_tlv),
+ SOC_SINGLE_TLV("AK4458 L2ch Digital Volume",
+ AK4458_0F_L2CHATT, 0/*shift*/, 0xFF/*max value*/,
+ 0/*invert*/, latt_tlv),
+ SOC_SINGLE_TLV("AK4458 R2ch Digital Volume",
+ AK4458_10_R2CHATT, 0, 0xFF, 0, ratt_tlv),
+ SOC_SINGLE_TLV("AK4458 L3ch Digital Volume",
+ AK4458_11_L3CHATT, 0/*shift*/, 0xFF/*max value*/,
+ 0/*invert*/, latt_tlv),
+ SOC_SINGLE_TLV("AK4458 R3ch Digital Volume",
+ AK4458_12_R3CHATT, 0, 0xFF, 0, ratt_tlv),
+ SOC_SINGLE_TLV("AK4458 L4ch Digital Volume",
+ AK4458_13_L4CHATT, 0/*shift*/, 0xFF/*max value*/,
+ 0/*invert*/, latt_tlv),
+ SOC_SINGLE_TLV("AK4458 R4ch Digital Volume",
+ AK4458_14_R4CHATT, 0, 0xFF, 0, ratt_tlv),
+
+ SOC_ENUM("AK4458 De-emphasis Response DAC1", ak4458_dac_enum[0]),
+ SOC_ENUM("AK4458 De-emphasis Response DAC2", ak4458_dac_enum[1]),
+ SOC_ENUM("AK4458 De-emphasis Response DAC3", ak4458_dac_enum[2]),
+ SOC_ENUM("AK4458 De-emphasis Response DAC4", ak4458_dac_enum[3]),
+ SOC_ENUM_EXT("AK4458 Digital Filter Setting", ak4458_dac_enum[4],
+ get_digfil, set_digfil),
+ SOC_ENUM("AK4458 Inverting Enable of DZFB", ak4458_dac_enum[5]),
+ SOC_ENUM("AK4458 Sound Mode", ak4458_dac_enum[6]),
+ SOC_ENUM_EXT("AK4458 SDS Setting", ak4458_dac_enum[7],
+ get_sds, set_sds),
+ SOC_ENUM("AK4458 FIR Filter Mode Setting", ak4458_dac_enum[8]),
+ SOC_ENUM("AK4458 TDM Mode Setting", ak4458_dac_enum[9]),
+ SOC_ENUM_EXT("AK4458 DAC1 LRch Setting", ak4458_dac_enum[10],
+ get_DAC1_LR, set_DAC1_LR),
+ SOC_ENUM_EXT("AK4458 DAC2 LRch Setting", ak4458_dac_enum[10],
+ get_DAC2_LR, set_DAC2_LR),
+ SOC_ENUM_EXT("AK4458 DAC3 LRch Setting", ak4458_dac_enum[10],
+ get_DAC3_LR, set_DAC3_LR),
+ SOC_ENUM_EXT("AK4458 DAC4 LRch Setting", ak4458_dac_enum[10],
+ get_DAC4_LR, set_DAC4_LR),
+ SOC_ENUM("AK4458 Attenuation transition Time Setting",
+ ak4458_dac_enum[11]),
+ SOC_ENUM("AK4458 BICK fs Setting", ak4458_dac_enum[12]),
+};
+
+static const char * const ak4458_dac_select_texts[] = { "OFF", "ON" };
+
+static const struct soc_enum ak4458_dac_mux_enum =
+ SOC_ENUM_SINGLE(0, 0,
+ ARRAY_SIZE(ak4458_dac_select_texts),
+ ak4458_dac_select_texts);
+static const struct snd_kcontrol_new ak4458_dac1_mux_control =
+ SOC_DAPM_ENUM("DAC1 Switch", ak4458_dac_mux_enum);
+static const struct snd_kcontrol_new ak4458_dac2_mux_control =
+ SOC_DAPM_ENUM("DAC2 Switch", ak4458_dac_mux_enum);
+static const struct snd_kcontrol_new ak4458_dac3_mux_control =
+ SOC_DAPM_ENUM("DAC3 Switch", ak4458_dac_mux_enum);
+static const struct snd_kcontrol_new ak4458_dac4_mux_control =
+ SOC_DAPM_ENUM("DAC4 Switch", ak4458_dac_mux_enum);
+
+/* ak4458 dapm widgets */
+static const struct snd_soc_dapm_widget ak4458_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("AK4458 DAC1", NULL, AK4458_0A_CONTROL6, 2, 0),/*pw*/
+ SND_SOC_DAPM_AIF_IN("AK4458 SDTI", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("AK4458 AOUTA"),
+
+ SND_SOC_DAPM_DAC("AK4458 DAC2", NULL, AK4458_0A_CONTROL6, 3, 0),/*pw*/
+ SND_SOC_DAPM_OUTPUT("AK4458 AOUTB"),
+
+ SND_SOC_DAPM_DAC("AK4458 DAC3", NULL, AK4458_0B_CONTROL7, 2, 0),/*pw*/
+ SND_SOC_DAPM_OUTPUT("AK4458 AOUTC"),
+
+ SND_SOC_DAPM_DAC("AK4458 DAC4", NULL, AK4458_0B_CONTROL7, 3, 0),/*pw*/
+ SND_SOC_DAPM_OUTPUT("AK4458 AOUTD"),
+
+ SND_SOC_DAPM_MUX("DAC1 to AOUTA", SND_SOC_NOPM, 0, 0,
+ &ak4458_dac1_mux_control),/*nopm*/
+ SND_SOC_DAPM_MUX("DAC2 to AOUTB", SND_SOC_NOPM, 0, 0,
+ &ak4458_dac2_mux_control),/*nopm*/
+ SND_SOC_DAPM_MUX("DAC3 to AOUTC", SND_SOC_NOPM, 0, 0,
+ &ak4458_dac3_mux_control),/*nopm*/
+ SND_SOC_DAPM_MUX("DAC4 to AOUTD", SND_SOC_NOPM, 0, 0,
+ &ak4458_dac4_mux_control),/*nopm*/
+};
+
+static const struct snd_soc_dapm_route ak4458_intercon[] = {
+ {"DAC1 to AOUTA", "ON", "AK4458 SDTI"},
+ {"AK4458 DAC1", NULL, "DAC1 to AOUTA"},
+ {"AK4458 AOUTA", NULL, "AK4458 DAC1"},
+
+ {"DAC2 to AOUTB", "ON", "AK4458 SDTI"},
+ {"AK4458 DAC2", NULL, "DAC2 to AOUTB"},
+ {"AK4458 AOUTB", NULL, "AK4458 DAC2"},
+
+ {"DAC3 to AOUTC", "ON", "AK4458 SDTI"},
+ {"AK4458 DAC3", NULL, "DAC3 to AOUTC"},
+ {"AK4458 AOUTC", NULL, "AK4458 DAC3"},
+
+ {"DAC4 to AOUTD", "ON", "AK4458 SDTI"},
+ {"AK4458 DAC4", NULL, "DAC4 to AOUTD"},
+ {"AK4458 AOUTD", NULL, "AK4458 DAC4"},
+
+};
+
+static int ak4458_rstn_control(struct snd_soc_codec *codec, int bit)
+{
+ u8 rstn;
+
+ dev_dbg(codec->dev, "%s(%d)\n", __func__, __LINE__);
+
+ rstn = snd_soc_read(codec, AK4458_00_CONTROL1);
+ rstn &= ~AK4458_RSTN_MASK;
+
+ if (bit)
+ rstn |= AK4458_RSTN;
+
+ return snd_soc_write(codec, AK4458_00_CONTROL1, rstn);
+}
+
+static int ak4458_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 ak4458_priv *ak4458 = snd_soc_codec_get_drvdata(codec);
+ u8 format;
+ int pcm_width = max(params_physical_width(params), ak4458->slot_width);
+ int ret;
+
+#ifdef AK4458_ACKS_USE_MANUAL_MODE
+ u8 dfs1, dfs2;
+#endif
+ int nfs1;
+
+ dev_dbg(dai->dev, "%s(%d)\n", __func__, __LINE__);
+
+ format = snd_soc_read(codec, AK4458_00_CONTROL1);
+ format &= ~AK4458_DIF_MASK;
+
+ nfs1 = params_rate(params);
+ ak4458->fs = nfs1;
+
+#ifdef AK4458_ACKS_USE_MANUAL_MODE
+ dfs1 = snd_soc_read(codec, AK4458_01_CONTROL2);
+ dfs1 &= ~AK4458_DFS01_MASK;
+
+ dfs2 = snd_soc_read(codec, AK4458_05_CONTROL4);
+ dfs2 &= ~AK4458_DFS2__MASK;
+
+ switch (nfs1) {
+ case 8000:
+ case 11025:
+ case 16000:
+ case 22050:
+ case 32000:
+ case 44100:
+ case 48000:
+ dfs1 |= AK4458_DFS01_48KHZ;
+ dfs2 |= AK4458_DFS2__48KHZ;
+ break;
+ case 88200:
+ case 96000:
+ dfs1 |= AK4458_DFS01_96KHZ;
+ dfs2 |= AK4458_DFS2__96KHZ;
+ break;
+ case 176400:
+ case 192000:
+ dfs1 |= AK4458_DFS01_192KHZ;
+ dfs2 |= AK4458_DFS2__192KHZ;
+ break;
+ case 352800:
+ case 384000:
+ dfs1 |= AK4458_DFS01_384KHZ;
+ dfs2 |= AK4458_DFS2__384KHZ;
+ break;
+ case 705600:
+ case 768000:
+ dfs1 |= AK4458_DFS01_768KHZ;
+ dfs2 |= AK4458_DFS2__768KHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, AK4458_01_CONTROL2, dfs1);
+ snd_soc_write(codec, AK4458_05_CONTROL4, dfs2);
+
+ ret = ak4458_rstn_control(codec, 0);
+ if (ret)
+ return ret;
+
+ ak4458_rstn_control(codec, 1);
+ if (ret)
+ return ret;
+#else
+ snd_soc_update_bits(codec, AK4458_00_CONTROL1, 0x80, 0x80);
+#endif
+
+ switch (pcm_width) {
+ case 16:
+ if (ak4458->fmt == SND_SOC_DAIFMT_I2S)
+ format |= AK4458_DIF_24BIT_I2S;
+ else
+ format |= AK4458_DIF_16BIT_LSB;
+ break;
+ case 32:
+ if (ak4458->fmt == SND_SOC_DAIFMT_I2S)
+ format |= AK4458_DIF_32BIT_I2S;
+ else if (ak4458->fmt == SND_SOC_DAIFMT_LEFT_J)
+ format |= AK4458_DIF_32BIT_MSB;
+ else if (ak4458->fmt == SND_SOC_DAIFMT_RIGHT_J)
+ format |= AK4458_DIF_32BIT_LSB;
+ else if (ak4458->fmt == SND_SOC_DAIFMT_DSP_B)
+ format |= AK4458_DIF_32BIT_MSB;
+ else
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, AK4458_00_CONTROL1, format);
+
+ ret = ak4458_rstn_control(codec, 0);
+ if (ret)
+ return ret;
+
+ ret = ak4458_rstn_control(codec, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ak4458_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ dev_dbg(dai->dev, "%s(%d)\n", __func__, __LINE__);
+
+ return 0;
+}
+
+static int ak4458_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak4458_priv *ak4458 = snd_soc_codec_get_drvdata(codec);
+ u8 format;
+ int ret;
+
+ /* set master/slave audio interface */
+ format = snd_soc_read(codec, AK4458_00_CONTROL1);
+
+ dev_dbg(dai->dev, "%s(%d) addr 00H = %02X\n",
+ __func__, __LINE__, format);
+
+ format &= ~AK4458_DIF_MASK;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS: /* Slave Mode */
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM: /* Master Mode is not supported */
+ case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ default:
+ dev_err(codec->dev, "Master mode unsupported\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ak4458->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ak4458->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ dev_err(codec->dev, "Audio format 0x%02X unsupported\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ /* set format */
+ dev_dbg(dai->dev, "%s(%d) addr 00H = %02X\n",
+ __func__, __LINE__, format);
+ snd_soc_write(codec, AK4458_00_CONTROL1, format);
+
+ ret = ak4458_rstn_control(codec, 0);
+ if (ret)
+ return ret;
+
+ ret = ak4458_rstn_control(codec, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ak4458_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *codec_dai)
+{
+ int ret = 0;
+
+ dev_dbg(codec_dai->dev, "%s(%d)\n", __func__, __LINE__);
+ return ret;
+}
+
+
+static int ak4458_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ dev_dbg(codec->dev, "%s(%d)\n", __func__, __LINE__);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ case SND_SOC_BIAS_STANDBY:
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+
+ return 0;
+}
+
+static const int att_speed[] = { 4080, 2040, 510, 255 };
+
+static int ak4458_set_dai_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak4458_priv *ak4458 = snd_soc_codec_get_drvdata(codec);
+ int nfs, ndt, ret, reg;
+ int ats;
+
+ nfs = ak4458->fs;
+
+ reg = snd_soc_read(codec, AK4458_0B_CONTROL7);
+ ats = (reg & 0xC0) >> 6;
+
+ dev_dbg(dai->dev, "%s mute[%s] nfs[%d]\n", __func__,
+ mute ? "ON":"OFF", nfs);
+
+ ndt = att_speed[ats] / (nfs / 1000);
+
+ if (mute) { /* SMUTE: 1 , MUTE */
+ ret = snd_soc_update_bits(codec, AK4458_01_CONTROL2, 0x01, 1);
+ mdelay(ndt);
+ dev_dbg(dai->dev, "%s(%d) mdelay(%d ms)\n",
+ __func__, __LINE__, ndt);
+
+ if (gpio_is_valid(ak4458->mute_gpio))
+ gpio_set_value_cansleep(ak4458->mute_gpio, 1);
+
+ dev_dbg(dai->dev, "%s External Mute = ON\n", __func__);
+ } else { /* SMUTE: 0 ,NORMAL operation */
+ if (gpio_is_valid(ak4458->mute_gpio))
+ gpio_set_value_cansleep(ak4458->mute_gpio, 0);
+
+ dev_dbg(dai->dev, "%s External Mute = OFF\n", __func__);
+ ret = snd_soc_update_bits(codec, AK4458_01_CONTROL2, 0x01, 0);
+ mdelay(ndt);
+ dev_dbg(dai->dev, "%s(%d) mdelay(%d ms)\n",
+ __func__, __LINE__, ndt);
+ }
+ dev_dbg(dai->dev, "%s(%d) ret[%d]\n", __func__, __LINE__, ret);
+
+ return 0;
+}
+
+static int ak4458_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak4458_priv *ak4458 = snd_soc_codec_get_drvdata(codec);
+ int tdm_mode = 0;
+ int reg;
+
+ ak4458->slots = slots;
+ ak4458->slot_width = slot_width;
+
+ switch(slots * slot_width) {
+ case 128:
+ tdm_mode = 1;
+ break;
+ case 256:
+ tdm_mode = 2;
+ break;
+ case 512:
+ tdm_mode = 3;
+ break;
+ default:
+ tdm_mode = 0;
+ break;
+ }
+
+ reg = snd_soc_read(codec, AK4458_0A_CONTROL6);
+ reg &= ~(0x3 << 6);
+ reg |= tdm_mode << 6;
+ snd_soc_write(codec, AK4458_0A_CONTROL6, reg);
+
+ return 0;
+}
+
+
+
+#define AK4458_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)
+ /* | SNDRV_PCM_RATE_384000 | SNDRV_PCM_RATE_768000 */
+
+#define AK4458_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const unsigned int ak4458_rates[] = {
+ 8000, 11025, 16000, 22050,
+ 32000, 44100, 48000, 88200,
+ 96000, 176400, 192000, 352800,
+ 384000, 705600, 768000, 1411200,
+ 2822400,
+};
+
+static const struct snd_pcm_hw_constraint_list ak4458_rate_constraints = {
+ .count = ARRAY_SIZE(ak4458_rates),
+ .list = ak4458_rates,
+};
+
+static int ak4458_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai) {
+ int ret;
+
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &ak4458_rate_constraints);
+
+ return ret;
+}
+
+static struct snd_soc_dai_ops ak4458_dai_ops = {
+ .startup = ak4458_startup,
+ .hw_params = ak4458_hw_params,
+ .set_sysclk = ak4458_set_dai_sysclk,
+ .set_fmt = ak4458_set_dai_fmt,
+ .trigger = ak4458_trigger,
+ .digital_mute = ak4458_set_dai_mute,
+ .set_tdm_slot = ak4458_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver ak4458_dai = {
+ .name = "ak4458-aif",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = AK4458_FORMATS,
+ },
+ .ops = &ak4458_dai_ops,
+};
+
+static int ak4458_init_reg(struct snd_soc_codec *codec)
+{
+ struct ak4458_priv *ak4458 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ dev_dbg(codec->dev, "%s(%d)\n", __func__, __LINE__);
+
+ /* External Mute ON */
+ if (gpio_is_valid(ak4458->mute_gpio))
+ gpio_set_value_cansleep(ak4458->mute_gpio, 1);
+
+ if (gpio_is_valid(ak4458->pdn_gpio)) {
+ gpio_set_value_cansleep(ak4458->pdn_gpio, 0);
+ usleep_range(1000, 2000);
+ gpio_set_value_cansleep(ak4458->pdn_gpio, 1);
+ usleep_range(1000, 2000);
+ }
+
+ ak4458_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+#ifndef AK4458_ACKS_USE_MANUAL_MODE
+ snd_soc_update_bits(codec, AK4458_00_CONTROL1,
+ 0x80, 0x80); /* ACKS bit = 1; 10000000 */
+ dev_dbg(codec->dev, "%s ACKS bit = 1\n", __func__);
+#endif
+
+ ret = ak4458_rstn_control(codec, 0);
+ if (ret)
+ return ret;
+
+ ret = ak4458_rstn_control(codec, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ak4458_codec_probe(struct snd_soc_codec *codec)
+{
+ struct ak4458_priv *ak4458 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ dev_dbg(codec->dev, "%s(%d)\n", __func__, __LINE__);
+
+ ret = ak4458_init_reg(codec);
+
+ ak4458->fs = 48000;
+
+ return ret;
+}
+
+static int ak4458_codec_remove(struct snd_soc_codec *codec)
+{
+ dev_dbg(codec->dev, "%s(%d)\n", __func__, __LINE__);
+
+ ak4458_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ak4458_runtime_suspend(struct device *dev)
+{
+ struct ak4458_priv *ak4458 = dev_get_drvdata(dev);
+
+ regcache_cache_only(ak4458->regmap, true);
+
+ if (gpio_is_valid(ak4458->pdn_gpio)) {
+ gpio_set_value_cansleep(ak4458->pdn_gpio, 0);
+ usleep_range(1000, 2000);
+ }
+
+ if (gpio_is_valid(ak4458->mute_gpio))
+ gpio_set_value_cansleep(ak4458->mute_gpio, 0);
+
+ return 0;
+}
+
+static int ak4458_runtime_resume(struct device *dev)
+{
+ struct ak4458_priv *ak4458 = dev_get_drvdata(dev);
+
+ if (gpio_is_valid(ak4458->mute_gpio))
+ gpio_set_value_cansleep(ak4458->mute_gpio, 1);
+
+ if (gpio_is_valid(ak4458->pdn_gpio)) {
+ gpio_set_value_cansleep(ak4458->pdn_gpio, 0);
+ usleep_range(1000, 2000);
+ gpio_set_value_cansleep(ak4458->pdn_gpio, 1);
+ usleep_range(1000, 2000);
+ }
+
+ regcache_cache_only(ak4458->regmap, false);
+ regcache_mark_dirty(ak4458->regmap);
+
+ return regcache_sync(ak4458->regmap);
+}
+#endif /* CONFIG_PM */
+
+struct snd_soc_codec_driver soc_codec_dev_ak4458 = {
+ .probe = ak4458_codec_probe,
+ .remove = ak4458_codec_remove,
+ .set_bias_level = ak4458_set_bias_level,
+
+ .component_driver = {
+ .controls = ak4458_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4458_snd_controls),
+ .dapm_widgets = ak4458_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4458_dapm_widgets),
+ .dapm_routes = ak4458_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4458_intercon),
+ },
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ak4458);
+
+const struct regmap_config ak4458_i2c_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = AK4458_14_R4CHATT,
+ .reg_defaults = ak4458_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4458_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(ak4458_i2c_regmap_config);
+
+const struct regmap_config ak4458_spi_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = AK4458_14_R4CHATT,
+ .rd_table = &ak4458_spi_readable_regs,
+ .reg_defaults = ak4458_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4458_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(ak4458_spi_regmap_config);
+
+int ak4458_probe(struct device *dev, struct regmap *regmap)
+{
+ struct ak4458_priv *ak4458;
+ struct device_node *np = dev->of_node;
+ int ret;
+ int i;
+
+ ak4458 = devm_kzalloc(dev, sizeof(*ak4458), GFP_KERNEL);
+ if (!ak4458)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, ak4458);
+
+ ak4458->dev = dev;
+ ak4458->regmap = regmap;
+
+ ak4458->pdn_gpio = of_get_named_gpio(np, "ak4458,pdn-gpio", 0);
+ if (gpio_is_valid(ak4458->pdn_gpio)) {
+ ret = devm_gpio_request_one(dev, ak4458->pdn_gpio,
+ GPIOF_OUT_INIT_LOW, "ak4458,pdn");
+ if (ret) {
+ dev_err(dev, "unable to get pdn gpio\n");
+ return ret;
+ }
+ }
+
+ ak4458->mute_gpio = of_get_named_gpio(np, "ak4458,mute_gpio", 0);
+ if (gpio_is_valid(ak4458->mute_gpio)) {
+ ret = devm_gpio_request_one(dev, ak4458->mute_gpio,
+ GPIOF_OUT_INIT_LOW, "ak4458,mute");
+ if (ret) {
+ dev_err(dev, "unable to get mute gpio\n");
+ return ret;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ak4458->supplies); i++)
+ ak4458->supplies[i].supply = ak4458_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ak4458->supplies),
+ ak4458->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ak4458->supplies),
+ ak4458->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(dev, &soc_codec_dev_ak4458,
+ &ak4458_dai, 1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register CODEC: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ak4458_probe);
+
+void ak4458_remove(struct device *dev)
+{
+ snd_soc_unregister_codec(dev);
+}
+EXPORT_SYMBOL_GPL(ak4458_remove);
+
+const struct dev_pm_ops ak4458_pm = {
+ SET_RUNTIME_PM_OPS(ak4458_runtime_suspend, ak4458_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+EXPORT_SYMBOL_GPL(ak4458_pm);
+
+MODULE_AUTHOR("Junichi Wakasugi <wakasugi.jb@om.asahi-kasei.co.jp>");
+MODULE_AUTHOR("Mihai Serban <mihai.serban@nxp.com>");
+MODULE_DESCRIPTION("ASoC AK4458 DAC driver");
+MODULE_LICENSE("GPL");