Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.4 289 lines 7.5 kB view raw
1/* 2* tegra_alc5632.c -- Toshiba AC100(PAZ00) machine ASoC driver 3* 4* Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net> 5* 6* Authors: Leon Romanovsky <leon@leon.nu> 7* Andrey Danin <danindrey@mail.ru> 8* Marc Dietrich <marvin24@gmx.de> 9* 10* This program is free software; you can redistribute it and/or modify 11* it under the terms of the GNU General Public License version 2 as 12* published by the Free Software Foundation. 13*/ 14 15#include <asm/mach-types.h> 16 17#include <linux/module.h> 18#include <linux/platform_device.h> 19#include <linux/slab.h> 20#include <linux/gpio.h> 21#include <linux/of_gpio.h> 22 23#include <sound/core.h> 24#include <sound/jack.h> 25#include <sound/pcm.h> 26#include <sound/pcm_params.h> 27#include <sound/soc.h> 28 29#include "../codecs/alc5632.h" 30 31#include "tegra_das.h" 32#include "tegra_i2s.h" 33#include "tegra_pcm.h" 34#include "tegra_asoc_utils.h" 35 36#define DRV_NAME "tegra-alc5632" 37 38#define GPIO_HP_DET BIT(0) 39 40struct tegra_alc5632 { 41 struct tegra_asoc_utils_data util_data; 42 struct platform_device *pcm_dev; 43 int gpio_requested; 44 int gpio_hp_det; 45}; 46 47static int tegra_alc5632_asoc_hw_params(struct snd_pcm_substream *substream, 48 struct snd_pcm_hw_params *params) 49{ 50 struct snd_soc_pcm_runtime *rtd = substream->private_data; 51 struct snd_soc_dai *codec_dai = rtd->codec_dai; 52 struct snd_soc_codec *codec = rtd->codec; 53 struct snd_soc_card *card = codec->card; 54 struct tegra_alc5632 *alc5632 = snd_soc_card_get_drvdata(card); 55 int srate, mclk; 56 int err; 57 58 srate = params_rate(params); 59 mclk = 512 * srate; 60 61 err = tegra_asoc_utils_set_rate(&alc5632->util_data, srate, mclk); 62 if (err < 0) { 63 dev_err(card->dev, "Can't configure clocks\n"); 64 return err; 65 } 66 67 err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, 68 SND_SOC_CLOCK_IN); 69 if (err < 0) { 70 dev_err(card->dev, "codec_dai clock not set\n"); 71 return err; 72 } 73 74 return 0; 75} 76 77static struct snd_soc_ops tegra_alc5632_asoc_ops = { 78 .hw_params = tegra_alc5632_asoc_hw_params, 79}; 80 81static struct snd_soc_jack tegra_alc5632_hs_jack; 82 83static struct snd_soc_jack_pin tegra_alc5632_hs_jack_pins[] = { 84 { 85 .pin = "Headset Mic", 86 .mask = SND_JACK_MICROPHONE, 87 }, 88 { 89 .pin = "Headset Stereophone", 90 .mask = SND_JACK_HEADPHONE, 91 }, 92}; 93 94static struct snd_soc_jack_gpio tegra_alc5632_hp_jack_gpio = { 95 .name = "Headset detection", 96 .report = SND_JACK_HEADSET, 97 .debounce_time = 150, 98 .invert = 1, 99}; 100 101static const struct snd_soc_dapm_widget tegra_alc5632_dapm_widgets[] = { 102 SND_SOC_DAPM_SPK("Int Spk", NULL), 103 SND_SOC_DAPM_HP("Headset Stereophone", NULL), 104 SND_SOC_DAPM_MIC("Headset Mic", NULL), 105 SND_SOC_DAPM_MIC("Digital Mic", NULL), 106}; 107 108static const struct snd_kcontrol_new tegra_alc5632_controls[] = { 109 SOC_DAPM_PIN_SWITCH("Int Spk"), 110}; 111 112static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd) 113{ 114 struct snd_soc_codec *codec = rtd->codec; 115 struct snd_soc_dapm_context *dapm = &codec->dapm; 116 struct device_node *np = codec->card->dev->of_node; 117 struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(codec->card); 118 119 snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET, 120 &tegra_alc5632_hs_jack); 121 snd_soc_jack_add_pins(&tegra_alc5632_hs_jack, 122 ARRAY_SIZE(tegra_alc5632_hs_jack_pins), 123 tegra_alc5632_hs_jack_pins); 124 125 machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0); 126 127 if (gpio_is_valid(machine->gpio_hp_det)) { 128 tegra_alc5632_hp_jack_gpio.gpio = machine->gpio_hp_det; 129 snd_soc_jack_add_gpios(&tegra_alc5632_hs_jack, 130 1, 131 &tegra_alc5632_hp_jack_gpio); 132 machine->gpio_requested |= GPIO_HP_DET; 133 } 134 135 snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); 136 137 return 0; 138} 139 140static struct snd_soc_dai_link tegra_alc5632_dai = { 141 .name = "ALC5632", 142 .stream_name = "ALC5632 PCM", 143 .platform_name = "tegra-pcm-audio", 144 .codec_dai_name = "alc5632-hifi", 145 .init = tegra_alc5632_asoc_init, 146 .ops = &tegra_alc5632_asoc_ops, 147 .dai_fmt = SND_SOC_DAIFMT_I2S 148 | SND_SOC_DAIFMT_NB_NF 149 | SND_SOC_DAIFMT_CBS_CFS, 150}; 151 152static struct snd_soc_card snd_soc_tegra_alc5632 = { 153 .name = "tegra-alc5632", 154 .owner = THIS_MODULE, 155 .dai_link = &tegra_alc5632_dai, 156 .num_links = 1, 157 .controls = tegra_alc5632_controls, 158 .num_controls = ARRAY_SIZE(tegra_alc5632_controls), 159 .dapm_widgets = tegra_alc5632_dapm_widgets, 160 .num_dapm_widgets = ARRAY_SIZE(tegra_alc5632_dapm_widgets), 161 .fully_routed = true, 162}; 163 164static __devinit int tegra_alc5632_probe(struct platform_device *pdev) 165{ 166 struct snd_soc_card *card = &snd_soc_tegra_alc5632; 167 struct tegra_alc5632 *alc5632; 168 int ret; 169 170 alc5632 = devm_kzalloc(&pdev->dev, 171 sizeof(struct tegra_alc5632), GFP_KERNEL); 172 if (!alc5632) { 173 dev_err(&pdev->dev, "Can't allocate tegra_alc5632\n"); 174 ret = -ENOMEM; 175 goto err; 176 } 177 178 card->dev = &pdev->dev; 179 platform_set_drvdata(pdev, card); 180 snd_soc_card_set_drvdata(card, alc5632); 181 182 alc5632->pcm_dev = ERR_PTR(-EINVAL); 183 184 if (!(pdev->dev.of_node)) { 185 dev_err(&pdev->dev, "Must be instantiated using device tree\n"); 186 ret = -EINVAL; 187 goto err; 188 } 189 190 ret = snd_soc_of_parse_card_name(card, "nvidia,model"); 191 if (ret) 192 goto err; 193 194 ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); 195 if (ret) 196 goto err; 197 198 tegra_alc5632_dai.codec_of_node = of_parse_phandle( 199 pdev->dev.of_node, "nvidia,audio-codec", 0); 200 201 if (!tegra_alc5632_dai.codec_of_node) { 202 dev_err(&pdev->dev, 203 "Property 'nvidia,audio-codec' missing or invalid\n"); 204 ret = -EINVAL; 205 goto err; 206 } 207 208 tegra_alc5632_dai.cpu_dai_of_node = of_parse_phandle( 209 pdev->dev.of_node, "nvidia,i2s-controller", 0); 210 if (!tegra_alc5632_dai.cpu_dai_of_node) { 211 dev_err(&pdev->dev, 212 "Property 'nvidia,i2s-controller' missing or invalid\n"); 213 ret = -EINVAL; 214 goto err; 215 } 216 217 alc5632->pcm_dev = platform_device_register_simple( 218 "tegra-pcm-audio", -1, NULL, 0); 219 if (IS_ERR(alc5632->pcm_dev)) { 220 dev_err(&pdev->dev, 221 "Can't instantiate tegra-pcm-audio\n"); 222 ret = PTR_ERR(alc5632->pcm_dev); 223 goto err; 224 } 225 226 ret = tegra_asoc_utils_init(&alc5632->util_data, &pdev->dev); 227 if (ret) 228 goto err_unregister; 229 230 ret = snd_soc_register_card(card); 231 if (ret) { 232 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", 233 ret); 234 goto err_fini_utils; 235 } 236 237 return 0; 238 239err_fini_utils: 240 tegra_asoc_utils_fini(&alc5632->util_data); 241err_unregister: 242 if (!IS_ERR(alc5632->pcm_dev)) 243 platform_device_unregister(alc5632->pcm_dev); 244err: 245 return ret; 246} 247 248static int __devexit tegra_alc5632_remove(struct platform_device *pdev) 249{ 250 struct snd_soc_card *card = platform_get_drvdata(pdev); 251 struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(card); 252 253 if (machine->gpio_requested & GPIO_HP_DET) 254 snd_soc_jack_free_gpios(&tegra_alc5632_hs_jack, 255 1, 256 &tegra_alc5632_hp_jack_gpio); 257 machine->gpio_requested = 0; 258 259 snd_soc_unregister_card(card); 260 261 tegra_asoc_utils_fini(&machine->util_data); 262 if (!IS_ERR(machine->pcm_dev)) 263 platform_device_unregister(machine->pcm_dev); 264 265 return 0; 266} 267 268static const struct of_device_id tegra_alc5632_of_match[] __devinitconst = { 269 { .compatible = "nvidia,tegra-audio-alc5632", }, 270 {}, 271}; 272 273static struct platform_driver tegra_alc5632_driver = { 274 .driver = { 275 .name = DRV_NAME, 276 .owner = THIS_MODULE, 277 .pm = &snd_soc_pm_ops, 278 .of_match_table = tegra_alc5632_of_match, 279 }, 280 .probe = tegra_alc5632_probe, 281 .remove = __devexit_p(tegra_alc5632_remove), 282}; 283module_platform_driver(tegra_alc5632_driver); 284 285MODULE_AUTHOR("Leon Romanovsky <leon@leon.nu>"); 286MODULE_DESCRIPTION("Tegra+ALC5632 machine ASoC driver"); 287MODULE_LICENSE("GPL"); 288MODULE_ALIAS("platform:" DRV_NAME); 289MODULE_DEVICE_TABLE(of, tegra_alc5632_of_match);