| OLD | NEW |
| 1 /* | 1 /* |
| 2 * harmony.c - Harmony machine ASoC driver | 2 * harmony.c - Harmony machine ASoC driver |
| 3 * | 3 * |
| 4 * Author: Stephen Warren <swarren@nvidia.com> | 4 * Author: Stephen Warren <swarren@nvidia.com> |
| 5 * Copyright (C) 2010 - NVIDIA, Inc. | 5 * Copyright (C) 2010-2011 - NVIDIA, Inc. |
| 6 * | 6 * |
| 7 * Based on code copyright/by: | 7 * Based on code copyright/by: |
| 8 * | 8 * |
| 9 * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd. | 9 * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd. |
| 10 * | 10 * |
| 11 * Copyright 2007 Wolfson Microelectronics PLC. | 11 * Copyright 2007 Wolfson Microelectronics PLC. |
| 12 * Author: Graeme Gregory | 12 * Author: Graeme Gregory |
| 13 * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com | 13 * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com |
| 14 * | 14 * |
| 15 * This program is free software; you can redistribute it and/or | 15 * This program is free software; you can redistribute it and/or |
| 16 * modify it under the terms of the GNU General Public License | 16 * modify it under the terms of the GNU General Public License |
| 17 * version 2 as published by the Free Software Foundation. | 17 * version 2 as published by the Free Software Foundation. |
| 18 * | 18 * |
| 19 * This program is distributed in the hope that it will be useful, but | 19 * This program is distributed in the hope that it will be useful, but |
| 20 * WITHOUT ANY WARRANTY; without even the implied warranty of | 20 * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 22 * General Public License for more details. | 22 * General Public License for more details. |
| 23 * | 23 * |
| 24 * You should have received a copy of the GNU General Public License | 24 * You should have received a copy of the GNU General Public License |
| 25 * along with this program; if not, write to the Free Software | 25 * along with this program; if not, write to the Free Software |
| 26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | 26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
| 27 * 02110-1301 USA | 27 * 02110-1301 USA |
| 28 * | 28 * |
| 29 */ | 29 */ |
| 30 | 30 |
| 31 #include <asm/mach-types.h> | 31 #include <asm/mach-types.h> |
| 32 |
| 32 #include <linux/module.h> | 33 #include <linux/module.h> |
| 34 #include <linux/platform_device.h> |
| 35 #include <linux/slab.h> |
| 36 #include <linux/gpio.h> |
| 37 |
| 38 #include <mach/harmony_audio.h> |
| 39 |
| 33 #include <sound/core.h> | 40 #include <sound/core.h> |
| 41 #include <sound/jack.h> |
| 34 #include <sound/pcm.h> | 42 #include <sound/pcm.h> |
| 35 #include <sound/pcm_params.h> | 43 #include <sound/pcm_params.h> |
| 36 #include <sound/soc.h> | 44 #include <sound/soc.h> |
| 37 #include <sound/soc-dapm.h> | 45 |
| 46 #include "../codecs/wm8903.h" |
| 38 | 47 |
| 39 #include "tegra_das.h" | 48 #include "tegra_das.h" |
| 40 #include "tegra_i2s.h" | 49 #include "tegra_i2s.h" |
| 41 #include "tegra_pcm.h" | 50 #include "tegra_pcm.h" |
| 42 #include "tegra_asoc_utils.h" | 51 #include "tegra_asoc_utils.h" |
| 43 | 52 |
| 44 #include "../codecs/wm8903.h" | 53 #define DRV_NAME "tegra-snd-harmony" |
| 45 | 54 |
| 46 #define PREFIX "ASoC Harmony: " | 55 #define GPIO_SPKR_EN BIT(0) |
| 56 #define GPIO_INT_MIC_EN BIT(1) |
| 57 #define GPIO_EXT_MIC_EN BIT(2) |
| 47 | 58 |
| 48 #define TEGRA_HP 0 | 59 struct tegra_harmony { |
| 49 #define TEGRA_MIC 1 | 60 » struct tegra_asoc_utils_data util_data; |
| 50 #define TEGRA_LINE 2 | 61 » struct harmony_audio_platform_data *pdata; |
| 51 #define TEGRA_HEADSET 3 | 62 » int gpio_requested; |
| 52 #define TEGRA_HP_OFF 4 | 63 }; |
| 53 #define TEGRA_SPK_ON 0 | |
| 54 #define TEGRA_SPK_OFF 1 | |
| 55 | |
| 56 /* codec register values */ | |
| 57 #define B07_INEMUTE 7 | |
| 58 #define B06_VOL_M3DB 6 | |
| 59 #define B00_IN_VOL 0 | |
| 60 #define B00_INR_ENA 0 | |
| 61 #define B01_INL_ENA 1 | |
| 62 #define R06_MICBIAS_CTRL_0 6 | |
| 63 #define B07_MICDET_HYST_ENA 7 | |
| 64 #define B04_MICDET_THR 4 | |
| 65 #define B02_MICSHORT_THR 2 | |
| 66 #define B01_MICDET_ENA 1 | |
| 67 #define B00_MICBIAS_ENA 0 | |
| 68 #define B15_DRC_ENA 15 | |
| 69 #define B03_DACL_ENA 3 | |
| 70 #define B02_DACR_ENA 2 | |
| 71 #define B01_ADCL_ENA 1 | |
| 72 #define B00_ADCR_ENA 0 | |
| 73 #define B06_IN_CM_ENA 6 | |
| 74 #define B04_IP_SEL_N 4 | |
| 75 #define B02_IP_SEL_P 2 | |
| 76 #define B00_MODE 0 | |
| 77 #define B06_AIF_ADCL 7 | |
| 78 #define B06_AIF_ADCR 6 | |
| 79 #define B05_ADC_HPF_CUT 5 | |
| 80 #define B04_ADC_HPF_ENA 4 | |
| 81 #define B01_ADCL_DATINV 1 | |
| 82 #define B00_ADCR_DATINV 0 | |
| 83 #define R20_SIDETONE_CTRL 32 | |
| 84 #define R29_DRC_1 41 | |
| 85 #define SET_REG_VAL(r, m, l, v) (((r)&(~((m)<<(l))))|(((v)&(m))<<(l))) | |
| 86 | |
| 87 static struct platform_device *harmony_snd_device; | |
| 88 static int tegra_jack_func; | |
| 89 static int tegra_spk_func; | |
| 90 | |
| 91 static void tegra_ext_control(struct snd_soc_codec *codec) | |
| 92 { | |
| 93 » /* set up jack connection */ | |
| 94 » switch (tegra_jack_func) { | |
| 95 » case TEGRA_HP: | |
| 96 » » /* set = unmute headphone */ | |
| 97 » » snd_soc_dapm_enable_pin(codec, "Mic Jack"); | |
| 98 » » snd_soc_dapm_disable_pin(codec, "Line Jack"); | |
| 99 » » snd_soc_dapm_enable_pin(codec, "Headphone Jack"); | |
| 100 » » snd_soc_dapm_disable_pin(codec, "Headset Jack"); | |
| 101 » » break; | |
| 102 » case TEGRA_MIC: | |
| 103 » » /* reset = mute headphone */ | |
| 104 » » snd_soc_dapm_enable_pin(codec, "Mic Jack"); | |
| 105 » » snd_soc_dapm_disable_pin(codec, "Line Jack"); | |
| 106 » » snd_soc_dapm_disable_pin(codec, "Headphone Jack"); | |
| 107 » » snd_soc_dapm_disable_pin(codec, "Headset Jack"); | |
| 108 » » break; | |
| 109 » case TEGRA_LINE: | |
| 110 » » snd_soc_dapm_disable_pin(codec, "Mic Jack"); | |
| 111 » » snd_soc_dapm_enable_pin(codec, "Line Jack"); | |
| 112 » » snd_soc_dapm_disable_pin(codec, "Headphone Jack"); | |
| 113 » » snd_soc_dapm_disable_pin(codec, "Headset Jack"); | |
| 114 » » break; | |
| 115 » case TEGRA_HEADSET: | |
| 116 » » snd_soc_dapm_enable_pin(codec, "Mic Jack"); | |
| 117 » » snd_soc_dapm_disable_pin(codec, "Line Jack"); | |
| 118 » » snd_soc_dapm_disable_pin(codec, "Headphone Jack"); | |
| 119 » » snd_soc_dapm_enable_pin(codec, "Headset Jack"); | |
| 120 » » break; | |
| 121 » } | |
| 122 | |
| 123 » if (tegra_spk_func == TEGRA_SPK_ON) | |
| 124 » » snd_soc_dapm_enable_pin(codec, "Ext Spk"); | |
| 125 » else | |
| 126 » » snd_soc_dapm_disable_pin(codec, "Ext Spk"); | |
| 127 | |
| 128 » /* signal a DAPM event */ | |
| 129 » snd_soc_dapm_sync(codec); | |
| 130 } | |
| 131 | 64 |
| 132 static int harmony_asoc_hw_params(struct snd_pcm_substream *substream, | 65 static int harmony_asoc_hw_params(struct snd_pcm_substream *substream, |
| 133 struct snd_pcm_hw_params *params) | 66 struct snd_pcm_hw_params *params) |
| 134 { | 67 { |
| 135 struct snd_soc_pcm_runtime *rtd = substream->private_data; | 68 struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| 136 struct snd_soc_dai *codec_dai = rtd->codec_dai; | 69 struct snd_soc_dai *codec_dai = rtd->codec_dai; |
| 137 struct snd_soc_codec *codec = codec_dai->codec; | |
| 138 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | 70 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; |
| 71 struct snd_soc_codec *codec = rtd->codec; |
| 72 struct snd_soc_card *card = codec->card; |
| 73 struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card); |
| 139 int srate, mclk, mclk_change; | 74 int srate, mclk, mclk_change; |
| 140 int err; | 75 int err; |
| 141 unsigned int ctrl_reg; | |
| 142 | 76 |
| 143 srate = params_rate(params); | 77 srate = params_rate(params); |
| 144 switch (srate) { | 78 switch (srate) { |
| 145 case 64000: | 79 case 64000: |
| 146 case 88200: | 80 case 88200: |
| 147 case 96000: | 81 case 96000: |
| 148 mclk = 128 * srate; | 82 mclk = 128 * srate; |
| 149 break; | 83 break; |
| 150 default: | 84 default: |
| 151 mclk = 256 * srate; | 85 mclk = 256 * srate; |
| 152 break; | 86 break; |
| 153 } | 87 } |
| 154 /* FIXME: Codec only requires >= 3MHz if OSR==0 */ | 88 /* FIXME: Codec only requires >= 3MHz if OSR==0 */ |
| 155 while (mclk < 6000000) | 89 while (mclk < 6000000) |
| 156 mclk *= 2; | 90 mclk *= 2; |
| 157 | 91 |
| 158 » err = tegra_asoc_utils_set_rate(srate, mclk, &mclk_change); | 92 » err = tegra_asoc_utils_set_rate(&harmony->util_data, srate, mclk, |
| 93 » » » » » &mclk_change); |
| 159 if (err < 0) { | 94 if (err < 0) { |
| 160 » » pr_err(PREFIX "Can't configure clocks\n"); | 95 » » dev_err(card->dev, "Can't configure clocks\n"); |
| 161 return err; | 96 return err; |
| 162 } | 97 } |
| 163 | 98 |
| 164 err = snd_soc_dai_set_fmt(codec_dai, | 99 err = snd_soc_dai_set_fmt(codec_dai, |
| 165 SND_SOC_DAIFMT_I2S | | 100 SND_SOC_DAIFMT_I2S | |
| 166 SND_SOC_DAIFMT_NB_NF | | 101 SND_SOC_DAIFMT_NB_NF | |
| 167 SND_SOC_DAIFMT_CBS_CFS); | 102 SND_SOC_DAIFMT_CBS_CFS); |
| 168 if (err < 0) { | 103 if (err < 0) { |
| 169 » » pr_err(PREFIX "codec_dai fmt not set\n"); | 104 » » dev_err(card->dev, "codec_dai fmt not set\n"); |
| 170 return err; | 105 return err; |
| 171 } | 106 } |
| 172 | 107 |
| 173 err = snd_soc_dai_set_fmt(cpu_dai, | 108 err = snd_soc_dai_set_fmt(cpu_dai, |
| 174 SND_SOC_DAIFMT_I2S | | 109 SND_SOC_DAIFMT_I2S | |
| 175 SND_SOC_DAIFMT_NB_NF | | 110 SND_SOC_DAIFMT_NB_NF | |
| 176 SND_SOC_DAIFMT_CBS_CFS); | 111 SND_SOC_DAIFMT_CBS_CFS); |
| 177 if (err < 0) { | 112 if (err < 0) { |
| 178 » » pr_err(PREFIX "cpu_dai fmt not set\n"); | 113 » » dev_err(card->dev, "cpu_dai fmt not set\n"); |
| 179 return err; | 114 return err; |
| 180 } | 115 } |
| 181 | 116 |
| 182 if (mclk_change) { | 117 if (mclk_change) { |
| 183 » err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, SND_SOC_CLOCK_IN); | 118 » » err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, |
| 184 » if (err < 0) { | 119 » » » » » SND_SOC_CLOCK_IN); |
| 185 » » pr_err(PREFIX "codec_dai clock not set\n"); | 120 » » if (err < 0) { |
| 186 » » return err; | 121 » » » dev_err(card->dev, "codec_dai clock not set\n"); |
| 187 » } | 122 » » » return err; |
| 123 » » } |
| 188 } | 124 } |
| 189 | 125 |
| 190 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
| 191 return 0; | |
| 192 | |
| 193 snd_soc_write(codec, WM8903_ANALOGUE_LEFT_INPUT_0, 0x7); | |
| 194 snd_soc_write(codec, WM8903_ANALOGUE_RIGHT_INPUT_0, 0x7); | |
| 195 | |
| 196 /* Mic Bias enable */ | |
| 197 ctrl_reg = (0x1 << B00_MICBIAS_ENA) | (0x1 << B01_MICDET_ENA); | |
| 198 snd_soc_write(codec, WM8903_MIC_BIAS_CONTROL_0, ctrl_reg); | |
| 199 | |
| 200 /* Enable DRC */ | |
| 201 ctrl_reg = snd_soc_read(codec, WM8903_DRC_0); | |
| 202 ctrl_reg |= (1 << B15_DRC_ENA); | |
| 203 snd_soc_write(codec, WM8903_DRC_0, ctrl_reg); | |
| 204 | |
| 205 /* Single Ended Mic */ | |
| 206 ctrl_reg = (0x0 << B06_IN_CM_ENA) | (0x0 << B00_MODE) | | |
| 207 (0x0 << B04_IP_SEL_N) | (0x1 << B02_IP_SEL_P); | |
| 208 /* Mic Setting */ | |
| 209 snd_soc_write(codec, WM8903_ANALOGUE_LEFT_INPUT_1, ctrl_reg); | |
| 210 snd_soc_write(codec, WM8903_ANALOGUE_RIGHT_INPUT_1, ctrl_reg); | |
| 211 | |
| 212 /* voulme for single ended mic */ | |
| 213 ctrl_reg = (0x5 << B00_IN_VOL); | |
| 214 snd_soc_write(codec, WM8903_ANALOGUE_LEFT_INPUT_0, ctrl_reg); | |
| 215 snd_soc_write(codec, WM8903_ANALOGUE_RIGHT_INPUT_0, ctrl_reg); | |
| 216 | |
| 217 /* replicate mic setting on both channels */ | |
| 218 ctrl_reg = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_0); | |
| 219 ctrl_reg = SET_REG_VAL(ctrl_reg, 0x1, B06_AIF_ADCR, 0x1); | |
| 220 ctrl_reg = SET_REG_VAL(ctrl_reg, 0x1, B06_AIF_ADCL, 0x1); | |
| 221 snd_soc_write(codec, WM8903_AUDIO_INTERFACE_0, ctrl_reg); | |
| 222 | |
| 223 /* Enable analog inputs */ | |
| 224 ctrl_reg = (0x1 << B01_INL_ENA) | (0x1 << B00_INR_ENA); | |
| 225 snd_soc_write(codec, WM8903_POWER_MANAGEMENT_0, ctrl_reg); | |
| 226 | |
| 227 /* ADC Settings */ | |
| 228 ctrl_reg = snd_soc_read(codec, WM8903_ADC_DIGITAL_0); | |
| 229 ctrl_reg |= (0x1<<B04_ADC_HPF_ENA); | |
| 230 snd_soc_write(codec, WM8903_ADC_DIGITAL_0, ctrl_reg); | |
| 231 | |
| 232 ctrl_reg = 0; | |
| 233 snd_soc_write(codec, R20_SIDETONE_CTRL, ctrl_reg); | |
| 234 | |
| 235 /* Enable ADC */ | |
| 236 ctrl_reg = snd_soc_read(codec, WM8903_POWER_MANAGEMENT_6); | |
| 237 ctrl_reg |= (0x1<<B00_ADCR_ENA)|(0x1<<B01_ADCL_ENA); | |
| 238 snd_soc_write(codec, WM8903_POWER_MANAGEMENT_6, ctrl_reg); | |
| 239 | |
| 240 /* Enable Sidetone */ | |
| 241 ctrl_reg = (0x1 << 2) | (0x2 << 0); | |
| 242 /* sidetone 0 db */ | |
| 243 ctrl_reg |= (12 << 8) | (12 << 4); | |
| 244 snd_soc_write(codec, R20_SIDETONE_CTRL, ctrl_reg); | |
| 245 | |
| 246 ctrl_reg = snd_soc_read(codec, R29_DRC_1); | |
| 247 ctrl_reg |= 0x3; /* mic volume 18 db */ | |
| 248 snd_soc_write(codec, R29_DRC_1, ctrl_reg); | |
| 249 | |
| 250 return 0; | |
| 251 } | |
| 252 | |
| 253 static int tegra_get_jack(struct snd_kcontrol *kcontrol, | |
| 254 struct snd_ctl_elem_value *ucontrol) | |
| 255 { | |
| 256 ucontrol->value.integer.value[0] = tegra_jack_func; | |
| 257 return 0; | |
| 258 } | |
| 259 | |
| 260 static int tegra_set_jack(struct snd_kcontrol *kcontrol, | |
| 261 struct snd_ctl_elem_value *ucontrol) | |
| 262 { | |
| 263 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | |
| 264 | |
| 265 if (tegra_jack_func == ucontrol->value.integer.value[0]) | |
| 266 return 0; | |
| 267 | |
| 268 tegra_jack_func = ucontrol->value.integer.value[0]; | |
| 269 tegra_ext_control(codec); | |
| 270 return 1; | |
| 271 } | |
| 272 | |
| 273 static int tegra_get_spk(struct snd_kcontrol *kcontrol, | |
| 274 struct snd_ctl_elem_value *ucontrol) | |
| 275 { | |
| 276 ucontrol->value.integer.value[0] = tegra_spk_func; | |
| 277 return 0; | |
| 278 } | |
| 279 | |
| 280 static int tegra_set_spk(struct snd_kcontrol *kcontrol, | |
| 281 struct snd_ctl_elem_value *ucontrol) | |
| 282 { | |
| 283 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | |
| 284 | |
| 285 | |
| 286 if (tegra_spk_func == ucontrol->value.integer.value[0]) | |
| 287 return 0; | |
| 288 | |
| 289 tegra_spk_func = ucontrol->value.integer.value[0]; | |
| 290 tegra_ext_control(codec); | |
| 291 return 1; | |
| 292 } | |
| 293 | |
| 294 /*tegra machine dapm widgets */ | |
| 295 static const struct snd_soc_dapm_widget wm8903_dapm_widgets[] = { | |
| 296 SND_SOC_DAPM_HP("Headphone Jack", NULL), | |
| 297 SND_SOC_DAPM_MIC("Mic Jack", NULL), | |
| 298 SND_SOC_DAPM_SPK("Ext Spk", NULL), | |
| 299 SND_SOC_DAPM_LINE("Line Jack", NULL), | |
| 300 SND_SOC_DAPM_HP("Headset Jack", NULL), | |
| 301 }; | |
| 302 | |
| 303 /* Tegra machine audio map (connections to the codec pins) */ | |
| 304 static const struct snd_soc_dapm_route audio_map[] = { | |
| 305 /* headset Jack - in = micin, out = LHPOUT*/ | |
| 306 {"Headset Jack", NULL, "HPOUTL"}, | |
| 307 | |
| 308 /* headphone connected to LHPOUT1, RHPOUT1 */ | |
| 309 {"Headphone Jack", NULL, "HPOUTR"}, {"Headphone Jack", NULL, "HPOUTL"}, | |
| 310 | |
| 311 /* speaker connected to LOUT, ROUT */ | |
| 312 {"Ext Spk", NULL, "LINEOUTR"}, {"Ext Spk", NULL, "LINEOUTL"}, | |
| 313 | |
| 314 /* mic is connected to MICIN (via right channel of headphone jack) */ | |
| 315 {"IN1R", NULL, "Mic Jack"}, | |
| 316 | |
| 317 /* Same as the above but no mic bias for line signals */ | |
| 318 {"IN2L", NULL, "Line Jack"}, | |
| 319 }; | |
| 320 | |
| 321 static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", | |
| 322 "Off" | |
| 323 }; | |
| 324 static const char *spk_function[] = {"On", "Off"}; | |
| 325 static const struct soc_enum tegra_enum[] = { | |
| 326 SOC_ENUM_SINGLE_EXT(5, jack_function), | |
| 327 SOC_ENUM_SINGLE_EXT(2, spk_function), | |
| 328 }; | |
| 329 | |
| 330 static const struct snd_kcontrol_new wm8903_tegra_controls[] = { | |
| 331 SOC_ENUM_EXT("Jack Function", tegra_enum[0], tegra_get_jack, | |
| 332 tegra_set_jack), | |
| 333 SOC_ENUM_EXT("Speaker Function", tegra_enum[1], tegra_get_spk, | |
| 334 tegra_set_spk), | |
| 335 }; | |
| 336 | |
| 337 static int harmony_asoc_init(struct snd_soc_pcm_runtime *rtd) | |
| 338 { | |
| 339 struct snd_soc_codec *codec = rtd->codec; | |
| 340 int err; | |
| 341 | |
| 342 /* Add tegra specific controls */ | |
| 343 err = snd_soc_add_controls(codec, wm8903_tegra_controls, | |
| 344 ARRAY_SIZE(wm8903_tegra_controls)); | |
| 345 if (err < 0) | |
| 346 return err; | |
| 347 | |
| 348 /* Add tegra specific widgets */ | |
| 349 snd_soc_dapm_new_controls(codec, wm8903_dapm_widgets, | |
| 350 ARRAY_SIZE(wm8903_dapm_widgets)); | |
| 351 | |
| 352 /* Set up tegra specific audio path audio_map */ | |
| 353 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | |
| 354 | |
| 355 /* Default to HP output */ | |
| 356 tegra_jack_func = TEGRA_HP; | |
| 357 tegra_spk_func = TEGRA_SPK_ON; | |
| 358 tegra_ext_control(codec); | |
| 359 | |
| 360 snd_soc_dapm_sync(codec); | |
| 361 | |
| 362 return 0; | 126 return 0; |
| 363 } | 127 } |
| 364 | 128 |
| 365 static struct snd_soc_ops harmony_asoc_ops = { | 129 static struct snd_soc_ops harmony_asoc_ops = { |
| 366 .hw_params = harmony_asoc_hw_params, | 130 .hw_params = harmony_asoc_hw_params, |
| 367 }; | 131 }; |
| 368 | 132 |
| 133 static struct snd_soc_jack harmony_hp_jack; |
| 134 |
| 135 static struct snd_soc_jack_pin harmony_hp_jack_pins[] = { |
| 136 { |
| 137 .pin = "Headphone Jack", |
| 138 .mask = SND_JACK_HEADPHONE, |
| 139 }, |
| 140 }; |
| 141 |
| 142 static struct snd_soc_jack_gpio harmony_hp_jack_gpios[] = { |
| 143 { |
| 144 .name = "headphone detect", |
| 145 .report = SND_JACK_HEADPHONE, |
| 146 .debounce_time = 150, |
| 147 .invert = 1, |
| 148 } |
| 149 }; |
| 150 |
| 151 static struct snd_soc_jack harmony_mic_jack; |
| 152 |
| 153 static struct snd_soc_jack_pin harmony_mic_jack_pins[] = { |
| 154 { |
| 155 .pin = "Mic Jack", |
| 156 .mask = SND_JACK_MICROPHONE, |
| 157 }, |
| 158 }; |
| 159 |
| 160 static int harmony_event_int_spk(struct snd_soc_dapm_widget *w, |
| 161 struct snd_kcontrol *k, int event) |
| 162 { |
| 163 struct snd_soc_codec *codec = w->codec; |
| 164 struct snd_soc_card *card = codec->card; |
| 165 struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card); |
| 166 struct harmony_audio_platform_data *pdata = harmony->pdata; |
| 167 |
| 168 gpio_set_value_cansleep(pdata->gpio_spkr_en, |
| 169 SND_SOC_DAPM_EVENT_ON(event)); |
| 170 |
| 171 return 0; |
| 172 } |
| 173 |
| 174 static const struct snd_soc_dapm_widget harmony_dapm_widgets[] = { |
| 175 SND_SOC_DAPM_SPK("Int Spk", harmony_event_int_spk), |
| 176 SND_SOC_DAPM_HP("Headphone Jack", NULL), |
| 177 SND_SOC_DAPM_MIC("Mic Jack", NULL), |
| 178 }; |
| 179 |
| 180 static const struct snd_soc_dapm_route harmony_audio_map[] = { |
| 181 {"Headphone Jack", NULL, "HPOUTR"}, |
| 182 {"Headphone Jack", NULL, "HPOUTL"}, |
| 183 {"Int Spk", NULL, "ROP"}, |
| 184 {"Int Spk", NULL, "RON"}, |
| 185 {"Int Spk", NULL, "LOP"}, |
| 186 {"Int Spk", NULL, "LON"}, |
| 187 {"Mic Bias", NULL, "Mic Jack"}, |
| 188 {"IN1L", NULL, "Mic Bias"}, |
| 189 }; |
| 190 |
| 191 static const struct snd_kcontrol_new harmony_controls[] = { |
| 192 SOC_DAPM_PIN_SWITCH("Int Spk"), |
| 193 }; |
| 194 |
| 195 static int harmony_asoc_init(struct snd_soc_pcm_runtime *rtd) |
| 196 { |
| 197 struct snd_soc_codec *codec = rtd->codec; |
| 198 struct snd_soc_dapm_context *dapm = &codec->dapm; |
| 199 struct snd_soc_card *card = codec->card; |
| 200 struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card); |
| 201 struct harmony_audio_platform_data *pdata = harmony->pdata; |
| 202 int ret; |
| 203 |
| 204 ret = gpio_request(pdata->gpio_spkr_en, "spkr_en"); |
| 205 if (ret) { |
| 206 dev_err(card->dev, "cannot get spkr_en gpio\n"); |
| 207 return ret; |
| 208 } |
| 209 harmony->gpio_requested |= GPIO_SPKR_EN; |
| 210 |
| 211 gpio_direction_output(pdata->gpio_spkr_en, 0); |
| 212 |
| 213 ret = gpio_request(pdata->gpio_int_mic_en, "int_mic_en"); |
| 214 if (ret) { |
| 215 dev_err(card->dev, "cannot get int_mic_en gpio\n"); |
| 216 return ret; |
| 217 } |
| 218 harmony->gpio_requested |= GPIO_INT_MIC_EN; |
| 219 |
| 220 /* Disable int mic; enable signal is active-high */ |
| 221 gpio_direction_output(pdata->gpio_int_mic_en, 0); |
| 222 |
| 223 ret = gpio_request(pdata->gpio_ext_mic_en, "ext_mic_en"); |
| 224 if (ret) { |
| 225 dev_err(card->dev, "cannot get ext_mic_en gpio\n"); |
| 226 return ret; |
| 227 } |
| 228 harmony->gpio_requested |= GPIO_EXT_MIC_EN; |
| 229 |
| 230 /* Enable ext mic; enable signal is active-low */ |
| 231 gpio_direction_output(pdata->gpio_ext_mic_en, 0); |
| 232 |
| 233 ret = snd_soc_add_controls(codec, harmony_controls, |
| 234 ARRAY_SIZE(harmony_controls)); |
| 235 if (ret < 0) |
| 236 return ret; |
| 237 |
| 238 snd_soc_dapm_new_controls(dapm, harmony_dapm_widgets, |
| 239 ARRAY_SIZE(harmony_dapm_widgets)); |
| 240 |
| 241 snd_soc_dapm_add_routes(dapm, harmony_audio_map, |
| 242 ARRAY_SIZE(harmony_audio_map)); |
| 243 |
| 244 harmony_hp_jack_gpios[0].gpio = pdata->gpio_hp_det; |
| 245 snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, |
| 246 &harmony_hp_jack); |
| 247 snd_soc_jack_add_pins(&harmony_hp_jack, |
| 248 ARRAY_SIZE(harmony_hp_jack_pins), |
| 249 harmony_hp_jack_pins); |
| 250 snd_soc_jack_add_gpios(&harmony_hp_jack, |
| 251 ARRAY_SIZE(harmony_hp_jack_gpios), |
| 252 harmony_hp_jack_gpios); |
| 253 |
| 254 snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE, |
| 255 &harmony_mic_jack); |
| 256 snd_soc_jack_add_pins(&harmony_mic_jack, |
| 257 ARRAY_SIZE(harmony_mic_jack_pins), |
| 258 harmony_mic_jack_pins); |
| 259 wm8903_mic_detect(codec, &harmony_mic_jack, SND_JACK_MICROPHONE, 0); |
| 260 |
| 261 snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); |
| 262 |
| 263 snd_soc_dapm_nc_pin(dapm, "IN3L"); |
| 264 snd_soc_dapm_nc_pin(dapm, "IN3R"); |
| 265 snd_soc_dapm_nc_pin(dapm, "LINEOUTL"); |
| 266 snd_soc_dapm_nc_pin(dapm, "LINEOUTR"); |
| 267 |
| 268 snd_soc_dapm_sync(dapm); |
| 269 |
| 270 return 0; |
| 271 } |
| 272 |
| 369 static struct snd_soc_dai_link harmony_wm8903_dai = { | 273 static struct snd_soc_dai_link harmony_wm8903_dai = { |
| 370 .name = "WM8903", | 274 .name = "WM8903", |
| 371 .stream_name = "WM8903 PCM", | 275 .stream_name = "WM8903 PCM", |
| 372 » .codec_name = "wm8903-codec.0-001a", | 276 » .codec_name = "wm8903.0-001a", |
| 373 .platform_name = "tegra-pcm-audio", | 277 .platform_name = "tegra-pcm-audio", |
| 374 .cpu_dai_name = "tegra-i2s.0", | 278 .cpu_dai_name = "tegra-i2s.0", |
| 375 .codec_dai_name = "wm8903-hifi", | 279 .codec_dai_name = "wm8903-hifi", |
| 376 .init = harmony_asoc_init, | 280 .init = harmony_asoc_init, |
| 377 .ops = &harmony_asoc_ops, | 281 .ops = &harmony_asoc_ops, |
| 378 }; | 282 }; |
| 379 | 283 |
| 380 static struct snd_soc_card snd_soc_harmony = { | 284 static struct snd_soc_card snd_soc_harmony = { |
| 381 .name = "tegra-harmony", | 285 .name = "tegra-harmony", |
| 382 .dai_link = &harmony_wm8903_dai, | 286 .dai_link = &harmony_wm8903_dai, |
| 383 .num_links = 1, | 287 .num_links = 1, |
| 384 }; | 288 }; |
| 385 | 289 |
| 386 static int __init harmony_soc_modinit(void) | 290 static __devinit int tegra_snd_harmony_probe(struct platform_device *pdev) |
| 387 { | 291 { |
| 292 struct snd_soc_card *card = &snd_soc_harmony; |
| 293 struct tegra_harmony *harmony; |
| 294 struct harmony_audio_platform_data *pdata; |
| 388 int ret; | 295 int ret; |
| 389 | 296 |
| 390 » if (!machine_is_harmony() && !machine_is_seaboard() && | 297 » if (!machine_is_harmony()) { |
| 391 » » !machine_is_ventana()) { | 298 » » dev_err(&pdev->dev, "Not running on Tegra Harmony!\n"); |
| 392 » » pr_err(PREFIX "Not running on Tegra Harmony/Seaboard/Ventana!\n"
); | |
| 393 return -ENODEV; | 299 return -ENODEV; |
| 394 } | 300 } |
| 395 | 301 |
| 396 » ret = tegra_asoc_utils_init(); | 302 » pdata = pdev->dev.platform_data; |
| 397 » if (ret) { | 303 » if (!pdata) { |
| 398 » » return ret; | 304 » » dev_err(&pdev->dev, "no platform data supplied\n"); |
| 305 » » return -EINVAL; |
| 399 } | 306 } |
| 400 | 307 |
| 401 » /* | 308 » harmony = kzalloc(sizeof(struct tegra_harmony), GFP_KERNEL); |
| 402 » * Create and register platform device | 309 » if (!harmony) { |
| 403 » */ | 310 » » dev_err(&pdev->dev, "Can't allocate tegra_harmony\n"); |
| 404 » harmony_snd_device = platform_device_alloc("soc-audio", -1); | 311 » » return -ENOMEM; |
| 405 » if (harmony_snd_device == NULL) { | |
| 406 » » pr_err(PREFIX "platform_device_alloc failed\n"); | |
| 407 » » ret = -ENOMEM; | |
| 408 » » goto err_clock_utils; | |
| 409 } | 312 } |
| 410 | 313 |
| 411 » platform_set_drvdata(harmony_snd_device, &snd_soc_harmony); | 314 » harmony->pdata = pdata; |
| 412 | 315 |
| 413 » ret = platform_device_add(harmony_snd_device); | 316 » ret = tegra_asoc_utils_init(&harmony->util_data, &pdev->dev); |
| 317 » if (ret) |
| 318 » » goto err_free_harmony; |
| 319 |
| 320 » card->dev = &pdev->dev; |
| 321 » platform_set_drvdata(pdev, card); |
| 322 » snd_soc_card_set_drvdata(card, harmony); |
| 323 |
| 324 » ret = snd_soc_register_card(card); |
| 414 if (ret) { | 325 if (ret) { |
| 415 » » pr_err(PREFIX "platform_device_add failed (%d)\n", | 326 » » dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", |
| 416 ret); | 327 ret); |
| 417 » » goto err_device_put; | 328 » » goto err_clear_drvdata; |
| 418 } | 329 } |
| 419 | 330 |
| 420 return 0; | 331 return 0; |
| 421 | 332 |
| 422 err_device_put: | 333 err_clear_drvdata: |
| 423 » platform_device_put(harmony_snd_device); | 334 » snd_soc_card_set_drvdata(card, NULL); |
| 424 err_clock_utils: | 335 » platform_set_drvdata(pdev, NULL); |
| 425 » tegra_asoc_utils_fini(); | 336 » card->dev = NULL; |
| 337 » tegra_asoc_utils_fini(&harmony->util_data); |
| 338 err_free_harmony: |
| 339 » kfree(harmony); |
| 426 return ret; | 340 return ret; |
| 427 } | 341 } |
| 428 module_init(harmony_soc_modinit); | |
| 429 | 342 |
| 430 static void __exit harmony_soc_modexit(void) | 343 static int __devexit tegra_snd_harmony_remove(struct platform_device *pdev) |
| 431 { | 344 { |
| 432 » platform_device_unregister(harmony_snd_device); | 345 » struct snd_soc_card *card = platform_get_drvdata(pdev); |
| 346 » struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card); |
| 347 » struct harmony_audio_platform_data *pdata = harmony->pdata; |
| 433 | 348 |
| 434 » tegra_asoc_utils_fini(); | 349 » snd_soc_unregister_card(card); |
| 350 |
| 351 » snd_soc_card_set_drvdata(card, NULL); |
| 352 » platform_set_drvdata(pdev, NULL); |
| 353 » card->dev = NULL; |
| 354 |
| 355 » tegra_asoc_utils_fini(&harmony->util_data); |
| 356 |
| 357 » if (harmony->gpio_requested & GPIO_EXT_MIC_EN) |
| 358 » » gpio_free(pdata->gpio_ext_mic_en); |
| 359 » if (harmony->gpio_requested & GPIO_INT_MIC_EN) |
| 360 » » gpio_free(pdata->gpio_int_mic_en); |
| 361 » if (harmony->gpio_requested & GPIO_SPKR_EN) |
| 362 » » gpio_free(pdata->gpio_spkr_en); |
| 363 |
| 364 » kfree(harmony); |
| 365 |
| 366 » return 0; |
| 435 } | 367 } |
| 436 module_exit(harmony_soc_modexit); | 368 |
| 369 static struct platform_driver tegra_snd_harmony_driver = { |
| 370 » .driver = { |
| 371 » » .name = DRV_NAME, |
| 372 » » .owner = THIS_MODULE, |
| 373 » }, |
| 374 » .probe = tegra_snd_harmony_probe, |
| 375 » .remove = __devexit_p(tegra_snd_harmony_remove), |
| 376 }; |
| 377 |
| 378 static int __init snd_tegra_harmony_init(void) |
| 379 { |
| 380 » return platform_driver_register(&tegra_snd_harmony_driver); |
| 381 } |
| 382 module_init(snd_tegra_harmony_init); |
| 383 |
| 384 static void __exit snd_tegra_harmony_exit(void) |
| 385 { |
| 386 » platform_driver_unregister(&tegra_snd_harmony_driver); |
| 387 } |
| 388 module_exit(snd_tegra_harmony_exit); |
| 437 | 389 |
| 438 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); | 390 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); |
| 439 MODULE_DESCRIPTION("Harmony machine ASoC driver"); | 391 MODULE_DESCRIPTION("Harmony machine ASoC driver"); |
| 440 MODULE_LICENSE("GPL"); | 392 MODULE_LICENSE("GPL"); |
| 393 MODULE_ALIAS("platform:" DRV_NAME); |
| OLD | NEW |