1/* 2 * ALSA SoC TWL6040 codec driver 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/module.h> 23#include <linux/moduleparam.h> 24#include <linux/init.h> 25#include <linux/delay.h> 26#include <linux/pm.h> 27#include <linux/i2c.h> 28#include <linux/gpio.h> 29#include <linux/platform_device.h> 30#include <linux/slab.h> 31#include <linux/i2c/twl.h> 32 33#include <sound/core.h> 34#include <sound/pcm.h> 35#include <sound/pcm_params.h> 36#include <sound/soc.h> 37#include <sound/soc-dapm.h> 38#include <sound/initval.h> 39#include <sound/tlv.h> 40 41#include "twl6040.h" 42 43#define TWL6040_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) 44#define TWL6040_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) 45 46/* codec private data */ 47struct twl6040_data { 48 struct snd_soc_codec codec; 49 int audpwron; 50 int naudint; 51 int codec_powered; 52 int pll; 53 int non_lp; 54 unsigned int sysclk; 55 struct snd_pcm_hw_constraint_list *sysclk_constraints; 56 struct completion ready; 57}; 58 59/* 60 * twl6040 register cache & default register settings 61 */ 62static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = { 63 0x00, /* not used 0x00 */ 64 0x4B, /* TWL6040_ASICID (ro) 0x01 */ 65 0x00, /* TWL6040_ASICREV (ro) 0x02 */ 66 0x00, /* TWL6040_INTID 0x03 */ 67 0x00, /* TWL6040_INTMR 0x04 */ 68 0x00, /* TWL6040_NCPCTRL 0x05 */ 69 0x00, /* TWL6040_LDOCTL 0x06 */ 70 0x60, /* TWL6040_HPPLLCTL 0x07 */ 71 0x00, /* TWL6040_LPPLLCTL 0x08 */ 72 0x4A, /* TWL6040_LPPLLDIV 0x09 */ 73 0x00, /* TWL6040_AMICBCTL 0x0A */ 74 0x00, /* TWL6040_DMICBCTL 0x0B */ 75 0x18, /* TWL6040_MICLCTL 0x0C - No input selected on Left Mic */ 76 0x18, /* TWL6040_MICRCTL 0x0D - No input selected on Right Mic */ 77 0x00, /* TWL6040_MICGAIN 0x0E */ 78 0x1B, /* TWL6040_LINEGAIN 0x0F */ 79 0x00, /* TWL6040_HSLCTL 0x10 */ 80 0x00, /* TWL6040_HSRCTL 0x11 */ 81 0x00, /* TWL6040_HSGAIN 0x12 */ 82 0x00, /* TWL6040_EARCTL 0x13 */ 83 0x00, /* TWL6040_HFLCTL 0x14 */ 84 0x00, /* TWL6040_HFLGAIN 0x15 */ 85 0x00, /* TWL6040_HFRCTL 0x16 */ 86 0x00, /* TWL6040_HFRGAIN 0x17 */ 87 0x00, /* TWL6040_VIBCTLL 0x18 */ 88 0x00, /* TWL6040_VIBDATL 0x19 */ 89 0x00, /* TWL6040_VIBCTLR 0x1A */ 90 0x00, /* TWL6040_VIBDATR 0x1B */ 91 0x00, /* TWL6040_HKCTL1 0x1C */ 92 0x00, /* TWL6040_HKCTL2 0x1D */ 93 0x00, /* TWL6040_GPOCTL 0x1E */ 94 0x00, /* TWL6040_ALB 0x1F */ 95 0x00, /* TWL6040_DLB 0x20 */ 96 0x00, /* not used 0x21 */ 97 0x00, /* not used 0x22 */ 98 0x00, /* not used 0x23 */ 99 0x00, /* not used 0x24 */ 100 0x00, /* not used 0x25 */ 101 0x00, /* not used 0x26 */ 102 0x00, /* not used 0x27 */ 103 0x00, /* TWL6040_TRIM1 0x28 */ 104 0x00, /* TWL6040_TRIM2 0x29 */ 105 0x00, /* TWL6040_TRIM3 0x2A */ 106 0x00, /* TWL6040_HSOTRIM 0x2B */ 107 0x00, /* TWL6040_HFOTRIM 0x2C */ 108 0x09, /* TWL6040_ACCCTL 0x2D */ 109 0x00, /* TWL6040_STATUS (ro) 0x2E */ 110}; 111 112/* 113 * twl6040 vio/gnd registers: 114 * registers under vio/gnd supply can be accessed 115 * before the power-up sequence, after NRESPWRON goes high 116 */ 117static const int twl6040_vio_reg[TWL6040_VIOREGNUM] = { 118 TWL6040_REG_ASICID, 119 TWL6040_REG_ASICREV, 120 TWL6040_REG_INTID, 121 TWL6040_REG_INTMR, 122 TWL6040_REG_NCPCTL, 123 TWL6040_REG_LDOCTL, 124 TWL6040_REG_AMICBCTL, 125 TWL6040_REG_DMICBCTL, 126 TWL6040_REG_HKCTL1, 127 TWL6040_REG_HKCTL2, 128 TWL6040_REG_GPOCTL, 129 TWL6040_REG_TRIM1, 130 TWL6040_REG_TRIM2, 131 TWL6040_REG_TRIM3, 132 TWL6040_REG_HSOTRIM, 133 TWL6040_REG_HFOTRIM, 134 TWL6040_REG_ACCCTL, 135 TWL6040_REG_STATUS, 136}; 137 138/* 139 * twl6040 vdd/vss registers: 140 * registers under vdd/vss supplies can only be accessed 141 * after the power-up sequence 142 */ 143static const int twl6040_vdd_reg[TWL6040_VDDREGNUM] = { 144 TWL6040_REG_HPPLLCTL, 145 TWL6040_REG_LPPLLCTL, 146 TWL6040_REG_LPPLLDIV, 147 TWL6040_REG_MICLCTL, 148 TWL6040_REG_MICRCTL, 149 TWL6040_REG_MICGAIN, 150 TWL6040_REG_LINEGAIN, 151 TWL6040_REG_HSLCTL, 152 TWL6040_REG_HSRCTL, 153 TWL6040_REG_HSGAIN, 154 TWL6040_REG_EARCTL, 155 TWL6040_REG_HFLCTL, 156 TWL6040_REG_HFLGAIN, 157 TWL6040_REG_HFRCTL, 158 TWL6040_REG_HFRGAIN, 159 TWL6040_REG_VIBCTLL, 160 TWL6040_REG_VIBDATL, 161 TWL6040_REG_VIBCTLR, 162 TWL6040_REG_VIBDATR, 163 TWL6040_REG_ALB, 164 TWL6040_REG_DLB, 165}; 166 167/* 168 * read twl6040 register cache 169 */ 170static inline unsigned int twl6040_read_reg_cache(struct snd_soc_codec *codec, 171 unsigned int reg) 172{ 173 u8 *cache = codec->reg_cache; 174 175 if (reg >= TWL6040_CACHEREGNUM) 176 return -EIO; 177 178 return cache[reg]; 179} 180 181/* 182 * write twl6040 register cache 183 */ 184static inline void twl6040_write_reg_cache(struct snd_soc_codec *codec, 185 u8 reg, u8 value) 186{ 187 u8 *cache = codec->reg_cache; 188 189 if (reg >= TWL6040_CACHEREGNUM) 190 return; 191 cache[reg] = value; 192} 193 194/* 195 * read from twl6040 hardware register 196 */ 197static int twl6040_read_reg_volatile(struct snd_soc_codec *codec, 198 unsigned int reg) 199{ 200 u8 value; 201 202 if (reg >= TWL6040_CACHEREGNUM) 203 return -EIO; 204 205 twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &value, reg); 206 twl6040_write_reg_cache(codec, reg, value); 207 208 return value; 209} 210 211/* 212 * write to the twl6040 register space 213 */ 214static int twl6040_write(struct snd_soc_codec *codec, 215 unsigned int reg, unsigned int value) 216{ 217 if (reg >= TWL6040_CACHEREGNUM) 218 return -EIO; 219 220 twl6040_write_reg_cache(codec, reg, value); 221 return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg); 222} 223 224static void twl6040_init_vio_regs(struct snd_soc_codec *codec) 225{ 226 u8 *cache = codec->reg_cache; 227 int reg, i; 228 229 /* allow registers to be accessed by i2c */ 230 twl6040_write(codec, TWL6040_REG_ACCCTL, cache[TWL6040_REG_ACCCTL]); 231 232 for (i = 0; i < TWL6040_VIOREGNUM; i++) { 233 reg = twl6040_vio_reg[i]; 234 /* skip read-only registers (ASICID, ASICREV, STATUS) */ 235 switch (reg) { 236 case TWL6040_REG_ASICID: 237 case TWL6040_REG_ASICREV: 238 case TWL6040_REG_STATUS: 239 continue; 240 default: 241 break; 242 } 243 twl6040_write(codec, reg, cache[reg]); 244 } 245} 246 247static void twl6040_init_vdd_regs(struct snd_soc_codec *codec) 248{ 249 u8 *cache = codec->reg_cache; 250 int reg, i; 251 252 for (i = 0; i < TWL6040_VDDREGNUM; i++) { 253 reg = twl6040_vdd_reg[i]; 254 twl6040_write(codec, reg, cache[reg]); 255 } 256} 257 258/* twl6040 codec manual power-up sequence */ 259static void twl6040_power_up(struct snd_soc_codec *codec) 260{ 261 u8 ncpctl, ldoctl, lppllctl, accctl; 262 263 ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL); 264 ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL); 265 lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); 266 accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL); 267 268 /* enable reference system */ 269 ldoctl |= TWL6040_REFENA; 270 twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); 271 msleep(10); 272 /* enable internal oscillator */ 273 ldoctl |= TWL6040_OSCENA; 274 twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); 275 udelay(10); 276 /* enable high-side ldo */ 277 ldoctl |= TWL6040_HSLDOENA; 278 twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); 279 udelay(244); 280 /* enable negative charge pump */ 281 ncpctl |= TWL6040_NCPENA | TWL6040_NCPOPEN; 282 twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl); 283 udelay(488); 284 /* enable low-side ldo */ 285 ldoctl |= TWL6040_LSLDOENA; 286 twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); 287 udelay(244); 288 /* enable low-power pll */ 289 lppllctl |= TWL6040_LPLLENA; 290 twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); 291 /* reset state machine */ 292 accctl |= TWL6040_RESETSPLIT; 293 twl6040_write(codec, TWL6040_REG_ACCCTL, accctl); 294 mdelay(5); 295 accctl &= ~TWL6040_RESETSPLIT; 296 twl6040_write(codec, TWL6040_REG_ACCCTL, accctl); 297 /* disable internal oscillator */ 298 ldoctl &= ~TWL6040_OSCENA; 299 twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); 300} 301 302/* twl6040 codec manual power-down sequence */ 303static void twl6040_power_down(struct snd_soc_codec *codec) 304{ 305 u8 ncpctl, ldoctl, lppllctl, accctl; 306 307 ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL); 308 ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL); 309 lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); 310 accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL); 311 312 /* enable internal oscillator */ 313 ldoctl |= TWL6040_OSCENA; 314 twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); 315 udelay(10); 316 /* disable low-power pll */ 317 lppllctl &= ~TWL6040_LPLLENA; 318 twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); 319 /* disable low-side ldo */ 320 ldoctl &= ~TWL6040_LSLDOENA; 321 twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); 322 udelay(244); 323 /* disable negative charge pump */ 324 ncpctl &= ~(TWL6040_NCPENA | TWL6040_NCPOPEN); 325 twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl); 326 udelay(488); 327 /* disable high-side ldo */ 328 ldoctl &= ~TWL6040_HSLDOENA; 329 twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); 330 udelay(244); 331 /* disable internal oscillator */ 332 ldoctl &= ~TWL6040_OSCENA; 333 twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); 334 /* disable reference system */ 335 ldoctl &= ~TWL6040_REFENA; 336 twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); 337 msleep(10); 338} 339 340/* set headset dac and driver power mode */ 341static int headset_power_mode(struct snd_soc_codec *codec, int high_perf) 342{ 343 int hslctl, hsrctl; 344 int mask = TWL6040_HSDRVMODEL | TWL6040_HSDACMODEL; 345 346 hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL); 347 hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL); 348 349 if (high_perf) { 350 hslctl &= ~mask; 351 hsrctl &= ~mask; 352 } else { 353 hslctl |= mask; 354 hsrctl |= mask; 355 } 356 357 twl6040_write(codec, TWL6040_REG_HSLCTL, hslctl); 358 twl6040_write(codec, TWL6040_REG_HSRCTL, hsrctl); 359 360 return 0; 361} 362 363static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w, 364 struct snd_kcontrol *kcontrol, int event) 365{ 366 msleep(1); 367 return 0; 368} 369 370static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w, 371 struct snd_kcontrol *kcontrol, int event) 372{ 373 struct snd_soc_codec *codec = w->codec; 374 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); 375 376 if (SND_SOC_DAPM_EVENT_ON(event)) 377 priv->non_lp++; 378 else 379 priv->non_lp--; 380 381 msleep(1); 382 383 return 0; 384} 385 386/* audio interrupt handler */ 387static irqreturn_t twl6040_naudint_handler(int irq, void *data) 388{ 389 struct snd_soc_codec *codec = data; 390 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); 391 u8 intid; 392 393 twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &intid, TWL6040_REG_INTID); 394 395 switch (intid) { 396 case TWL6040_THINT: 397 dev_alert(codec->dev, "die temp over-limit detection\n"); 398 break; 399 case TWL6040_PLUGINT: 400 case TWL6040_UNPLUGINT: 401 case TWL6040_HOOKINT: 402 break; 403 case TWL6040_HFINT: 404 dev_alert(codec->dev, "hf drivers over current detection\n"); 405 break; 406 case TWL6040_VIBINT: 407 dev_alert(codec->dev, "vib drivers over current detection\n"); 408 break; 409 case TWL6040_READYINT: 410 complete(&priv->ready); 411 break; 412 default: 413 dev_err(codec->dev, "unknown audio interrupt %d\n", intid); 414 break; 415 } 416 417 return IRQ_HANDLED; 418} 419 420/* 421 * MICATT volume control: 422 * from -6 to 0 dB in 6 dB steps 423 */ 424static DECLARE_TLV_DB_SCALE(mic_preamp_tlv, -600, 600, 0); 425 426/* 427 * MICGAIN volume control: 428 * from 6 to 30 dB in 6 dB steps 429 */ 430static DECLARE_TLV_DB_SCALE(mic_amp_tlv, 600, 600, 0); 431 432/* 433 * HSGAIN volume control: 434 * from -30 to 0 dB in 2 dB steps 435 */ 436static DECLARE_TLV_DB_SCALE(hs_tlv, -3000, 200, 0); 437 438/* 439 * HFGAIN volume control: 440 * from -52 to 6 dB in 2 dB steps 441 */ 442static DECLARE_TLV_DB_SCALE(hf_tlv, -5200, 200, 0); 443 444/* 445 * EPGAIN volume control: 446 * from -24 to 6 dB in 2 dB steps 447 */ 448static DECLARE_TLV_DB_SCALE(ep_tlv, -2400, 200, 0); 449 450/* Left analog microphone selection */ 451static const char *twl6040_amicl_texts[] = 452 {"Headset Mic", "Main Mic", "Aux/FM Left", "Off"}; 453 454/* Right analog microphone selection */ 455static const char *twl6040_amicr_texts[] = 456 {"Headset Mic", "Sub Mic", "Aux/FM Right", "Off"}; 457 458static const struct soc_enum twl6040_enum[] = { 459 SOC_ENUM_SINGLE(TWL6040_REG_MICLCTL, 3, 3, twl6040_amicl_texts), 460 SOC_ENUM_SINGLE(TWL6040_REG_MICRCTL, 3, 3, twl6040_amicr_texts), 461}; 462 463static const struct snd_kcontrol_new amicl_control = 464 SOC_DAPM_ENUM("Route", twl6040_enum[0]); 465 466static const struct snd_kcontrol_new amicr_control = 467 SOC_DAPM_ENUM("Route", twl6040_enum[1]); 468 469/* Headset DAC playback switches */ 470static const struct snd_kcontrol_new hsdacl_switch_controls = 471 SOC_DAPM_SINGLE("Switch", TWL6040_REG_HSLCTL, 5, 1, 0); 472 473static const struct snd_kcontrol_new hsdacr_switch_controls = 474 SOC_DAPM_SINGLE("Switch", TWL6040_REG_HSRCTL, 5, 1, 0); 475 476/* Handsfree DAC playback switches */ 477static const struct snd_kcontrol_new hfdacl_switch_controls = 478 SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFLCTL, 2, 1, 0); 479 480static const struct snd_kcontrol_new hfdacr_switch_controls = 481 SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 2, 1, 0); 482 483static const struct snd_kcontrol_new ep_driver_switch_controls = 484 SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0); 485 486static const struct snd_kcontrol_new twl6040_snd_controls[] = { 487 /* Capture gains */ 488 SOC_DOUBLE_TLV("Capture Preamplifier Volume", 489 TWL6040_REG_MICGAIN, 6, 7, 1, 1, mic_preamp_tlv), 490 SOC_DOUBLE_TLV("Capture Volume", 491 TWL6040_REG_MICGAIN, 0, 3, 4, 0, mic_amp_tlv), 492 493 /* Playback gains */ 494 SOC_DOUBLE_TLV("Headset Playback Volume", 495 TWL6040_REG_HSGAIN, 0, 4, 0xF, 1, hs_tlv), 496 SOC_DOUBLE_R_TLV("Handsfree Playback Volume", 497 TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv), 498 SOC_SINGLE_TLV("Earphone Playback Volume", 499 TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv), 500}; 501 502static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { 503 /* Inputs */ 504 SND_SOC_DAPM_INPUT("MAINMIC"), 505 SND_SOC_DAPM_INPUT("HSMIC"), 506 SND_SOC_DAPM_INPUT("SUBMIC"), 507 SND_SOC_DAPM_INPUT("AFML"), 508 SND_SOC_DAPM_INPUT("AFMR"), 509 510 /* Outputs */ 511 SND_SOC_DAPM_OUTPUT("HSOL"), 512 SND_SOC_DAPM_OUTPUT("HSOR"), 513 SND_SOC_DAPM_OUTPUT("HFL"), 514 SND_SOC_DAPM_OUTPUT("HFR"), 515 SND_SOC_DAPM_OUTPUT("EP"), 516 517 /* Analog input muxes for the capture amplifiers */ 518 SND_SOC_DAPM_MUX("Analog Left Capture Route", 519 SND_SOC_NOPM, 0, 0, &amicl_control), 520 SND_SOC_DAPM_MUX("Analog Right Capture Route", 521 SND_SOC_NOPM, 0, 0, &amicr_control), 522 523 /* Analog capture PGAs */ 524 SND_SOC_DAPM_PGA("MicAmpL", 525 TWL6040_REG_MICLCTL, 0, 0, NULL, 0), 526 SND_SOC_DAPM_PGA("MicAmpR", 527 TWL6040_REG_MICRCTL, 0, 0, NULL, 0), 528 529 /* ADCs */ 530 SND_SOC_DAPM_ADC("ADC Left", "Left Front Capture", 531 TWL6040_REG_MICLCTL, 2, 0), 532 SND_SOC_DAPM_ADC("ADC Right", "Right Front Capture", 533 TWL6040_REG_MICRCTL, 2, 0), 534 535 /* Microphone bias */ 536 SND_SOC_DAPM_MICBIAS("Headset Mic Bias", 537 TWL6040_REG_AMICBCTL, 0, 0), 538 SND_SOC_DAPM_MICBIAS("Main Mic Bias", 539 TWL6040_REG_AMICBCTL, 4, 0), 540 SND_SOC_DAPM_MICBIAS("Digital Mic1 Bias", 541 TWL6040_REG_DMICBCTL, 0, 0), 542 SND_SOC_DAPM_MICBIAS("Digital Mic2 Bias", 543 TWL6040_REG_DMICBCTL, 4, 0), 544 545 /* DACs */ 546 SND_SOC_DAPM_DAC_E("HSDAC Left", "Headset Playback", 547 TWL6040_REG_HSLCTL, 0, 0, 548 twl6040_hs_dac_event, 549 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), 550 SND_SOC_DAPM_DAC_E("HSDAC Right", "Headset Playback", 551 TWL6040_REG_HSRCTL, 0, 0, 552 twl6040_hs_dac_event, 553 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), 554 SND_SOC_DAPM_DAC_E("HFDAC Left", "Handsfree Playback", 555 TWL6040_REG_HFLCTL, 0, 0, 556 twl6040_power_mode_event, 557 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), 558 SND_SOC_DAPM_DAC_E("HFDAC Right", "Handsfree Playback", 559 TWL6040_REG_HFRCTL, 0, 0, 560 twl6040_power_mode_event, 561 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), 562 563 /* Analog playback switches */ 564 SND_SOC_DAPM_SWITCH("HSDAC Left Playback", 565 SND_SOC_NOPM, 0, 0, &hsdacl_switch_controls), 566 SND_SOC_DAPM_SWITCH("HSDAC Right Playback", 567 SND_SOC_NOPM, 0, 0, &hsdacr_switch_controls), 568 SND_SOC_DAPM_SWITCH("HFDAC Left Playback", 569 SND_SOC_NOPM, 0, 0, &hfdacl_switch_controls), 570 SND_SOC_DAPM_SWITCH("HFDAC Right Playback", 571 SND_SOC_NOPM, 0, 0, &hfdacr_switch_controls), 572 573 /* Analog playback drivers */ 574 SND_SOC_DAPM_PGA_E("Handsfree Left Driver", 575 TWL6040_REG_HFLCTL, 4, 0, NULL, 0, 576 twl6040_power_mode_event, 577 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), 578 SND_SOC_DAPM_PGA_E("Handsfree Right Driver", 579 TWL6040_REG_HFRCTL, 4, 0, NULL, 0, 580 twl6040_power_mode_event, 581 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), 582 SND_SOC_DAPM_PGA("Headset Left Driver", 583 TWL6040_REG_HSLCTL, 2, 0, NULL, 0), 584 SND_SOC_DAPM_PGA("Headset Right Driver", 585 TWL6040_REG_HSRCTL, 2, 0, NULL, 0), 586 SND_SOC_DAPM_SWITCH_E("Earphone Driver", 587 SND_SOC_NOPM, 0, 0, &ep_driver_switch_controls, 588 twl6040_power_mode_event, 589 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), 590 591 /* Analog playback PGAs */ 592 SND_SOC_DAPM_PGA("HFDAC Left PGA", 593 TWL6040_REG_HFLCTL, 1, 0, NULL, 0), 594 SND_SOC_DAPM_PGA("HFDAC Right PGA", 595 TWL6040_REG_HFRCTL, 1, 0, NULL, 0), 596 597}; 598 599static const struct snd_soc_dapm_route intercon[] = { 600 /* Capture path */ 601 {"Analog Left Capture Route", "Headset Mic", "HSMIC"}, 602 {"Analog Left Capture Route", "Main Mic", "MAINMIC"}, 603 {"Analog Left Capture Route", "Aux/FM Left", "AFML"}, 604 605 {"Analog Right Capture Route", "Headset Mic", "HSMIC"}, 606 {"Analog Right Capture Route", "Sub Mic", "SUBMIC"}, 607 {"Analog Right Capture Route", "Aux/FM Right", "AFMR"}, 608 609 {"MicAmpL", NULL, "Analog Left Capture Route"}, 610 {"MicAmpR", NULL, "Analog Right Capture Route"}, 611 612 {"ADC Left", NULL, "MicAmpL"}, 613 {"ADC Right", NULL, "MicAmpR"}, 614 615 /* Headset playback path */ 616 {"HSDAC Left Playback", "Switch", "HSDAC Left"}, 617 {"HSDAC Right Playback", "Switch", "HSDAC Right"}, 618 619 {"Headset Left Driver", NULL, "HSDAC Left Playback"}, 620 {"Headset Right Driver", NULL, "HSDAC Right Playback"}, 621 622 {"HSOL", NULL, "Headset Left Driver"}, 623 {"HSOR", NULL, "Headset Right Driver"}, 624 625 /* Earphone playback path */ 626 {"Earphone Driver", "Switch", "HSDAC Left"}, 627 {"EP", NULL, "Earphone Driver"}, 628 629 /* Handsfree playback path */ 630 {"HFDAC Left Playback", "Switch", "HFDAC Left"}, 631 {"HFDAC Right Playback", "Switch", "HFDAC Right"}, 632 633 {"HFDAC Left PGA", NULL, "HFDAC Left Playback"}, 634 {"HFDAC Right PGA", NULL, "HFDAC Right Playback"}, 635 636 {"Handsfree Left Driver", "Switch", "HFDAC Left PGA"}, 637 {"Handsfree Right Driver", "Switch", "HFDAC Right PGA"}, 638 639 {"HFL", NULL, "Handsfree Left Driver"}, 640 {"HFR", NULL, "Handsfree Right Driver"}, 641}; 642 643static int twl6040_add_widgets(struct snd_soc_codec *codec) 644{ 645 snd_soc_dapm_new_controls(codec, twl6040_dapm_widgets, 646 ARRAY_SIZE(twl6040_dapm_widgets)); 647 648 snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); 649 650 snd_soc_dapm_new_widgets(codec); 651 652 return 0; 653} 654 655static int twl6040_power_up_completion(struct snd_soc_codec *codec, 656 int naudint) 657{ 658 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); 659 int time_left; 660 u8 intid; 661 662 time_left = wait_for_completion_timeout(&priv->ready, 663 msecs_to_jiffies(48)); 664 665 if (!time_left) { 666 twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &intid, 667 TWL6040_REG_INTID); 668 if (!(intid & TWL6040_READYINT)) { 669 dev_err(codec->dev, "timeout waiting for READYINT\n"); 670 return -ETIMEDOUT; 671 } 672 } 673 674 priv->codec_powered = 1; 675 676 return 0; 677} 678 679static int twl6040_set_bias_level(struct snd_soc_codec *codec, 680 enum snd_soc_bias_level level) 681{ 682 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); 683 int audpwron = priv->audpwron; 684 int naudint = priv->naudint; 685 int ret; 686 687 switch (level) { 688 case SND_SOC_BIAS_ON: 689 break; 690 case SND_SOC_BIAS_PREPARE: 691 break; 692 case SND_SOC_BIAS_STANDBY: 693 if (priv->codec_powered) 694 break; 695 696 if (gpio_is_valid(audpwron)) { 697 /* use AUDPWRON line */ 698 gpio_set_value(audpwron, 1); 699 700 /* wait for power-up completion */ 701 ret = twl6040_power_up_completion(codec, naudint); 702 if (ret) 703 return ret; 704 705 /* sync registers updated during power-up sequence */ 706 twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL); 707 twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL); 708 twl6040_read_reg_volatile(codec, TWL6040_REG_LPPLLCTL); 709 } else { 710 /* use manual power-up sequence */ 711 twl6040_power_up(codec); 712 priv->codec_powered = 1; 713 } 714 715 /* initialize vdd/vss registers with reg_cache */ 716 twl6040_init_vdd_regs(codec); 717 break; 718 case SND_SOC_BIAS_OFF: 719 if (!priv->codec_powered) 720 break; 721 722 if (gpio_is_valid(audpwron)) { 723 /* use AUDPWRON line */ 724 gpio_set_value(audpwron, 0); 725 726 /* power-down sequence latency */ 727 udelay(500); 728 729 /* sync registers updated during power-down sequence */ 730 twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL); 731 twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL); 732 twl6040_write_reg_cache(codec, TWL6040_REG_LPPLLCTL, 733 0x00); 734 } else { 735 /* use manual power-down sequence */ 736 twl6040_power_down(codec); 737 } 738 739 priv->codec_powered = 0; 740 break; 741 } 742 743 codec->bias_level = level; 744 745 return 0; 746} 747 748/* set of rates for each pll: low-power and high-performance */ 749 750static unsigned int lp_rates[] = { 751 88200, 752 96000, 753}; 754 755static struct snd_pcm_hw_constraint_list lp_constraints = { 756 .count = ARRAY_SIZE(lp_rates), 757 .list = lp_rates, 758}; 759 760static unsigned int hp_rates[] = { 761 96000, 762}; 763 764static struct snd_pcm_hw_constraint_list hp_constraints = { 765 .count = ARRAY_SIZE(hp_rates), 766 .list = hp_rates, 767}; 768 769static int twl6040_startup(struct snd_pcm_substream *substream, 770 struct snd_soc_dai *dai) 771{ 772 struct snd_soc_pcm_runtime *rtd = substream->private_data; 773 struct snd_soc_device *socdev = rtd->socdev; 774 struct snd_soc_codec *codec = socdev->card->codec; 775 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); 776 777 if (!priv->sysclk) { 778 dev_err(codec->dev, 779 "no mclk configured, call set_sysclk() on init\n"); 780 return -EINVAL; 781 } 782 783 /* 784 * capture is not supported at 17.64 MHz, 785 * it's reserved for headset low-power playback scenario 786 */ 787 if ((priv->sysclk == 17640000) && substream->stream) { 788 dev_err(codec->dev, 789 "capture mode is not supported at %dHz\n", 790 priv->sysclk); 791 return -EINVAL; 792 } 793 794 snd_pcm_hw_constraint_list(substream->runtime, 0, 795 SNDRV_PCM_HW_PARAM_RATE, 796 priv->sysclk_constraints); 797 798 return 0; 799} 800 801static int twl6040_hw_params(struct snd_pcm_substream *substream, 802 struct snd_pcm_hw_params *params, 803 struct snd_soc_dai *dai) 804{ 805 struct snd_soc_pcm_runtime *rtd = substream->private_data; 806 struct snd_soc_device *socdev = rtd->socdev; 807 struct snd_soc_codec *codec = socdev->card->codec; 808 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); 809 u8 lppllctl; 810 int rate; 811 812 /* nothing to do for high-perf pll, it supports only 48 kHz */ 813 if (priv->pll == TWL6040_HPPLL_ID) 814 return 0; 815 816 lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); 817 818 rate = params_rate(params); 819 switch (rate) { 820 case 88200: 821 lppllctl |= TWL6040_LPLLFIN; 822 priv->sysclk = 17640000; 823 break; 824 case 96000: 825 lppllctl &= ~TWL6040_LPLLFIN; 826 priv->sysclk = 19200000; 827 break; 828 default: 829 dev_err(codec->dev, "unsupported rate %d\n", rate); 830 return -EINVAL; 831 } 832 833 twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); 834 835 return 0; 836} 837 838static int twl6040_trigger(struct snd_pcm_substream *substream, 839 int cmd, struct snd_soc_dai *dai) 840{ 841 struct snd_soc_pcm_runtime *rtd = substream->private_data; 842 struct snd_soc_device *socdev = rtd->socdev; 843 struct snd_soc_codec *codec = socdev->card->codec; 844 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); 845 846 switch (cmd) { 847 case SNDRV_PCM_TRIGGER_START: 848 case SNDRV_PCM_TRIGGER_RESUME: 849 /* 850 * low-power playback mode is restricted 851 * for headset path only 852 */ 853 if ((priv->sysclk == 17640000) && priv->non_lp) { 854 dev_err(codec->dev, 855 "some enabled paths aren't supported at %dHz\n", 856 priv->sysclk); 857 return -EPERM; 858 } 859 break; 860 default: 861 break; 862 } 863 864 return 0; 865} 866 867static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai, 868 int clk_id, unsigned int freq, int dir) 869{ 870 struct snd_soc_codec *codec = codec_dai->codec; 871 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); 872 u8 hppllctl, lppllctl; 873 874 hppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_HPPLLCTL); 875 lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); 876 877 switch (clk_id) { 878 case TWL6040_SYSCLK_SEL_LPPLL: 879 switch (freq) { 880 case 32768: 881 /* headset dac and driver must be in low-power mode */ 882 headset_power_mode(codec, 0); 883 884 /* clk32k input requires low-power pll */ 885 lppllctl |= TWL6040_LPLLENA; 886 twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); 887 mdelay(5); 888 lppllctl &= ~TWL6040_HPLLSEL; 889 twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); 890 hppllctl &= ~TWL6040_HPLLENA; 891 twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl); 892 break; 893 default: 894 dev_err(codec->dev, "unknown mclk freq %d\n", freq); 895 return -EINVAL; 896 } 897 898 /* lppll divider */ 899 switch (priv->sysclk) { 900 case 17640000: 901 lppllctl |= TWL6040_LPLLFIN; 902 break; 903 case 19200000: 904 lppllctl &= ~TWL6040_LPLLFIN; 905 break; 906 default: 907 /* sysclk not yet configured */ 908 lppllctl &= ~TWL6040_LPLLFIN; 909 priv->sysclk = 19200000; 910 break; 911 } 912 913 twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); 914 915 priv->pll = TWL6040_LPPLL_ID; 916 priv->sysclk_constraints = &lp_constraints; 917 break; 918 case TWL6040_SYSCLK_SEL_HPPLL: 919 hppllctl &= ~TWL6040_MCLK_MSK; 920 921 switch (freq) { 922 case 12000000: 923 /* mclk input, pll enabled */ 924 hppllctl |= TWL6040_MCLK_12000KHZ | 925 TWL6040_HPLLSQRBP | 926 TWL6040_HPLLENA; 927 break; 928 case 19200000: 929 /* mclk input, pll disabled */ 930 hppllctl |= TWL6040_MCLK_19200KHZ | 931 TWL6040_HPLLSQRENA | 932 TWL6040_HPLLBP; 933 break; 934 case 26000000: 935 /* mclk input, pll enabled */ 936 hppllctl |= TWL6040_MCLK_26000KHZ | 937 TWL6040_HPLLSQRBP | 938 TWL6040_HPLLENA; 939 break; 940 case 38400000: 941 /* clk slicer, pll disabled */ 942 hppllctl |= TWL6040_MCLK_38400KHZ | 943 TWL6040_HPLLSQRENA | 944 TWL6040_HPLLBP; 945 break; 946 default: 947 dev_err(codec->dev, "unknown mclk freq %d\n", freq); 948 return -EINVAL; 949 } 950 951 /* headset dac and driver must be in high-performance mode */ 952 headset_power_mode(codec, 1); 953 954 twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl); 955 udelay(500); 956 lppllctl |= TWL6040_HPLLSEL; 957 twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); 958 lppllctl &= ~TWL6040_LPLLENA; 959 twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); 960 961 /* high-performance pll can provide only 19.2 MHz */ 962 priv->pll = TWL6040_HPPLL_ID; 963 priv->sysclk = 19200000; 964 priv->sysclk_constraints = &hp_constraints; 965 break; 966 default: 967 dev_err(codec->dev, "unknown clk_id %d\n", clk_id); 968 return -EINVAL; 969 } 970 971 return 0; 972} 973 974static struct snd_soc_dai_ops twl6040_dai_ops = { 975 .startup = twl6040_startup, 976 .hw_params = twl6040_hw_params, 977 .trigger = twl6040_trigger, 978 .set_sysclk = twl6040_set_dai_sysclk, 979}; 980 981struct snd_soc_dai twl6040_dai = { 982 .name = "twl6040", 983 .playback = { 984 .stream_name = "Playback", 985 .channels_min = 1, 986 .channels_max = 4, 987 .rates = TWL6040_RATES, 988 .formats = TWL6040_FORMATS, 989 }, 990 .capture = { 991 .stream_name = "Capture", 992 .channels_min = 1, 993 .channels_max = 2, 994 .rates = TWL6040_RATES, 995 .formats = TWL6040_FORMATS, 996 }, 997 .ops = &twl6040_dai_ops, 998}; 999EXPORT_SYMBOL_GPL(twl6040_dai); 1000 1001#ifdef CONFIG_PM 1002static int twl6040_suspend(struct platform_device *pdev, pm_message_t state) 1003{ 1004 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 1005 struct snd_soc_codec *codec = socdev->card->codec; 1006 1007 twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); 1008 1009 return 0; 1010} 1011 1012static int twl6040_resume(struct platform_device *pdev) 1013{ 1014 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 1015 struct snd_soc_codec *codec = socdev->card->codec; 1016 1017 twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 1018 1019 return 0; 1020} 1021#else 1022#define twl6040_suspend NULL 1023#define twl6040_resume NULL 1024#endif 1025 1026static struct snd_soc_codec *twl6040_codec; 1027 1028static int twl6040_probe(struct platform_device *pdev) 1029{ 1030 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 1031 struct snd_soc_codec *codec; 1032 int ret = 0; 1033 1034 BUG_ON(!twl6040_codec); 1035 1036 codec = twl6040_codec; 1037 socdev->card->codec = codec; 1038 1039 /* register pcms */ 1040 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); 1041 if (ret < 0) { 1042 dev_err(&pdev->dev, "failed to create pcms\n"); 1043 return ret; 1044 } 1045 1046 snd_soc_add_controls(codec, twl6040_snd_controls, 1047 ARRAY_SIZE(twl6040_snd_controls)); 1048 twl6040_add_widgets(codec); 1049 1050 if (ret < 0) { 1051 dev_err(&pdev->dev, "failed to register card\n"); 1052 goto card_err; 1053 } 1054 1055 return ret; 1056 1057card_err: 1058 snd_soc_free_pcms(socdev); 1059 snd_soc_dapm_free(socdev); 1060 return ret; 1061} 1062 1063static int twl6040_remove(struct platform_device *pdev) 1064{ 1065 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 1066 struct snd_soc_codec *codec = socdev->card->codec; 1067 1068 twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); 1069 snd_soc_free_pcms(socdev); 1070 snd_soc_dapm_free(socdev); 1071 kfree(codec); 1072 1073 return 0; 1074} 1075 1076struct snd_soc_codec_device soc_codec_dev_twl6040 = { 1077 .probe = twl6040_probe, 1078 .remove = twl6040_remove, 1079 .suspend = twl6040_suspend, 1080 .resume = twl6040_resume, 1081}; 1082EXPORT_SYMBOL_GPL(soc_codec_dev_twl6040); 1083 1084static int __devinit twl6040_codec_probe(struct platform_device *pdev) 1085{ 1086 struct twl4030_codec_data *twl_codec = pdev->dev.platform_data; 1087 struct snd_soc_codec *codec; 1088 struct twl6040_data *priv; 1089 int audpwron, naudint; 1090 int ret = 0; 1091 1092 priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL); 1093 if (priv == NULL) 1094 return -ENOMEM; 1095 1096 if (twl_codec) { 1097 audpwron = twl_codec->audpwron_gpio; 1098 naudint = twl_codec->naudint_irq; 1099 } else { 1100 audpwron = -EINVAL; 1101 naudint = 0; 1102 } 1103 1104 priv->audpwron = audpwron; 1105 priv->naudint = naudint; 1106 1107 codec = &priv->codec; 1108 codec->dev = &pdev->dev; 1109 twl6040_dai.dev = &pdev->dev; 1110 1111 codec->name = "twl6040"; 1112 codec->owner = THIS_MODULE; 1113 codec->read = twl6040_read_reg_cache; 1114 codec->write = twl6040_write; 1115 codec->set_bias_level = twl6040_set_bias_level; 1116 snd_soc_codec_set_drvdata(codec, priv); 1117 codec->dai = &twl6040_dai; 1118 codec->num_dai = 1; 1119 codec->reg_cache_size = ARRAY_SIZE(twl6040_reg); 1120 codec->reg_cache = kmemdup(twl6040_reg, sizeof(twl6040_reg), 1121 GFP_KERNEL); 1122 if (codec->reg_cache == NULL) { 1123 ret = -ENOMEM; 1124 goto cache_err; 1125 } 1126 1127 mutex_init(&codec->mutex); 1128 INIT_LIST_HEAD(&codec->dapm_widgets); 1129 INIT_LIST_HEAD(&codec->dapm_paths); 1130 init_completion(&priv->ready); 1131 1132 if (gpio_is_valid(audpwron)) { 1133 ret = gpio_request(audpwron, "audpwron"); 1134 if (ret) 1135 goto gpio1_err; 1136 1137 ret = gpio_direction_output(audpwron, 0); 1138 if (ret) 1139 goto gpio2_err; 1140 1141 priv->codec_powered = 0; 1142 } 1143 1144 if (naudint) { 1145 /* audio interrupt */ 1146 ret = request_threaded_irq(naudint, NULL, 1147 twl6040_naudint_handler, 1148 IRQF_TRIGGER_LOW | IRQF_ONESHOT, 1149 "twl6040_codec", codec); 1150 if (ret) 1151 goto gpio2_err; 1152 } else { 1153 if (gpio_is_valid(audpwron)) { 1154 /* enable only codec ready interrupt */ 1155 twl6040_write_reg_cache(codec, TWL6040_REG_INTMR, 1156 ~TWL6040_READYMSK & TWL6040_ALLINT_MSK); 1157 } else { 1158 /* no interrupts at all */ 1159 twl6040_write_reg_cache(codec, TWL6040_REG_INTMR, 1160 TWL6040_ALLINT_MSK); 1161 } 1162 } 1163 1164 /* init vio registers */ 1165 twl6040_init_vio_regs(codec); 1166 1167 /* power on device */ 1168 ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 1169 if (ret) 1170 goto irq_err; 1171 1172 ret = snd_soc_register_codec(codec); 1173 if (ret) 1174 goto reg_err; 1175 1176 twl6040_codec = codec; 1177 1178 ret = snd_soc_register_dai(&twl6040_dai); 1179 if (ret) 1180 goto dai_err; 1181 1182 return 0; 1183 1184dai_err: 1185 snd_soc_unregister_codec(codec); 1186 twl6040_codec = NULL; 1187reg_err: 1188 twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); 1189irq_err: 1190 if (naudint) 1191 free_irq(naudint, codec); 1192gpio2_err: 1193 if (gpio_is_valid(audpwron)) 1194 gpio_free(audpwron); 1195gpio1_err: 1196 kfree(codec->reg_cache); 1197cache_err: 1198 kfree(priv); 1199 return ret; 1200} 1201 1202static int __devexit twl6040_codec_remove(struct platform_device *pdev) 1203{ 1204 struct twl6040_data *priv = snd_soc_codec_get_drvdata(twl6040_codec); 1205 int audpwron = priv->audpwron; 1206 int naudint = priv->naudint; 1207 1208 if (gpio_is_valid(audpwron)) 1209 gpio_free(audpwron); 1210 1211 if (naudint) 1212 free_irq(naudint, twl6040_codec); 1213 1214 snd_soc_unregister_dai(&twl6040_dai); 1215 snd_soc_unregister_codec(twl6040_codec); 1216 1217 kfree(twl6040_codec); 1218 twl6040_codec = NULL; 1219 1220 return 0; 1221} 1222 1223static struct platform_driver twl6040_codec_driver = { 1224 .driver = { 1225 .name = "twl6040_codec", 1226 .owner = THIS_MODULE, 1227 }, 1228 .probe = twl6040_codec_probe, 1229 .remove = __devexit_p(twl6040_codec_remove), 1230}; 1231 1232static int __init twl6040_codec_init(void) 1233{ 1234 return platform_driver_register(&twl6040_codec_driver); 1235} 1236module_init(twl6040_codec_init); 1237 1238static void __exit twl6040_codec_exit(void) 1239{ 1240 platform_driver_unregister(&twl6040_codec_driver); 1241} 1242module_exit(twl6040_codec_exit); 1243 1244MODULE_DESCRIPTION("ASoC TWL6040 codec driver"); 1245MODULE_AUTHOR("Misael Lopez Cruz"); 1246MODULE_LICENSE("GPL"); 1247