1/* 2 * linux/sound/soc/pxa/z2.c 3 * 4 * SoC Audio driver for Aeronix Zipit Z2 5 * 6 * Copyright (C) 2009 Ken McGuire <kenm@desertweyr.com> 7 * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 */ 13 14#include <linux/module.h> 15#include <linux/moduleparam.h> 16#include <linux/timer.h> 17#include <linux/interrupt.h> 18#include <linux/platform_device.h> 19#include <linux/gpio.h> 20 21#include <sound/core.h> 22#include <sound/pcm.h> 23#include <sound/soc.h> 24#include <sound/jack.h> 25 26#include <asm/mach-types.h> 27#include <mach/hardware.h> 28#include <mach/audio.h> 29#include <mach/z2.h> 30 31#include "../codecs/wm8750.h" 32#include "pxa2xx-i2s.h" 33 34static struct snd_soc_card snd_soc_z2; 35 36static int z2_hw_params(struct snd_pcm_substream *substream, 37 struct snd_pcm_hw_params *params) 38{ 39 struct snd_soc_pcm_runtime *rtd = substream->private_data; 40 struct snd_soc_dai *codec_dai = rtd->codec_dai; 41 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 42 unsigned int clk = 0; 43 int ret = 0; 44 45 switch (params_rate(params)) { 46 case 8000: 47 case 16000: 48 case 48000: 49 case 96000: 50 clk = 12288000; 51 break; 52 case 11025: 53 case 22050: 54 case 44100: 55 clk = 11289600; 56 break; 57 } 58 59 /* set the codec system clock for DAC and ADC */ 60 ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk, 61 SND_SOC_CLOCK_IN); 62 if (ret < 0) 63 return ret; 64 65 /* set the I2S system clock as input (unused) */ 66 ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, 67 SND_SOC_CLOCK_IN); 68 if (ret < 0) 69 return ret; 70 71 return 0; 72} 73 74static struct snd_soc_jack hs_jack; 75 76/* Headset jack detection DAPM pins */ 77static struct snd_soc_jack_pin hs_jack_pins[] = { 78 { 79 .pin = "Mic Jack", 80 .mask = SND_JACK_MICROPHONE, 81 }, 82 { 83 .pin = "Headphone Jack", 84 .mask = SND_JACK_HEADPHONE, 85 }, 86 { 87 .pin = "Ext Spk", 88 .mask = SND_JACK_HEADPHONE, 89 .invert = 1 90 }, 91}; 92 93/* Headset jack detection gpios */ 94static struct snd_soc_jack_gpio hs_jack_gpios[] = { 95 { 96 .gpio = GPIO37_ZIPITZ2_HEADSET_DETECT, 97 .name = "hsdet-gpio", 98 .report = SND_JACK_HEADSET, 99 .debounce_time = 200, 100 .invert = 1, 101 }, 102}; 103 104/* z2 machine dapm widgets */ 105static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { 106 SND_SOC_DAPM_HP("Headphone Jack", NULL), 107 SND_SOC_DAPM_MIC("Mic Jack", NULL), 108 SND_SOC_DAPM_SPK("Ext Spk", NULL), 109 110 /* headset is a mic and mono headphone */ 111 SND_SOC_DAPM_HP("Headset Jack", NULL), 112}; 113 114/* Z2 machine audio_map */ 115static const struct snd_soc_dapm_route z2_audio_map[] = { 116 117 /* headphone connected to LOUT1, ROUT1 */ 118 {"Headphone Jack", NULL, "LOUT1"}, 119 {"Headphone Jack", NULL, "ROUT1"}, 120 121 /* ext speaker connected to LOUT2, ROUT2 */ 122 {"Ext Spk", NULL , "ROUT2"}, 123 {"Ext Spk", NULL , "LOUT2"}, 124 125 /* mic is connected to R input 2 - with bias */ 126 {"RINPUT2", NULL, "Mic Bias"}, 127 {"Mic Bias", NULL, "Mic Jack"}, 128}; 129 130/* 131 * Logic for a wm8750 as connected on a Z2 Device 132 */ 133static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd) 134{ 135 struct snd_soc_codec *codec = rtd->codec; 136 struct snd_soc_dapm_context *dapm = &codec->dapm; 137 int ret; 138 139 /* NC codec pins */ 140 snd_soc_dapm_disable_pin(dapm, "LINPUT3"); 141 snd_soc_dapm_disable_pin(dapm, "RINPUT3"); 142 snd_soc_dapm_disable_pin(dapm, "OUT3"); 143 snd_soc_dapm_disable_pin(dapm, "MONO1"); 144 145 /* Jack detection API stuff */ 146 ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET, 147 &hs_jack, hs_jack_pins, 148 ARRAY_SIZE(hs_jack_pins)); 149 if (ret) 150 goto err; 151 152 ret = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios), 153 hs_jack_gpios); 154 if (ret) 155 goto err; 156 157 return 0; 158 159err: 160 return ret; 161} 162 163static struct snd_soc_ops z2_ops = { 164 .hw_params = z2_hw_params, 165}; 166 167/* z2 digital audio interface glue - connects codec <--> CPU */ 168static struct snd_soc_dai_link z2_dai = { 169 .name = "wm8750", 170 .stream_name = "WM8750", 171 .cpu_dai_name = "pxa2xx-i2s", 172 .codec_dai_name = "wm8750-hifi", 173 .platform_name = "pxa-pcm-audio", 174 .codec_name = "wm8750.0-001b", 175 .init = z2_wm8750_init, 176 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 177 SND_SOC_DAIFMT_CBS_CFS, 178 .ops = &z2_ops, 179}; 180 181/* z2 audio machine driver */ 182static struct snd_soc_card snd_soc_z2 = { 183 .name = "Z2", 184 .owner = THIS_MODULE, 185 .dai_link = &z2_dai, 186 .num_links = 1, 187 188 .dapm_widgets = wm8750_dapm_widgets, 189 .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets), 190 .dapm_routes = z2_audio_map, 191 .num_dapm_routes = ARRAY_SIZE(z2_audio_map), 192}; 193 194static struct platform_device *z2_snd_device; 195 196static int __init z2_init(void) 197{ 198 int ret; 199 200 if (!machine_is_zipit2()) 201 return -ENODEV; 202 203 z2_snd_device = platform_device_alloc("soc-audio", -1); 204 if (!z2_snd_device) 205 return -ENOMEM; 206 207 platform_set_drvdata(z2_snd_device, &snd_soc_z2); 208 ret = platform_device_add(z2_snd_device); 209 210 if (ret) 211 platform_device_put(z2_snd_device); 212 213 return ret; 214} 215 216static void __exit z2_exit(void) 217{ 218 platform_device_unregister(z2_snd_device); 219} 220 221module_init(z2_init); 222module_exit(z2_exit); 223 224MODULE_AUTHOR("Ken McGuire <kenm@desertweyr.com>, " 225 "Marek Vasut <marek.vasut@gmail.com>"); 226MODULE_DESCRIPTION("ALSA SoC ZipitZ2"); 227MODULE_LICENSE("GPL"); 228