diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/tegra/tegra_i2s.c | 177 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_pcm.c | 10 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_soc.h | 19 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_soc_controls.c | 431 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_soc_wm8753.c | 429 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_soc_wm8903.c | 375 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_spdif.c | 39 |
7 files changed, 815 insertions, 665 deletions
diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c index e460a76968f6..1f40cff697d8 100644 --- a/sound/soc/tegra/tegra_i2s.c +++ b/sound/soc/tegra/tegra_i2s.c @@ -1,7 +1,7 @@ /* * tegra_i2s.c -- ALSA Soc Audio Layer * - * (c) 2010 Nvidia Graphics Pvt. Ltd. + * (c) 2010-2011 Nvidia Graphics Pvt. Ltd. * http://www.nvidia.com * * (c) 2006 Wolfson Microelectronics PLC. @@ -24,8 +24,6 @@ struct tegra_i2s_info { struct platform_device *pdev; struct tegra_audio_platform_data *pdata; struct clk *i2s_clk; - struct clk *dap_mclk; - struct clk *audio_sync_clk; phys_addr_t i2s_phys; void __iomem *i2s_base; @@ -34,6 +32,7 @@ struct tegra_i2s_info { int irq; /* Control for whole I2S (Data format, etc.) */ unsigned int bit_format; + bool i2s_master; int ref_count; struct i2s_runtime_data i2s_regs; struct das_regs_cache das_regs; @@ -118,26 +117,27 @@ static int tegra_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct tegra_i2s_info *info = dai->private_data; - int ret = 0; - int val; unsigned int i2s_id = dai->id; - unsigned int rate; + int val; + unsigned int rate, sample_size; + switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: val = I2S_BIT_SIZE_16; + sample_size = 16; break; case SNDRV_PCM_FORMAT_S24_LE: val = I2S_BIT_SIZE_24; + sample_size = 24; break; case SNDRV_PCM_FORMAT_S32_LE: val = I2S_BIT_SIZE_32; + sample_size = 32; break; default: - ret =-EINVAL; - goto err; + return -EINVAL; } - i2s_set_bit_size(i2s_id, val); switch (params_rate(params)) { @@ -150,36 +150,46 @@ static int tegra_i2s_hw_params(struct snd_pcm_substream *substream, val = params_rate(params); break; default: - ret = -EINVAL; - goto err; + return -EINVAL; } - rate = clk_get_rate(info->i2s_clk); - if (info->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP) - rate *= 2; + if (info->i2s_master) { + /* Min BCLK = samplerate * channel * bits per sample * 2 */ + rate = val * params_channels(params) * sample_size * 2; + + /* For DSP mode we need double BCLK */ + if (info->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP) + rate *= 2; - i2s_set_channel_bit_count(i2s_id, val, rate); + /* Ensure I2s clk rate is atleast greater than min BCLK */ + clk_set_rate(info->i2s_clk, rate); + if (clk_get_rate(info->i2s_clk) < rate) + clk_set_rate(info->i2s_clk, rate << 1); - return 0; + rate = clk_get_rate(info->i2s_clk); + if (info->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP) + rate *= 2; -err: - return ret; + i2s_set_channel_bit_count(i2s_id, val, rate); + } + + return 0; } static int tegra_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { - int val1; - int val2; + struct tegra_i2s_info *info = cpu_dai->private_data; unsigned int i2s_id = cpu_dai->id; + int val1, val2; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: val1 = 1; break; case SND_SOC_DAIFMT_CBM_CFM: - val1= 0; + val1 = 0; break; case SND_SOC_DAIFMT_CBS_CFM: case SND_SOC_DAIFMT_CBM_CFS: @@ -188,6 +198,8 @@ static int tegra_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, default: return -EINVAL; } + i2s_set_master(i2s_id, val1); + info->i2s_master = val1; switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_A: @@ -213,48 +225,8 @@ static int tegra_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, default: return -EINVAL; } - - i2s_set_bit_format(i2s_id,val1); - i2s_set_left_right_control_polarity(i2s_id,val2); - - /* Clock inversion */ - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_DSP_A: - case SND_SOC_DAIFMT_DSP_B: - /* frame inversion not valid for DSP modes */ - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - break; - case SND_SOC_DAIFMT_IB_NF: - /* aif1 |= WM8903_AIF_BCLK_INV; */ - break; - default: - return -EINVAL; - } - break; - case SND_SOC_DAIFMT_I2S: - case SND_SOC_DAIFMT_RIGHT_J: - case SND_SOC_DAIFMT_LEFT_J: - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - break; - case SND_SOC_DAIFMT_IB_IF: - /* aif1 |= WM8903_AIF_BCLK_INV | - * WM8903_AIF_LRCLK_INV; */ - break; - case SND_SOC_DAIFMT_IB_NF: - /* aif1 |= WM8903_AIF_BCLK_INV; */ - break; - case SND_SOC_DAIFMT_NB_IF: - /* aif1 |= WM8903_AIF_LRCLK_INV; */ - break; - default: - return -EINVAL; - } - break; - default: - return -EINVAL; - } + i2s_set_bit_format(i2s_id, val1); + i2s_set_left_right_control_polarity(i2s_id, val2); return 0; } @@ -262,18 +234,6 @@ static int tegra_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, static int tegra_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int dir) { - struct tegra_i2s_info* info = cpu_dai->private_data; - struct tegra_audio_platform_data *pdata = info->pdev->dev.platform_data; - - if (info && info->i2s_clk) { - clk_set_rate(info->i2s_clk, pdata->i2s_clk_rate); - } - else { - pr_err("%s: could not get i2s-%d clock\n", __func__, - cpu_dai->id+1); - return -EIO; - } - return 0; } @@ -310,7 +270,6 @@ static int i2s_configure(struct tegra_i2s_info *info ) { struct platform_device *pdev = info->pdev; struct tegra_audio_platform_data *pdata = pdev->dev.platform_data; - struct clk *i2s_clk; unsigned int i2s_id = pdev->id; unsigned int rate; @@ -319,22 +278,15 @@ static int i2s_configure(struct tegra_i2s_info *info ) i2s_fifo_clear(i2s_id, I2S_FIFO_RX); i2s_set_left_right_control_polarity(i2s_id, 0); /* default */ - i2s_clk = clk_get(&pdev->dev, NULL); - if (!i2s_clk) { - dev_err(&pdev->dev, "%s: could not get i2s clock\n", - __func__); - return -EIO; - } - - rate = clk_get_rate(i2s_clk); + rate = clk_get_rate(info->i2s_clk); if (info->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP) rate *= 2; + i2s_set_master(i2s_id, pdata->i2s_master); + info->i2s_master = pdata->i2s_master; if (pdata->i2s_master && pdata->i2s_master_clk) i2s_set_channel_bit_count(i2s_id, pdata->i2s_master_clk, rate); - i2s_set_master(i2s_id, pdata->i2s_master); - i2s_set_fifo_mode(i2s_id, I2S_FIFO_TX, 1); i2s_set_fifo_mode(i2s_id, I2S_FIFO_RX, 0); @@ -350,10 +302,12 @@ int tegra_i2s_suspend(struct snd_soc_dai *cpu_dai) { struct tegra_i2s_info *info = cpu_dai->private_data; + clk_enable(info->i2s_clk); + i2s_get_all_regs(cpu_dai->id, &info->i2s_regs); tegra_das_get_all_regs(&info->das_regs); - clk_disable(info->dap_mclk); + clk_disable(info->i2s_clk); return 0; } @@ -362,12 +316,14 @@ int tegra_i2s_resume(struct snd_soc_dai *cpu_dai) { struct tegra_i2s_info *info = cpu_dai->private_data; - clk_enable(info->dap_mclk); + clk_enable(info->i2s_clk); tegra_das_set_all_regs(&info->das_regs); i2s_set_all_regs(cpu_dai->id, &info->i2s_regs); tegra_jack_resume(); + clk_disable(info->i2s_clk); + return 0; } @@ -381,13 +337,9 @@ static int tegra_i2s_startup(struct snd_pcm_substream *substream, { struct tegra_i2s_info *info = dai->private_data; - if (!info->ref_count) { - /* set das pins state to normal */ - tegra_das_power_mode(true); - - clk_enable(info->audio_sync_clk); + if (!info->ref_count) clk_enable(info->i2s_clk); - } + info->ref_count++; return 0; } @@ -400,13 +352,8 @@ static void tegra_i2s_shutdown(struct snd_pcm_substream *substream, if (info->ref_count > 0) info->ref_count--; - if (!info->ref_count) { + if (!info->ref_count) clk_disable(info->i2s_clk); - clk_disable(info->audio_sync_clk); - - /* set das pins state to tristate */ - tegra_das_power_mode(false); - } return; } @@ -434,13 +381,13 @@ struct snd_soc_dai tegra_i2s_dai[] = { .suspend = tegra_i2s_suspend, .resume = tegra_i2s_resume, .playback = { - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = TEGRA_SAMPLE_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .capture = { - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = TEGRA_SAMPLE_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE, @@ -455,14 +402,14 @@ struct snd_soc_dai tegra_i2s_dai[] = { .resume = tegra_i2s_resume, .playback = { .channels_min = 1, - .channels_max = 1, - .rates = TEGRA_VOICE_SAMPLE_RATES, + .channels_max = 2, + .rates = TEGRA_SAMPLE_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .capture = { .channels_min = 1, - .channels_max = 1, - .rates = TEGRA_VOICE_SAMPLE_RATES, + .channels_max = 2, + .rates = TEGRA_SAMPLE_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .ops = &tegra_i2s_dai_ops, @@ -531,22 +478,9 @@ static int tegra_i2s_driver_probe(struct platform_device *pdev) err = PTR_ERR(info->i2s_clk); goto fail_unmap_mem; } + clk_enable(info->i2s_clk); clk_set_rate(info->i2s_clk, info->pdata->i2s_clk_rate); - info->dap_mclk = i2s_get_clock_by_name(info->pdata->dap_clk); - if (IS_ERR(info->dap_mclk)) { - err = PTR_ERR(info->dap_mclk); - goto fail_unmap_mem; - } - clk_enable(info->dap_mclk); - - info->audio_sync_clk = i2s_get_clock_by_name( - info->pdata->audio_sync_clk); - if (IS_ERR(info->audio_sync_clk)) { - err = PTR_ERR(info->audio_sync_clk); - goto fail_unmap_mem; - } - info->bit_format = TEGRA_AUDIO_BIT_FORMAT_DEFAULT; if (info->pdata->mode == I2S_BIT_FORMAT_DSP) info->bit_format = TEGRA_AUDIO_BIT_FORMAT_DSP; @@ -563,6 +497,9 @@ static int tegra_i2s_driver_probe(struct platform_device *pdev) } } + /* Disable i2s clk to save power */ + clk_disable(info->i2s_clk); + return 0; fail_unmap_mem: diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c index 28f6b6b0d09d..83314f23de53 100644 --- a/sound/soc/tegra/tegra_pcm.c +++ b/sound/soc/tegra/tegra_pcm.c @@ -1,7 +1,7 @@ /* * tegra_pcm.c -- ALSA Soc Audio Layer * - * (c) 2010 Nvidia Graphics Pvt. Ltd. + * (c) 2010-2011 Nvidia Graphics Pvt. Ltd. * http://www.nvidia.com * * (c) 2006 Wolfson Microelectronics PLC. @@ -146,14 +146,14 @@ static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { prtd->state = STATE_INIT; prtd->dma_state = STATE_INIT; - tegra_pcm_play(prtd); /* dma enqueue req1 */ - tegra_pcm_play(prtd); /* dma enqueue req2 */ + for (i = 0; i < DMA_REQ_QCOUNT; i++) + tegra_pcm_play(prtd); /* dma enqueue req */ } else if (prtd->state != STATE_INIT) { /* start recording */ prtd->state = STATE_INIT; prtd->dma_state = STATE_INIT; - tegra_pcm_capture(prtd); /* dma enqueue req1 */ - tegra_pcm_capture(prtd); /* dma enqueue req2 */ + for (i = 0; i < DMA_REQ_QCOUNT; i++) + tegra_pcm_capture(prtd); /* dma enqueue req */ } break; case SNDRV_PCM_TRIGGER_STOP: diff --git a/sound/soc/tegra/tegra_soc.h b/sound/soc/tegra/tegra_soc.h index 8dd2efaa6d13..238497aacb2c 100644 --- a/sound/soc/tegra/tegra_soc.h +++ b/sound/soc/tegra/tegra_soc.h @@ -1,7 +1,7 @@ /* * tegra_soc.h -- SoC audio for tegra * - * (c) 2010 Nvidia Graphics Pvt. Ltd. + * (c) 2010-2011 Nvidia Graphics Pvt. Ltd. * http://www.nvidia.com * * Copyright 2007 Wolfson Microelectronics PLC. @@ -80,6 +80,16 @@ #define DMA_STEP_SIZE_MIN 8 #define DMA_REQ_QCOUNT 2 +#define TEGRA_AUDIO_OFF 0x0 +#define TEGRA_HEADPHONE 0x1 +#define TEGRA_LINEOUT 0x2 +#define TEGRA_SPK 0x4 +#define TEGRA_EAR_SPK 0x8 +#define TEGRA_INT_MIC 0x10 +#define TEGRA_EXT_MIC 0x20 +#define TEGRA_LINEIN 0x40 +#define TEGRA_HEADSET 0x80 + struct tegra_dma_channel; struct tegra_runtime_data { @@ -97,9 +107,14 @@ struct tegra_runtime_data { struct tegra_audio_data { struct snd_soc_codec *codec; + struct clk *dap_mclk; + bool init_done; + int play_device; int capture_device; bool is_call_mode; + + int codec_con; }; struct wired_jack_conf { @@ -113,8 +128,8 @@ struct wired_jack_conf { int amp_reg_enabled; }; +void tegra_ext_control(struct snd_soc_codec *codec, int new_con); int tegra_controls_init(struct snd_soc_codec *codec); -void tegra_controls_exit(void); int tegra_jack_init(struct snd_soc_codec *codec); void tegra_jack_exit(void); diff --git a/sound/soc/tegra/tegra_soc_controls.c b/sound/soc/tegra/tegra_soc_controls.c index 04ffd880ed0e..bd948509df62 100644 --- a/sound/soc/tegra/tegra_soc_controls.c +++ b/sound/soc/tegra/tegra_soc_controls.c @@ -1,7 +1,7 @@ /* * tegra_soc_controls.c -- alsa controls for tegra SoC * - * Copyright (c) 2010, NVIDIA Corporation. + * Copyright (c) 2010-2011, NVIDIA Corporation. * * 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 @@ -18,259 +18,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include <linux/gpio.h> -#include <sound/soc-dapm.h> -#include <linux/regulator/consumer.h> -#include <mach/audio.h> - #include "tegra_soc.h" -#define TEGRA_HP 0 -#define TEGRA_MIC 1 -#define TEGRA_LINE 2 -#define TEGRA_HEADSET 3 -#define TEGRA_HP_OFF 4 - -#define TEGRA_LINEOUT_ON 0 -#define TEGRA_LINEOUT_OFF 1 - -#define TEGRA_INT_SPK_ON 0 -#define TEGRA_INT_SPK_OFF 1 - -extern struct wired_jack_conf tegra_wired_jack_conf; - -static struct tegra_audio_data *audio_data; - -static int tegra_jack_func; -static int tegra_lineout_func; -static int tegra_spk_func; - -static void tegra_ext_control(struct snd_soc_codec *codec) -{ - /* set up jack connection */ - switch (tegra_jack_func) { - case TEGRA_HP: - /* set = unmute headphone */ - snd_soc_dapm_enable_pin(codec, "Mic Jack"); - snd_soc_dapm_disable_pin(codec, "Line Jack"); - snd_soc_dapm_enable_pin(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "Headset Jack"); - break; - case TEGRA_MIC: - /* reset = mute headphone */ - snd_soc_dapm_enable_pin(codec, "Mic Jack"); - snd_soc_dapm_disable_pin(codec, "Line Jack"); - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "Headset Jack"); - break; - case TEGRA_LINE: - snd_soc_dapm_disable_pin(codec, "Mic Jack"); - snd_soc_dapm_enable_pin(codec, "Line Jack"); - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "Headset Jack"); - break; - case TEGRA_HEADSET: - snd_soc_dapm_enable_pin(codec, "Mic Jack"); - snd_soc_dapm_disable_pin(codec, "Line Jack"); - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - snd_soc_dapm_enable_pin(codec, "Headset Jack"); - break; - } - - - if (tegra_lineout_func == TEGRA_LINEOUT_ON) { - snd_soc_dapm_enable_pin(codec, "Lineout"); - } else { - snd_soc_dapm_disable_pin(codec, "Lineout"); - } - - if (tegra_spk_func == TEGRA_INT_SPK_ON) { - snd_soc_dapm_enable_pin(codec, "Int Spk"); - if (tegra_wired_jack_conf.amp_reg && - !tegra_wired_jack_conf.amp_reg_enabled) { - regulator_enable(tegra_wired_jack_conf.amp_reg); - tegra_wired_jack_conf.amp_reg_enabled = 1; - } - } else { - snd_soc_dapm_disable_pin(codec, "Int Spk"); - if (tegra_wired_jack_conf.amp_reg && - tegra_wired_jack_conf.amp_reg_enabled) { - regulator_disable(tegra_wired_jack_conf.amp_reg); - tegra_wired_jack_conf.amp_reg_enabled = 0; - } - } - - /* signal a DAPM event */ - snd_soc_dapm_sync(codec); -} - -static int tegra_get_jack(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = tegra_jack_func; - return 0; -} - -static int tegra_set_jack(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - - if (tegra_jack_func == ucontrol->value.integer.value[0]) - return 0; - - tegra_jack_func = ucontrol->value.integer.value[0]; - tegra_ext_control(codec); - return 1; -} - -static int tegra_get_lineout(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = tegra_lineout_func; - return 0; -} - -static int tegra_set_lineout(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - - if (tegra_lineout_func == ucontrol->value.integer.value[0]) - return 0; - - tegra_lineout_func = ucontrol->value.integer.value[0]; - tegra_ext_control(codec); - return 1; -} - -static int tegra_get_spk(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = tegra_spk_func; - return 0; -} - -static int tegra_set_spk(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - - - if (tegra_spk_func == ucontrol->value.integer.value[0]) - return 0; - - tegra_spk_func = ucontrol->value.integer.value[0]; - tegra_ext_control(codec); - return 1; -} - -static int tegra_dapm_event_int_spk(struct snd_soc_dapm_widget* w, - struct snd_kcontrol* k, int event) -{ - if (tegra_wired_jack_conf.en_spkr != -1) - gpio_set_value_cansleep(tegra_wired_jack_conf.en_spkr, - SND_SOC_DAPM_EVENT_ON(event)); - - return 0; -} - -static int tegra_dapm_event_int_mic(struct snd_soc_dapm_widget* w, - struct snd_kcontrol* k, int event) -{ - if (tegra_wired_jack_conf.en_mic_int != -1) - gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_int, - SND_SOC_DAPM_EVENT_ON(event)); - - if (tegra_wired_jack_conf.en_mic_ext != -1) - gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_ext, - !(SND_SOC_DAPM_EVENT_ON(event))); - - return 0; -} - -static int tegra_dapm_event_ext_mic(struct snd_soc_dapm_widget* w, - struct snd_kcontrol* k, int event) -{ - if (tegra_wired_jack_conf.en_mic_ext != -1) - gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_ext, - SND_SOC_DAPM_EVENT_ON(event)); - - if (tegra_wired_jack_conf.en_mic_int != -1) - gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_int, - !(SND_SOC_DAPM_EVENT_ON(event))); - - return 0; -} - -/*tegra machine dapm widgets */ -static const struct snd_soc_dapm_widget tegra_dapm_widgets[] = { - SND_SOC_DAPM_HP("Headphone Jack", NULL), - SND_SOC_DAPM_MIC("Mic Jack", tegra_dapm_event_ext_mic), - SND_SOC_DAPM_MIC("Int Mic", tegra_dapm_event_int_mic), - SND_SOC_DAPM_SPK("Lineout", NULL), - SND_SOC_DAPM_SPK("Int Spk", tegra_dapm_event_int_spk), - SND_SOC_DAPM_LINE("Line Jack", NULL), - SND_SOC_DAPM_HP("Headset Jack", NULL), -}; - -/* Tegra machine audio map (connections to the codec pins) */ -static const struct snd_soc_dapm_route audio_map[] = { - - /* headset Jack - in = micin, out = LHPOUT*/ - {"Headset Jack", NULL, "HPOUTL"}, - {"Headset Jack", NULL, "HPOUTR"}, - - /* headphone connected to LHPOUT1, RHPOUT1 */ - {"Headphone Jack", NULL, "HPOUTR"}, - {"Headphone Jack", NULL, "HPOUTL"}, - - /* build-in speaker connected to LON/P RON/P */ - {"Int Spk", NULL, "RON"}, - {"Int Spk", NULL, "ROP"}, - {"Int Spk", NULL, "LON"}, - {"Int Spk", NULL, "LOP"}, - - /* lineout connected to LINEOUTR and LINEOUTL */ - {"Lineout", NULL, "LINEOUTR"}, - {"Lineout", NULL, "LINEOUTL"}, - - /* external mic is stero */ - {"IN1L", NULL, "Mic Jack"}, - {"IN1R", NULL, "Mic Jack"}, - - /* internal mic is mono */ - {"IN1R", NULL, "Int Mic"}, - - {"IN3L", NULL, "Line Jack"}, - {"IN3R", NULL, "Line Jack"}, -}; - -static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", - "Off" - }; -static const char *lineout_function[] = {"On", "Off"}; -static const char *spk_function[] = {"On", "Off"}; - -static const struct soc_enum tegra_enum[] = { - SOC_ENUM_SINGLE_EXT(5, jack_function), - SOC_ENUM_SINGLE_EXT(2, lineout_function), - SOC_ENUM_SINGLE_EXT(2, spk_function), -}; - -static const struct snd_kcontrol_new tegra_controls[] = { - SOC_ENUM_EXT("Jack Function", tegra_enum[0], tegra_get_jack, - tegra_set_jack), - SOC_ENUM_EXT("Lineout Function", tegra_enum[1], tegra_get_lineout, - tegra_set_lineout), - SOC_ENUM_EXT("Speaker Function", tegra_enum[2], tegra_get_spk, - tegra_set_spk), -}; - -static void tegra_audio_route(int device_new, int is_call_mode_new) +static void tegra_audio_route(struct tegra_audio_data* audio_data, + int device_new, int is_call_mode_new) { int play_device_new = device_new & TEGRA_AUDIO_DEVICE_OUT_ALL; int capture_device_new = device_new & TEGRA_AUDIO_DEVICE_IN_ALL; + int codec_con = audio_data->codec_con; int is_bt_sco_mode = (play_device_new & TEGRA_AUDIO_DEVICE_OUT_BT_SCO) || (capture_device_new & TEGRA_AUDIO_DEVICE_OUT_BT_SCO); @@ -279,51 +34,46 @@ static void tegra_audio_route(int device_new, int is_call_mode_new) (audio_data->capture_device & TEGRA_AUDIO_DEVICE_OUT_BT_SCO); if (play_device_new != audio_data->play_device) { - if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_HEADPHONE) { - tegra_jack_func = TEGRA_HP; - tegra_spk_func = TEGRA_INT_SPK_OFF; - } - else if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_HEADSET) { - tegra_jack_func = TEGRA_HEADSET; - tegra_spk_func = TEGRA_INT_SPK_OFF; - } - else if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_LINE) { - tegra_jack_func = TEGRA_LINE; - tegra_spk_func = TEGRA_INT_SPK_OFF; - } + codec_con &= ~(TEGRA_HEADPHONE | TEGRA_LINEOUT | + TEGRA_SPK | TEGRA_EAR_SPK | TEGRA_HEADSET); - if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_SPEAKER) { - tegra_lineout_func = TEGRA_LINEOUT_OFF; - tegra_spk_func = TEGRA_INT_SPK_ON; - } - else if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_EAR_SPEAKER) { - tegra_spk_func = TEGRA_INT_SPK_OFF; - tegra_lineout_func = TEGRA_LINEOUT_ON; - } - else { - tegra_lineout_func = TEGRA_LINEOUT_OFF; - tegra_spk_func = TEGRA_INT_SPK_OFF; - } - tegra_ext_control(audio_data->codec); + if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_HEADPHONE) + codec_con |= TEGRA_HEADPHONE; + + if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_LINE) + codec_con |= TEGRA_LINEOUT; + + if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_SPEAKER) + codec_con |= TEGRA_SPK; + + if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_EAR_SPEAKER) + codec_con |= TEGRA_EAR_SPK; + + if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_HEADSET) + codec_con |= TEGRA_HEADSET; + + tegra_ext_control(audio_data->codec, codec_con); audio_data->play_device = play_device_new; } if (capture_device_new != audio_data->capture_device) { + codec_con &= ~(TEGRA_INT_MIC | TEGRA_EXT_MIC | + TEGRA_LINEIN | TEGRA_HEADSET); + if (capture_device_new & (TEGRA_AUDIO_DEVICE_IN_BUILTIN_MIC | - TEGRA_AUDIO_DEVICE_IN_MIC | - TEGRA_AUDIO_DEVICE_IN_BACK_MIC)) { - if ((tegra_jack_func != TEGRA_HP) && - (tegra_jack_func != TEGRA_HEADSET)) { - tegra_jack_func = TEGRA_MIC; - } - } - else if (capture_device_new & TEGRA_AUDIO_DEVICE_IN_HEADSET) { - tegra_jack_func = TEGRA_HEADSET; - } - else if (capture_device_new & TEGRA_AUDIO_DEVICE_IN_LINE) { - tegra_jack_func = TEGRA_LINE; - } - tegra_ext_control(audio_data->codec); + TEGRA_AUDIO_DEVICE_IN_BACK_MIC)) + codec_con |= TEGRA_INT_MIC; + + if (capture_device_new & TEGRA_AUDIO_DEVICE_IN_MIC) + codec_con |= TEGRA_EXT_MIC; + + if (capture_device_new & TEGRA_AUDIO_DEVICE_IN_LINE) + codec_con |= TEGRA_LINEIN; + + if (capture_device_new & TEGRA_AUDIO_DEVICE_IN_HEADSET) + codec_con |= TEGRA_HEADSET; + + tegra_ext_control(audio_data->codec, codec_con); audio_data->capture_device = capture_device_new; } @@ -362,6 +112,8 @@ static int tegra_play_route_info(struct snd_kcontrol *kcontrol, static int tegra_play_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct tegra_audio_data* audio_data = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = TEGRA_AUDIO_DEVICE_NONE; if (audio_data) { ucontrol->value.integer.value[0] = audio_data->play_device; @@ -373,12 +125,14 @@ static int tegra_play_route_get(struct snd_kcontrol *kcontrol, static int tegra_play_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct tegra_audio_data* audio_data = snd_kcontrol_chip(kcontrol); + if (audio_data) { int play_device_new = ucontrol->value.integer.value[0] & TEGRA_AUDIO_DEVICE_OUT_ALL; if (audio_data->play_device != play_device_new) { - tegra_audio_route( + tegra_audio_route(audio_data, play_device_new | audio_data->capture_device, audio_data->is_call_mode); return 1; @@ -411,6 +165,8 @@ static int tegra_capture_route_info(struct snd_kcontrol *kcontrol, static int tegra_capture_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct tegra_audio_data* audio_data = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = TEGRA_AUDIO_DEVICE_NONE; if (audio_data) { ucontrol->value.integer.value[0] = audio_data->capture_device; @@ -422,12 +178,14 @@ static int tegra_capture_route_get(struct snd_kcontrol *kcontrol, static int tegra_capture_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct tegra_audio_data* audio_data = snd_kcontrol_chip(kcontrol); + if (audio_data) { int capture_device_new = ucontrol->value.integer.value[0] & TEGRA_AUDIO_DEVICE_IN_ALL; if (audio_data->capture_device != capture_device_new) { - tegra_audio_route( + tegra_audio_route(audio_data, audio_data->play_device | capture_device_new, audio_data->is_call_mode); return 1; @@ -460,6 +218,8 @@ static int tegra_call_mode_info(struct snd_kcontrol *kcontrol, static int tegra_call_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct tegra_audio_data* audio_data = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = TEGRA_AUDIO_DEVICE_NONE; if (audio_data) { ucontrol->value.integer.value[0] = audio_data->is_call_mode; @@ -471,11 +231,13 @@ static int tegra_call_mode_get(struct snd_kcontrol *kcontrol, static int tegra_call_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct tegra_audio_data* audio_data = snd_kcontrol_chip(kcontrol); + if (audio_data) { int is_call_mode_new = ucontrol->value.integer.value[0]; if (audio_data->is_call_mode != is_call_mode_new) { - tegra_audio_route( + tegra_audio_route(audio_data, audio_data->play_device | audio_data->capture_device, is_call_mode_new); @@ -498,81 +260,26 @@ struct snd_kcontrol_new tegra_call_mode_control = { int tegra_controls_init(struct snd_soc_codec *codec) { + struct tegra_audio_data* audio_data = codec->socdev->codec_data; int err; - if (codec == NULL) - return -ENODEV; + /* Add play route control */ + err = snd_ctl_add(codec->card, + snd_ctl_new1(&tegra_play_route_control, audio_data)); + if (err < 0) + return err; - if (!audio_data) { - audio_data = kzalloc(sizeof(*audio_data), GFP_KERNEL); - if (!audio_data) { - pr_err("failed to allocate tegra_audio_data \n"); - return -ENOMEM; - } + /* Add capture route control */ + err = snd_ctl_add(codec->card, + snd_ctl_new1(&tegra_capture_route_control, audio_data)); + if (err < 0) + return err; - /* Add tegra specific controls */ - err = snd_soc_add_controls(codec, tegra_controls, - ARRAY_SIZE(tegra_controls)); - if (err < 0) - goto fail; - - /* Add tegra specific widgets */ - snd_soc_dapm_new_controls(codec, tegra_dapm_widgets, - ARRAY_SIZE(tegra_dapm_widgets)); - - /* Set up tegra specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, - ARRAY_SIZE(audio_map)); - - audio_data->codec = codec; - /* Add play route control */ - err = snd_ctl_add(codec->card, - snd_ctl_new1(&tegra_play_route_control, NULL)); - if (err < 0) - goto fail; - - /* Add capture route control */ - err = snd_ctl_add(codec->card, - snd_ctl_new1(&tegra_capture_route_control, NULL)); - if (err < 0) - goto fail; - - /* Add call mode switch control */ - err = snd_ctl_add(codec->card, - snd_ctl_new1(&tegra_call_mode_control, NULL)); - if (err < 0) - goto fail; - - /* Add jack detection */ - err = tegra_jack_init(codec); - if (err < 0) - goto fail; - - /* Default to HP output */ - tegra_jack_func = TEGRA_HP; - tegra_lineout_func = TEGRA_LINEOUT_ON; - tegra_spk_func = TEGRA_INT_SPK_OFF; - tegra_ext_control(codec); - - snd_soc_dapm_sync(codec); - } + /* Add call mode switch control */ + err = snd_ctl_add(codec->card, + snd_ctl_new1(&tegra_call_mode_control, audio_data)); + if (err < 0) + return err; return 0; - -fail: - if (audio_data) { - kfree(audio_data); - audio_data = 0; - } - return err; -} - -void tegra_controls_exit(void) -{ - tegra_jack_exit(); - - if (audio_data) { - kfree(audio_data); - audio_data = 0; - } } diff --git a/sound/soc/tegra/tegra_soc_wm8753.c b/sound/soc/tegra/tegra_soc_wm8753.c index 0be84a031354..b285e6f82b33 100644 --- a/sound/soc/tegra/tegra_soc_wm8753.c +++ b/sound/soc/tegra/tegra_soc_wm8753.c @@ -1,7 +1,7 @@ /* * tegra_soc_wm8753.c -- SoC audio for tegra * - * Copyright 2011 Nvidia Graphics Pvt. Ltd. + * Copyright 2010-2011 Nvidia Graphics Pvt. Ltd. * * Author: Sachin Nikam * snikam@nvidia.com @@ -26,6 +26,7 @@ #include "tegra_soc.h" #include "../codecs/wm8753.h" +#include <sound/soc-dapm.h> #include <linux/regulator/consumer.h> #include <linux/types.h> @@ -142,6 +143,7 @@ static struct platform_device *tegra_snd_device; static struct regulator* wm8753_reg; extern struct snd_soc_dai tegra_i2s_dai[]; +extern struct snd_soc_dai tegra_spdif_dai; extern struct snd_soc_dai tegra_generic_codec_dai[]; extern struct snd_soc_platform tegra_soc_platform; @@ -151,61 +153,49 @@ static int tegra_hifi_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - int err; - unsigned int value; - unsigned int channels, rate, bit_size = 16; struct snd_soc_codec *codec = codec_dai->codec; + struct tegra_audio_data* audio_data = rtd->socdev->codec_data; + enum dac_dap_data_format data_fmt; + int dai_flag = 0, sys_clk; + unsigned int value; + int err; - rate = params_rate(params); /* Sampling Rate in Hz */ - channels = params_channels(params); /* Number of channels */ - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - bit_size = 16; - break; - case SNDRV_PCM_FORMAT_S20_3LE: - bit_size = 20; - break; - case SNDRV_PCM_FORMAT_S24_LE: - bit_size = 24; - break; - case SNDRV_PCM_FORMAT_S32_LE: - bit_size = 32; - break; - default: - pr_err(KERN_ERR "Invalid pcm format size\n"); - return EINVAL; - } + if (tegra_das_is_port_master(tegra_audio_codec_type_hifi)) + dai_flag |= SND_SOC_DAIFMT_CBM_CFM; + else + dai_flag |= SND_SOC_DAIFMT_CBS_CFS; + + data_fmt = tegra_das_get_codec_data_fmt(tegra_audio_codec_type_hifi); - err = snd_soc_dai_set_fmt(codec_dai, - SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); + /* We are supporting DSP and I2s format for now */ + if (data_fmt & dac_dap_data_format_i2s) + dai_flag |= SND_SOC_DAIFMT_I2S; + else + dai_flag |= SND_SOC_DAIFMT_DSP_A; + + err = snd_soc_dai_set_fmt(codec_dai, dai_flag); if (err < 0) { - pr_err(KERN_ERR "codec_dai fmt not set\n"); + pr_err("codec_dai fmt not set \n"); return err; } - err = snd_soc_dai_set_fmt(cpu_dai, - SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); + + err = snd_soc_dai_set_fmt(cpu_dai, dai_flag); if (err < 0) { - pr_err(KERN_ERR "cpu_dai fmt not set\n"); + pr_err("cpu_dai fmt not set \n"); return err; } - err = snd_soc_dai_set_sysclk(codec_dai, 0, bit_size*rate*channels*2*4, - SND_SOC_CLOCK_IN); + sys_clk = clk_get_rate(audio_data->dap_mclk); + err = snd_soc_dai_set_sysclk(codec_dai, 0, sys_clk, SND_SOC_CLOCK_IN); if (err < 0) { - pr_err(KERN_ERR "codec_dai clock not set\n"); + pr_err("codec_dai clock not set\n"); return err; } - err = snd_soc_dai_set_sysclk(cpu_dai, 0, bit_size*rate*channels*2, - SND_SOC_CLOCK_IN); + err = snd_soc_dai_set_sysclk(cpu_dai, 0, sys_clk, SND_SOC_CLOCK_IN); if (err < 0) { - pr_err(KERN_ERR "cpu_dai clock not set\n"); + pr_err("cpu_dai clock not set\n"); return err; } @@ -326,24 +316,49 @@ static int tegra_voice_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + struct tegra_audio_data* audio_data = rtd->socdev->codec_data; + enum dac_dap_data_format data_fmt; + int dai_flag = 0, sys_clk; int err; - err = snd_soc_dai_set_fmt(cpu_dai, - SND_SOC_DAIFMT_DSP_A | \ - SND_SOC_DAIFMT_NB_NF | \ - SND_SOC_DAIFMT_CBS_CFS); + if (tegra_das_is_port_master(tegra_audio_codec_type_bluetooth)) + dai_flag |= SND_SOC_DAIFMT_CBM_CFM; + else + dai_flag |= SND_SOC_DAIFMT_CBS_CFS; + + data_fmt = tegra_das_get_codec_data_fmt(tegra_audio_codec_type_bluetooth); + + /* We are supporting DSP and I2s format for now */ + if (data_fmt & dac_dap_data_format_dsp) + dai_flag |= SND_SOC_DAIFMT_DSP_A; + else + dai_flag |= SND_SOC_DAIFMT_I2S; + err = snd_soc_dai_set_fmt(codec_dai, dai_flag); if (err < 0) { - pr_err("%s:cpu_dai fmt not set \n", __func__); - return err; + pr_err("codec_dai fmt not set \n"); + return err; } - err = snd_soc_dai_set_sysclk(cpu_dai, 0, I2S2_CLK, SND_SOC_CLOCK_IN); + err = snd_soc_dai_set_fmt(cpu_dai, dai_flag); + if (err < 0) { + pr_err("cpu_dai fmt not set \n"); + return err; + } + sys_clk = clk_get_rate(audio_data->dap_mclk); + err = snd_soc_dai_set_sysclk(codec_dai, 0, sys_clk, SND_SOC_CLOCK_IN); if (err < 0) { - pr_err("%s:cpu_dai clock not set\n", __func__); - return err; + pr_err("cpu_dai clock not set\n"); + return err; + } + + err = snd_soc_dai_set_sysclk(cpu_dai, 0, sys_clk, SND_SOC_CLOCK_IN); + if (err < 0) { + pr_err("cpu_dai clock not set\n"); + return err; } return 0; @@ -354,14 +369,161 @@ static int tegra_voice_hw_free(struct snd_pcm_substream *substream) return 0; } +static int tegra_spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + return 0; +} + +int tegra_codec_startup(struct snd_pcm_substream *substream) +{ + tegra_das_power_mode(true); + + return 0; +} + +void tegra_codec_shutdown(struct snd_pcm_substream *substream) +{ + tegra_das_power_mode(false); +} + +int tegra_soc_suspend_pre(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +int tegra_soc_suspend_post(struct platform_device *pdev, pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct tegra_audio_data* audio_data = socdev->codec_data; + + clk_disable(audio_data->dap_mclk); + + return 0; +} + +int tegra_soc_resume_pre(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct tegra_audio_data* audio_data = socdev->codec_data; + + clk_enable(audio_data->dap_mclk); + + return 0; +} + +int tegra_soc_resume_post(struct platform_device *pdev) +{ + return 0; +} + static struct snd_soc_ops tegra_hifi_ops = { .hw_params = tegra_hifi_hw_params, .hw_free = tegra_hifi_hw_free, + .startup = tegra_codec_startup, + .shutdown = tegra_codec_shutdown, }; static struct snd_soc_ops tegra_voice_ops = { .hw_params = tegra_voice_hw_params, .hw_free = tegra_voice_hw_free, + .startup = tegra_codec_startup, + .shutdown = tegra_codec_shutdown, +}; + +static struct snd_soc_ops tegra_spdif_ops = { + .hw_params = tegra_spdif_hw_params, +}; + +void tegra_ext_control(struct snd_soc_codec *codec, int new_con) +{ + struct tegra_audio_data* audio_data = codec->socdev->codec_data; + + /* Disconnect old codec routes and connect new routes*/ + if (new_con & TEGRA_HEADPHONE) + snd_soc_dapm_enable_pin(codec, "Headphone"); + else + snd_soc_dapm_disable_pin(codec, "Headphone"); + + if (new_con & TEGRA_EAR_SPK) + snd_soc_dapm_enable_pin(codec, "EarPiece"); + else + snd_soc_dapm_disable_pin(codec, "EarPiece"); + + if (new_con & TEGRA_SPK) + snd_soc_dapm_enable_pin(codec, "Int Spk"); + else + snd_soc_dapm_disable_pin(codec, "Int Spk"); + + if (new_con & TEGRA_INT_MIC) + snd_soc_dapm_enable_pin(codec, "Int Mic"); + else + snd_soc_dapm_disable_pin(codec, "Int Mic"); + + if (new_con & TEGRA_EXT_MIC) + snd_soc_dapm_enable_pin(codec, "Ext Mic"); + else + snd_soc_dapm_disable_pin(codec, "Ext Mic"); + + if (new_con & TEGRA_LINEIN) + snd_soc_dapm_enable_pin(codec, "Linein"); + else + snd_soc_dapm_disable_pin(codec, "Linein"); + + if (new_con & TEGRA_HEADSET) + snd_soc_dapm_enable_pin(codec, "Headset"); + else + snd_soc_dapm_disable_pin(codec, "Headset"); + + /* signal a DAPM event */ + snd_soc_dapm_sync(codec); + audio_data->codec_con = new_con; +} + +/*tegra machine dapm widgets */ +static const struct snd_soc_dapm_widget tegra_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_HP("EarPiece", NULL), + SND_SOC_DAPM_HP("Headset", NULL), + SND_SOC_DAPM_SPK("Int Spk", NULL), + SND_SOC_DAPM_MIC("Ext Mic", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_LINE("Linein", NULL), +}; + +/* Tegra machine audio map (connections to the codec pins) */ +static const struct snd_soc_dapm_route audio_map[] = { + + /* headphone connected to LHPOUT1, RHPOUT1 */ + {"Headphone", NULL, "ROUT1"}, + {"Headphone", NULL, "LOUT1"}, + + /* earpiece */ + {"EarPiece", NULL, "ROUT2"}, + {"EarPiece", NULL, "LOUT2"}, + + /* headset Jack */ + {"Headset", NULL, "ROUT1"}, + {"Headset", NULL, "LOUT1"}, + {"MIC1", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Headset"}, + + /* build-in speaker */ + {"Int Spk", NULL, "ROUT1"}, + {"Int Spk", NULL, "LOUT1"}, + + /* internal mic is mono */ + {"MIC1", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Int Mic"}, + + /* external mic is stero */ + {"MIC2", NULL, "Mic Bias"}, + {"MIC2N", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Ext Mic"}, + + /* Line in */ + {"LINE1", NULL, "Linein"}, + {"LINE2", NULL, "Linein"}, }; static void wm8753_intr_work(struct work_struct *work) @@ -413,73 +575,106 @@ static irqreturn_t wm8753_irq(int irq, void *data) static int tegra_codec_init(struct snd_soc_codec *codec) { - int ret; + struct tegra_audio_data* audio_data = codec->socdev->codec_data; + int ret = 0; unsigned int value; - ret = tegra_controls_init(codec); - if (ret < 0) - goto failed; - - if (!wm8753_jack) { - - wm8753_jack = kzalloc(sizeof(*wm8753_jack), GFP_KERNEL); - if (!wm8753_jack) { - pr_err("failed to allocate wm8753-jack\n"); - return -ENOMEM; + if (!audio_data->init_done) { + audio_data->dap_mclk = tegra_das_get_dap_mclk(); + if (!audio_data->dap_mclk) { + pr_err("Failed to get dap mclk \n"); + ret = -ENODEV; + return ret; } + clk_enable(audio_data->dap_mclk); - wm8753_jack->gpio = TEGRA_GPIO_PW3; - wm8753_jack->pcodec = codec; - - INIT_WORK(&wm8753_jack->work, wm8753_intr_work); - - ret = snd_jack_new(codec->card, "Headphone Jack", SND_JACK_HEADPHONE, - &wm8753_jack->jack); - if (ret < 0) - goto failed; - - ret = gpio_request(wm8753_jack->gpio, "headphone-detect-gpio"); - if (ret) - goto failed; - - ret = gpio_direction_input(wm8753_jack->gpio); - if (ret) - goto gpio_failed; + /* Add tegra specific widgets */ + snd_soc_dapm_new_controls(codec, tegra_dapm_widgets, + ARRAY_SIZE(tegra_dapm_widgets)); - tegra_gpio_enable(wm8753_jack->gpio); + /* Set up tegra specific audio path audio_map */ + snd_soc_dapm_add_routes(codec, audio_map, + ARRAY_SIZE(audio_map)); - ret = request_irq(gpio_to_irq(wm8753_jack->gpio), - wm8753_irq, - IRQF_TRIGGER_FALLING, - "wm8753", - wm8753_jack); + /* Add jack detection */ + ret = tegra_jack_init(codec); + if (ret < 0) { + pr_err("Failed in jack init \n"); + return ret; + } - if (ret) - goto gpio_failed; + /* Default to OFF */ + tegra_ext_control(codec, TEGRA_AUDIO_OFF); - /* Configure GPIO2 pin to generate the interrupt */ - value = snd_soc_read(codec, WM8753_GPIO2); - value |= (WM8753_GPIO2_GP2M_0 | WM8753_GPIO2_GP2M_1); - value &= ~(WM8753_GPIO2_GP2M_2); - snd_soc_write(codec, WM8753_GPIO2, value); + ret = tegra_controls_init(codec); + if (ret < 0) { + pr_err("Failed in controls init \n"); + return ret; + } - /* Active low Interrupt */ - value = snd_soc_read(codec, WM8753_GPIO1); - value |= (WM8753_GPIO1_INTCON_1 | WM8753_GPIO1_INTCON_0); - snd_soc_write(codec, WM8753_GPIO1, value); + if (!wm8753_jack) { + wm8753_jack = kzalloc(sizeof(*wm8753_jack), GFP_KERNEL); + if (!wm8753_jack) { + pr_err("failed to allocate wm8753-jack\n"); + return -ENOMEM; + } + + wm8753_jack->gpio = TEGRA_GPIO_PW3; + wm8753_jack->pcodec = codec; + + INIT_WORK(&wm8753_jack->work, wm8753_intr_work); + + ret = snd_jack_new(codec->card, "Headphone Jack", SND_JACK_HEADPHONE, + &wm8753_jack->jack); + if (ret < 0) + goto failed; + + ret = gpio_request(wm8753_jack->gpio, "headphone-detect-gpio"); + if (ret) + goto failed; + + ret = gpio_direction_input(wm8753_jack->gpio); + if (ret) + goto gpio_failed; + + tegra_gpio_enable(wm8753_jack->gpio); + + ret = request_irq(gpio_to_irq(wm8753_jack->gpio), + wm8753_irq, + IRQF_TRIGGER_FALLING, + "wm8753", + wm8753_jack); + + if (ret) + goto gpio_failed; + + /* Configure GPIO2 pin to generate the interrupt */ + value = snd_soc_read(codec, WM8753_GPIO2); + value |= (WM8753_GPIO2_GP2M_0 | WM8753_GPIO2_GP2M_1); + value &= ~(WM8753_GPIO2_GP2M_2); + snd_soc_write(codec, WM8753_GPIO2, value); + + /* Active low Interrupt */ + value = snd_soc_read(codec, WM8753_GPIO1); + value |= (WM8753_GPIO1_INTCON_1 | WM8753_GPIO1_INTCON_0); + snd_soc_write(codec, WM8753_GPIO1, value); + + /* GPIO4 interrupt polarity -- interupt when low i.e Headphone connected */ + value = snd_soc_read(codec, WM8753_INTPOL); + value |= (WM8753_INTPOL_GPIO4IPOL); + snd_soc_write(codec, WM8753_INTPOL, value); + + /* GPIO4 interrupt enable and disable other interrupts */ + value = snd_soc_read(codec, WM8753_INTEN); + value |= (WM8753_INTEN_GPIO4IEN); + value &= ~(WM8753_INTEN_MICSHTEN | WM8753_INTEN_MICDETEN | + WM8753_INTEN_GPIO3IEN | WM8753_INTEN_HPSWIEN | + WM8753_INTEN_GPIO5IEN | WM8753_INTEN_TSDIEN); + snd_soc_write(codec, WM8753_INTEN, value); + } - /* GPIO4 interrupt polarity -- interupt when low i.e Headphone connected */ - value = snd_soc_read(codec, WM8753_INTPOL); - value |= (WM8753_INTPOL_GPIO4IPOL); - snd_soc_write(codec, WM8753_INTPOL, value); - - /* GPIO4 interrupt enable and disable other interrupts */ - value = snd_soc_read(codec, WM8753_INTEN); - value |= (WM8753_INTEN_GPIO4IEN); - value &= ~(WM8753_INTEN_MICSHTEN | WM8753_INTEN_MICDETEN | - WM8753_INTEN_GPIO3IEN | WM8753_INTEN_HPSWIEN | - WM8753_INTEN_GPIO5IEN | WM8753_INTEN_TSDIEN); - snd_soc_write(codec, WM8753_INTEN, value); + audio_data->codec = codec; + audio_data->init_done = 1; } return ret; @@ -509,6 +704,22 @@ static struct snd_soc_dai_link tegra_soc_dai[] = { .init = tegra_codec_init, .ops = &tegra_voice_ops, }, + { + .name = "Tegra-spdif", + .stream_name = "Tegra Spdif", + .cpu_dai = &tegra_spdif_dai, + .codec_dai = &tegra_generic_codec_dai[1], + .init = tegra_codec_init, + .ops = &tegra_spdif_ops, + }, +}; + +static struct tegra_audio_data audio_data = { + .init_done = 0, + .play_device = TEGRA_AUDIO_DEVICE_NONE, + .capture_device = TEGRA_AUDIO_DEVICE_NONE, + .is_call_mode = false, + .codec_con = TEGRA_AUDIO_OFF, }; static struct snd_soc_card tegra_snd_soc = { @@ -516,11 +727,16 @@ static struct snd_soc_card tegra_snd_soc = { .platform = &tegra_soc_platform, .dai_link = tegra_soc_dai, .num_links = ARRAY_SIZE(tegra_soc_dai), + .suspend_pre = tegra_soc_suspend_pre, + .suspend_post = tegra_soc_suspend_post, + .resume_pre = tegra_soc_resume_pre, + .resume_post = tegra_soc_resume_post, }; static struct snd_soc_device tegra_snd_devdata = { .card = &tegra_snd_soc, .codec_dev = &soc_codec_dev_wm8753, + .codec_data = &audio_data, }; static int __init tegra_init(void) @@ -571,7 +787,6 @@ err_put_regulator: static void __exit tegra_exit(void) { - tegra_controls_exit(); platform_device_unregister(tegra_snd_device); regulator_disable(wm8753_reg); regulator_put(wm8753_reg); diff --git a/sound/soc/tegra/tegra_soc_wm8903.c b/sound/soc/tegra/tegra_soc_wm8903.c index 3fe2dd4dc468..b76b6dd575f8 100644 --- a/sound/soc/tegra/tegra_soc_wm8903.c +++ b/sound/soc/tegra/tegra_soc_wm8903.c @@ -1,7 +1,7 @@ /* * tegra_soc_wm8903.c -- SoC audio for tegra * - * (c) 2010 Nvidia Graphics Pvt. Ltd. + * (c) 2010-2011 Nvidia Graphics Pvt. Ltd. * http://www.nvidia.com * * Copyright 2007 Wolfson Microelectronics PLC. @@ -16,6 +16,9 @@ */ #include "tegra_soc.h" +#include <linux/gpio.h> +#include <sound/soc-dapm.h> +#include <linux/regulator/consumer.h> #include "../codecs/wm8903.h" static struct platform_device *tegra_snd_device; @@ -24,22 +27,15 @@ extern struct snd_soc_dai tegra_i2s_dai[]; extern struct snd_soc_dai tegra_spdif_dai; extern struct snd_soc_dai tegra_generic_codec_dai[]; extern struct snd_soc_platform tegra_soc_platform; +extern struct wired_jack_conf tegra_wired_jack_conf; /* codec register values */ -#define B07_INEMUTE 7 -#define B06_VOL_M3DB 6 #define B00_IN_VOL 0 #define B00_INR_ENA 0 #define B01_INL_ENA 1 -#define R06_MICBIAS_CTRL_0 6 -#define B07_MICDET_HYST_ENA 7 -#define B04_MICDET_THR 4 -#define B02_MICSHORT_THR 2 #define B01_MICDET_ENA 1 #define B00_MICBIAS_ENA 0 #define B15_DRC_ENA 15 -#define B03_DACL_ENA 3 -#define B02_DACR_ENA 2 #define B01_ADCL_ENA 1 #define B00_ADCR_ENA 0 #define B06_IN_CM_ENA 6 @@ -48,106 +44,116 @@ extern struct snd_soc_platform tegra_soc_platform; #define B00_MODE 0 #define B06_AIF_ADCL 7 #define B06_AIF_ADCR 6 -#define B05_ADC_HPF_CUT 5 #define B04_ADC_HPF_ENA 4 -#define B01_ADCL_DATINV 1 -#define B00_ADCR_DATINV 0 #define R20_SIDETONE_CTRL 32 #define R29_DRC_1 41 #define SET_REG_VAL(r,m,l,v) (((r)&(~((m)<<(l))))|(((v)&(m))<<(l))) + static int tegra_hifi_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 *codec_dai = rtd->dai->codec_dai; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - int err; struct snd_soc_codec *codec = codec_dai->codec; - int CtrlReg = 0; - int VolumeCtrlReg = 0; - int SidetoneCtrlReg = 0; - int SideToneAtenuation = 0; - - err = snd_soc_dai_set_fmt(codec_dai, - SND_SOC_DAIFMT_I2S | \ - SND_SOC_DAIFMT_NB_NF | \ - SND_SOC_DAIFMT_CBS_CFS); + struct tegra_audio_data* audio_data = rtd->socdev->codec_data; + enum dac_dap_data_format data_fmt; + int dai_flag = 0, sys_clk; + int err; + + if (tegra_das_is_port_master(tegra_audio_codec_type_hifi)) + dai_flag |= SND_SOC_DAIFMT_CBM_CFM; + else + dai_flag |= SND_SOC_DAIFMT_CBS_CFS; + + data_fmt = tegra_das_get_codec_data_fmt(tegra_audio_codec_type_hifi); + + /* We are supporting DSP and I2s format for now */ + if (data_fmt & dac_dap_data_format_i2s) + dai_flag |= SND_SOC_DAIFMT_I2S; + else + dai_flag |= SND_SOC_DAIFMT_DSP_A; + + err = snd_soc_dai_set_fmt(codec_dai, dai_flag); if (err < 0) { pr_err("codec_dai fmt not set \n"); return err; } - err = snd_soc_dai_set_fmt(cpu_dai, - SND_SOC_DAIFMT_I2S | \ - SND_SOC_DAIFMT_NB_NF | \ - SND_SOC_DAIFMT_CBS_CFS); + err = snd_soc_dai_set_fmt(cpu_dai, dai_flag); if (err < 0) { pr_err("cpu_dai fmt not set \n"); return err; } - err = snd_soc_dai_set_sysclk(codec_dai, 0, I2S1_CLK, SND_SOC_CLOCK_IN); + sys_clk = clk_get_rate(audio_data->dap_mclk); + err = snd_soc_dai_set_sysclk(codec_dai, 0, sys_clk, SND_SOC_CLOCK_IN); if (err < 0) { pr_err("codec_dai clock not set\n"); return err; } - err = snd_soc_dai_set_sysclk(cpu_dai, 0, I2S1_CLK, SND_SOC_CLOCK_IN); + err = snd_soc_dai_set_sysclk(cpu_dai, 0, sys_clk, SND_SOC_CLOCK_IN); if (err < 0) { pr_err("cpu_dai clock not set\n"); return err; } if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) { + int CtrlReg = 0; + int VolumeCtrlReg = 0; + int SidetoneCtrlReg = 0; + int SideToneAtenuation = 0; + snd_soc_write(codec, WM8903_ANALOGUE_LEFT_INPUT_0, 0X7); snd_soc_write(codec, WM8903_ANALOGUE_RIGHT_INPUT_0, 0X7); - // Mic Bias enable + /* Mic Bias enable */ CtrlReg = (0x1<<B00_MICBIAS_ENA) | (0x1<<B01_MICDET_ENA); snd_soc_write(codec, WM8903_MIC_BIAS_CONTROL_0, CtrlReg); - // Enable DRC + /* Enable DRC */ CtrlReg = snd_soc_read(codec, WM8903_DRC_0); CtrlReg |= (1<<B15_DRC_ENA); snd_soc_write(codec, WM8903_DRC_0, CtrlReg); - // Single Ended Mic + /* Single Ended Mic */ CtrlReg = (0x0<<B06_IN_CM_ENA) | (0x0<<B00_MODE) | (0x0<<B04_IP_SEL_N) | (0x1<<B02_IP_SEL_P); VolumeCtrlReg = (0x1C << B00_IN_VOL); - // Mic Setting + /* Mic Setting */ snd_soc_write(codec, WM8903_ANALOGUE_LEFT_INPUT_1, CtrlReg); snd_soc_write(codec, WM8903_ANALOGUE_RIGHT_INPUT_1, CtrlReg); - // voulme for single ended mic + /* voulme for single ended mic */ snd_soc_write(codec, WM8903_ANALOGUE_LEFT_INPUT_0, VolumeCtrlReg); snd_soc_write(codec, WM8903_ANALOGUE_RIGHT_INPUT_0, VolumeCtrlReg); - // replicate mic setting on both channels + /* replicate mic setting on both channels */ CtrlReg = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_0); CtrlReg = SET_REG_VAL(CtrlReg, 0x1, B06_AIF_ADCR, 0x0); CtrlReg = SET_REG_VAL(CtrlReg, 0x1, B06_AIF_ADCL, 0x0); snd_soc_write(codec, WM8903_AUDIO_INTERFACE_0, CtrlReg); - // Enable analog inputs + /* Enable analog inputs */ CtrlReg = (0x1<<B01_INL_ENA) | (0x1<<B00_INR_ENA); snd_soc_write(codec, WM8903_POWER_MANAGEMENT_0, CtrlReg); - // ADC Settings + /* ADC Settings */ CtrlReg = snd_soc_read(codec, WM8903_ADC_DIGITAL_0); CtrlReg |= (0x1<<B04_ADC_HPF_ENA); snd_soc_write(codec, WM8903_ADC_DIGITAL_0, CtrlReg); SidetoneCtrlReg = 0; snd_soc_write(codec, R20_SIDETONE_CTRL, SidetoneCtrlReg); - // Enable ADC + /* Enable ADC */ CtrlReg = snd_soc_read(codec, WM8903_POWER_MANAGEMENT_6); CtrlReg |= (0x1<<B00_ADCR_ENA)|(0x1<<B01_ADCL_ENA); snd_soc_write(codec, WM8903_POWER_MANAGEMENT_6, CtrlReg); - // Enable Sidetone + /* Enable Sidetone */ SidetoneCtrlReg = (0x1<<2) | (0x2<<0); - SideToneAtenuation = 12 ; // sidetone 0 db + SideToneAtenuation = 12 ; /* sidetone 0 db */ SidetoneCtrlReg |= (SideToneAtenuation<<8) | (SideToneAtenuation<<4); snd_soc_write(codec, R20_SIDETONE_CTRL, SidetoneCtrlReg); CtrlReg = snd_soc_read(codec, R29_DRC_1); - CtrlReg |= 0x3; //mic volume 18 db + CtrlReg |= 0x3; /*mic volume 18 db */ snd_soc_write(codec, R29_DRC_1, CtrlReg); } @@ -158,23 +164,51 @@ static int tegra_voice_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 *codec_dai = rtd->dai->codec_dai; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + struct tegra_audio_data* audio_data = rtd->socdev->codec_data; + enum dac_dap_data_format data_fmt; + int dai_flag = 0, sys_clk; int err; - err = snd_soc_dai_set_fmt(cpu_dai, - SND_SOC_DAIFMT_DSP_A | \ - SND_SOC_DAIFMT_NB_NF | \ - SND_SOC_DAIFMT_CBS_CFS); + if (tegra_das_is_port_master(tegra_audio_codec_type_bluetooth)) + dai_flag |= SND_SOC_DAIFMT_CBM_CFM; + else + dai_flag |= SND_SOC_DAIFMT_CBS_CFS; + + data_fmt = tegra_das_get_codec_data_fmt(tegra_audio_codec_type_bluetooth); + + /* We are supporting DSP and I2s format for now */ + if (data_fmt & dac_dap_data_format_dsp) + dai_flag |= SND_SOC_DAIFMT_DSP_A; + else + dai_flag |= SND_SOC_DAIFMT_I2S; + + err = snd_soc_dai_set_fmt(codec_dai, dai_flag); + if (err < 0) { + pr_err("codec_dai fmt not set \n"); + return err; + } + + err = snd_soc_dai_set_fmt(cpu_dai, dai_flag); if (err < 0) { pr_err("cpu_dai fmt not set \n"); return err; } - err = snd_soc_dai_set_sysclk(cpu_dai, 0, I2S2_CLK, SND_SOC_CLOCK_IN); + sys_clk = clk_get_rate(audio_data->dap_mclk); + err = snd_soc_dai_set_sysclk(codec_dai, 0, sys_clk, SND_SOC_CLOCK_IN); if (err < 0) { pr_err("cpu_dai clock not set\n"); return err; } + + err = snd_soc_dai_set_sysclk(cpu_dai, 0, sys_clk, SND_SOC_CLOCK_IN); + if (err < 0) { + pr_err("cpu_dai clock not set\n"); + return err; + } + return 0; } @@ -184,21 +218,251 @@ static int tegra_spdif_hw_params(struct snd_pcm_substream *substream, return 0; } +int tegra_codec_startup(struct snd_pcm_substream *substream) +{ + tegra_das_power_mode(true); + + return 0; +} + +void tegra_codec_shutdown(struct snd_pcm_substream *substream) +{ + tegra_das_power_mode(false); +} + +int tegra_soc_suspend_pre(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +int tegra_soc_suspend_post(struct platform_device *pdev, pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct tegra_audio_data* audio_data = socdev->codec_data; + + clk_disable(audio_data->dap_mclk); + + return 0; +} + +int tegra_soc_resume_pre(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct tegra_audio_data* audio_data = socdev->codec_data; + + clk_enable(audio_data->dap_mclk); + + return 0; +} + +int tegra_soc_resume_post(struct platform_device *pdev) +{ + return 0; +} + static struct snd_soc_ops tegra_hifi_ops = { .hw_params = tegra_hifi_hw_params, + .startup = tegra_codec_startup, + .shutdown = tegra_codec_shutdown, }; static struct snd_soc_ops tegra_voice_ops = { .hw_params = tegra_voice_hw_params, + .startup = tegra_codec_startup, + .shutdown = tegra_codec_shutdown, }; static struct snd_soc_ops tegra_spdif_ops = { .hw_params = tegra_spdif_hw_params, }; +void tegra_ext_control(struct snd_soc_codec *codec, int new_con) +{ + struct tegra_audio_data* audio_data = codec->socdev->codec_data; + + /* Disconnect old codec routes and connect new routes*/ + if (new_con & TEGRA_HEADPHONE) + snd_soc_dapm_enable_pin(codec, "Headphone"); + else + snd_soc_dapm_disable_pin(codec, "Headphone"); + + if (new_con & (TEGRA_LINEOUT | TEGRA_EAR_SPK)) + snd_soc_dapm_enable_pin(codec, "Lineout"); + else + snd_soc_dapm_disable_pin(codec, "Lineout"); + + if (new_con & TEGRA_SPK) + snd_soc_dapm_enable_pin(codec, "Int Spk"); + else + snd_soc_dapm_disable_pin(codec, "Int Spk"); + + if (new_con & TEGRA_INT_MIC) + snd_soc_dapm_enable_pin(codec, "Int Mic"); + else + snd_soc_dapm_disable_pin(codec, "Int Mic"); + + if (new_con & TEGRA_EXT_MIC) + snd_soc_dapm_enable_pin(codec, "Ext Mic"); + else + snd_soc_dapm_disable_pin(codec, "Ext Mic"); + + if (new_con & TEGRA_LINEIN) + snd_soc_dapm_enable_pin(codec, "Linein"); + else + snd_soc_dapm_disable_pin(codec, "Linein"); + + if (new_con & TEGRA_HEADSET) + snd_soc_dapm_enable_pin(codec, "Headset"); + else + snd_soc_dapm_disable_pin(codec, "Headset"); + + /* signal a DAPM event */ + snd_soc_dapm_sync(codec); + audio_data->codec_con = new_con; +} + +static int tegra_dapm_event_int_spk(struct snd_soc_dapm_widget* w, + struct snd_kcontrol* k, int event) +{ + if (tegra_wired_jack_conf.en_spkr != -1) { + if (tegra_wired_jack_conf.amp_reg) { + if (SND_SOC_DAPM_EVENT_ON(event) && + !tegra_wired_jack_conf.amp_reg_enabled) { + regulator_enable(tegra_wired_jack_conf.amp_reg); + tegra_wired_jack_conf.amp_reg_enabled = 1; + } + else if (!SND_SOC_DAPM_EVENT_ON(event) && + tegra_wired_jack_conf.amp_reg_enabled) { + regulator_disable(tegra_wired_jack_conf.amp_reg); + tegra_wired_jack_conf.amp_reg_enabled = 0; + } + } + + gpio_set_value_cansleep(tegra_wired_jack_conf.en_spkr, + SND_SOC_DAPM_EVENT_ON(event) ? 1 : 0); + } + + return 0; +} + +static int tegra_dapm_event_int_mic(struct snd_soc_dapm_widget* w, + struct snd_kcontrol* k, int event) +{ + if (tegra_wired_jack_conf.en_mic_int != -1) + gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_int, + SND_SOC_DAPM_EVENT_ON(event) ? 1 : 0); + + if (tegra_wired_jack_conf.en_mic_ext != -1) + gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_ext, + SND_SOC_DAPM_EVENT_ON(event) ? 0 : 1); + + return 0; +} + +static int tegra_dapm_event_ext_mic(struct snd_soc_dapm_widget* w, + struct snd_kcontrol* k, int event) +{ + if (tegra_wired_jack_conf.en_mic_ext != -1) + gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_ext, + SND_SOC_DAPM_EVENT_ON(event) ? 1 : 0); + + if (tegra_wired_jack_conf.en_mic_int != -1) + gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_int, + SND_SOC_DAPM_EVENT_ON(event) ? 0 : 1); + + return 0; +} + +/*tegra machine dapm widgets */ +static const struct snd_soc_dapm_widget tegra_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_HP("Headset", NULL), + SND_SOC_DAPM_SPK("Lineout", NULL), + SND_SOC_DAPM_SPK("Int Spk", tegra_dapm_event_int_spk), + SND_SOC_DAPM_MIC("Ext Mic", tegra_dapm_event_ext_mic), + SND_SOC_DAPM_MIC("Int Mic", tegra_dapm_event_int_mic), + SND_SOC_DAPM_LINE("Linein", NULL), +}; + +/* Tegra machine audio map (connections to the codec pins) */ +static const struct snd_soc_dapm_route audio_map[] = { + + /* headphone connected to LHPOUT1, RHPOUT1 */ + {"Headphone", NULL, "HPOUTR"}, + {"Headphone", NULL, "HPOUTL"}, + + /* headset Jack - in = micin, out = HPOUT*/ + {"Headset", NULL, "HPOUTR"}, + {"Headset", NULL, "HPOUTL"}, + {"IN1L", NULL, "Headset"}, + {"IN1R", NULL, "Headset"}, + + /* lineout connected to LINEOUTR and LINEOUTL */ + {"Lineout", NULL, "LINEOUTR"}, + {"Lineout", NULL, "LINEOUTL"}, + + /* build-in speaker connected to LON/P RON/P */ + {"Int Spk", NULL, "RON"}, + {"Int Spk", NULL, "ROP"}, + {"Int Spk", NULL, "LON"}, + {"Int Spk", NULL, "LOP"}, + + /* internal mic is mono */ + {"IN1R", NULL, "Int Mic"}, + + /* external mic is stero */ + {"IN1L", NULL, "Ext Mic"}, + {"IN1R", NULL, "Ext Mic"}, + + /* Line In */ + {"IN3L", NULL, "Linein"}, + {"IN3R", NULL, "Linein"}, +}; + + static int tegra_codec_init(struct snd_soc_codec *codec) { - return tegra_controls_init(codec); + struct tegra_audio_data* audio_data = codec->socdev->codec_data; + int err = 0; + + if (!audio_data->init_done) { + audio_data->dap_mclk = tegra_das_get_dap_mclk(); + if (!audio_data->dap_mclk) { + pr_err("Failed to get dap mclk \n"); + err = -ENODEV; + return err; + } + clk_enable(audio_data->dap_mclk); + + /* Add tegra specific widgets */ + snd_soc_dapm_new_controls(codec, tegra_dapm_widgets, + ARRAY_SIZE(tegra_dapm_widgets)); + + /* Set up tegra specific audio path audio_map */ + snd_soc_dapm_add_routes(codec, audio_map, + ARRAY_SIZE(audio_map)); + + /* Add jack detection */ + err = tegra_jack_init(codec); + if (err < 0) { + pr_err("Failed in jack init \n"); + return err; + } + + /* Default to OFF */ + tegra_ext_control(codec, TEGRA_AUDIO_OFF); + + err = tegra_controls_init(codec); + if (err < 0) { + pr_err("Failed in controls init \n"); + return err; + } + + audio_data->codec = codec; + audio_data->init_done = 1; + } + + return err; } static struct snd_soc_dai_link tegra_soc_dai[] = { @@ -228,17 +492,29 @@ static struct snd_soc_dai_link tegra_soc_dai[] = { }, }; +static struct tegra_audio_data audio_data = { + .init_done = 0, + .play_device = TEGRA_AUDIO_DEVICE_NONE, + .capture_device = TEGRA_AUDIO_DEVICE_NONE, + .is_call_mode = false, + .codec_con = TEGRA_AUDIO_OFF, +}; + static struct snd_soc_card tegra_snd_soc = { .name = "tegra", .platform = &tegra_soc_platform, .dai_link = tegra_soc_dai, .num_links = ARRAY_SIZE(tegra_soc_dai), + .suspend_pre = tegra_soc_suspend_pre, + .suspend_post = tegra_soc_suspend_post, + .resume_pre = tegra_soc_resume_pre, + .resume_post = tegra_soc_resume_post, }; - static struct snd_soc_device tegra_snd_devdata = { .card = &tegra_snd_soc, .codec_dev = &soc_codec_dev_wm8903, + .codec_data = &audio_data, }; static int __init tegra_init(void) @@ -248,11 +524,12 @@ static int __init tegra_init(void) tegra_snd_device = platform_device_alloc("soc-audio", -1); if (!tegra_snd_device) { pr_err("failed to allocate soc-audio \n"); - return ENOMEM; + return -ENOMEM; } platform_set_drvdata(tegra_snd_device, &tegra_snd_devdata); tegra_snd_devdata.dev = &tegra_snd_device->dev; + ret = platform_device_add(tegra_snd_device); if (ret) { pr_err("audio device could not be added \n"); @@ -272,7 +549,7 @@ fail: static void __exit tegra_exit(void) { - tegra_controls_exit(); + tegra_jack_exit(); platform_device_unregister(tegra_snd_device); } diff --git a/sound/soc/tegra/tegra_spdif.c b/sound/soc/tegra/tegra_spdif.c index d69cbd7cc86c..53c0b19c15d2 100644 --- a/sound/soc/tegra/tegra_spdif.c +++ b/sound/soc/tegra/tegra_spdif.c @@ -1,7 +1,7 @@ /* * tegra_spdif.c -- ALSA Soc Audio Layer * - * Copyright (c) 2011, NVIDIA Corporation. + * Copyright (c) 2010-2011, NVIDIA Corporation. * * 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 @@ -109,22 +109,24 @@ static int tegra_spdif_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct tegra_spdif_info *info = dai->private_data; - int ret = 0; int val; + unsigned int rate, sample_size; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: val = SPDIF_BIT_MODE_MODE16BIT; + sample_size = 16; break; case SNDRV_PCM_FORMAT_S24_LE: val = SPDIF_BIT_MODE_MODE24BIT; + sample_size = 16; break; case SNDRV_PCM_FORMAT_S32_LE: val = SPDIF_BIT_MODE_MODERAW; + sample_size = 32; break; default: - ret =-EINVAL; - goto err; + return -EINVAL; } spdif_set_bit_mode(info->spdif_base, val); @@ -140,15 +142,20 @@ static int tegra_spdif_hw_params(struct snd_pcm_substream *substream, val = params_rate(params); break; default: - ret = -EINVAL; - goto err; + return -EINVAL; } + /* Min BCLK = samplerate * channel * bits per sample * 4 */ + rate = val * params_channels(params) * sample_size * 4; + + /* Ensure Spdif clk rate is atleast greater than min BCLK */ + clk_set_rate(info->spdif_clk, rate); + if (clk_get_rate(info->spdif_clk) < rate) + clk_set_rate(info->spdif_clk, rate << 1); + spdif_set_sample_rate(info->spdif_base, val); - return 0; -err: - return ret; + return 0; } @@ -161,17 +168,6 @@ static int tegra_spdif_set_dai_fmt(struct snd_soc_dai *cpu_dai, static int tegra_spdif_set_dai_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int dir) { - struct tegra_spdif_info* info = cpu_dai->private_data; - struct tegra_audio_platform_data *pdata = info->pdev->dev.platform_data; - - if (info && info->spdif_clk) { - clk_set_rate(info->spdif_clk, pdata->spdif_clk_rate); - } - else { - pr_err("%s: could not get spdif clock\n", __func__); - return -EIO; - } - return 0; } @@ -349,6 +345,7 @@ static int tegra_spdif_driver_probe(struct platform_device *pdev) err = PTR_ERR(info->spdif_clk); goto fail_unmap_mem; } + clk_enable(info->spdif_clk); clk_set_rate(info->spdif_clk, info->pdata->spdif_clk_rate); spdif_initialize(info->spdif_base, AUDIO_TX_MODE); @@ -360,6 +357,8 @@ static int tegra_spdif_driver_probe(struct platform_device *pdev) if (err) goto fail_unmap_mem; + /* Disable SPDIF clock to save power */ + clk_disable(info->spdif_clk); return 0; fail_unmap_mem: |