diff options
-rw-r--r-- | sound/soc/codecs/wm8903.c | 182 | ||||
-rw-r--r-- | sound/soc/codecs/wm8994.c | 21 | ||||
-rw-r--r-- | sound/soc/codecs/wm9081.c | 4 | ||||
-rw-r--r-- | sound/soc/samsung/Kconfig | 8 | ||||
-rw-r--r-- | sound/soc/samsung/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/samsung/h1940_uda1380.c | 296 |
6 files changed, 423 insertions, 90 deletions
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 620793e51666..d015745a886b 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -218,10 +218,11 @@ struct wm8903_priv { int sysclk; int irq; - /* Reference counts */ + int fs; + int deemph; + + /* Reference count */ int class_w_users; - int playback_active; - int capture_active; struct completion wseq; @@ -230,9 +231,6 @@ struct wm8903_priv { int mic_short; int mic_last_report; int mic_delay; - - struct snd_pcm_substream *master_substream; - struct snd_pcm_substream *slave_substream; }; static int wm8903_volatile_register(unsigned int reg) @@ -462,6 +460,72 @@ static int wm8903_class_w_put(struct snd_kcontrol *kcontrol, .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } +static int wm8903_deemph[] = { 0, 32000, 44100, 48000 }; + +static int wm8903_set_deemph(struct snd_soc_codec *codec) +{ + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + int val, i, best; + + /* If we're using deemphasis select the nearest available sample + * rate. + */ + if (wm8903->deemph) { + best = 1; + for (i = 2; i < ARRAY_SIZE(wm8903_deemph); i++) { + if (abs(wm8903_deemph[i] - wm8903->fs) < + abs(wm8903_deemph[best] - wm8903->fs)) + best = i; + } + + val = best << WM8903_DEEMPH_SHIFT; + } else { + best = 0; + val = 0; + } + + dev_dbg(codec->dev, "Set deemphasis %d (%dHz)\n", + best, wm8903_deemph[best]); + + return snd_soc_update_bits(codec, WM8903_DAC_DIGITAL_1, + WM8903_DEEMPH_MASK, val); +} + +static int wm8903_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = wm8903->deemph; + + return 0; +} + +static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + int deemph = ucontrol->value.enumerated.item[0]; + int ret = 0; + + if (deemph > 1) + return -EINVAL; + + mutex_lock(&codec->mutex); + if (wm8903->deemph != deemph) { + wm8903->deemph = deemph; + + wm8903_set_deemph(codec); + + ret = 1; + } + mutex_unlock(&codec->mutex); + + return ret; +} + /* ALSA can only do steps of .01dB */ static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); @@ -474,6 +538,23 @@ static const DECLARE_TLV_DB_SCALE(drc_tlv_min, 0, 600, 0); static const DECLARE_TLV_DB_SCALE(drc_tlv_max, 1200, 600, 0); static const DECLARE_TLV_DB_SCALE(drc_tlv_startup, -300, 50, 0); +static const char *hpf_mode_text[] = { + "Hi-fi", "Voice 1", "Voice 2", "Voice 3" +}; + +static const struct soc_enum hpf_mode = + SOC_ENUM_SINGLE(WM8903_ADC_DIGITAL_0, 5, 4, hpf_mode_text); + +static const char *osr_text[] = { + "Low power", "High performance" +}; + +static const struct soc_enum adc_osr = + SOC_ENUM_SINGLE(WM8903_ANALOGUE_ADC_0, 0, 2, osr_text); + +static const struct soc_enum dac_osr = + SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 0, 2, osr_text); + static const char *drc_slope_text[] = { "1", "1/2", "1/4", "1/8", "1/16", "0" }; @@ -536,13 +617,6 @@ static const char *mute_mode_text[] = { static const struct soc_enum mute_mode = SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 9, 2, mute_mode_text); -static const char *dac_deemphasis_text[] = { - "Disabled", "32kHz", "44.1kHz", "48kHz" -}; - -static const struct soc_enum dac_deemphasis = - SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 1, 4, dac_deemphasis_text); - static const char *companding_text[] = { "ulaw", "alaw" }; @@ -612,6 +686,9 @@ SOC_SINGLE("Right Input PGA Common Mode Switch", WM8903_ANALOGUE_RIGHT_INPUT_1, 6, 1, 0), /* ADCs */ +SOC_ENUM("ADC OSR", adc_osr), +SOC_SINGLE("HPF Switch", WM8903_ADC_DIGITAL_0, 4, 1, 0), +SOC_ENUM("HPF Mode", hpf_mode), SOC_SINGLE("DRC Switch", WM8903_DRC_0, 15, 1, 0), SOC_ENUM("DRC Compressor Slope R0", drc_slope_r0), SOC_ENUM("DRC Compressor Slope R1", drc_slope_r1), @@ -641,14 +718,16 @@ SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8903_DAC_DIGITAL_0, 4, 8, 12, 0, digital_sidetone_tlv), /* DAC */ +SOC_ENUM("DAC OSR", dac_osr), SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8903_DAC_DIGITAL_VOLUME_LEFT, WM8903_DAC_DIGITAL_VOLUME_RIGHT, 1, 120, 0, digital_tlv), SOC_ENUM("DAC Soft Mute Rate", soft_mute), SOC_ENUM("DAC Mute Mode", mute_mode), SOC_SINGLE("DAC Mono Switch", WM8903_DAC_DIGITAL_1, 12, 1, 0), -SOC_ENUM("DAC De-emphasis", dac_deemphasis), SOC_ENUM("DAC Companding Mode", dac_companding), SOC_SINGLE("DAC Companding Switch", WM8903_AUDIO_INTERFACE_0, 1, 1, 0), +SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0, + wm8903_get_deemph, wm8903_put_deemph), /* Headphones */ SOC_DOUBLE_R("Headphone Switch", @@ -1222,58 +1301,6 @@ static struct { { 0, 0 }, }; -static int wm8903_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - struct snd_pcm_runtime *master_runtime; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - wm8903->playback_active++; - else - wm8903->capture_active++; - - /* The DAI has shared clocks so if we already have a playback or - * capture going then constrain this substream to match it. - */ - if (wm8903->master_substream) { - master_runtime = wm8903->master_substream->runtime; - - dev_dbg(codec->dev, "Constraining to %d bits\n", - master_runtime->sample_bits); - - snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_SAMPLE_BITS, - master_runtime->sample_bits, - master_runtime->sample_bits); - - wm8903->slave_substream = substream; - } else - wm8903->master_substream = substream; - - return 0; -} - -static void wm8903_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - wm8903->playback_active--; - else - wm8903->capture_active--; - - if (wm8903->master_substream == substream) - wm8903->master_substream = wm8903->slave_substream; - - wm8903->slave_substream = NULL; -} - static int wm8903_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -1298,11 +1325,6 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream, u16 clock1 = snd_soc_read(codec, WM8903_CLOCK_RATES_1); u16 dac_digital1 = snd_soc_read(codec, WM8903_DAC_DIGITAL_1); - if (substream == wm8903->slave_substream) { - dev_dbg(codec->dev, "Ignoring hw_params for slave substream\n"); - return 0; - } - /* Enable sloping stopband filter for low sample rates */ if (fs <= 24000) dac_digital1 |= WM8903_DAC_SB_FILT; @@ -1320,19 +1342,6 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream, } } - /* Constraints should stop us hitting this but let's make sure */ - if (wm8903->capture_active) - switch (sample_rates[dsp_config].rate) { - case 88200: - case 96000: - dev_err(codec->dev, "%dHz unsupported by ADC\n", - fs); - return -EINVAL; - - default: - break; - } - dev_dbg(codec->dev, "DSP fs = %dHz\n", sample_rates[dsp_config].rate); clock1 &= ~WM8903_SAMPLE_RATE_MASK; clock1 |= sample_rates[dsp_config].value; @@ -1428,6 +1437,9 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream, aif2 |= bclk_divs[bclk_div].div; aif3 |= bclk / fs; + wm8903->fs = params_rate(params); + wm8903_set_deemph(codec); + snd_soc_write(codec, WM8903_CLOCK_RATES_0, clock0); snd_soc_write(codec, WM8903_CLOCK_RATES_1, clock1); snd_soc_write(codec, WM8903_AUDIO_INTERFACE_1, aif1); @@ -1571,8 +1583,6 @@ static irqreturn_t wm8903_irq(int irq, void *data) SNDRV_PCM_FMTBIT_S24_LE) static struct snd_soc_dai_ops wm8903_dai_ops = { - .startup = wm8903_startup, - .shutdown = wm8903_shutdown, .hw_params = wm8903_hw_params, .digital_mute = wm8903_digital_mute, .set_fmt = wm8903_set_dai_fmt, diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index da48802340fc..af104acd75f8 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -558,6 +558,16 @@ static const struct soc_enum aif2dacl_src = static const struct soc_enum aif2dacr_src = SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, 14, 2, aif_chan_src_text); +static const char *osr_text[] = { + "Low Power", "High Performance", +}; + +static const struct soc_enum dac_osr = + SOC_ENUM_SINGLE(WM8994_OVERSAMPLING, 0, 2, osr_text); + +static const struct soc_enum adc_osr = + SOC_ENUM_SINGLE(WM8994_OVERSAMPLING, 1, 2, osr_text); + static void wm8958_mbc_apply(struct snd_soc_codec *codec, int mbc, int start) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); @@ -780,13 +790,13 @@ SOC_DOUBLE_R_TLV("AIF2ADC Volume", WM8994_AIF2_ADC_LEFT_VOLUME, SOC_ENUM("AIF1ADCL Source", aif1adcl_src), SOC_ENUM("AIF1ADCR Source", aif1adcr_src), -SOC_ENUM("AIF2ADCL Source", aif1adcl_src), -SOC_ENUM("AIF2ADCR Source", aif1adcr_src), +SOC_ENUM("AIF2ADCL Source", aif2adcl_src), +SOC_ENUM("AIF2ADCR Source", aif2adcr_src), SOC_ENUM("AIF1DACL Source", aif1dacl_src), SOC_ENUM("AIF1DACR Source", aif1dacr_src), -SOC_ENUM("AIF2DACL Source", aif1dacl_src), -SOC_ENUM("AIF2DACR Source", aif1dacr_src), +SOC_ENUM("AIF2DACL Source", aif2dacl_src), +SOC_ENUM("AIF2DACR Source", aif2dacr_src), SOC_DOUBLE_R_TLV("AIF1DAC1 Volume", WM8994_AIF1_DAC1_LEFT_VOLUME, WM8994_AIF1_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv), @@ -834,6 +844,9 @@ SOC_DOUBLE("AIF1ADC2 HPF Switch", WM8994_AIF1_ADC2_FILTERS, 12, 11, 1, 0), SOC_ENUM("AIF2ADC HPF Mode", aif2adc_hpf), SOC_DOUBLE("AIF2ADC HPF Switch", WM8994_AIF2_ADC_FILTERS, 12, 11, 1, 0), +SOC_ENUM("ADC OSR", adc_osr), +SOC_ENUM("DAC OSR", dac_osr), + SOC_DOUBLE_R_TLV("DAC1 Volume", WM8994_DAC1_LEFT_VOLUME, WM8994_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv), SOC_DOUBLE_R("DAC1 Switch", WM8994_DAC1_LEFT_VOLUME, diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index c7060775e88b..e5055b28c638 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -590,6 +590,10 @@ static int wm9081_set_fll(struct snd_soc_codec *codec, int fll_id, reg5 |= fll_div.fll_clk_ref_div << WM9081_FLL_CLK_REF_DIV_SHIFT; snd_soc_write(codec, WM9081_FLL_CONTROL_5, reg5); + /* Set gain to the recommended value */ + snd_soc_update_bits(codec, WM9081_FLL_CONTROL_4, + WM9081_FLL_GAIN_MASK, 0); + /* Enable the FLL */ snd_soc_write(codec, WM9081_FLL_CONTROL_1, reg1 | WM9081_FLL_ENA); diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index eb45cf90d4e2..67cdad4ee535 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -116,6 +116,14 @@ config ASOC_SAMSUNG_SIMTEC_HERMES select SND_SOC_TLV320AIC3X select ASOC_SAMSUNG_SIMTEC +config ASOC_SAMSUNG_H1940_UDA1380 + tristate "Audio support for the HP iPAQ H1940" + depends on ASOC_SAMSUNG && ARCH_H1940 + select SND_S3C24XX_I2S + select SND_SOC_UDA1380 + help + This driver provides audio support for HP iPAQ h1940 PDA. + config ASOC_SAMSUNG_RX1950_UDA1380 tristate "Audio support for the HP iPAQ RX1950" depends on ASOC_SAMSUNG && MACH_RX1950 diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index 0d24f95c8b1c..622e76eb9775 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile @@ -27,6 +27,7 @@ snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o +snd-soc-h1940-uda1380-objs := h1940_uda1380.o snd-soc-rx1950-uda1380-objs := rx1950_uda1380.o snd-soc-smdk-wm8580-objs := smdk_wm8580.o snd-soc-smdk-wm9713-objs := smdk_wm9713.o @@ -43,6 +44,7 @@ obj-$(CONFIG_ASOC_SAMSUNG_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o obj-$(CONFIG_ASOC_SAMSUNG_SIMTEC) += snd-soc-s3c24xx-simtec.o obj-$(CONFIG_ASOC_SAMSUNG_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o obj-$(CONFIG_ASOC_SAMSUNG_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o +obj-$(CONFIG_ASOC_SAMSUNG_H1940_UDA1380) += snd-soc-h1940-uda1380.o obj-$(CONFIG_ASOC_SAMSUNG_RX1950_UDA1380) += snd-soc-rx1950-uda1380.o obj-$(CONFIG_ASOC_SAMSUNG_SMDK_WM8580) += snd-soc-smdk-wm8580.o obj-$(CONFIG_ASOC_SAMSUNG_SMDK_WM9713) += snd-soc-smdk-wm9713.o diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c new file mode 100644 index 000000000000..c45f7ce14d61 --- /dev/null +++ b/sound/soc/samsung/h1940_uda1380.c @@ -0,0 +1,296 @@ +/* + * h1940-uda1380.c -- ALSA Soc Audio Layer + * + * Copyright (c) 2010 Arnaud Patard <[email protected]> + * Copyright (c) 2010 Vasily Khoruzhick <[email protected]> + * + * Based on version from Arnaud Patard <[email protected]> + * + * 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/platform_device.h> +#include <linux/i2c.h> +#include <linux/gpio.h> + +#include <sound/soc.h> +#include <sound/uda1380.h> +#include <sound/jack.h> + +#include <plat/regs-iis.h> + +#include <mach/h1940-latch.h> + +#include <asm/mach-types.h> + +#include "dma.h" +#include "s3c24xx-i2s.h" +#include "../codecs/uda1380.h" + +static unsigned int rates[] = { + 11025, + 22050, + 44100, +}; + +static struct snd_pcm_hw_constraint_list hw_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static struct snd_soc_jack hp_jack; + +static struct snd_soc_jack_pin hp_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Speaker", + .mask = SND_JACK_HEADPHONE, + .invert = 1, + }, +}; + +static struct snd_soc_jack_gpio hp_jack_gpios[] = { + { + .gpio = S3C2410_GPG(4), + .name = "hp-gpio", + .report = SND_JACK_HEADPHONE, + .invert = 1, + .debounce_time = 200, + }, +}; + +static int h1940_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.rate_min = hw_rates.list[0]; + runtime->hw.rate_max = hw_rates.list[hw_rates.count - 1]; + runtime->hw.rates = SNDRV_PCM_RATE_KNOT; + + return snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &hw_rates); +} + +static int h1940_hw_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 *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int div; + int ret; + unsigned int rate = params_rate(params); + + switch (rate) { + case 11025: + case 22050: + case 44100: + div = s3c24xx_i2s_get_clockrate() / (384 * rate); + if (s3c24xx_i2s_get_clockrate() % (384 * rate) > (192 * rate)) + div++; + break; + default: + dev_err(&rtd->dev, "%s: rate %d is not supported\n", + __func__, rate); + return -EINVAL; + } + + /* set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* select clock source */ + ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_PCLK, rate, + SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + /* set MCLK division for sample rate */ + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, + S3C2410_IISMOD_384FS); + if (ret < 0) + return ret; + + /* set BCLK division for sample rate */ + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK, + S3C2410_IISMOD_32FS); + if (ret < 0) + return ret; + + /* set prescaler division for sample rate */ + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER, + S3C24XX_PRESCALE(div, div)); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops h1940_ops = { + .startup = h1940_startup, + .hw_params = h1940_hw_params, +}; + +static int h1940_spk_power(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) + gpio_set_value(H1940_LATCH_AUDIO_POWER, 1); + else + gpio_set_value(H1940_LATCH_AUDIO_POWER, 0); + + return 0; +} + +/* h1940 machine dapm widgets */ +static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_SPK("Speaker", h1940_spk_power), +}; + +/* h1940 machine audio_map */ +static const struct snd_soc_dapm_route audio_map[] = { + /* headphone connected to VOUTLHP, VOUTRHP */ + {"Headphone Jack", NULL, "VOUTLHP"}, + {"Headphone Jack", NULL, "VOUTRHP"}, + + /* ext speaker connected to VOUTL, VOUTR */ + {"Speaker", NULL, "VOUTL"}, + {"Speaker", NULL, "VOUTR"}, + + /* mic is connected to VINM */ + {"VINM", NULL, "Mic Jack"}, +}; + +static struct platform_device *s3c24xx_snd_device; + +static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + int err; + + /* Add h1940 specific widgets */ + err = snd_soc_dapm_new_controls(dapm, uda1380_dapm_widgets, + ARRAY_SIZE(uda1380_dapm_widgets)); + if (err) + return err; + + /* Set up h1940 specific audio path audio_mapnects */ + err = snd_soc_dapm_add_routes(dapm, audio_map, + ARRAY_SIZE(audio_map)); + if (err) + return err; + + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin(dapm, "Speaker"); + snd_soc_dapm_enable_pin(dapm, "Mic Jack"); + + snd_soc_dapm_sync(dapm); + + snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, + &hp_jack); + + snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins), + hp_jack_pins); + + snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios), + hp_jack_gpios); + + return 0; +} + +/* s3c24xx digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link h1940_uda1380_dai[] = { + { + .name = "uda1380", + .stream_name = "UDA1380 Duplex", + .cpu_dai_name = "s3c24xx-iis", + .codec_dai_name = "uda1380-hifi", + .init = h1940_uda1380_init, + .platform_name = "samsung-audio", + .codec_name = "uda1380-codec.0-001a", + .ops = &h1940_ops, + }, +}; + +static struct snd_soc_card h1940_asoc = { + .name = "h1940", + .dai_link = h1940_uda1380_dai, + .num_links = ARRAY_SIZE(h1940_uda1380_dai), +}; + +static int __init h1940_init(void) +{ + int ret; + + if (!machine_is_h1940()) + return -ENODEV; + + /* configure some gpios */ + ret = gpio_request(H1940_LATCH_AUDIO_POWER, "speaker-power"); + if (ret) + goto err_out; + + ret = gpio_direction_output(H1940_LATCH_AUDIO_POWER, 0); + if (ret) + goto err_gpio; + + s3c24xx_snd_device = platform_device_alloc("soc-audio", -1); + if (!s3c24xx_snd_device) { + ret = -ENOMEM; + goto err_gpio; + } + + platform_set_drvdata(s3c24xx_snd_device, &h1940_asoc); + ret = platform_device_add(s3c24xx_snd_device); + + if (ret) + goto err_plat; + + return 0; + +err_plat: + platform_device_put(s3c24xx_snd_device); +err_gpio: + gpio_free(H1940_LATCH_AUDIO_POWER); + +err_out: + return ret; +} + +static void __exit h1940_exit(void) +{ + platform_device_unregister(s3c24xx_snd_device); + snd_soc_jack_free_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios), + hp_jack_gpios); + gpio_free(H1940_LATCH_AUDIO_POWER); +} + +module_init(h1940_init); +module_exit(h1940_exit); + +/* Module information */ +MODULE_AUTHOR("Arnaud Patard, Vasily Khoruzhick"); +MODULE_DESCRIPTION("ALSA SoC H1940"); +MODULE_LICENSE("GPL"); |