]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00141217-8 ASoC: SGTL5000: Add DAP support for sgtl5000 codec
authorFrank Li <Frank.Li@freescale.com>
Tue, 29 Mar 2011 01:45:35 +0000 (09:45 +0800)
committerOliver Wendt <ow@karo-electronics.de>
Mon, 30 Sep 2013 12:08:48 +0000 (14:08 +0200)
upgrade to 2.6.38 kernel
Add Digital Audio Process(DAP) for sgtl5000 codec
Audio: imx53,loco,audio: make Loco sgtl5000 codec work
config audmux when ssi probed, and fix sdma watermark settings

Signed-off-by: Zeng Zhaoming <b32542@freescale.com>
sound/soc/codecs/sgtl5000.c
sound/soc/imx/Kconfig
sound/soc/imx/Makefile
sound/soc/imx/imx-sgtl5000.c [new file with mode: 0644]

index c49317c255d1b3cfcaea69a303148c2e2ec4f872..556fd939a05b67fe913cf45a2b9360b6e93cc410 100644 (file)
@@ -171,6 +171,28 @@ static int small_pop_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static int dap_enable_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(w->codec, SGTL5000_DAP_CTRL,
+                       0x1, 0x1);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(w->codec, SGTL5000_DAP_CTRL,
+                       0x1, 0);
+               snd_soc_dapm_disable_pin(&w->codec->dapm, "DAP_MIXER Mixer Channel");
+               snd_soc_dapm_sync(&w->codec->dapm);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
 /* input sources for ADC */
 static const char *adc_mux_text[] = {
        "MIC_IN", "LINE_IN"
@@ -183,15 +205,50 @@ static const struct snd_kcontrol_new adc_mux =
 SOC_DAPM_ENUM("Capture Mux", adc_enum);
 
 /* input sources for DAC */
-static const char *dac_mux_text[] = {
+static const char *hp_mux_text[] = {
        "DAC", "LINE_IN"
 };
 
-static const struct soc_enum dac_enum =
-SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 6, 2, dac_mux_text);
+static const struct soc_enum hp_enum =
+SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 6, 2, hp_mux_text);
 
-static const struct snd_kcontrol_new dac_mux =
-SOC_DAPM_ENUM("Headphone Mux", dac_enum);
+static const struct snd_kcontrol_new hp_mux =
+SOC_DAPM_ENUM("Headphone Mux", hp_enum);
+
+static const char *dap_in_mux_text[] = {
+       "ADC", "I2S_IN"
+};
+static const struct soc_enum dap_in_enum =
+SOC_ENUM_SINGLE(SGTL5000_CHIP_SSS_CTRL, 6, 2, dap_in_mux_text);
+
+static const struct snd_kcontrol_new dap_in_mux =
+SOC_DAPM_ENUM("DAP input mux", dap_in_enum);
+
+static const struct soc_enum dap_mixer_enum =
+SOC_ENUM_SINGLE(SGTL5000_CHIP_SSS_CTRL, 8, 2, dap_in_mux_text);
+
+static const struct snd_kcontrol_new dap_mixer_mux =
+SOC_DAPM_ENUM("DAP mixer channel mux", dap_mixer_enum);
+
+static const char *dap_out_mux_text[] = {
+       "ADC", "I2S_IN", "reserved", "DAP"
+};
+
+static const struct soc_enum dac_in_enum =
+SOC_ENUM_SINGLE(SGTL5000_CHIP_SSS_CTRL, 4, 4, dap_out_mux_text);
+
+static const struct snd_kcontrol_new dac_in_mux =
+SOC_DAPM_ENUM("DAC input mux", dac_in_enum);
+
+static const struct soc_enum i2s_out_enum =
+SOC_ENUM_SINGLE(SGTL5000_CHIP_SSS_CTRL, 0, 4, dap_out_mux_text);
+
+static const struct snd_kcontrol_new i2s_out_mux =
+SOC_DAPM_ENUM("i2s output mux", i2s_out_enum);
+
+static const struct snd_kcontrol_new dap_mixer_controls[] = {
+SOC_DAPM_SINGLE("Mixer Channel", SGTL5000_DAP_CTRL, 4, 1, 0),
+};
 
 static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
        SND_SOC_DAPM_INPUT("LINE_IN"),
