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 codec DAI configuration */ 60 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | 61 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); 62 if (ret < 0) 63 return ret; 64 65 /* set cpu DAI configuration */ 66 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | 67 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); 68 if (ret < 0) 69 return ret; 70 71 /* set the codec system clock for DAC and ADC */ 72 ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk, 73 SND_SOC_CLOCK_IN); 74 if (ret < 0) 75 return ret; 76 77 /* set the I2S system clock as input (unused) */ 78 ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, 79 SND_SOC_CLOCK_IN); 80 if (ret < 0) 81 return ret; 82 83 return 0; 84} 85 86static struct snd_soc_jack hs_jack; 87 88/* Headset jack detection DAPM pins */ 89static struct snd_soc_jack_pin hs_jack_pins[] = { 90 { 91 .pin = "Mic Jack", 92 .mask = SND_JACK_MICROPHONE, 93 }, 94 { 95 .pin = "Headphone Jack", 96 .mask = SND_JACK_HEADPHONE, 97 }, 98 { 99 .pin = "Ext Spk", 100 .mask = SND_JACK_HEADPHONE, 101 .invert = 1 102 }, 103}; 104 105/* Headset jack detection gpios */ 106static struct snd_soc_jack_gpio hs_jack_gpios[] = { 107 { 108 .gpio = GPIO37_ZIPITZ2_HEADSET_DETECT, 109 .name = "hsdet-gpio", 110 .report = SND_JACK_HEADSET, 111 .debounce_time = 200, 112 .invert = 1, 113 }, 114}; 115 116/* z2 machine dapm widgets */ 117static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { 118 SND_SOC_DAPM_HP("Headphone Jack", NULL), 119 SND_SOC_DAPM_MIC("Mic Jack", NULL), 120 SND_SOC_DAPM_SPK("Ext Spk", NULL), 121 122 /* headset is a mic and mono headphone */ 123 SND_SOC_DAPM_HP("Headset Jack", NULL), 124}; 125 126/* Z2 machine audio_map */ 127static const struct snd_soc_dapm_route audio_map[] = { 128 129 /* headphone connected to LOUT1, ROUT1 */ 130 {"Headphone Jack", NULL, "LOUT1"}, 131 {"Headphone Jack", NULL, "ROUT1"}, 132 133 /* ext speaker connected to LOUT2, ROUT2 */ 134 {"Ext Spk", NULL , "ROUT2"}, 135 {"Ext Spk", NULL , "LOUT2"}, 136 137 /* mic is connected to R input 2 - with bias */ 138 {"RINPUT2", NULL, "Mic Bias"}, 139 {"Mic Bias", NULL, "Mic Jack"}, 140}; 141 142/* 143 * Logic for a wm8750 as connected on a Z2 Device 144 */ 145static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd) 146{ 147 struct snd_soc_codec *codec = rtd->codec; 148 struct snd_soc_dapm_context *dapm = &codec->dapm; 149 int ret; 150 151 /* NC codec pins */ 152 snd_soc_dapm_disable_pin(dapm, "LINPUT3"); 153 snd_soc_dapm_disable_pin(dapm, "RINPUT3"); 154 snd_soc_dapm_disable_pin(dapm, "OUT3"); 155 snd_soc_dapm_disable_pin(dapm, "MONO1"); 156 157 /* Add z2 specific widgets */ 158 snd_soc_dapm_new_controls(dapm, wm8750_dapm_widgets, 159 ARRAY_SIZE(wm8750_dapm_widgets)); 160 161 /* Set up z2 specific audio paths */ 162 snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); 163 164 ret = snd_soc_dapm_sync(dapm); 165 if (ret) 166 goto err; 167 168 /* Jack detection API stuff */ 169 ret = snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET, 170 &hs_jack); 171 if (ret) 172 goto err; 173 174 ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), 175 hs_jack_pins); 176 if (ret) 177 goto err; 178 179 ret = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios), 180 hs_jack_gpios); 181 if (ret) 182 goto err; 183 184 return 0; 185 186err: 187 return ret; 188} 189 190static struct snd_soc_ops z2_ops = { 191 .hw_params = z2_hw_params, 192}; 193 194/* z2 digital audio interface glue - connects codec <--> CPU */ 195static struct snd_soc_dai_link z2_dai = { 196 .name = "wm8750", 197 .stream_name = "WM8750", 198 .cpu_dai_name = "pxa2xx-i2s", 199 .codec_dai_name = "wm8750-hifi", 200 .platform_name = "pxa-pcm-audio", 201 .codec_name = "wm8750-codec.0-001b", 202 .init = z2_wm8750_init, 203 .ops = &z2_ops, 204}; 205 206/* z2 audio machine driver */ 207static struct snd_soc_card snd_soc_z2 = { 208 .name = "Z2", 209 .dai_link = &z2_dai, 210 .num_links = 1, 211}; 212 213static struct platform_device *z2_snd_device; 214 215static int __init z2_init(void) 216{ 217 int ret; 218 219 if (!machine_is_zipit2()) 220 return -ENODEV; 221 222 z2_snd_device = platform_device_alloc("soc-audio", -1); 223 if (!z2_snd_device) 224 return -ENOMEM; 225 226 platform_set_drvdata(z2_snd_device, &snd_soc_z2); 227 ret = platform_device_add(z2_snd_device); 228 229 if (ret) 230 platform_device_put(z2_snd_device); 231 232 return ret; 233} 234 235static void __exit z2_exit(void) 236{ 237 platform_device_unregister(z2_snd_device); 238} 239 240module_init(z2_init); 241module_exit(z2_exit); 242 243MODULE_AUTHOR("Ken McGuire <kenm@desertweyr.com>, " 244 "Marek Vasut <marek.vasut@gmail.com>"); 245MODULE_DESCRIPTION("ALSA SoC ZipitZ2"); 246MODULE_LICENSE("GPL"); 247