diff options
Diffstat (limited to 'kernel/sound/soc/pxa/z2.c')
-rw-r--r-- | kernel/sound/soc/pxa/z2.c | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/kernel/sound/soc/pxa/z2.c b/kernel/sound/soc/pxa/z2.c new file mode 100644 index 000000000..bcbfbe830 --- /dev/null +++ b/kernel/sound/soc/pxa/z2.c @@ -0,0 +1,227 @@ +/* + * linux/sound/soc/pxa/z2.c + * + * SoC Audio driver for Aeronix Zipit Z2 + * + * Copyright (C) 2009 Ken McGuire <kenm@desertweyr.com> + * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/jack.h> + +#include <asm/mach-types.h> +#include <mach/hardware.h> +#include <mach/audio.h> +#include <mach/z2.h> + +#include "../codecs/wm8750.h" +#include "pxa2xx-i2s.h" + +static struct snd_soc_card snd_soc_z2; + +static int z2_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; + unsigned int clk = 0; + int ret = 0; + + switch (params_rate(params)) { + case 8000: + case 16000: + case 48000: + case 96000: + clk = 12288000; + break; + case 11025: + case 22050: + case 44100: + clk = 11289600; + break; + } + + /* set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + /* set the I2S system clock as input (unused) */ + ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_jack hs_jack; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin hs_jack_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Ext Spk", + .mask = SND_JACK_HEADPHONE, + .invert = 1 + }, +}; + +/* Headset jack detection gpios */ +static struct snd_soc_jack_gpio hs_jack_gpios[] = { + { + .gpio = GPIO37_ZIPITZ2_HEADSET_DETECT, + .name = "hsdet-gpio", + .report = SND_JACK_HEADSET, + .debounce_time = 200, + .invert = 1, + }, +}; + +/* z2 machine dapm widgets */ +static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), + + /* headset is a mic and mono headphone */ + SND_SOC_DAPM_HP("Headset Jack", NULL), +}; + +/* Z2 machine audio_map */ +static const struct snd_soc_dapm_route z2_audio_map[] = { + + /* headphone connected to LOUT1, ROUT1 */ + {"Headphone Jack", NULL, "LOUT1"}, + {"Headphone Jack", NULL, "ROUT1"}, + + /* ext speaker connected to LOUT2, ROUT2 */ + {"Ext Spk", NULL , "ROUT2"}, + {"Ext Spk", NULL , "LOUT2"}, + + /* mic is connected to R input 2 - with bias */ + {"RINPUT2", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Mic Jack"}, +}; + +/* + * Logic for a wm8750 as connected on a Z2 Device + */ +static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; + + /* NC codec pins */ + snd_soc_dapm_disable_pin(dapm, "LINPUT3"); + snd_soc_dapm_disable_pin(dapm, "RINPUT3"); + snd_soc_dapm_disable_pin(dapm, "OUT3"); + snd_soc_dapm_disable_pin(dapm, "MONO1"); + + /* Jack detection API stuff */ + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET, + &hs_jack, hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); + if (ret) + goto err; + + ret = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios), + hs_jack_gpios); + if (ret) + goto err; + + return 0; + +err: + return ret; +} + +static struct snd_soc_ops z2_ops = { + .hw_params = z2_hw_params, +}; + +/* z2 digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link z2_dai = { + .name = "wm8750", + .stream_name = "WM8750", + .cpu_dai_name = "pxa2xx-i2s", + .codec_dai_name = "wm8750-hifi", + .platform_name = "pxa-pcm-audio", + .codec_name = "wm8750.0-001b", + .init = z2_wm8750_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &z2_ops, +}; + +/* z2 audio machine driver */ +static struct snd_soc_card snd_soc_z2 = { + .name = "Z2", + .owner = THIS_MODULE, + .dai_link = &z2_dai, + .num_links = 1, + + .dapm_widgets = wm8750_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets), + .dapm_routes = z2_audio_map, + .num_dapm_routes = ARRAY_SIZE(z2_audio_map), +}; + +static struct platform_device *z2_snd_device; + +static int __init z2_init(void) +{ + int ret; + + if (!machine_is_zipit2()) + return -ENODEV; + + z2_snd_device = platform_device_alloc("soc-audio", -1); + if (!z2_snd_device) + return -ENOMEM; + + platform_set_drvdata(z2_snd_device, &snd_soc_z2); + ret = platform_device_add(z2_snd_device); + + if (ret) + platform_device_put(z2_snd_device); + + return ret; +} + +static void __exit z2_exit(void) +{ + platform_device_unregister(z2_snd_device); +} + +module_init(z2_init); +module_exit(z2_exit); + +MODULE_AUTHOR("Ken McGuire <kenm@desertweyr.com>, " + "Marek Vasut <marek.vasut@gmail.com>"); +MODULE_DESCRIPTION("ALSA SoC ZipitZ2"); +MODULE_LICENSE("GPL"); |