| OLD | NEW |
| (Empty) | |
| 1 /* |
| 2 * seaboard.c - Seaboard machine ASoC driver |
| 3 * |
| 4 * Author: Stephen Warren <swarren@nvidia.com> |
| 5 * Copyright (C) 2010-2011 - NVIDIA, Inc. |
| 6 * |
| 7 * Based on code copyright/by: |
| 8 * |
| 9 * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd. |
| 10 * |
| 11 * Copyright 2007 Wolfson Microelectronics PLC. |
| 12 * Author: Graeme Gregory |
| 13 * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com |
| 14 * |
| 15 * This program is free software; you can redistribute it and/or |
| 16 * modify it under the terms of the GNU General Public License |
| 17 * version 2 as published by the Free Software Foundation. |
| 18 * |
| 19 * This program is distributed in the hope that it will be useful, but |
| 20 * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 22 * General Public License for more details. |
| 23 * |
| 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 |
| 26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
| 27 * 02110-1301 USA |
| 28 * |
| 29 */ |
| 30 |
| 31 #include <asm/mach-types.h> |
| 32 |
| 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/seaboard_audio.h> |
| 39 |
| 40 #include <sound/core.h> |
| 41 #include <sound/jack.h> |
| 42 #include <sound/pcm.h> |
| 43 #include <sound/pcm_params.h> |
| 44 #include <sound/soc.h> |
| 45 |
| 46 #include "../codecs/wm8903.h" |
| 47 |
| 48 #include "tegra_das.h" |
| 49 #include "tegra_i2s.h" |
| 50 #include "tegra_pcm.h" |
| 51 #include "tegra_asoc_utils.h" |
| 52 |
| 53 #define DRV_NAME "tegra-snd-seaboard" |
| 54 |
| 55 #define GPIO_SPKR_EN BIT(0) |
| 56 |
| 57 struct tegra_seaboard { |
| 58 struct tegra_asoc_utils_data util_data; |
| 59 struct seaboard_audio_platform_data *pdata; |
| 60 int gpio_requested; |
| 61 }; |
| 62 |
| 63 static int seaboard_asoc_hw_params(struct snd_pcm_substream *substream, |
| 64 struct snd_pcm_hw_params *params) |
| 65 { |
| 66 struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| 67 struct snd_soc_dai *codec_dai = rtd->codec_dai; |
| 68 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; |
| 69 struct snd_soc_codec *codec = rtd->codec; |
| 70 struct snd_soc_card *card = codec->card; |
| 71 struct tegra_seaboard *seaboard = snd_soc_card_get_drvdata(card); |
| 72 int srate, mclk, mclk_change; |
| 73 int err; |
| 74 |
| 75 srate = params_rate(params); |
| 76 switch (srate) { |
| 77 case 64000: |
| 78 case 88200: |
| 79 case 96000: |
| 80 mclk = 128 * srate; |
| 81 break; |
| 82 default: |
| 83 mclk = 256 * srate; |
| 84 break; |
| 85 } |
| 86 /* FIXME: Codec only requires >= 3MHz if OSR==0 */ |
| 87 while (mclk < 6000000) |
| 88 mclk *= 2; |
| 89 |
| 90 err = tegra_asoc_utils_set_rate(&seaboard->util_data, srate, mclk, |
| 91 &mclk_change); |
| 92 if (err < 0) { |
| 93 dev_err(card->dev, "Can't configure clocks\n"); |
| 94 return err; |
| 95 } |
| 96 |
| 97 err = snd_soc_dai_set_fmt(codec_dai, |
| 98 SND_SOC_DAIFMT_I2S | |
| 99 SND_SOC_DAIFMT_NB_NF | |
| 100 SND_SOC_DAIFMT_CBS_CFS); |
| 101 if (err < 0) { |
| 102 dev_err(card->dev, "codec_dai fmt not set\n"); |
| 103 return err; |
| 104 } |
| 105 |
| 106 err = snd_soc_dai_set_fmt(cpu_dai, |
| 107 SND_SOC_DAIFMT_I2S | |
| 108 SND_SOC_DAIFMT_NB_NF | |
| 109 SND_SOC_DAIFMT_CBS_CFS); |
| 110 if (err < 0) { |
| 111 dev_err(card->dev, "cpu_dai fmt not set\n"); |
| 112 return err; |
| 113 } |
| 114 |
| 115 if (mclk_change) { |
| 116 err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, |
| 117 SND_SOC_CLOCK_IN); |
| 118 if (err < 0) { |
| 119 dev_err(card->dev, "codec_dai clock not set\n"); |
| 120 return err; |
| 121 } |
| 122 } |
| 123 |
| 124 return 0; |
| 125 } |
| 126 |
| 127 static struct snd_soc_ops seaboard_asoc_ops = { |
| 128 .hw_params = seaboard_asoc_hw_params, |
| 129 }; |
| 130 |
| 131 static struct snd_soc_jack seaboard_hp_jack; |
| 132 |
| 133 static struct snd_soc_jack_pin seaboard_hp_jack_pins[] = { |
| 134 { |
| 135 .pin = "Headphone Jack", |
| 136 .mask = SND_JACK_HEADPHONE, |
| 137 }, |
| 138 }; |
| 139 |
| 140 static struct snd_soc_jack_gpio seaboard_hp_jack_gpios[] = { |
| 141 { |
| 142 .name = "headphone detect", |
| 143 .report = SND_JACK_HEADPHONE, |
| 144 .debounce_time = 150, |
| 145 .invert = 1, |
| 146 } |
| 147 }; |
| 148 |
| 149 static struct snd_soc_jack seaboard_mic_jack; |
| 150 |
| 151 static struct snd_soc_jack_pin seaboard_mic_jack_pins[] = { |
| 152 { |
| 153 .pin = "Mic Jack", |
| 154 .mask = SND_JACK_MICROPHONE, |
| 155 }, |
| 156 }; |
| 157 |
| 158 static int seaboard_event_int_spk(struct snd_soc_dapm_widget *w, |
| 159 struct snd_kcontrol *k, int event) |
| 160 { |
| 161 struct snd_soc_codec *codec = w->codec; |
| 162 struct snd_soc_card *card = codec->card; |
| 163 struct tegra_seaboard *seaboard = snd_soc_card_get_drvdata(card); |
| 164 struct seaboard_audio_platform_data *pdata = seaboard->pdata; |
| 165 |
| 166 gpio_set_value_cansleep(pdata->gpio_spkr_en, |
| 167 SND_SOC_DAPM_EVENT_ON(event)); |
| 168 |
| 169 return 0; |
| 170 } |
| 171 |
| 172 static const struct snd_soc_dapm_widget seaboard_dapm_widgets[] = { |
| 173 SND_SOC_DAPM_SPK("Int Spk", seaboard_event_int_spk), |
| 174 SND_SOC_DAPM_HP("Headphone Jack", NULL), |
| 175 SND_SOC_DAPM_MIC("Mic Jack", NULL), |
| 176 }; |
| 177 |
| 178 static const struct snd_soc_dapm_route seaboard_audio_map[] = { |
| 179 {"Headphone Jack", NULL, "HPOUTR"}, |
| 180 {"Headphone Jack", NULL, "HPOUTL"}, |
| 181 {"Int Spk", NULL, "ROP"}, |
| 182 {"Int Spk", NULL, "RON"}, |
| 183 {"Int Spk", NULL, "LOP"}, |
| 184 {"Int Spk", NULL, "LON"}, |
| 185 {"Mic Bias", NULL, "Mic Jack"}, |
| 186 {"IN1R", NULL, "Mic Bias"}, |
| 187 }; |
| 188 |
| 189 static const struct snd_kcontrol_new seaboard_controls[] = { |
| 190 SOC_DAPM_PIN_SWITCH("Int Spk"), |
| 191 }; |
| 192 |
| 193 static int seaboard_asoc_init(struct snd_soc_pcm_runtime *rtd) |
| 194 { |
| 195 struct snd_soc_codec *codec = rtd->codec; |
| 196 struct snd_soc_dapm_context *dapm = &codec->dapm; |
| 197 struct snd_soc_card *card = codec->card; |
| 198 struct tegra_seaboard *seaboard = snd_soc_card_get_drvdata(card); |
| 199 struct seaboard_audio_platform_data *pdata = seaboard->pdata; |
| 200 int ret; |
| 201 |
| 202 ret = gpio_request(pdata->gpio_spkr_en, "spkr_en"); |
| 203 if (ret) { |
| 204 dev_err(card->dev, "cannot get spkr_en gpio\n"); |
| 205 return ret; |
| 206 } |
| 207 seaboard->gpio_requested |= GPIO_SPKR_EN; |
| 208 |
| 209 gpio_direction_output(pdata->gpio_spkr_en, 0); |
| 210 |
| 211 ret = snd_soc_add_controls(codec, seaboard_controls, |
| 212 ARRAY_SIZE(seaboard_controls)); |
| 213 if (ret < 0) |
| 214 return ret; |
| 215 |
| 216 snd_soc_dapm_new_controls(dapm, seaboard_dapm_widgets, |
| 217 ARRAY_SIZE(seaboard_dapm_widgets)); |
| 218 |
| 219 snd_soc_dapm_add_routes(dapm, seaboard_audio_map, |
| 220 ARRAY_SIZE(seaboard_audio_map)); |
| 221 |
| 222 seaboard_hp_jack_gpios[0].gpio = pdata->gpio_hp_det; |
| 223 snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, |
| 224 &seaboard_hp_jack); |
| 225 snd_soc_jack_add_pins(&seaboard_hp_jack, |
| 226 ARRAY_SIZE(seaboard_hp_jack_pins), |
| 227 seaboard_hp_jack_pins); |
| 228 snd_soc_jack_add_gpios(&seaboard_hp_jack, |
| 229 ARRAY_SIZE(seaboard_hp_jack_gpios), |
| 230 seaboard_hp_jack_gpios); |
| 231 |
| 232 snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE, |
| 233 &seaboard_mic_jack); |
| 234 snd_soc_jack_add_pins(&seaboard_mic_jack, |
| 235 ARRAY_SIZE(seaboard_mic_jack_pins), |
| 236 seaboard_mic_jack_pins); |
| 237 wm8903_mic_detect(codec, &seaboard_mic_jack, SND_JACK_MICROPHONE, 0); |
| 238 |
| 239 snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); |
| 240 |
| 241 snd_soc_dapm_nc_pin(dapm, "IN3L"); |
| 242 snd_soc_dapm_nc_pin(dapm, "IN3R"); |
| 243 snd_soc_dapm_nc_pin(dapm, "LINEOUTL"); |
| 244 snd_soc_dapm_nc_pin(dapm, "LINEOUTR"); |
| 245 |
| 246 snd_soc_dapm_sync(dapm); |
| 247 |
| 248 return 0; |
| 249 } |
| 250 |
| 251 static struct snd_soc_dai_link seaboard_wm8903_dai = { |
| 252 .name = "WM8903", |
| 253 .stream_name = "WM8903 PCM", |
| 254 .codec_name = "wm8903.0-001a", |
| 255 .platform_name = "tegra-pcm-audio", |
| 256 .cpu_dai_name = "tegra-i2s.0", |
| 257 .codec_dai_name = "wm8903-hifi", |
| 258 .init = seaboard_asoc_init, |
| 259 .ops = &seaboard_asoc_ops, |
| 260 }; |
| 261 |
| 262 static struct snd_soc_card snd_soc_seaboard = { |
| 263 .name = "tegra-seaboard", |
| 264 .dai_link = &seaboard_wm8903_dai, |
| 265 .num_links = 1, |
| 266 }; |
| 267 |
| 268 static __devinit int tegra_snd_seaboard_probe(struct platform_device *pdev) |
| 269 { |
| 270 struct snd_soc_card *card = &snd_soc_seaboard; |
| 271 struct tegra_seaboard *seaboard; |
| 272 struct seaboard_audio_platform_data *pdata; |
| 273 int ret; |
| 274 |
| 275 if (!machine_is_seaboard()) { |
| 276 dev_err(&pdev->dev, "Not running on Tegra seaboard!\n"); |
| 277 return -ENODEV; |
| 278 } |
| 279 |
| 280 pdata = pdev->dev.platform_data; |
| 281 if (!pdata) { |
| 282 dev_err(&pdev->dev, "no platform data supplied\n"); |
| 283 return -EINVAL; |
| 284 } |
| 285 |
| 286 seaboard = kzalloc(sizeof(struct tegra_seaboard), GFP_KERNEL); |
| 287 if (!seaboard) { |
| 288 dev_err(&pdev->dev, "Can't allocate tegra_seaboard\n"); |
| 289 return -ENOMEM; |
| 290 } |
| 291 |
| 292 seaboard->pdata = pdata; |
| 293 |
| 294 ret = tegra_asoc_utils_init(&seaboard->util_data, &pdev->dev); |
| 295 if (ret) |
| 296 goto err_free_seaboard; |
| 297 |
| 298 card->dev = &pdev->dev; |
| 299 platform_set_drvdata(pdev, card); |
| 300 snd_soc_card_set_drvdata(card, seaboard); |
| 301 |
| 302 ret = snd_soc_register_card(card); |
| 303 if (ret) { |
| 304 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", |
| 305 ret); |
| 306 goto err_clear_drvdata; |
| 307 } |
| 308 |
| 309 return 0; |
| 310 |
| 311 err_clear_drvdata: |
| 312 snd_soc_card_set_drvdata(card, NULL); |
| 313 platform_set_drvdata(pdev, NULL); |
| 314 card->dev = NULL; |
| 315 tegra_asoc_utils_fini(&seaboard->util_data); |
| 316 err_free_seaboard: |
| 317 kfree(seaboard); |
| 318 return ret; |
| 319 } |
| 320 |
| 321 static int __devexit tegra_snd_seaboard_remove(struct platform_device *pdev) |
| 322 { |
| 323 struct snd_soc_card *card = platform_get_drvdata(pdev); |
| 324 struct tegra_seaboard *seaboard = snd_soc_card_get_drvdata(card); |
| 325 struct seaboard_audio_platform_data *pdata = seaboard->pdata; |
| 326 |
| 327 snd_soc_unregister_card(card); |
| 328 |
| 329 snd_soc_card_set_drvdata(card, NULL); |
| 330 platform_set_drvdata(pdev, NULL); |
| 331 card->dev = NULL; |
| 332 |
| 333 tegra_asoc_utils_fini(&seaboard->util_data); |
| 334 |
| 335 if (seaboard->gpio_requested & GPIO_SPKR_EN) |
| 336 gpio_free(pdata->gpio_spkr_en); |
| 337 |
| 338 kfree(seaboard); |
| 339 |
| 340 return 0; |
| 341 } |
| 342 |
| 343 static struct platform_driver tegra_snd_seaboard_driver = { |
| 344 .driver = { |
| 345 .name = DRV_NAME, |
| 346 .owner = THIS_MODULE, |
| 347 }, |
| 348 .probe = tegra_snd_seaboard_probe, |
| 349 .remove = __devexit_p(tegra_snd_seaboard_remove), |
| 350 }; |
| 351 |
| 352 static int __init snd_tegra_seaboard_init(void) |
| 353 { |
| 354 return platform_driver_register(&tegra_snd_seaboard_driver); |
| 355 } |
| 356 module_init(snd_tegra_seaboard_init); |
| 357 |
| 358 static void __exit snd_tegra_seaboard_exit(void) |
| 359 { |
| 360 platform_driver_unregister(&tegra_snd_seaboard_driver); |
| 361 } |
| 362 module_exit(snd_tegra_seaboard_exit); |
| 363 |
| 364 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); |
| 365 MODULE_DESCRIPTION("Seaboard machine ASoC driver"); |
| 366 MODULE_LICENSE("GPL"); |
| 367 MODULE_ALIAS("platform:" DRV_NAME); |
| OLD | NEW |