1/* 2 * sdp3430.c -- SoC audio for TI OMAP3430 SDP 3 * 4 * Author: Misael Lopez Cruz <x0052729@ti.com> 5 * 6 * Based on: 7 * Author: Steve Sakoman <steve@sakoman.com> 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License 11 * version 2 as published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, but 14 * WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 21 * 02110-1301 USA 22 * 23 */ 24 25#include <linux/clk.h> 26#include <linux/platform_device.h> 27#include <linux/i2c/twl.h> 28#include <sound/core.h> 29#include <sound/pcm.h> 30#include <sound/soc.h> 31#include <sound/soc-dapm.h> 32#include <sound/jack.h> 33 34#include <asm/mach-types.h> 35#include <mach/hardware.h> 36#include <mach/gpio.h> 37#include <plat/mcbsp.h> 38 39#include "omap-mcbsp.h" 40#include "omap-pcm.h" 41#include "../codecs/twl4030.h" 42 43/* TWL4030 PMBR1 Register */ 44#define TWL4030_INTBR_PMBR1 0x0D 45/* TWL4030 PMBR1 Register GPIO6 mux bit */ 46#define TWL4030_GPIO6_PWM0_MUTE(value) (value << 2) 47 48static struct snd_soc_card snd_soc_sdp3430; 49 50static int sdp3430_hw_params(struct snd_pcm_substream *substream, 51 struct snd_pcm_hw_params *params) 52{ 53 struct snd_soc_pcm_runtime *rtd = substream->private_data; 54 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; 55 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 56 int ret; 57 58 /* Set codec DAI configuration */ 59 ret = snd_soc_dai_set_fmt(codec_dai, 60 SND_SOC_DAIFMT_I2S | 61 SND_SOC_DAIFMT_NB_NF | 62 SND_SOC_DAIFMT_CBM_CFM); 63 if (ret < 0) { 64 printk(KERN_ERR "can't set codec DAI configuration\n"); 65 return ret; 66 } 67 68 /* Set cpu DAI configuration */ 69 ret = snd_soc_dai_set_fmt(cpu_dai, 70 SND_SOC_DAIFMT_I2S | 71 SND_SOC_DAIFMT_NB_NF | 72 SND_SOC_DAIFMT_CBM_CFM); 73 if (ret < 0) { 74 printk(KERN_ERR "can't set cpu DAI configuration\n"); 75 return ret; 76 } 77 78 /* Set the codec system clock for DAC and ADC */ 79 ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000, 80 SND_SOC_CLOCK_IN); 81 if (ret < 0) { 82 printk(KERN_ERR "can't set codec system clock\n"); 83 return ret; 84 } 85 86 return 0; 87} 88 89static struct snd_soc_ops sdp3430_ops = { 90 .hw_params = sdp3430_hw_params, 91}; 92 93static int sdp3430_hw_voice_params(struct snd_pcm_substream *substream, 94 struct snd_pcm_hw_params *params) 95{ 96 struct snd_soc_pcm_runtime *rtd = substream->private_data; 97 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; 98 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 99 int ret; 100 101 /* Set codec DAI configuration */ 102 ret = snd_soc_dai_set_fmt(codec_dai, 103 SND_SOC_DAIFMT_DSP_A | 104 SND_SOC_DAIFMT_IB_NF | 105 SND_SOC_DAIFMT_CBM_CFM); 106 if (ret) { 107 printk(KERN_ERR "can't set codec DAI configuration\n"); 108 return ret; 109 } 110 111 /* Set cpu DAI configuration */ 112 ret = snd_soc_dai_set_fmt(cpu_dai, 113 SND_SOC_DAIFMT_DSP_A | 114 SND_SOC_DAIFMT_IB_NF | 115 SND_SOC_DAIFMT_CBM_CFM); 116 if (ret < 0) { 117 printk(KERN_ERR "can't set cpu DAI configuration\n"); 118 return ret; 119 } 120 121 /* Set the codec system clock for DAC and ADC */ 122 ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000, 123 SND_SOC_CLOCK_IN); 124 if (ret < 0) { 125 printk(KERN_ERR "can't set codec system clock\n"); 126 return ret; 127 } 128 129 return 0; 130} 131 132static struct snd_soc_ops sdp3430_voice_ops = { 133 .hw_params = sdp3430_hw_voice_params, 134}; 135 136/* Headset jack */ 137static struct snd_soc_jack hs_jack; 138 139/* Headset jack detection DAPM pins */ 140static struct snd_soc_jack_pin hs_jack_pins[] = { 141 { 142 .pin = "Headset Mic", 143 .mask = SND_JACK_MICROPHONE, 144 }, 145 { 146 .pin = "Headset Stereophone", 147 .mask = SND_JACK_HEADPHONE, 148 }, 149}; 150 151/* Headset jack detection gpios */ 152static struct snd_soc_jack_gpio hs_jack_gpios[] = { 153 { 154 .gpio = (OMAP_MAX_GPIO_LINES + 2), 155 .name = "hsdet-gpio", 156 .report = SND_JACK_HEADSET, 157 .debounce_time = 200, 158 }, 159}; 160 161/* SDP3430 machine DAPM */ 162static const struct snd_soc_dapm_widget sdp3430_twl4030_dapm_widgets[] = { 163 SND_SOC_DAPM_MIC("Ext Mic", NULL), 164 SND_SOC_DAPM_SPK("Ext Spk", NULL), 165 SND_SOC_DAPM_MIC("Headset Mic", NULL), 166 SND_SOC_DAPM_HP("Headset Stereophone", NULL), 167}; 168 169static const struct snd_soc_dapm_route audio_map[] = { 170 /* External Mics: MAINMIC, SUBMIC with bias*/ 171 {"MAINMIC", NULL, "Mic Bias 1"}, 172 {"SUBMIC", NULL, "Mic Bias 2"}, 173 {"Mic Bias 1", NULL, "Ext Mic"}, 174 {"Mic Bias 2", NULL, "Ext Mic"}, 175 176 /* External Speakers: HFL, HFR */ 177 {"Ext Spk", NULL, "HFL"}, 178 {"Ext Spk", NULL, "HFR"}, 179 180 /* Headset Mic: HSMIC with bias */ 181 {"HSMIC", NULL, "Headset Mic Bias"}, 182 {"Headset Mic Bias", NULL, "Headset Mic"}, 183 184 /* Headset Stereophone (Headphone): HSOL, HSOR */ 185 {"Headset Stereophone", NULL, "HSOL"}, 186 {"Headset Stereophone", NULL, "HSOR"}, 187}; 188 189static int sdp3430_twl4030_init(struct snd_soc_codec *codec) 190{ 191 int ret; 192 193 /* Add SDP3430 specific widgets */ 194 ret = snd_soc_dapm_new_controls(codec, sdp3430_twl4030_dapm_widgets, 195 ARRAY_SIZE(sdp3430_twl4030_dapm_widgets)); 196 if (ret) 197 return ret; 198 199 /* Set up SDP3430 specific audio path audio_map */ 200 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); 201 202 /* SDP3430 connected pins */ 203 snd_soc_dapm_enable_pin(codec, "Ext Mic"); 204 snd_soc_dapm_enable_pin(codec, "Ext Spk"); 205 snd_soc_dapm_disable_pin(codec, "Headset Mic"); 206 snd_soc_dapm_disable_pin(codec, "Headset Stereophone"); 207 208 /* TWL4030 not connected pins */ 209 snd_soc_dapm_nc_pin(codec, "AUXL"); 210 snd_soc_dapm_nc_pin(codec, "AUXR"); 211 snd_soc_dapm_nc_pin(codec, "CARKITMIC"); 212 snd_soc_dapm_nc_pin(codec, "DIGIMIC0"); 213 snd_soc_dapm_nc_pin(codec, "DIGIMIC1"); 214 215 snd_soc_dapm_nc_pin(codec, "OUTL"); 216 snd_soc_dapm_nc_pin(codec, "OUTR"); 217 snd_soc_dapm_nc_pin(codec, "EARPIECE"); 218 snd_soc_dapm_nc_pin(codec, "PREDRIVEL"); 219 snd_soc_dapm_nc_pin(codec, "PREDRIVER"); 220 snd_soc_dapm_nc_pin(codec, "CARKITL"); 221 snd_soc_dapm_nc_pin(codec, "CARKITR"); 222 223 ret = snd_soc_dapm_sync(codec); 224 if (ret) 225 return ret; 226 227 /* Headset jack detection */ 228 ret = snd_soc_jack_new(&snd_soc_sdp3430, "Headset Jack", 229 SND_JACK_HEADSET, &hs_jack); 230 if (ret) 231 return ret; 232 233 ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), 234 hs_jack_pins); 235 if (ret) 236 return ret; 237 238 ret = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios), 239 hs_jack_gpios); 240 241 return ret; 242} 243 244static int sdp3430_twl4030_voice_init(struct snd_soc_codec *codec) 245{ 246 unsigned short reg; 247 248 /* Enable voice interface */ 249 reg = codec->read(codec, TWL4030_REG_VOICE_IF); 250 reg |= TWL4030_VIF_DIN_EN | TWL4030_VIF_DOUT_EN | TWL4030_VIF_EN; 251 codec->write(codec, TWL4030_REG_VOICE_IF, reg); 252 253 return 0; 254} 255 256 257/* Digital audio interface glue - connects codec <--> CPU */ 258static struct snd_soc_dai_link sdp3430_dai[] = { 259 { 260 .name = "TWL4030 I2S", 261 .stream_name = "TWL4030 Audio", 262 .cpu_dai = &omap_mcbsp_dai[0], 263 .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI], 264 .init = sdp3430_twl4030_init, 265 .ops = &sdp3430_ops, 266 }, 267 { 268 .name = "TWL4030 PCM", 269 .stream_name = "TWL4030 Voice", 270 .cpu_dai = &omap_mcbsp_dai[1], 271 .codec_dai = &twl4030_dai[TWL4030_DAI_VOICE], 272 .init = sdp3430_twl4030_voice_init, 273 .ops = &sdp3430_voice_ops, 274 }, 275}; 276 277/* Audio machine driver */ 278static struct snd_soc_card snd_soc_sdp3430 = { 279 .name = "SDP3430", 280 .platform = &omap_soc_platform, 281 .dai_link = sdp3430_dai, 282 .num_links = ARRAY_SIZE(sdp3430_dai), 283}; 284 285/* twl4030 setup */ 286static struct twl4030_setup_data twl4030_setup = { 287 .ramp_delay_value = 3, 288 .sysclk = 26000, 289 .hs_extmute = 1, 290}; 291 292/* Audio subsystem */ 293static struct snd_soc_device sdp3430_snd_devdata = { 294 .card = &snd_soc_sdp3430, 295 .codec_dev = &soc_codec_dev_twl4030, 296 .codec_data = &twl4030_setup, 297}; 298 299static struct platform_device *sdp3430_snd_device; 300 301static int __init sdp3430_soc_init(void) 302{ 303 int ret; 304 u8 pin_mux; 305 306 if (!machine_is_omap_3430sdp()) { 307 pr_debug("Not SDP3430!\n"); 308 return -ENODEV; 309 } 310 printk(KERN_INFO "SDP3430 SoC init\n"); 311 312 sdp3430_snd_device = platform_device_alloc("soc-audio", -1); 313 if (!sdp3430_snd_device) { 314 printk(KERN_ERR "Platform device allocation failed\n"); 315 return -ENOMEM; 316 } 317 318 platform_set_drvdata(sdp3430_snd_device, &sdp3430_snd_devdata); 319 sdp3430_snd_devdata.dev = &sdp3430_snd_device->dev; 320 *(unsigned int *)sdp3430_dai[0].cpu_dai->private_data = 1; /* McBSP2 */ 321 *(unsigned int *)sdp3430_dai[1].cpu_dai->private_data = 2; /* McBSP3 */ 322 323 /* Set TWL4030 GPIO6 as EXTMUTE signal */ 324 twl_i2c_read_u8(TWL4030_MODULE_INTBR, &pin_mux, 325 TWL4030_INTBR_PMBR1); 326 pin_mux &= ~TWL4030_GPIO6_PWM0_MUTE(0x03); 327 pin_mux |= TWL4030_GPIO6_PWM0_MUTE(0x02); 328 twl_i2c_write_u8(TWL4030_MODULE_INTBR, pin_mux, 329 TWL4030_INTBR_PMBR1); 330 331 ret = platform_device_add(sdp3430_snd_device); 332 if (ret) 333 goto err1; 334 335 return 0; 336 337err1: 338 printk(KERN_ERR "Unable to add platform device\n"); 339 platform_device_put(sdp3430_snd_device); 340 341 return ret; 342} 343module_init(sdp3430_soc_init); 344 345static void __exit sdp3430_soc_exit(void) 346{ 347 snd_soc_jack_free_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios), 348 hs_jack_gpios); 349 350 platform_device_unregister(sdp3430_snd_device); 351} 352module_exit(sdp3430_soc_exit); 353 354MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>"); 355MODULE_DESCRIPTION("ALSA SoC SDP3430"); 356MODULE_LICENSE("GPL"); 357