Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.1-rc6 620 lines 15 kB view raw
1/* 2 * MFD driver for TWL6040 audio device 3 * 4 * Authors: Misael Lopez Cruz <misael.lopez@ti.com> 5 * Jorge Eduardo Candelaria <jorge.candelaria@ti.com> 6 * Peter Ujfalusi <peter.ujfalusi@ti.com> 7 * 8 * Copyright: (C) 2011 Texas Instruments, Inc. 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 * 14 * This program is distributed in the hope that it will be useful, but 15 * WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 22 * 02110-1301 USA 23 * 24 */ 25 26#include <linux/module.h> 27#include <linux/types.h> 28#include <linux/slab.h> 29#include <linux/kernel.h> 30#include <linux/platform_device.h> 31#include <linux/gpio.h> 32#include <linux/delay.h> 33#include <linux/i2c/twl.h> 34#include <linux/mfd/core.h> 35#include <linux/mfd/twl6040.h> 36 37static struct platform_device *twl6040_dev; 38 39int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg) 40{ 41 int ret; 42 u8 val = 0; 43 44 mutex_lock(&twl6040->io_mutex); 45 ret = twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &val, reg); 46 if (ret < 0) { 47 mutex_unlock(&twl6040->io_mutex); 48 return ret; 49 } 50 mutex_unlock(&twl6040->io_mutex); 51 52 return val; 53} 54EXPORT_SYMBOL(twl6040_reg_read); 55 56int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val) 57{ 58 int ret; 59 60 mutex_lock(&twl6040->io_mutex); 61 ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg); 62 mutex_unlock(&twl6040->io_mutex); 63 64 return ret; 65} 66EXPORT_SYMBOL(twl6040_reg_write); 67 68int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask) 69{ 70 int ret; 71 u8 val; 72 73 mutex_lock(&twl6040->io_mutex); 74 ret = twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &val, reg); 75 if (ret) 76 goto out; 77 78 val |= mask; 79 ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg); 80out: 81 mutex_unlock(&twl6040->io_mutex); 82 return ret; 83} 84EXPORT_SYMBOL(twl6040_set_bits); 85 86int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask) 87{ 88 int ret; 89 u8 val; 90 91 mutex_lock(&twl6040->io_mutex); 92 ret = twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &val, reg); 93 if (ret) 94 goto out; 95 96 val &= ~mask; 97 ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg); 98out: 99 mutex_unlock(&twl6040->io_mutex); 100 return ret; 101} 102EXPORT_SYMBOL(twl6040_clear_bits); 103 104/* twl6040 codec manual power-up sequence */ 105static int twl6040_power_up(struct twl6040 *twl6040) 106{ 107 u8 ldoctl, ncpctl, lppllctl; 108 int ret; 109 110 /* enable high-side LDO, reference system and internal oscillator */ 111 ldoctl = TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA; 112 ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 113 if (ret) 114 return ret; 115 usleep_range(10000, 10500); 116 117 /* enable negative charge pump */ 118 ncpctl = TWL6040_NCPENA; 119 ret = twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl); 120 if (ret) 121 goto ncp_err; 122 usleep_range(1000, 1500); 123 124 /* enable low-side LDO */ 125 ldoctl |= TWL6040_LSLDOENA; 126 ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 127 if (ret) 128 goto lsldo_err; 129 usleep_range(1000, 1500); 130 131 /* enable low-power PLL */ 132 lppllctl = TWL6040_LPLLENA; 133 ret = twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); 134 if (ret) 135 goto lppll_err; 136 usleep_range(5000, 5500); 137 138 /* disable internal oscillator */ 139 ldoctl &= ~TWL6040_OSCENA; 140 ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 141 if (ret) 142 goto osc_err; 143 144 return 0; 145 146osc_err: 147 lppllctl &= ~TWL6040_LPLLENA; 148 twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); 149lppll_err: 150 ldoctl &= ~TWL6040_LSLDOENA; 151 twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 152lsldo_err: 153 ncpctl &= ~TWL6040_NCPENA; 154 twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl); 155ncp_err: 156 ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA); 157 twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 158 159 return ret; 160} 161 162/* twl6040 manual power-down sequence */ 163static void twl6040_power_down(struct twl6040 *twl6040) 164{ 165 u8 ncpctl, ldoctl, lppllctl; 166 167 ncpctl = twl6040_reg_read(twl6040, TWL6040_REG_NCPCTL); 168 ldoctl = twl6040_reg_read(twl6040, TWL6040_REG_LDOCTL); 169 lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL); 170 171 /* enable internal oscillator */ 172 ldoctl |= TWL6040_OSCENA; 173 twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 174 usleep_range(1000, 1500); 175 176 /* disable low-power PLL */ 177 lppllctl &= ~TWL6040_LPLLENA; 178 twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); 179 180 /* disable low-side LDO */ 181 ldoctl &= ~TWL6040_LSLDOENA; 182 twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 183 184 /* disable negative charge pump */ 185 ncpctl &= ~TWL6040_NCPENA; 186 twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl); 187 188 /* disable high-side LDO, reference system and internal oscillator */ 189 ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA); 190 twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); 191} 192 193static irqreturn_t twl6040_naudint_handler(int irq, void *data) 194{ 195 struct twl6040 *twl6040 = data; 196 u8 intid, status; 197 198 intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); 199 200 if (intid & TWL6040_READYINT) 201 complete(&twl6040->ready); 202 203 if (intid & TWL6040_THINT) { 204 status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS); 205 if (status & TWL6040_TSHUTDET) { 206 dev_warn(&twl6040_dev->dev, 207 "Thermal shutdown, powering-off"); 208 twl6040_power(twl6040, 0); 209 } else { 210 dev_warn(&twl6040_dev->dev, 211 "Leaving thermal shutdown, powering-on"); 212 twl6040_power(twl6040, 1); 213 } 214 } 215 216 return IRQ_HANDLED; 217} 218 219static int twl6040_power_up_completion(struct twl6040 *twl6040, 220 int naudint) 221{ 222 int time_left; 223 u8 intid; 224 225 time_left = wait_for_completion_timeout(&twl6040->ready, 226 msecs_to_jiffies(144)); 227 if (!time_left) { 228 intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); 229 if (!(intid & TWL6040_READYINT)) { 230 dev_err(&twl6040_dev->dev, 231 "timeout waiting for READYINT\n"); 232 return -ETIMEDOUT; 233 } 234 } 235 236 return 0; 237} 238 239int twl6040_power(struct twl6040 *twl6040, int on) 240{ 241 int audpwron = twl6040->audpwron; 242 int naudint = twl6040->irq; 243 int ret = 0; 244 245 mutex_lock(&twl6040->mutex); 246 247 if (on) { 248 /* already powered-up */ 249 if (twl6040->power_count++) 250 goto out; 251 252 if (gpio_is_valid(audpwron)) { 253 /* use AUDPWRON line */ 254 gpio_set_value(audpwron, 1); 255 /* wait for power-up completion */ 256 ret = twl6040_power_up_completion(twl6040, naudint); 257 if (ret) { 258 dev_err(&twl6040_dev->dev, 259 "automatic power-down failed\n"); 260 twl6040->power_count = 0; 261 goto out; 262 } 263 } else { 264 /* use manual power-up sequence */ 265 ret = twl6040_power_up(twl6040); 266 if (ret) { 267 dev_err(&twl6040_dev->dev, 268 "manual power-up failed\n"); 269 twl6040->power_count = 0; 270 goto out; 271 } 272 } 273 /* Default PLL configuration after power up */ 274 twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL; 275 twl6040->sysclk = 19200000; 276 } else { 277 /* already powered-down */ 278 if (!twl6040->power_count) { 279 dev_err(&twl6040_dev->dev, 280 "device is already powered-off\n"); 281 ret = -EPERM; 282 goto out; 283 } 284 285 if (--twl6040->power_count) 286 goto out; 287 288 if (gpio_is_valid(audpwron)) { 289 /* use AUDPWRON line */ 290 gpio_set_value(audpwron, 0); 291 292 /* power-down sequence latency */ 293 usleep_range(500, 700); 294 } else { 295 /* use manual power-down sequence */ 296 twl6040_power_down(twl6040); 297 } 298 twl6040->sysclk = 0; 299 } 300 301out: 302 mutex_unlock(&twl6040->mutex); 303 return ret; 304} 305EXPORT_SYMBOL(twl6040_power); 306 307int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, 308 unsigned int freq_in, unsigned int freq_out) 309{ 310 u8 hppllctl, lppllctl; 311 int ret = 0; 312 313 mutex_lock(&twl6040->mutex); 314 315 hppllctl = twl6040_reg_read(twl6040, TWL6040_REG_HPPLLCTL); 316 lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL); 317 318 switch (pll_id) { 319 case TWL6040_SYSCLK_SEL_LPPLL: 320 /* low-power PLL divider */ 321 switch (freq_out) { 322 case 17640000: 323 lppllctl |= TWL6040_LPLLFIN; 324 break; 325 case 19200000: 326 lppllctl &= ~TWL6040_LPLLFIN; 327 break; 328 default: 329 dev_err(&twl6040_dev->dev, 330 "freq_out %d not supported\n", freq_out); 331 ret = -EINVAL; 332 goto pll_out; 333 } 334 twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); 335 336 switch (freq_in) { 337 case 32768: 338 lppllctl |= TWL6040_LPLLENA; 339 twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, 340 lppllctl); 341 mdelay(5); 342 lppllctl &= ~TWL6040_HPLLSEL; 343 twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, 344 lppllctl); 345 hppllctl &= ~TWL6040_HPLLENA; 346 twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL, 347 hppllctl); 348 break; 349 default: 350 dev_err(&twl6040_dev->dev, 351 "freq_in %d not supported\n", freq_in); 352 ret = -EINVAL; 353 goto pll_out; 354 } 355 break; 356 case TWL6040_SYSCLK_SEL_HPPLL: 357 /* high-performance PLL can provide only 19.2 MHz */ 358 if (freq_out != 19200000) { 359 dev_err(&twl6040_dev->dev, 360 "freq_out %d not supported\n", freq_out); 361 ret = -EINVAL; 362 goto pll_out; 363 } 364 365 hppllctl &= ~TWL6040_MCLK_MSK; 366 367 switch (freq_in) { 368 case 12000000: 369 /* PLL enabled, active mode */ 370 hppllctl |= TWL6040_MCLK_12000KHZ | 371 TWL6040_HPLLENA; 372 break; 373 case 19200000: 374 /* 375 * PLL disabled 376 * (enable PLL if MCLK jitter quality 377 * doesn't meet specification) 378 */ 379 hppllctl |= TWL6040_MCLK_19200KHZ; 380 break; 381 case 26000000: 382 /* PLL enabled, active mode */ 383 hppllctl |= TWL6040_MCLK_26000KHZ | 384 TWL6040_HPLLENA; 385 break; 386 case 38400000: 387 /* PLL enabled, active mode */ 388 hppllctl |= TWL6040_MCLK_38400KHZ | 389 TWL6040_HPLLENA; 390 break; 391 default: 392 dev_err(&twl6040_dev->dev, 393 "freq_in %d not supported\n", freq_in); 394 ret = -EINVAL; 395 goto pll_out; 396 } 397 398 /* enable clock slicer to ensure input waveform is square */ 399 hppllctl |= TWL6040_HPLLSQRENA; 400 401 twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL, hppllctl); 402 usleep_range(500, 700); 403 lppllctl |= TWL6040_HPLLSEL; 404 twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); 405 lppllctl &= ~TWL6040_LPLLENA; 406 twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); 407 break; 408 default: 409 dev_err(&twl6040_dev->dev, "unknown pll id %d\n", pll_id); 410 ret = -EINVAL; 411 goto pll_out; 412 } 413 414 twl6040->sysclk = freq_out; 415 twl6040->pll = pll_id; 416 417pll_out: 418 mutex_unlock(&twl6040->mutex); 419 return ret; 420} 421EXPORT_SYMBOL(twl6040_set_pll); 422 423int twl6040_get_pll(struct twl6040 *twl6040) 424{ 425 if (twl6040->power_count) 426 return twl6040->pll; 427 else 428 return -ENODEV; 429} 430EXPORT_SYMBOL(twl6040_get_pll); 431 432unsigned int twl6040_get_sysclk(struct twl6040 *twl6040) 433{ 434 return twl6040->sysclk; 435} 436EXPORT_SYMBOL(twl6040_get_sysclk); 437 438static struct resource twl6040_vibra_rsrc[] = { 439 { 440 .flags = IORESOURCE_IRQ, 441 }, 442}; 443 444static struct resource twl6040_codec_rsrc[] = { 445 { 446 .flags = IORESOURCE_IRQ, 447 }, 448}; 449 450static int __devinit twl6040_probe(struct platform_device *pdev) 451{ 452 struct twl4030_audio_data *pdata = pdev->dev.platform_data; 453 struct twl6040 *twl6040; 454 struct mfd_cell *cell = NULL; 455 int ret, children = 0; 456 457 if (!pdata) { 458 dev_err(&pdev->dev, "Platform data is missing\n"); 459 return -EINVAL; 460 } 461 462 /* In order to operate correctly we need valid interrupt config */ 463 if (!pdata->naudint_irq || !pdata->irq_base) { 464 dev_err(&pdev->dev, "Invalid IRQ configuration\n"); 465 return -EINVAL; 466 } 467 468 twl6040 = kzalloc(sizeof(struct twl6040), GFP_KERNEL); 469 if (!twl6040) 470 return -ENOMEM; 471 472 platform_set_drvdata(pdev, twl6040); 473 474 twl6040_dev = pdev; 475 twl6040->dev = &pdev->dev; 476 twl6040->audpwron = pdata->audpwron_gpio; 477 twl6040->irq = pdata->naudint_irq; 478 twl6040->irq_base = pdata->irq_base; 479 480 mutex_init(&twl6040->mutex); 481 mutex_init(&twl6040->io_mutex); 482 init_completion(&twl6040->ready); 483 484 twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV); 485 486 if (gpio_is_valid(twl6040->audpwron)) { 487 ret = gpio_request(twl6040->audpwron, "audpwron"); 488 if (ret) 489 goto gpio1_err; 490 491 ret = gpio_direction_output(twl6040->audpwron, 0); 492 if (ret) 493 goto gpio2_err; 494 } 495 496 /* ERRATA: Automatic power-up is not possible in ES1.0 */ 497 if (twl6040->rev == TWL6040_REV_ES1_0) 498 twl6040->audpwron = -EINVAL; 499 500 /* codec interrupt */ 501 ret = twl6040_irq_init(twl6040); 502 if (ret) 503 goto gpio2_err; 504 505 ret = request_threaded_irq(twl6040->irq_base + TWL6040_IRQ_READY, 506 NULL, twl6040_naudint_handler, 0, 507 "twl6040_irq_ready", twl6040); 508 if (ret) { 509 dev_err(twl6040->dev, "READY IRQ request failed: %d\n", 510 ret); 511 goto irq_err; 512 } 513 514 /* dual-access registers controlled by I2C only */ 515 twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL); 516 517 if (pdata->codec) { 518 int irq = twl6040->irq_base + TWL6040_IRQ_PLUG; 519 520 cell = &twl6040->cells[children]; 521 cell->name = "twl6040-codec"; 522 twl6040_codec_rsrc[0].start = irq; 523 twl6040_codec_rsrc[0].end = irq; 524 cell->resources = twl6040_codec_rsrc; 525 cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc); 526 cell->platform_data = pdata->codec; 527 cell->pdata_size = sizeof(*pdata->codec); 528 children++; 529 } 530 531 if (pdata->vibra) { 532 int irq = twl6040->irq_base + TWL6040_IRQ_VIB; 533 534 cell = &twl6040->cells[children]; 535 cell->name = "twl6040-vibra"; 536 twl6040_vibra_rsrc[0].start = irq; 537 twl6040_vibra_rsrc[0].end = irq; 538 cell->resources = twl6040_vibra_rsrc; 539 cell->num_resources = ARRAY_SIZE(twl6040_vibra_rsrc); 540 541 cell->platform_data = pdata->vibra; 542 cell->pdata_size = sizeof(*pdata->vibra); 543 children++; 544 } 545 546 if (children) { 547 ret = mfd_add_devices(&pdev->dev, pdev->id, twl6040->cells, 548 children, NULL, 0); 549 if (ret) 550 goto mfd_err; 551 } else { 552 dev_err(&pdev->dev, "No platform data found for children\n"); 553 ret = -ENODEV; 554 goto mfd_err; 555 } 556 557 return 0; 558 559mfd_err: 560 free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040); 561irq_err: 562 twl6040_irq_exit(twl6040); 563gpio2_err: 564 if (gpio_is_valid(twl6040->audpwron)) 565 gpio_free(twl6040->audpwron); 566gpio1_err: 567 platform_set_drvdata(pdev, NULL); 568 kfree(twl6040); 569 twl6040_dev = NULL; 570 return ret; 571} 572 573static int __devexit twl6040_remove(struct platform_device *pdev) 574{ 575 struct twl6040 *twl6040 = platform_get_drvdata(pdev); 576 577 if (twl6040->power_count) 578 twl6040_power(twl6040, 0); 579 580 if (gpio_is_valid(twl6040->audpwron)) 581 gpio_free(twl6040->audpwron); 582 583 free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040); 584 twl6040_irq_exit(twl6040); 585 586 mfd_remove_devices(&pdev->dev); 587 platform_set_drvdata(pdev, NULL); 588 kfree(twl6040); 589 twl6040_dev = NULL; 590 591 return 0; 592} 593 594static struct platform_driver twl6040_driver = { 595 .probe = twl6040_probe, 596 .remove = __devexit_p(twl6040_remove), 597 .driver = { 598 .owner = THIS_MODULE, 599 .name = "twl6040", 600 }, 601}; 602 603static int __devinit twl6040_init(void) 604{ 605 return platform_driver_register(&twl6040_driver); 606} 607module_init(twl6040_init); 608 609static void __devexit twl6040_exit(void) 610{ 611 platform_driver_unregister(&twl6040_driver); 612} 613 614module_exit(twl6040_exit); 615 616MODULE_DESCRIPTION("TWL6040 MFD"); 617MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>"); 618MODULE_AUTHOR("Jorge Eduardo Candelaria <jorge.candelaria@ti.com>"); 619MODULE_LICENSE("GPL"); 620MODULE_ALIAS("platform:twl6040");