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