1/* 2 * mcpdm.c -- McPDM interface driver 3 * 4 * Author: Jorge Eduardo Candelaria <x0107209@ti.com> 5 * Copyright (C) 2009 - Texas Instruments, Inc. 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * version 2 as published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, but 12 * WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 19 * 02110-1301 USA 20 * 21 */ 22 23#include <linux/module.h> 24#include <linux/init.h> 25#include <linux/device.h> 26#include <linux/platform_device.h> 27#include <linux/wait.h> 28#include <linux/slab.h> 29#include <linux/interrupt.h> 30#include <linux/err.h> 31#include <linux/clk.h> 32#include <linux/delay.h> 33#include <linux/io.h> 34#include <linux/irq.h> 35 36#include "mcpdm.h" 37 38static struct omap_mcpdm *mcpdm; 39 40static inline void omap_mcpdm_write(u16 reg, u32 val) 41{ 42 __raw_writel(val, mcpdm->io_base + reg); 43} 44 45static inline int omap_mcpdm_read(u16 reg) 46{ 47 return __raw_readl(mcpdm->io_base + reg); 48} 49 50static void omap_mcpdm_reg_dump(void) 51{ 52 dev_dbg(mcpdm->dev, "***********************\n"); 53 dev_dbg(mcpdm->dev, "IRQSTATUS_RAW: 0x%04x\n", 54 omap_mcpdm_read(MCPDM_IRQSTATUS_RAW)); 55 dev_dbg(mcpdm->dev, "IRQSTATUS: 0x%04x\n", 56 omap_mcpdm_read(MCPDM_IRQSTATUS)); 57 dev_dbg(mcpdm->dev, "IRQENABLE_SET: 0x%04x\n", 58 omap_mcpdm_read(MCPDM_IRQENABLE_SET)); 59 dev_dbg(mcpdm->dev, "IRQENABLE_CLR: 0x%04x\n", 60 omap_mcpdm_read(MCPDM_IRQENABLE_CLR)); 61 dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n", 62 omap_mcpdm_read(MCPDM_IRQWAKE_EN)); 63 dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n", 64 omap_mcpdm_read(MCPDM_DMAENABLE_SET)); 65 dev_dbg(mcpdm->dev, "DMAENABLE_CLR: 0x%04x\n", 66 omap_mcpdm_read(MCPDM_DMAENABLE_CLR)); 67 dev_dbg(mcpdm->dev, "DMAWAKEEN: 0x%04x\n", 68 omap_mcpdm_read(MCPDM_DMAWAKEEN)); 69 dev_dbg(mcpdm->dev, "CTRL: 0x%04x\n", 70 omap_mcpdm_read(MCPDM_CTRL)); 71 dev_dbg(mcpdm->dev, "DN_DATA: 0x%04x\n", 72 omap_mcpdm_read(MCPDM_DN_DATA)); 73 dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n", 74 omap_mcpdm_read(MCPDM_UP_DATA)); 75 dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n", 76 omap_mcpdm_read(MCPDM_FIFO_CTRL_DN)); 77 dev_dbg(mcpdm->dev, "FIFO_CTRL_UP: 0x%04x\n", 78 omap_mcpdm_read(MCPDM_FIFO_CTRL_UP)); 79 dev_dbg(mcpdm->dev, "DN_OFFSET: 0x%04x\n", 80 omap_mcpdm_read(MCPDM_DN_OFFSET)); 81 dev_dbg(mcpdm->dev, "***********************\n"); 82} 83 84/* 85 * Takes the McPDM module in and out of reset state. 86 * Uplink and downlink can be reset individually. 87 */ 88static void omap_mcpdm_reset_capture(int reset) 89{ 90 int ctrl = omap_mcpdm_read(MCPDM_CTRL); 91 92 if (reset) 93 ctrl |= SW_UP_RST; 94 else 95 ctrl &= ~SW_UP_RST; 96 97 omap_mcpdm_write(MCPDM_CTRL, ctrl); 98} 99 100static void omap_mcpdm_reset_playback(int reset) 101{ 102 int ctrl = omap_mcpdm_read(MCPDM_CTRL); 103 104 if (reset) 105 ctrl |= SW_DN_RST; 106 else 107 ctrl &= ~SW_DN_RST; 108 109 omap_mcpdm_write(MCPDM_CTRL, ctrl); 110} 111 112/* 113 * Enables the transfer through the PDM interface to/from the Phoenix 114 * codec by enabling the corresponding UP or DN channels. 115 */ 116void omap_mcpdm_start(int stream) 117{ 118 int ctrl = omap_mcpdm_read(MCPDM_CTRL); 119 120 if (stream) 121 ctrl |= mcpdm->up_channels; 122 else 123 ctrl |= mcpdm->dn_channels; 124 125 omap_mcpdm_write(MCPDM_CTRL, ctrl); 126} 127 128/* 129 * Disables the transfer through the PDM interface to/from the Phoenix 130 * codec by disabling the corresponding UP or DN channels. 131 */ 132void omap_mcpdm_stop(int stream) 133{ 134 int ctrl = omap_mcpdm_read(MCPDM_CTRL); 135 136 if (stream) 137 ctrl &= ~mcpdm->up_channels; 138 else 139 ctrl &= ~mcpdm->dn_channels; 140 141 omap_mcpdm_write(MCPDM_CTRL, ctrl); 142} 143 144/* 145 * Configures McPDM uplink for audio recording. 146 * This function should be called before omap_mcpdm_start. 147 */ 148int omap_mcpdm_capture_open(struct omap_mcpdm_link *uplink) 149{ 150 int irq_mask = 0; 151 int ctrl; 152 153 if (!uplink) 154 return -EINVAL; 155 156 mcpdm->uplink = uplink; 157 158 /* Enable irq request generation */ 159 irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK; 160 omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask); 161 162 /* Configure uplink threshold */ 163 if (uplink->threshold > UP_THRES_MAX) 164 uplink->threshold = UP_THRES_MAX; 165 166 omap_mcpdm_write(MCPDM_FIFO_CTRL_UP, uplink->threshold); 167 168 /* Configure DMA controller */ 169 omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_UP_ENABLE); 170 171 /* Set pdm out format */ 172 ctrl = omap_mcpdm_read(MCPDM_CTRL); 173 ctrl &= ~PDMOUTFORMAT; 174 ctrl |= uplink->format & PDMOUTFORMAT; 175 176 /* Uplink channels */ 177 mcpdm->up_channels = uplink->channels & (PDM_UP_MASK | PDM_STATUS_MASK); 178 179 omap_mcpdm_write(MCPDM_CTRL, ctrl); 180 181 return 0; 182} 183 184/* 185 * Configures McPDM downlink for audio playback. 186 * This function should be called before omap_mcpdm_start. 187 */ 188int omap_mcpdm_playback_open(struct omap_mcpdm_link *downlink) 189{ 190 int irq_mask = 0; 191 int ctrl; 192 193 if (!downlink) 194 return -EINVAL; 195 196 mcpdm->downlink = downlink; 197 198 /* Enable irq request generation */ 199 irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK; 200 omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask); 201 202 /* Configure uplink threshold */ 203 if (downlink->threshold > DN_THRES_MAX) 204 downlink->threshold = DN_THRES_MAX; 205 206 omap_mcpdm_write(MCPDM_FIFO_CTRL_DN, downlink->threshold); 207 208 /* Enable DMA request generation */ 209 omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_DN_ENABLE); 210 211 /* Set pdm out format */ 212 ctrl = omap_mcpdm_read(MCPDM_CTRL); 213 ctrl &= ~PDMOUTFORMAT; 214 ctrl |= downlink->format & PDMOUTFORMAT; 215 216 /* Downlink channels */ 217 mcpdm->dn_channels = downlink->channels & (PDM_DN_MASK | PDM_CMD_MASK); 218 219 omap_mcpdm_write(MCPDM_CTRL, ctrl); 220 221 return 0; 222} 223 224/* 225 * Cleans McPDM uplink configuration. 226 * This function should be called when the stream is closed. 227 */ 228int omap_mcpdm_capture_close(struct omap_mcpdm_link *uplink) 229{ 230 int irq_mask = 0; 231 232 if (!uplink) 233 return -EINVAL; 234 235 /* Disable irq request generation */ 236 irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK; 237 omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask); 238 239 /* Disable DMA request generation */ 240 omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_UP_ENABLE); 241 242 /* Clear Downlink channels */ 243 mcpdm->up_channels = 0; 244 245 mcpdm->uplink = NULL; 246 247 return 0; 248} 249 250/* 251 * Cleans McPDM downlink configuration. 252 * This function should be called when the stream is closed. 253 */ 254int omap_mcpdm_playback_close(struct omap_mcpdm_link *downlink) 255{ 256 int irq_mask = 0; 257 258 if (!downlink) 259 return -EINVAL; 260 261 /* Disable irq request generation */ 262 irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK; 263 omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask); 264 265 /* Disable DMA request generation */ 266 omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_DN_ENABLE); 267 268 /* clear Downlink channels */ 269 mcpdm->dn_channels = 0; 270 271 mcpdm->downlink = NULL; 272 273 return 0; 274} 275 276static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id) 277{ 278 struct omap_mcpdm *mcpdm_irq = dev_id; 279 int irq_status; 280 281 irq_status = omap_mcpdm_read(MCPDM_IRQSTATUS); 282 283 /* Acknowledge irq event */ 284 omap_mcpdm_write(MCPDM_IRQSTATUS, irq_status); 285 286 if (irq & MCPDM_DN_IRQ_FULL) { 287 dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status); 288 omap_mcpdm_reset_playback(1); 289 omap_mcpdm_playback_open(mcpdm_irq->downlink); 290 omap_mcpdm_reset_playback(0); 291 } 292 293 if (irq & MCPDM_DN_IRQ_EMPTY) { 294 dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status); 295 omap_mcpdm_reset_playback(1); 296 omap_mcpdm_playback_open(mcpdm_irq->downlink); 297 omap_mcpdm_reset_playback(0); 298 } 299 300 if (irq & MCPDM_DN_IRQ) { 301 dev_dbg(mcpdm_irq->dev, "DN write request\n"); 302 } 303 304 if (irq & MCPDM_UP_IRQ_FULL) { 305 dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status); 306 omap_mcpdm_reset_capture(1); 307 omap_mcpdm_capture_open(mcpdm_irq->uplink); 308 omap_mcpdm_reset_capture(0); 309 } 310 311 if (irq & MCPDM_UP_IRQ_EMPTY) { 312 dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status); 313 omap_mcpdm_reset_capture(1); 314 omap_mcpdm_capture_open(mcpdm_irq->uplink); 315 omap_mcpdm_reset_capture(0); 316 } 317 318 if (irq & MCPDM_UP_IRQ) { 319 dev_dbg(mcpdm_irq->dev, "UP write request\n"); 320 } 321 322 return IRQ_HANDLED; 323} 324 325int omap_mcpdm_request(void) 326{ 327 int ret; 328 329 clk_enable(mcpdm->clk); 330 331 spin_lock(&mcpdm->lock); 332 333 if (!mcpdm->free) { 334 dev_err(mcpdm->dev, "McPDM interface is in use\n"); 335 spin_unlock(&mcpdm->lock); 336 ret = -EBUSY; 337 goto err; 338 } 339 mcpdm->free = 0; 340 341 spin_unlock(&mcpdm->lock); 342 343 /* Disable lines while request is ongoing */ 344 omap_mcpdm_write(MCPDM_CTRL, 0x00); 345 346 ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler, 347 0, "McPDM", (void *)mcpdm); 348 if (ret) { 349 dev_err(mcpdm->dev, "Request for McPDM IRQ failed\n"); 350 goto err; 351 } 352 353 return 0; 354 355err: 356 clk_disable(mcpdm->clk); 357 return ret; 358} 359 360void omap_mcpdm_free(void) 361{ 362 spin_lock(&mcpdm->lock); 363 if (mcpdm->free) { 364 dev_err(mcpdm->dev, "McPDM interface is already free\n"); 365 spin_unlock(&mcpdm->lock); 366 return; 367 } 368 mcpdm->free = 1; 369 spin_unlock(&mcpdm->lock); 370 371 clk_disable(mcpdm->clk); 372 373 free_irq(mcpdm->irq, (void *)mcpdm); 374} 375 376/* Enable/disable DC offset cancelation for the analog 377 * headset path (PDM channels 1 and 2). 378 */ 379int omap_mcpdm_set_offset(int offset1, int offset2) 380{ 381 int offset; 382 383 if ((offset1 > DN_OFST_MAX) || (offset2 > DN_OFST_MAX)) 384 return -EINVAL; 385 386 offset = (offset1 << DN_OFST_RX1) | (offset2 << DN_OFST_RX2); 387 388 /* offset cancellation for channel 1 */ 389 if (offset1) 390 offset |= DN_OFST_RX1_EN; 391 else 392 offset &= ~DN_OFST_RX1_EN; 393 394 /* offset cancellation for channel 2 */ 395 if (offset2) 396 offset |= DN_OFST_RX2_EN; 397 else 398 offset &= ~DN_OFST_RX2_EN; 399 400 omap_mcpdm_write(MCPDM_DN_OFFSET, offset); 401 402 return 0; 403} 404 405static int __devinit omap_mcpdm_probe(struct platform_device *pdev) 406{ 407 struct resource *res; 408 int ret = 0; 409 410 mcpdm = kzalloc(sizeof(struct omap_mcpdm), GFP_KERNEL); 411 if (!mcpdm) { 412 ret = -ENOMEM; 413 goto exit; 414 } 415 416 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 417 if (res == NULL) { 418 dev_err(&pdev->dev, "no resource\n"); 419 goto err_resource; 420 } 421 422 spin_lock_init(&mcpdm->lock); 423 mcpdm->free = 1; 424 mcpdm->io_base = ioremap(res->start, resource_size(res)); 425 if (!mcpdm->io_base) { 426 ret = -ENOMEM; 427 goto err_resource; 428 } 429 430 mcpdm->irq = platform_get_irq(pdev, 0); 431 432 mcpdm->clk = clk_get(&pdev->dev, "pdm_ck"); 433 if (IS_ERR(mcpdm->clk)) { 434 ret = PTR_ERR(mcpdm->clk); 435 dev_err(&pdev->dev, "unable to get pdm_ck: %d\n", ret); 436 goto err_clk; 437 } 438 439 mcpdm->dev = &pdev->dev; 440 platform_set_drvdata(pdev, mcpdm); 441 442 return 0; 443 444err_clk: 445 iounmap(mcpdm->io_base); 446err_resource: 447 kfree(mcpdm); 448exit: 449 return ret; 450} 451 452static int __devexit omap_mcpdm_remove(struct platform_device *pdev) 453{ 454 struct omap_mcpdm *mcpdm_ptr = platform_get_drvdata(pdev); 455 456 platform_set_drvdata(pdev, NULL); 457 458 clk_put(mcpdm_ptr->clk); 459 460 iounmap(mcpdm_ptr->io_base); 461 462 mcpdm_ptr->clk = NULL; 463 mcpdm_ptr->free = 0; 464 mcpdm_ptr->dev = NULL; 465 466 kfree(mcpdm_ptr); 467 468 return 0; 469} 470 471static struct platform_driver omap_mcpdm_driver = { 472 .probe = omap_mcpdm_probe, 473 .remove = __devexit_p(omap_mcpdm_remove), 474 .driver = { 475 .name = "omap-mcpdm", 476 }, 477}; 478 479static struct platform_device *omap_mcpdm_device; 480 481static int __init omap_mcpdm_init(void) 482{ 483 return platform_driver_register(&omap_mcpdm_driver); 484} 485arch_initcall(omap_mcpdm_init); 486