@@ -200,6 +257,16 @@ static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
        SND_SOC_DAPM_OUTPUT("HP_OUT"),
        SND_SOC_DAPM_OUTPUT("LINE_OUT"),
 
+       /* aif for i2s input */
+       SND_SOC_DAPM_AIF_IN("AIFIN", "Playback",
+                               0, SGTL5000_CHIP_DIG_POWER,
+                               0, 0),
+
+       /* aif for i2s output */
+       SND_SOC_DAPM_AIF_OUT("AIFOUT", "Capture",
+                               0, SGTL5000_CHIP_DIG_POWER,
+                               1, 0),
+
        SND_SOC_DAPM_MICBIAS_E("Mic Bias", SGTL5000_CHIP_MIC_CTRL, 8, 0,
                                mic_bias_event,
                                SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
@@ -212,20 +279,19 @@ static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
                        SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
 
        SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0, &adc_mux),
-       SND_SOC_DAPM_MUX("Headphone Mux", SND_SOC_NOPM, 0, 0, &dac_mux),
+       SND_SOC_DAPM_MUX("Headphone Mux", SND_SOC_NOPM, 0, 0, &hp_mux),
+       SND_SOC_DAPM_MUX_E("DAP_IN", SGTL5000_CHIP_DIG_POWER, 4, 0, &dap_in_mux,
+                               dap_enable_event,
+                               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_MUX("MIX_MUX", SND_SOC_NOPM, 0, 0, &dap_mixer_mux),
+       SND_SOC_DAPM_MUX("DAC_IN", SND_SOC_NOPM, 0, 0, &dac_in_mux),
+       SND_SOC_DAPM_MUX("I2S_OUT", SND_SOC_NOPM, 0, 0, &i2s_out_mux),
 
-       /* aif for i2s input */
-       SND_SOC_DAPM_AIF_IN("AIFIN", "Playback",
-                               0, SGTL5000_CHIP_DIG_POWER,
-                               0, 0),
-
-       /* aif for i2s output */
-       SND_SOC_DAPM_AIF_OUT("AIFOUT", "Capture",
-                               0, SGTL5000_CHIP_DIG_POWER,
-                               1, 0),
+       SND_SOC_DAPM_MIXER("DAP_MIXER", SND_SOC_NOPM, 0, 0,
+               &dap_mixer_controls[0],
+               ARRAY_SIZE(dap_mixer_controls)),
 
        SND_SOC_DAPM_ADC("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0),
-
        SND_SOC_DAPM_DAC("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0),
 };
 
@@ -235,9 +301,26 @@ static const struct snd_soc_dapm_route audio_map[] = {
        {"Capture Mux", "MIC_IN", "MIC_IN"},    /* mic_in --> adc_mux */
 
        {"ADC", NULL, "Capture Mux"},           /* adc_mux --> adc */
-       {"AIFOUT", NULL, "ADC"},                /* adc --> i2s_out */
 
-       {"DAC", NULL, "AIFIN"},                 /* i2s-->dac,skip audio mux */
+       {"DAP_IN", "ADC", "ADC"},
+       {"DAP_IN", "I2S_IN", "AIFIN"},
+
+       {"MIX_MUX", "ADC", "ADC"},
+       {"MIX_MUX", "I2S_IN", "AIFIN"},
+
+       {"DAP_MIXER", NULL, "DAP_IN"},
+       {"DAP_MIXER", "Mixer Channel", "MIX_MUX"},
+
+       {"I2S_OUT", "ADC", "ADC"},              /* ADC --> I2S_OUT */
+       {"I2S_OUT", "I2S_IN", "AIFIN"},         /* ADC --> Audio Switch */
+       {"I2S_OUT", "DAP", "DAP_MIXER"},        /* ADC --> Audio Switch */
+       {"AIFOUT", NULL, "I2S_OUT"},            /* ADC --> Audio Switch */
+
+       {"DAC_IN", "ADC", "ADC"},               /* i2s-->dac,skip audio mux */
+       {"DAC_IN", "I2S_IN", "AIFIN"},          /* i2s-->dac,skip audio mux */
+       {"DAC_IN", "DAP", "DAP_MIXER"},         /* i2s-->dac,skip audio mux */
+       {"DAC", NULL, "DAC_IN"},
+
        {"Headphone Mux", "DAC", "DAC"},        /* dac --> hp_mux */
        {"LO", NULL, "DAC"},                    /* dac --> line_out */
 
@@ -363,6 +446,117 @@ static int dac_put_volsw(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
+/*
+ * we need to caculator threshold dB by:
+ * register_value = ((10^(dB/20))*0.636)*2^15 ==>
+ * dB = (fls(register_value) - 14.347)*6.02
+ */
+static int avc_thrd_get(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u16 reg = snd_soc_read(codec, SGTL5000_DAP_AVC_THRESHOLD);
+       int l;
+       /* 1/1000 of dB */
+       int fraction[5] = {585, 322, 170, 87, 44};
+       int i;
+       int tmp;
+
+       if (!reg) {
+               ucontrol->value.integer.value[0] = 96;
+               ucontrol->value.integer.value[1] = 96;
+               return 0;
+       }
+
+       /* caculate fls(register_value) */
+       l = fls(reg) - 1;
+       tmp = l * 1000; /* scale-up 1000 times */
+       for (i = l-1; i >= 0 && i >= l-5; i--) {
+               if (reg & 0x1 << i)
+                       tmp += fraction[l-1-i];
+       }
+       tmp = ((tmp - 14347) * 6) / 1000;
+       tmp = clamp(-tmp, 0, 96);
+
+       ucontrol->value.integer.value[0] = tmp;
+       ucontrol->value.integer.value[1] = tmp;
+
+       return 0;
+}
+
+/*
+ * we need to caculator threshold dB by:
+ * register_value = ((10^(dB/20))*0.636)*2^15
+ *               = 2^(3.322*db/20 + 15) * 0.636
+ *               = 2^(3.322*db/20 + 15 - 0.653)
+ */
+static int avc_thrd_put(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u16 reg;
+       int db;
+       int tmp;
+       int remain;
+       int i;
+       int fraction[5] = {1414, 1189, 1091, 1044, 1022};
+
+       db = -(ucontrol->value.integer.value[0]);
+
+       tmp = ((3322 * db) / 20 + 15000 - 653);
+       tmp = clamp(tmp, 0, 16000);
+
+       remain = tmp % 1000;
+       tmp = tmp / 1000;
+
+       reg = 0x1 << tmp;
+
+       for (i = 0; i < 5; i++) {
+               int level = 1000 / (0x1 << (i+1));
+               if (remain >= level) {
+                       remain -= level;
+                       reg = reg * fraction[i] / 1000;
+               }
+       }
+
+       snd_soc_write(codec, SGTL5000_DAP_AVC_THRESHOLD, reg);
+
+       return 0;
+}
+
+static int mixer_vol_get(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *control =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       u16 reg = snd_soc_read(codec, control->reg);
+       int vol;
+
+       vol = (reg * 100) >> 15;
+
+       ucontrol->value.integer.value[0] = vol;
+       ucontrol->value.integer.value[1] = vol;
+       return 0;
+}
+
+static int mixer_vol_put(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *control =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       u16 reg;
+       int vol;
+
+       vol = ucontrol->value.integer.value[0];
+       reg = (vol << 15) / 100;
+       snd_soc_write(codec, control->reg, reg);
+
+       return 0;
+}
+
+
 static const DECLARE_TLV_DB_SCALE(capture_6db_attenuate, -600, 600, 0);
 
 /* tlv for mic gain, 0db 20db 30db 40db */
@@ -374,6 +568,14 @@ static const unsigned int mic_gain_tlv[] = {
 
 /* tlv for hp volume, -51.5db to 12.0db, step .5db */
 static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0);
+static const unsigned int bass_high_filter_freq[] = {
+       TLV_DB_RANGE_HEAD(7),
+       0, 0, TLV_DB_SCALE_ITEM(80, 0, 0),
+       1, 6, TLV_DB_SCALE_ITEM(100, 25, 0),
+};
+static const DECLARE_TLV_DB_SCALE(avc_max_gain, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(avc_threshold, 0, 1, 0);
+static const DECLARE_TLV_DB_SCALE(mixer_volume, 0, 1, 0);
 
 static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
        /* SOC_DOUBLE_S8_TLV with invert */
@@ -403,6 +605,37 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
 
        SOC_SINGLE_TLV("Mic Volume", SGTL5000_CHIP_MIC_CTRL,
                        0, 4, 0, mic_gain_tlv),
+
+       /* Bass Enhance enable */
+       SOC_SINGLE("Bass Enable", SGTL5000_DAP_BASS_ENHANCE,
+                       0, 1, 0),
+       SOC_SINGLE_TLV("Bass Filter Feq", SGTL5000_DAP_BASS_ENHANCE,
+                       6, 7, 0, bass_high_filter_freq),
+       SOC_SINGLE("Bass Volume", SGTL5000_DAP_BASS_ENHANCE_CTRL,
+                       8, 0x3f, 1),
+       /* Bass Harmonic Level Control */
+       SOC_SINGLE("Bass Level", SGTL5000_DAP_BASS_ENHANCE_CTRL,
+                       0, 0x7f, 1),
+
+       /* DAP Surround */
+       SOC_SINGLE("Surround Width", SGTL5000_DAP_SURROUND,
+                       4, 0x7, 0),
+
+       /* DAP MAIN Channel Voluem */
+       SOC_SINGLE_EXT_TLV("Main Channel Volume", SGTL5000_DAP_MAIN_CHAN,
+                       0, 200, 0, mixer_vol_get, mixer_vol_put, mixer_volume),
+       SOC_SINGLE_EXT_TLV("Mixer Channel Volume", SGTL5000_DAP_MIX_CHAN,
+                       0, 200, 0, mixer_vol_get, mixer_vol_put, mixer_volume),
+
+       /* DAP AVC */
+       SOC_SINGLE("AVC Enable", SGTL5000_DAP_AVC_CTRL,
+                       0, 1, 0),
+       SOC_SINGLE("AVC Hard Limit", SGTL5000_DAP_AVC_CTRL,
+                       5, 1, 0),
+       SOC_SINGLE_TLV("AVC Max Gain", SGTL5000_DAP_AVC_CTRL,
+                       12, 2, 0, avc_max_gain),
+       SOC_SINGLE_EXT_TLV("AVC Threshold (-dB)", SGTL5000_DAP_AVC_THRESHOLD,
+                       0, 96, 0, avc_thrd_get, avc_thrd_put, avc_threshold),
 };
 
 /* mute the codec used by alsa core */
@@ -747,6 +980,20 @@ static int ldo_regulator_enable(struct regulator_dev *dev)
        if (ldo_regulator_is_enabled(dev))
                return 0;
 
+       snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                               SGTL5000_LINREG_SIMPLE_POWERUP|
+                               SGTL5000_STARTUP_POWERUP|
+                               SGTL5000_REFTOP_POWERUP,
+                               0);
+       udelay(10);
+       snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                               SGTL5000_LINREG_SIMPLE_POWERUP|
+                               SGTL5000_STARTUP_POWERUP|
+                               SGTL5000_REFTOP_POWERUP,
+                               SGTL5000_LINREG_SIMPLE_POWERUP|
+                               SGTL5000_STARTUP_POWERUP|
+                               SGTL5000_REFTOP_POWERUP);
+       udelay(10);
        /* set regulator value firstly */
        reg = (1600 - ldo->voltage / 1000) / 50;
        reg = clamp(reg, 0x0, 0xf);
@@ -951,8 +1198,7 @@ static struct snd_soc_dai_driver sgtl5000_dai = {
        .symmetric_rates = 1,
 };
 
-static int sgtl5000_volatile_register(struct snd_soc_codec *codec,
-                                       unsigned int reg)
+static int sgtl5000_volatile_register(unsigned int reg)
 {
        switch (reg) {
        case SGTL5000_CHIP_ID:
@@ -1199,7 +1445,6 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
                }
 
                sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME;
-
                ret = regulator_bulk_get(codec->dev,
                                ARRAY_SIZE(sgtl5000->supplies),
                                sgtl5000->supplies);
index bb699bb55a502c041b9d47fff2f5c396c8c431c8..1b1206fca7a7a8c2b07fc048798deed9e957a5df 100644 (file)
@@ -44,6 +44,15 @@ config SND_SOC_PHYCORE_AC97
          Say Y if you want to add support for SoC audio on Phytec phyCORE
          and phyCARD boards in AC97 mode
 
+config SND_SOC_IMX_SGTL5000
+       tristate "SoC Audio support for i.MX boards with sgtl5000"
+       depends on MACH_MX35_3DS || MACH_MX51_BABBAGE
+       select SND_SOC_SGTL5000
+       select SND_MXC_SOC_MX2
+       help
+         Say Y if you want to add support for SoC audio on an i.MX board with
+         a sgtl5000 codec.
+
 config SND_SOC_EUKREA_TLV320
        tristate "Eukrea TLV320"
        depends on MACH_EUKREA_MBIMX27_BASEBOARD \
index d6d609ba7e24afa539e0cc9804caee1a43026379..38693ed074a0a7172858f410c6c273f42be39736 100644 (file)
@@ -12,8 +12,10 @@ snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
 snd-soc-phycore-ac97-objs := phycore-ac97.o
 snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
 snd-soc-wm1133-ev1-objs := wm1133-ev1.o
+snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
 
 obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
 obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
 obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
 obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
+obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
diff --git a/sound/soc/imx/imx-sgtl5000.c b/sound/soc/imx/imx-sgtl5000.c
new file mode 100644 (file)
index 0000000..618d0fe
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * sound/soc/imx/3ds-sgtl5000.c --  SoC audio for i.MX 3ds boards with
+ *                                  sgtl5000 codec
+ *
+ * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ *
+ * 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/moduleparam.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <asm/mach-types.h>
+#include <mach/audmux.h>
+#include <linux/fsl_devices.h>
+
+#include "../codecs/sgtl5000.h"
+#include "imx-ssi.h"
+
+static struct imx_sgtl5000_priv {
+       int sysclk;
+       int hw;
+       struct platform_device *pdev;
+} card_priv;
+
+static struct snd_soc_card imx_sgtl5000;
+
+static int sgtl5000_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       u32 dai_format;
+       int ret;
+       unsigned int channels = params_channels(params);
+
+       snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, card_priv.sysclk, 0);
+
+       snd_soc_dai_set_sysclk(codec_dai, SGTL5000_LRCLK, params_rate(params), 0);
+
+       dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+               SND_SOC_DAIFMT_CBM_CFM;
+
+       /* set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
+       if (ret < 0)
+               return ret;
+
+
+       /* TODO: The SSI driver should figure this out for us */
+       switch (channels) {
+       case 2:
+               snd_soc_dai_set_tdm_slot(cpu_dai, 0xfffffffc, 0xfffffffc, 2, 0);
+               break;
+       case 1:
+               snd_soc_dai_set_tdm_slot(cpu_dai, 0xfffffffe, 0xfffffffe, 1, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* set cpu DAI configuration */
+       dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
+               SND_SOC_DAIFMT_CBM_CFM;
+       ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static struct snd_soc_ops imx_sgtl5000_hifi_ops = {
+       .hw_params = sgtl5000_params,
+};
+
+static int sgtl5000_jack_func;
+static int sgtl5000_spk_func;
+static int sgtl5000_line_in_func;
+
+static const char *jack_function[] = { "off", "on"};
+
+static const char *spk_function[] = { "off", "on" };
+
+static const char *line_in_function[] = { "off", "on" };
+
+static const struct soc_enum sgtl5000_enum[] = {
+       SOC_ENUM_SINGLE_EXT(2, jack_function),
+       SOC_ENUM_SINGLE_EXT(2, spk_function),
+       SOC_ENUM_SINGLE_EXT(2, line_in_function),
+};
+
+static int sgtl5000_get_jack(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.enumerated.item[0] = sgtl5000_jack_func;
+       return 0;
+}
+
+static int sgtl5000_set_jack(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+       if (sgtl5000_jack_func == ucontrol->value.enumerated.item[0])
+               return 0;
+
+       sgtl5000_jack_func = ucontrol->value.enumerated.item[0];
+       if (sgtl5000_jack_func)
+               snd_soc_dapm_enable_pin(&codec->dapm, "Headphone Jack");
+       else
+               snd_soc_dapm_disable_pin(&codec->dapm, "Headphone Jack");
+
+       snd_soc_dapm_sync(&codec->dapm);
+       return 1;
+}
+
+static int sgtl5000_get_spk(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.enumerated.item[0] = sgtl5000_spk_func;
+       return 0;
+}
+
+static int sgtl5000_set_spk(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+       if (sgtl5000_spk_func == ucontrol->value.enumerated.item[0])
+               return 0;
+
+       sgtl5000_spk_func = ucontrol->value.enumerated.item[0];
+       if (sgtl5000_spk_func)
+               snd_soc_dapm_enable_pin(&codec->dapm, "Ext Spk");
+       else
+               snd_soc_dapm_disable_pin(&codec->dapm, "Ext Spk");
+
+       snd_soc_dapm_sync(&codec->dapm);
+       return 1;
+}
+
+static int sgtl5000_get_line_in(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.enumerated.item[0] = sgtl5000_line_in_func;
+       return 0;
+}
+
+static int sgtl5000_set_line_in(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+       if (sgtl5000_line_in_func == ucontrol->value.enumerated.item[0])
+               return 0;
+
+       sgtl5000_line_in_func = ucontrol->value.enumerated.item[0];
+       if (sgtl5000_line_in_func)
+               snd_soc_dapm_enable_pin(&codec->dapm, "Line In Jack");
+       else
+               snd_soc_dapm_disable_pin(&codec->dapm, "Line In Jack");
+
+       snd_soc_dapm_sync(&codec->dapm);
+       return 1;
+}
+
+/* imx_3stack card dapm widgets */
+static const struct snd_soc_dapm_widget imx_3stack_dapm_widgets[] = {
+       SND_SOC_DAPM_MIC("Mic Jack", NULL),
+       SND_SOC_DAPM_LINE("Line In Jack", NULL),
+       SND_SOC_DAPM_SPK("Ext Spk", NULL),
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+};
+
+static const struct snd_kcontrol_new sgtl5000_machine_controls[] = {
+       SOC_ENUM_EXT("Jack Function", sgtl5000_enum[0], sgtl5000_get_jack,
+                    sgtl5000_set_jack),
+       SOC_ENUM_EXT("Speaker Function", sgtl5000_enum[1], sgtl5000_get_spk,
+                    sgtl5000_set_spk),
+       SOC_ENUM_EXT("Line In Function", sgtl5000_enum[1], sgtl5000_get_line_in,
+                    sgtl5000_set_line_in),
+};
+
+/* imx_3stack machine connections to the codec pins */
+static const struct snd_soc_dapm_route audio_map[] = {
+
+       /* Mic Jack --> MIC_IN (with automatic bias) */
+       {"MIC_IN", NULL, "Mic Jack"},
+
+       /* Line in Jack --> LINE_IN */
+       {"LINE_IN", NULL, "Line In Jack"},
+
+       /* HP_OUT --> Headphone Jack */
+       {"Headphone Jack", NULL, "HP_OUT"},
+
+       /* LINE_OUT --> Ext Speaker */
+       {"Ext Spk", NULL, "LINE_OUT"},
+};
+
+static int imx_3stack_sgtl5000_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_codec *codec = rtd->codec;
+       int ret;
+
+       ret = snd_soc_add_controls(codec, sgtl5000_machine_controls,
+                       ARRAY_SIZE(sgtl5000_machine_controls));
+       if (ret)
+               return ret;
+
+       /* Add imx_3stack specific widgets */
+       snd_soc_dapm_new_controls(&codec->dapm, imx_3stack_dapm_widgets,
+                                 ARRAY_SIZE(imx_3stack_dapm_widgets));
+
+       /* Set up imx_3stack specific audio path audio_map */
+       snd_soc_dapm_add_routes(&codec->dapm, audio_map, ARRAY_SIZE(audio_map));
+
+       snd_soc_dapm_disable_pin(&codec->dapm, "Line In Jack");
+       snd_soc_dapm_enable_pin(&codec->dapm, "Headphone Jack");
+
+       snd_soc_dapm_sync(&codec->dapm);
+
+       return 0;
+}
+
+static struct snd_soc_dai_link imx_sgtl5000_dai[] = {
+       {
+               .name           = "HiFi",
+               .stream_name    = "HiFi",
+               .codec_dai_name = "sgtl5000",
+               .codec_name     = "sgtl5000.1-000a",
+               .cpu_dai_name   = "imx-ssi.0",
+               .platform_name  = "imx-pcm-audio.0",
+               .init           = imx_3stack_sgtl5000_init,
+               .ops            = &imx_sgtl5000_hifi_ops,
+       },
+};
+
+static struct snd_soc_card imx_sgtl5000 = {
+       .name           = "sgtl5000-audio",
+       .dai_link       = imx_sgtl5000_dai,
+       .num_links      = ARRAY_SIZE(imx_sgtl5000_dai),
+};
+
+static struct platform_device *imx_sgtl5000_snd_device;
+
+static int imx_audmux_config(int slave, int master)
+{
+       unsigned int ptcr, pdcr;
+       slave = slave - 1;
+       master = master - 1;
+
+       /* SSI0 mastered by port 5 */
+       ptcr = MXC_AUDMUX_V2_PTCR_SYN |
+               MXC_AUDMUX_V2_PTCR_TFSDIR |
+               MXC_AUDMUX_V2_PTCR_TFSEL(master) |
+               MXC_AUDMUX_V2_PTCR_TCLKDIR |
+               MXC_AUDMUX_V2_PTCR_TCSEL(master);
+       pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(master);
+       mxc_audmux_v2_configure_port(slave, ptcr, pdcr);
+
+       ptcr = MXC_AUDMUX_V2_PTCR_SYN;
+       pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(slave);
+       mxc_audmux_v2_configure_port(master, ptcr, pdcr);
+
+       return 0;
+}
+
+static int __devinit imx_sgtl5000_probe(struct platform_device *pdev)
+{
+       struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+
+       int ret = 0;
+
+       card_priv.pdev = pdev;
+
+       imx_audmux_config(plat->src_port, plat->ext_port);
+
+       ret = -EINVAL;
+       if (plat->init && plat->init())
+               return ret;
+
+       card_priv.sysclk = plat->sysclk;
+
+       return 0;
+}
+
+static int imx_sgtl5000_remove(struct platform_device *pdev)
+{
+       struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+
+       if (plat->finit)
+               plat->finit();
+
+       return 0;
+}
+
+static struct platform_driver imx_sgtl5000_audio_driver = {
+       .probe = imx_sgtl5000_probe,
+       .remove = imx_sgtl5000_remove,
+       .driver = {
+                  .name = "imx-sgtl5000",
+                  },
+};
+
+static int __init imx_sgtl5000_init(void)
+{
+       int ret;
+
+       ret = platform_driver_register(&imx_sgtl5000_audio_driver);
+       if (ret)
+               return -ENOMEM;
+
+       if (machine_is_mx35_3ds())
+               imx_sgtl5000_dai[0].codec_name = "sgtl5000.0-000a";
+       else
+               imx_sgtl5000_dai[0].codec_name = "sgtl5000.1-000a";
+
+       imx_sgtl5000_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!imx_sgtl5000_snd_device)
+               return -ENOMEM;
+
+       platform_set_drvdata(imx_sgtl5000_snd_device, &imx_sgtl5000);
+
+       ret = platform_device_add(imx_sgtl5000_snd_device);
+
+       if (ret) {
+               printk(KERN_ERR "ASoC: Platform device allocation failed\n");
+               platform_device_put(imx_sgtl5000_snd_device);
+       }
+
+       return ret;
+}
+
+static void __exit imx_sgtl5000_exit(void)
+{
+       platform_driver_unregister(&imx_sgtl5000_audio_driver);
+       platform_device_unregister(imx_sgtl5000_snd_device);
+}
+
+late_initcall(imx_sgtl5000_init);
+module_exit(imx_sgtl5000_exit);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("PhyCORE ALSA SoC driver");
+MODULE_LICENSE("GPL");