aboutsummaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorFrank Li <Frank.Li@freescale.com>2011-03-29 09:45:35 +0800
committerEric Miao <eric.miao@canonical.com>2011-11-10 07:37:33 +0800
commit369dc7a272270629a85f2f6e5ebbe29bd324e79c (patch)
tree928b7923e08e825fb97b727216a85c5422e42baf /sound
parenta38367b85627bd76277127715f0dee904f943c6d (diff)
ENGR00141217-8 ASoC: SGTL5000: Add DAP support for sgtl5000 codec
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>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/sgtl5000.c287
-rw-r--r--sound/soc/imx/Kconfig9
-rw-r--r--sound/soc/imx/Makefile2
-rw-r--r--sound/soc/imx/imx-pcm-dma-mx2.c4
-rw-r--r--sound/soc/imx/imx-sgtl5000.c355
5 files changed, 636 insertions, 21 deletions
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 1f7217f703e..37bd6146190 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -213,6 +213,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"
@@ -225,15 +247,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"),
@@ -242,6 +299,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),
@@ -254,20 +321,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),
};
@@ -277,9 +343,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 */
@@ -405,6 +488,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 */
@@ -416,6 +610,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 */
@@ -445,6 +647,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 */
@@ -788,6 +1021,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);
@@ -979,8 +1226,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:
@@ -1233,7 +1479,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);
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig
index 9eeb8f0d67e..feab5d38bfd 100644
--- a/sound/soc/imx/Kconfig
+++ b/sound/soc/imx/Kconfig
@@ -40,6 +40,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 \
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile
index b67fc02a4ec..9d9ac39c4c4 100644
--- a/sound/soc/imx/Makefile
+++ b/sound/soc/imx/Makefile
@@ -11,7 +11,9 @@ obj-$(CONFIG_SND_MXC_SOC_MX2) += snd-soc-imx-mx2.o
snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
snd-soc-phycore-ac97-objs := phycore-ac97.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_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-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c
index 671ef8dd524..4d758024610 100644
--- a/sound/soc/imx/imx-pcm-dma-mx2.c
+++ b/sound/soc/imx/imx-pcm-dma-mx2.c
@@ -303,6 +303,10 @@ static struct snd_soc_platform_driver imx_soc_platform_mx2 = {
static int __devinit imx_soc_platform_probe(struct platform_device *pdev)
{
+ struct imx_ssi *ssi = platform_get_drvdata(pdev);
+ ssi->dma_params_tx.burstsize = 4;
+ ssi->dma_params_rx.burstsize = 6;
+
return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2);
}
diff --git a/sound/soc/imx/imx-sgtl5000.c b/sound/soc/imx/imx-sgtl5000.c
new file mode 100644
index 00000000000..618d0fe018b
--- /dev/null
+++ b/sound/soc/imx/imx-sgtl5000.c
@@ -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");