diff options
Diffstat (limited to 'sound/soc/samsung')
-rw-r--r-- | sound/soc/samsung/Kconfig | 15 | ||||
-rw-r--r-- | sound/soc/samsung/Makefile | 4 | ||||
-rw-r--r-- | sound/soc/samsung/goni_wm8994.c | 1 | ||||
-rw-r--r-- | sound/soc/samsung/neo1973_wm8753.c | 1 | ||||
-rw-r--r-- | sound/soc/samsung/smdk_wm8580pcm.c | 206 | ||||
-rw-r--r-- | sound/soc/samsung/speyside.c | 332 |
6 files changed, 557 insertions, 2 deletions
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index a3fdfb6..459566b 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -162,3 +162,18 @@ config SND_SOC_SAMSUNG_SMDK_SPDIF select SND_SAMSUNG_SPDIF help Say Y if you want to add support for SoC S/PDIF audio on the SMDK. + +config SND_SOC_SMDK_WM8580_PCM + tristate "SoC PCM Audio support for WM8580 on SMDK" + depends on SND_SOC_SAMSUNG && (MACH_SMDK6450 || MACH_SMDKV210 || MACH_SMDKC110) + select SND_SOC_WM8580 + select SND_SAMSUNG_PCM + help + Say Y if you want to add support for SoC audio on the SMDK. + +config SND_SOC_SPEYSIDE + tristate "Audio support for Wolfson Speyside" + depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 + select SND_SAMSUNG_I2S + select SND_SOC_WM8915 + select SND_SOC_WM9081 diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index 294dec0..683843a 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile @@ -34,6 +34,8 @@ snd-soc-smdk-wm9713-objs := smdk_wm9713.o snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o snd-soc-goni-wm8994-objs := goni_wm8994.o snd-soc-smdk-spdif-objs := smdk_spdif.o +snd-soc-smdk-wm8580pcm-objs := smdk_wm8580pcm.o +snd-soc-speyside-objs := speyside.o obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o @@ -51,3 +53,5 @@ obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_WM9713) += snd-soc-smdk-wm9713.o obj-$(CONFIG_SND_SOC_SMARTQ) += snd-soc-s3c64xx-smartq-wm8987.o obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_SPDIF) += snd-soc-smdk-spdif.o obj-$(CONFIG_SND_SOC_GONI_AQUILA_WM8994) += snd-soc-goni-wm8994.o +obj-$(CONFIG_SND_SOC_SMDK_WM8580_PCM) += snd-soc-smdk-wm8580pcm.o +obj-$(CONFIG_SND_SOC_SPEYSIDE) += snd-soc-speyside.o diff --git a/sound/soc/samsung/goni_wm8994.c b/sound/soc/samsung/goni_wm8994.c index 0e80dae..eb6d72e 100644 --- a/sound/soc/samsung/goni_wm8994.c +++ b/sound/soc/samsung/goni_wm8994.c @@ -246,7 +246,6 @@ static struct snd_soc_dai_link goni_dai[] = { .stream_name = "Voice", .cpu_dai_name = "goni-voice-dai", .codec_dai_name = "wm8994-aif2", - .platform_name = "samsung-audio", .codec_name = "wm8994-codec.0-001a", .ops = &goni_voice_ops, }, diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c index 4522309..16152ed 100644 --- a/sound/soc/samsung/neo1973_wm8753.c +++ b/sound/soc/samsung/neo1973_wm8753.c @@ -432,7 +432,6 @@ static struct snd_soc_dai_link neo1973_dai[] = { { /* Voice via BT */ .name = "Bluetooth", .stream_name = "Voice", - .platform_name = "samsung-audio", .cpu_dai_name = "dfbmcs320-pcm", .codec_dai_name = "wm8753-voice", .codec_name = "wm8753-codec.0-001a", diff --git a/sound/soc/samsung/smdk_wm8580pcm.c b/sound/soc/samsung/smdk_wm8580pcm.c new file mode 100644 index 0000000..0d12092 --- /dev/null +++ b/sound/soc/samsung/smdk_wm8580pcm.c @@ -0,0 +1,206 @@ +/* + * sound/soc/samsung/smdk_wm8580pcm.c + * + * Copyright (c) 2011 Samsung Electronics Co. Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include <sound/soc.h> +#include <sound/pcm_params.h> +#include <sound/pcm.h> + +#include <asm/mach-types.h> + +#include "../codecs/wm8580.h" +#include "dma.h" +#include "pcm.h" + +/* + * Board Settings: + * o '1' means 'ON' + * o '0' means 'OFF' + * o 'X' means 'Don't care' + * + * SMDK6410, SMDK6440, SMDK6450 Base B/D: CFG1-0000, CFG2-1111 + * SMDKC110, SMDKV210: CFGB11-100100, CFGB12-0000 + */ + +#define SMDK_WM8580_EXT_OSC 12000000 +#define SMDK_WM8580_EXT_MCLK 4096000 +#define SMDK_WM8580_EXT_VOICE 2048000 + +static unsigned long mclk_freq; +static unsigned long xtal_freq; + +/* + * If MCLK clock directly gets from XTAL, we don't have to use PLL + * to make MCLK, but if XTAL clock source connects with other codec + * pin (like XTI), we should have to set codec's PLL to make MCLK. + * Because Samsung SoC does not support pcmcdclk output like I2S. + */ + +static int smdk_wm8580_pcm_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->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int rfs, ret; + + switch (params_rate(params)) { + case 8000: + break; + default: + printk(KERN_ERR "%s:%d Sampling Rate %u not supported!\n", + __func__, __LINE__, params_rate(params)); + return -EINVAL; + } + + rfs = mclk_freq / params_rate(params) / 2; + + /* Set the codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B + | SND_SOC_DAIFMT_IB_NF + | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* Set the cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_B + | SND_SOC_DAIFMT_IB_NF + | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + if (mclk_freq == xtal_freq) { + ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_MCLK, + mclk_freq, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK, + WM8580_CLKSRC_MCLK); + if (ret < 0) + return ret; + } else { + ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_PLLA, + mclk_freq, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK, + WM8580_CLKSRC_PLLA); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0, + xtal_freq, mclk_freq); + if (ret < 0) + return ret; + } + + /* Set PCM source clock on CPU */ + ret = snd_soc_dai_set_sysclk(cpu_dai, S3C_PCM_CLKSRC_MUX, + mclk_freq, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + /* Set SCLK_DIV for making bclk */ + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_PCM_SCLK_PER_FS, rfs); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops smdk_wm8580_pcm_ops = { + .hw_params = smdk_wm8580_pcm_hw_params, +}; + +static struct snd_soc_dai_link smdk_dai[] = { + { + .name = "WM8580 PAIF PCM RX", + .stream_name = "Playback", + .cpu_dai_name = "samsung-pcm.0", + .codec_dai_name = "wm8580-hifi-playback", + .platform_name = "samsung-audio", + .codec_name = "wm8580-codec.0-001b", + .ops = &smdk_wm8580_pcm_ops, + }, { + .name = "WM8580 PAIF PCM TX", + .stream_name = "Capture", + .cpu_dai_name = "samsung-pcm.0", + .codec_dai_name = "wm8580-hifi-capture", + .platform_name = "samsung-audio", + .codec_name = "wm8580-codec.0-001b", + .ops = &smdk_wm8580_pcm_ops, + }, +}; + +static struct snd_soc_card smdk_pcm = { + .name = "SMDK-PCM", + .dai_link = smdk_dai, + .num_links = 2, +}; + +/* + * After SMDKC110 Base Board's Rev is '0.1', 12MHz External OSC(X1) + * is absent (or not connected), so we connect EXT_VOICE_CLK(OSC4), + * 2.0484Mhz, directly with MCLK both Codec and SoC. + */ +static int __devinit snd_smdk_probe(struct platform_device *pdev) +{ + int ret = 0; + + xtal_freq = SMDK_WM8580_EXT_OSC; + mclk_freq = SMDK_WM8580_EXT_MCLK; + + if (machine_is_smdkc110() || machine_is_smdkv210()) + xtal_freq = mclk_freq = SMDK_WM8580_EXT_VOICE; + + smdk_pcm.dev = &pdev->dev; + ret = snd_soc_register_card(&smdk_pcm); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret); + return ret; + } + + return 0; +} + +static int __devexit snd_smdk_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&smdk_pcm); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver snd_smdk_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "samsung-smdk-pcm", + }, + .probe = snd_smdk_probe, + .remove = __devexit_p(snd_smdk_remove), +}; + +static int __init smdk_audio_init(void) +{ + return platform_driver_register(&snd_smdk_driver); +} + +module_init(smdk_audio_init); + +static void __exit smdk_audio_exit(void) +{ + platform_driver_unregister(&snd_smdk_driver); +} + +module_exit(smdk_audio_exit); + +MODULE_AUTHOR("Sangbeom Kim, <sbkim73@samsung.com>"); +MODULE_DESCRIPTION("ALSA SoC SMDK WM8580 for PCM"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c new file mode 100644 index 0000000..360a333 --- /dev/null +++ b/sound/soc/samsung/speyside.c @@ -0,0 +1,332 @@ +/* + * Speyside audio support + * + * Copyright 2011 Wolfson Microelectronics + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/jack.h> +#include <linux/gpio.h> + +#include "../codecs/wm8915.h" +#include "../codecs/wm9081.h" + +#define WM8915_HPSEL_GPIO 214 + +static int speyside_set_bias_level(struct snd_soc_card *card, + enum snd_soc_bias_level level) +{ + struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + int ret; + + switch (level) { + case SND_SOC_BIAS_STANDBY: + ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_MCLK1, + 32768, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_pll(codec_dai, WM8915_FLL_MCLK1, + 0, 0, 0); + if (ret < 0) { + pr_err("Failed to stop FLL\n"); + return ret; + } + + default: + break; + } + + return 0; +} + +static int speyside_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S + | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S + | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_pll(codec_dai, 0, WM8915_FLL_MCLK1, + 32768, 256 * 48000); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_FLL, + 256 * 48000, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops speyside_ops = { + .hw_params = speyside_hw_params, +}; + +static struct snd_soc_jack speyside_headset; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin speyside_headset_pins[] = { + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, +}; + +/* Default the headphone selection to active high */ +static int speyside_jack_polarity; + +static int speyside_get_micbias(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + if (speyside_jack_polarity && (strcmp(source->name, "MICB1") == 0)) + return 1; + if (!speyside_jack_polarity && (strcmp(source->name, "MICB2") == 0)) + return 1; + + return 0; +} + +static void speyside_set_polarity(struct snd_soc_codec *codec, + int polarity) +{ + speyside_jack_polarity = !polarity; + gpio_direction_output(WM8915_HPSEL_GPIO, speyside_jack_polarity); + + /* Re-run DAPM to make sure we're using the correct mic bias */ + snd_soc_dapm_sync(&codec->dapm); +} + +static int speyside_wm8915_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_codec *codec = rtd->codec; + int ret; + + ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK1, 32768, 0); + if (ret < 0) + return ret; + + ret = gpio_request(WM8915_HPSEL_GPIO, "HP_SEL"); + if (ret != 0) + pr_err("Failed to request HP_SEL GPIO: %d\n", ret); + gpio_direction_output(WM8915_HPSEL_GPIO, speyside_jack_polarity); + + ret = snd_soc_jack_new(codec, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &speyside_headset); + if (ret) + return ret; + + ret = snd_soc_jack_add_pins(&speyside_headset, + ARRAY_SIZE(speyside_headset_pins), + speyside_headset_pins); + if (ret) + return ret; + + wm8915_detect(codec, &speyside_headset, speyside_set_polarity); + + return 0; +} + +static int speyside_late_probe(struct snd_soc_card *card) +{ + snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone"); + snd_soc_dapm_ignore_suspend(&card->dapm, "Headset Mic"); + snd_soc_dapm_ignore_suspend(&card->dapm, "Main AMIC"); + snd_soc_dapm_ignore_suspend(&card->dapm, "Main DMIC"); + snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker"); + snd_soc_dapm_ignore_suspend(&card->dapm, "WM1250 Output"); + snd_soc_dapm_ignore_suspend(&card->dapm, "WM1250 Input"); + + return 0; +} + +static struct snd_soc_dai_link speyside_dai[] = { + { + .name = "CPU", + .stream_name = "CPU", + .cpu_dai_name = "samsung-i2s.0", + .codec_dai_name = "wm8915-aif1", + .platform_name = "samsung-audio", + .codec_name = "wm8915.1-001a", + .init = speyside_wm8915_init, + .ops = &speyside_ops, + }, + { + .name = "Baseband", + .stream_name = "Baseband", + .cpu_dai_name = "wm8915-aif2", + .codec_dai_name = "wm1250-ev1", + .codec_name = "wm1250-ev1.1-0027", + .ops = &speyside_ops, + .ignore_suspend = 1, + }, +}; + +static int speyside_wm9081_init(struct snd_soc_dapm_context *dapm) +{ + snd_soc_dapm_nc_pin(dapm, "LINEOUT"); + + /* At any time the WM9081 is active it will have this clock */ + return snd_soc_codec_set_sysclk(dapm->codec, WM9081_SYSCLK_MCLK, + 48000 * 256, 0); +} + +static struct snd_soc_aux_dev speyside_aux_dev[] = { + { + .name = "wm9081", + .codec_name = "wm9081.1-006c", + .init = speyside_wm9081_init, + }, +}; + +static struct snd_soc_codec_conf speyside_codec_conf[] = { + { + .dev_name = "wm9081.1-006c", + .name_prefix = "Sub", + }, +}; + +static const struct snd_kcontrol_new controls[] = { + SOC_DAPM_PIN_SWITCH("Main Speaker"), + SOC_DAPM_PIN_SWITCH("Main DMIC"), + SOC_DAPM_PIN_SWITCH("Main AMIC"), + SOC_DAPM_PIN_SWITCH("WM1250 Input"), + SOC_DAPM_PIN_SWITCH("WM1250 Output"), +}; + +static struct snd_soc_dapm_widget widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + + SND_SOC_DAPM_SPK("Main Speaker", NULL), + + SND_SOC_DAPM_MIC("Main AMIC", NULL), + SND_SOC_DAPM_MIC("Main DMIC", NULL), +}; + +static struct snd_soc_dapm_route audio_paths[] = { + { "IN1RN", NULL, "MICB1" }, + { "IN1RP", NULL, "MICB1" }, + { "IN1RN", NULL, "MICB2" }, + { "IN1RP", NULL, "MICB2" }, + { "MICB1", NULL, "Headset Mic", speyside_get_micbias }, + { "MICB2", NULL, "Headset Mic", speyside_get_micbias }, + + { "IN1LP", NULL, "MICB2" }, + { "IN1RN", NULL, "MICB1" }, + { "MICB2", NULL, "Main AMIC" }, + + { "DMIC1DAT", NULL, "MICB1" }, + { "DMIC2DAT", NULL, "MICB1" }, + { "MICB1", NULL, "Main DMIC" }, + + { "Headphone", NULL, "HPOUT1L" }, + { "Headphone", NULL, "HPOUT1R" }, + + { "Sub IN1", NULL, "HPOUT2L" }, + { "Sub IN2", NULL, "HPOUT2R" }, + + { "Main Speaker", NULL, "Sub SPKN" }, + { "Main Speaker", NULL, "Sub SPKP" }, + { "Main Speaker", NULL, "SPKDAT" }, +}; + +static struct snd_soc_card speyside = { + .name = "Speyside", + .dai_link = speyside_dai, + .num_links = ARRAY_SIZE(speyside_dai), + .aux_dev = speyside_aux_dev, + .num_aux_devs = ARRAY_SIZE(speyside_aux_dev), + .codec_conf = speyside_codec_conf, + .num_configs = ARRAY_SIZE(speyside_codec_conf), + + .set_bias_level = speyside_set_bias_level, + + .controls = controls, + .num_controls = ARRAY_SIZE(controls), + .dapm_widgets = widgets, + .num_dapm_widgets = ARRAY_SIZE(widgets), + .dapm_routes = audio_paths, + .num_dapm_routes = ARRAY_SIZE(audio_paths), + + .late_probe = speyside_late_probe, +}; + +static __devinit int speyside_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &speyside; + int ret; + + card->dev = &pdev->dev; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", + ret); + return ret; + } + + return 0; +} + +static int __devexit speyside_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + + return 0; +} + +static struct platform_driver speyside_driver = { + .driver = { + .name = "speyside", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = speyside_probe, + .remove = __devexit_p(speyside_remove), +}; + +static int __init speyside_audio_init(void) +{ + return platform_driver_register(&speyside_driver); +} +module_init(speyside_audio_init); + +static void __exit speyside_audio_exit(void) +{ + platform_driver_unregister(&speyside_driver); +} +module_exit(speyside_audio_exit); + +MODULE_DESCRIPTION("Speyside audio support"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:speyside"); |