1/* 2 * mfld_machine.c - ASoc Machine driver for Intel Medfield MID platform 3 * 4 * Copyright (C) 2010 Intel Corp 5 * Author: Vinod Koul <vinod.koul@intel.com> 6 * Author: Harsha Priya <priya.harsha@intel.com> 7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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 as published by 11 * the Free Software Foundation; version 2 of the License. 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 along 19 * with this program; if not, write to the Free Software Foundation, Inc., 20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 21 * 22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 23 */ 24 25#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 26 27#include <linux/init.h> 28#include <linux/device.h> 29#include <linux/slab.h> 30#include <linux/io.h> 31#include <linux/module.h> 32#include <sound/pcm.h> 33#include <sound/pcm_params.h> 34#include <sound/soc.h> 35#include <sound/jack.h> 36#include "../codecs/sn95031.h" 37 38#define MID_MONO 1 39#define MID_STEREO 2 40#define MID_MAX_CAP 5 41#define MFLD_JACK_INSERT 0x04 42 43enum soc_mic_bias_zones { 44 MFLD_MV_START = 0, 45 /* mic bias volutage range for Headphones*/ 46 MFLD_MV_HP = 400, 47 /* mic bias volutage range for American Headset*/ 48 MFLD_MV_AM_HS = 650, 49 /* mic bias volutage range for Headset*/ 50 MFLD_MV_HS = 2000, 51 MFLD_MV_UNDEFINED, 52}; 53 54static unsigned int hs_switch; 55static unsigned int lo_dac; 56static struct snd_soc_codec *mfld_codec; 57 58struct mfld_mc_private { 59 void __iomem *int_base; 60 u8 interrupt_status; 61}; 62 63struct snd_soc_jack mfld_jack; 64 65/*Headset jack detection DAPM pins */ 66static struct snd_soc_jack_pin mfld_jack_pins[] = { 67 { 68 .pin = "Headphones", 69 .mask = SND_JACK_HEADPHONE, 70 }, 71 { 72 .pin = "AMIC1", 73 .mask = SND_JACK_MICROPHONE, 74 }, 75}; 76 77/* jack detection voltage zones */ 78static struct snd_soc_jack_zone mfld_zones[] = { 79 {MFLD_MV_START, MFLD_MV_AM_HS, SND_JACK_HEADPHONE}, 80 {MFLD_MV_AM_HS, MFLD_MV_HS, SND_JACK_HEADSET}, 81}; 82 83/* sound card controls */ 84static const char *headset_switch_text[] = {"Earpiece", "Headset"}; 85 86static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"}; 87 88static const struct soc_enum headset_enum = 89 SOC_ENUM_SINGLE_EXT(2, headset_switch_text); 90 91static const struct soc_enum lo_enum = 92 SOC_ENUM_SINGLE_EXT(4, lo_text); 93 94static int headset_get_switch(struct snd_kcontrol *kcontrol, 95 struct snd_ctl_elem_value *ucontrol) 96{ 97 ucontrol->value.integer.value[0] = hs_switch; 98 return 0; 99} 100 101static int headset_set_switch(struct snd_kcontrol *kcontrol, 102 struct snd_ctl_elem_value *ucontrol) 103{ 104 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 105 struct snd_soc_dapm_context *dapm = &card->dapm; 106 107 if (ucontrol->value.integer.value[0] == hs_switch) 108 return 0; 109 110 snd_soc_dapm_mutex_lock(dapm); 111 112 if (ucontrol->value.integer.value[0]) { 113 pr_debug("hs_set HS path\n"); 114 snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones"); 115 snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT"); 116 } else { 117 pr_debug("hs_set EP path\n"); 118 snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones"); 119 snd_soc_dapm_enable_pin_unlocked(dapm, "EPOUT"); 120 } 121 122 snd_soc_dapm_sync_unlocked(dapm); 123 124 snd_soc_dapm_mutex_unlock(dapm); 125 126 hs_switch = ucontrol->value.integer.value[0]; 127 128 return 0; 129} 130 131static void lo_enable_out_pins(struct snd_soc_dapm_context *dapm) 132{ 133 snd_soc_dapm_enable_pin_unlocked(dapm, "IHFOUTL"); 134 snd_soc_dapm_enable_pin_unlocked(dapm, "IHFOUTR"); 135 snd_soc_dapm_enable_pin_unlocked(dapm, "LINEOUTL"); 136 snd_soc_dapm_enable_pin_unlocked(dapm, "LINEOUTR"); 137 snd_soc_dapm_enable_pin_unlocked(dapm, "VIB1OUT"); 138 snd_soc_dapm_enable_pin_unlocked(dapm, "VIB2OUT"); 139 if (hs_switch) { 140 snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones"); 141 snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT"); 142 } else { 143 snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones"); 144 snd_soc_dapm_enable_pin_unlocked(dapm, "EPOUT"); 145 } 146} 147 148static int lo_get_switch(struct snd_kcontrol *kcontrol, 149 struct snd_ctl_elem_value *ucontrol) 150{ 151 ucontrol->value.integer.value[0] = lo_dac; 152 return 0; 153} 154 155static int lo_set_switch(struct snd_kcontrol *kcontrol, 156 struct snd_ctl_elem_value *ucontrol) 157{ 158 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 159 struct snd_soc_dapm_context *dapm = &card->dapm; 160 161 if (ucontrol->value.integer.value[0] == lo_dac) 162 return 0; 163 164 snd_soc_dapm_mutex_lock(dapm); 165 166 /* we dont want to work with last state of lineout so just enable all 167 * pins and then disable pins not required 168 */ 169 lo_enable_out_pins(dapm); 170 171 switch (ucontrol->value.integer.value[0]) { 172 case 0: 173 pr_debug("set vibra path\n"); 174 snd_soc_dapm_disable_pin_unlocked(dapm, "VIB1OUT"); 175 snd_soc_dapm_disable_pin_unlocked(dapm, "VIB2OUT"); 176 snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0); 177 break; 178 179 case 1: 180 pr_debug("set hs path\n"); 181 snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones"); 182 snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT"); 183 snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x22); 184 break; 185 186 case 2: 187 pr_debug("set spkr path\n"); 188 snd_soc_dapm_disable_pin_unlocked(dapm, "IHFOUTL"); 189 snd_soc_dapm_disable_pin_unlocked(dapm, "IHFOUTR"); 190 snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x44); 191 break; 192 193 case 3: 194 pr_debug("set null path\n"); 195 snd_soc_dapm_disable_pin_unlocked(dapm, "LINEOUTL"); 196 snd_soc_dapm_disable_pin_unlocked(dapm, "LINEOUTR"); 197 snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x66); 198 break; 199 } 200 201 snd_soc_dapm_sync_unlocked(dapm); 202 203 snd_soc_dapm_mutex_unlock(dapm); 204 205 lo_dac = ucontrol->value.integer.value[0]; 206 return 0; 207} 208 209static const struct snd_kcontrol_new mfld_snd_controls[] = { 210 SOC_ENUM_EXT("Playback Switch", headset_enum, 211 headset_get_switch, headset_set_switch), 212 SOC_ENUM_EXT("Lineout Mux", lo_enum, 213 lo_get_switch, lo_set_switch), 214}; 215 216static const struct snd_soc_dapm_widget mfld_widgets[] = { 217 SND_SOC_DAPM_HP("Headphones", NULL), 218 SND_SOC_DAPM_MIC("Mic", NULL), 219}; 220 221static const struct snd_soc_dapm_route mfld_map[] = { 222 {"Headphones", NULL, "HPOUTR"}, 223 {"Headphones", NULL, "HPOUTL"}, 224 {"Mic", NULL, "AMIC1"}, 225}; 226 227static void mfld_jack_check(unsigned int intr_status) 228{ 229 struct mfld_jack_data jack_data; 230 231 if (!mfld_codec) 232 return; 233 234 jack_data.mfld_jack = &mfld_jack; 235 jack_data.intr_id = intr_status; 236 237 sn95031_jack_detection(mfld_codec, &jack_data); 238 /* TODO: add american headset detection post gpiolib support */ 239} 240 241static int mfld_init(struct snd_soc_pcm_runtime *runtime) 242{ 243 struct snd_soc_dapm_context *dapm = &runtime->card->dapm; 244 int ret_val; 245 246 /* default is earpiece pin, userspace sets it explcitly */ 247 snd_soc_dapm_disable_pin(dapm, "Headphones"); 248 /* default is lineout NC, userspace sets it explcitly */ 249 snd_soc_dapm_disable_pin(dapm, "LINEOUTL"); 250 snd_soc_dapm_disable_pin(dapm, "LINEOUTR"); 251 lo_dac = 3; 252 hs_switch = 0; 253 /* we dont use linein in this so set to NC */ 254 snd_soc_dapm_disable_pin(dapm, "LINEINL"); 255 snd_soc_dapm_disable_pin(dapm, "LINEINR"); 256 257 /* Headset and button jack detection */ 258 ret_val = snd_soc_card_jack_new(runtime->card, 259 "Intel(R) MID Audio Jack", SND_JACK_HEADSET | 260 SND_JACK_BTN_0 | SND_JACK_BTN_1, &mfld_jack, 261 mfld_jack_pins, ARRAY_SIZE(mfld_jack_pins)); 262 if (ret_val) { 263 pr_err("jack creation failed\n"); 264 return ret_val; 265 } 266 267 ret_val = snd_soc_jack_add_zones(&mfld_jack, 268 ARRAY_SIZE(mfld_zones), mfld_zones); 269 if (ret_val) { 270 pr_err("adding jack zones failed\n"); 271 return ret_val; 272 } 273 274 mfld_codec = runtime->codec; 275 276 /* we want to check if anything is inserted at boot, 277 * so send a fake event to codec and it will read adc 278 * to find if anything is there or not */ 279 mfld_jack_check(MFLD_JACK_INSERT); 280 return ret_val; 281} 282 283static struct snd_soc_dai_link mfld_msic_dailink[] = { 284 { 285 .name = "Medfield Headset", 286 .stream_name = "Headset", 287 .cpu_dai_name = "Headset-cpu-dai", 288 .codec_dai_name = "SN95031 Headset", 289 .codec_name = "sn95031", 290 .platform_name = "sst-platform", 291 .init = mfld_init, 292 }, 293 { 294 .name = "Medfield Speaker", 295 .stream_name = "Speaker", 296 .cpu_dai_name = "Speaker-cpu-dai", 297 .codec_dai_name = "SN95031 Speaker", 298 .codec_name = "sn95031", 299 .platform_name = "sst-platform", 300 .init = NULL, 301 }, 302 { 303 .name = "Medfield Vibra", 304 .stream_name = "Vibra1", 305 .cpu_dai_name = "Vibra1-cpu-dai", 306 .codec_dai_name = "SN95031 Vibra1", 307 .codec_name = "sn95031", 308 .platform_name = "sst-platform", 309 .init = NULL, 310 }, 311 { 312 .name = "Medfield Haptics", 313 .stream_name = "Vibra2", 314 .cpu_dai_name = "Vibra2-cpu-dai", 315 .codec_dai_name = "SN95031 Vibra2", 316 .codec_name = "sn95031", 317 .platform_name = "sst-platform", 318 .init = NULL, 319 }, 320 { 321 .name = "Medfield Compress", 322 .stream_name = "Speaker", 323 .cpu_dai_name = "Compress-cpu-dai", 324 .codec_dai_name = "SN95031 Speaker", 325 .codec_name = "sn95031", 326 .platform_name = "sst-platform", 327 .init = NULL, 328 }, 329}; 330 331/* SoC card */ 332static struct snd_soc_card snd_soc_card_mfld = { 333 .name = "medfield_audio", 334 .owner = THIS_MODULE, 335 .dai_link = mfld_msic_dailink, 336 .num_links = ARRAY_SIZE(mfld_msic_dailink), 337 338 .controls = mfld_snd_controls, 339 .num_controls = ARRAY_SIZE(mfld_snd_controls), 340 .dapm_widgets = mfld_widgets, 341 .num_dapm_widgets = ARRAY_SIZE(mfld_widgets), 342 .dapm_routes = mfld_map, 343 .num_dapm_routes = ARRAY_SIZE(mfld_map), 344}; 345 346static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev) 347{ 348 struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev; 349 350 memcpy_fromio(&mc_private->interrupt_status, 351 ((void *)(mc_private->int_base)), 352 sizeof(u8)); 353 return IRQ_WAKE_THREAD; 354} 355 356static irqreturn_t snd_mfld_jack_detection(int irq, void *data) 357{ 358 struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data; 359 360 mfld_jack_check(mc_drv_ctx->interrupt_status); 361 362 return IRQ_HANDLED; 363} 364 365static int snd_mfld_mc_probe(struct platform_device *pdev) 366{ 367 int ret_val = 0, irq; 368 struct mfld_mc_private *mc_drv_ctx; 369 struct resource *irq_mem; 370 371 pr_debug("snd_mfld_mc_probe called\n"); 372 373 /* retrive the irq number */ 374 irq = platform_get_irq(pdev, 0); 375 376 /* audio interrupt base of SRAM location where 377 * interrupts are stored by System FW */ 378 mc_drv_ctx = devm_kzalloc(&pdev->dev, sizeof(*mc_drv_ctx), GFP_ATOMIC); 379 if (!mc_drv_ctx) { 380 pr_err("allocation failed\n"); 381 return -ENOMEM; 382 } 383 384 irq_mem = platform_get_resource_byname( 385 pdev, IORESOURCE_MEM, "IRQ_BASE"); 386 if (!irq_mem) { 387 pr_err("no mem resource given\n"); 388 return -ENODEV; 389 } 390 mc_drv_ctx->int_base = devm_ioremap_nocache(&pdev->dev, irq_mem->start, 391 resource_size(irq_mem)); 392 if (!mc_drv_ctx->int_base) { 393 pr_err("Mapping of cache failed\n"); 394 return -ENOMEM; 395 } 396 /* register for interrupt */ 397 ret_val = devm_request_threaded_irq(&pdev->dev, irq, 398 snd_mfld_jack_intr_handler, 399 snd_mfld_jack_detection, 400 IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx); 401 if (ret_val) { 402 pr_err("cannot register IRQ\n"); 403 return ret_val; 404 } 405 /* register the soc card */ 406 snd_soc_card_mfld.dev = &pdev->dev; 407 ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_mfld); 408 if (ret_val) { 409 pr_debug("snd_soc_register_card failed %d\n", ret_val); 410 return ret_val; 411 } 412 platform_set_drvdata(pdev, mc_drv_ctx); 413 pr_debug("successfully exited probe\n"); 414 return 0; 415} 416 417static struct platform_driver snd_mfld_mc_driver = { 418 .driver = { 419 .name = "msic_audio", 420 }, 421 .probe = snd_mfld_mc_probe, 422}; 423 424module_platform_driver(snd_mfld_mc_driver); 425 426MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver"); 427MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); 428MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); 429MODULE_LICENSE("GPL v2"); 430MODULE_ALIAS("platform:msic-audio"); 431