linux/sound/soc/blackfin/bf6xx-sport.c
<<
>>
Prefs
   1/*
   2 * bf6xx_sport.c Analog Devices BF6XX SPORT driver
   3 *
   4 * Copyright (c) 2012 Analog Devices Inc.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA.
  18 */
  19
  20#include <linux/device.h>
  21#include <linux/dma-mapping.h>
  22#include <linux/interrupt.h>
  23#include <linux/module.h>
  24#include <linux/platform_device.h>
  25#include <linux/slab.h>
  26
  27#include <asm/blackfin.h>
  28#include <asm/dma.h>
  29#include <asm/portmux.h>
  30
  31#include "bf6xx-sport.h"
  32
  33int sport_set_tx_params(struct sport_device *sport,
  34                        struct sport_params *params)
  35{
  36        if (sport->tx_regs->spctl & SPORT_CTL_SPENPRI)
  37                return -EBUSY;
  38        sport->tx_regs->spctl = params->spctl | SPORT_CTL_SPTRAN;
  39        sport->tx_regs->div = params->div;
  40        SSYNC();
  41        return 0;
  42}
  43EXPORT_SYMBOL(sport_set_tx_params);
  44
  45int sport_set_rx_params(struct sport_device *sport,
  46                        struct sport_params *params)
  47{
  48        if (sport->rx_regs->spctl & SPORT_CTL_SPENPRI)
  49                return -EBUSY;
  50        sport->rx_regs->spctl = params->spctl & ~SPORT_CTL_SPTRAN;
  51        sport->rx_regs->div = params->div;
  52        SSYNC();
  53        return 0;
  54}
  55EXPORT_SYMBOL(sport_set_rx_params);
  56
  57static int compute_wdsize(size_t wdsize)
  58{
  59        switch (wdsize) {
  60        case 1:
  61                return WDSIZE_8 | PSIZE_8;
  62        case 2:
  63                return WDSIZE_16 | PSIZE_16;
  64        default:
  65                return WDSIZE_32 | PSIZE_32;
  66        }
  67}
  68
  69void sport_tx_start(struct sport_device *sport)
  70{
  71        set_dma_next_desc_addr(sport->tx_dma_chan, sport->tx_desc);
  72        set_dma_config(sport->tx_dma_chan, DMAFLOW_LIST | DI_EN
  73                        | compute_wdsize(sport->wdsize) | NDSIZE_6);
  74        enable_dma(sport->tx_dma_chan);
  75        sport->tx_regs->spctl |= SPORT_CTL_SPENPRI;
  76        SSYNC();
  77}
  78EXPORT_SYMBOL(sport_tx_start);
  79
  80void sport_rx_start(struct sport_device *sport)
  81{
  82        set_dma_next_desc_addr(sport->rx_dma_chan, sport->rx_desc);
  83        set_dma_config(sport->rx_dma_chan, DMAFLOW_LIST | DI_EN | WNR
  84                        | compute_wdsize(sport->wdsize) | NDSIZE_6);
  85        enable_dma(sport->rx_dma_chan);
  86        sport->rx_regs->spctl |= SPORT_CTL_SPENPRI;
  87        SSYNC();
  88}
  89EXPORT_SYMBOL(sport_rx_start);
  90
  91void sport_tx_stop(struct sport_device *sport)
  92{
  93        sport->tx_regs->spctl &= ~SPORT_CTL_SPENPRI;
  94        SSYNC();
  95        disable_dma(sport->tx_dma_chan);
  96}
  97EXPORT_SYMBOL(sport_tx_stop);
  98
  99void sport_rx_stop(struct sport_device *sport)
 100{
 101        sport->rx_regs->spctl &= ~SPORT_CTL_SPENPRI;
 102        SSYNC();
 103        disable_dma(sport->rx_dma_chan);
 104}
 105EXPORT_SYMBOL(sport_rx_stop);
 106
 107void sport_set_tx_callback(struct sport_device *sport,
 108                void (*tx_callback)(void *), void *tx_data)
 109{
 110        sport->tx_callback = tx_callback;
 111        sport->tx_data = tx_data;
 112}
 113EXPORT_SYMBOL(sport_set_tx_callback);
 114
 115void sport_set_rx_callback(struct sport_device *sport,
 116                void (*rx_callback)(void *), void *rx_data)
 117{
 118        sport->rx_callback = rx_callback;
 119        sport->rx_data = rx_data;
 120}
 121EXPORT_SYMBOL(sport_set_rx_callback);
 122
 123static void setup_desc(struct dmasg *desc, void *buf, int fragcount,
 124                size_t fragsize, unsigned int cfg,
 125                unsigned int count, size_t wdsize)
 126{
 127
 128        int i;
 129
 130        for (i = 0; i < fragcount; ++i) {
 131                desc[i].next_desc_addr  = &(desc[i + 1]);
 132                desc[i].start_addr = (unsigned long)buf + i*fragsize;
 133                desc[i].cfg = cfg;
 134                desc[i].x_count = count;
 135                desc[i].x_modify = wdsize;
 136                desc[i].y_count = 0;
 137                desc[i].y_modify = 0;
 138        }
 139
 140        /* make circular */
 141        desc[fragcount-1].next_desc_addr = desc;
 142}
 143
 144int sport_config_tx_dma(struct sport_device *sport, void *buf,
 145                int fragcount, size_t fragsize)
 146{
 147        unsigned int count;
 148        unsigned int cfg;
 149        dma_addr_t addr;
 150
 151        count = fragsize/sport->wdsize;
 152
 153        if (sport->tx_desc)
 154                dma_free_coherent(NULL, sport->tx_desc_size,
 155                                sport->tx_desc, 0);
 156
 157        sport->tx_desc = dma_alloc_coherent(NULL,
 158                        fragcount * sizeof(struct dmasg), &addr, 0);
 159        sport->tx_desc_size = fragcount * sizeof(struct dmasg);
 160        if (!sport->tx_desc)
 161                return -ENOMEM;
 162
 163        sport->tx_buf = buf;
 164        sport->tx_fragsize = fragsize;
 165        sport->tx_frags = fragcount;
 166        cfg = DMAFLOW_LIST | DI_EN | compute_wdsize(sport->wdsize) | NDSIZE_6;
 167
 168        setup_desc(sport->tx_desc, buf, fragcount, fragsize,
 169                        cfg|DMAEN, count, sport->wdsize);
 170
 171        return 0;
 172}
 173EXPORT_SYMBOL(sport_config_tx_dma);
 174
 175int sport_config_rx_dma(struct sport_device *sport, void *buf,
 176                int fragcount, size_t fragsize)
 177{
 178        unsigned int count;
 179        unsigned int cfg;
 180        dma_addr_t addr;
 181
 182        count = fragsize/sport->wdsize;
 183
 184        if (sport->rx_desc)
 185                dma_free_coherent(NULL, sport->rx_desc_size,
 186                                sport->rx_desc, 0);
 187
 188        sport->rx_desc = dma_alloc_coherent(NULL,
 189                        fragcount * sizeof(struct dmasg), &addr, 0);
 190        sport->rx_desc_size = fragcount * sizeof(struct dmasg);
 191        if (!sport->rx_desc)
 192                return -ENOMEM;
 193
 194        sport->rx_buf = buf;
 195        sport->rx_fragsize = fragsize;
 196        sport->rx_frags = fragcount;
 197        cfg = DMAFLOW_LIST | DI_EN | compute_wdsize(sport->wdsize)
 198                | WNR | NDSIZE_6;
 199
 200        setup_desc(sport->rx_desc, buf, fragcount, fragsize,
 201                        cfg|DMAEN, count, sport->wdsize);
 202
 203        return 0;
 204}
 205EXPORT_SYMBOL(sport_config_rx_dma);
 206
 207unsigned long sport_curr_offset_tx(struct sport_device *sport)
 208{
 209        unsigned long curr = get_dma_curr_addr(sport->tx_dma_chan);
 210
 211        return (unsigned char *)curr - sport->tx_buf;
 212}
 213EXPORT_SYMBOL(sport_curr_offset_tx);
 214
 215unsigned long sport_curr_offset_rx(struct sport_device *sport)
 216{
 217        unsigned long curr = get_dma_curr_addr(sport->rx_dma_chan);
 218
 219        return (unsigned char *)curr - sport->rx_buf;
 220}
 221EXPORT_SYMBOL(sport_curr_offset_rx);
 222
 223static irqreturn_t sport_tx_irq(int irq, void *dev_id)
 224{
 225        struct sport_device *sport = dev_id;
 226        static unsigned long status;
 227
 228        status = get_dma_curr_irqstat(sport->tx_dma_chan);
 229        if (status & (DMA_DONE|DMA_ERR)) {
 230                clear_dma_irqstat(sport->tx_dma_chan);
 231                SSYNC();
 232        }
 233        if (sport->tx_callback)
 234                sport->tx_callback(sport->tx_data);
 235        return IRQ_HANDLED;
 236}
 237
 238static irqreturn_t sport_rx_irq(int irq, void *dev_id)
 239{
 240        struct sport_device *sport = dev_id;
 241        unsigned long status;
 242
 243        status = get_dma_curr_irqstat(sport->rx_dma_chan);
 244        if (status & (DMA_DONE|DMA_ERR)) {
 245                clear_dma_irqstat(sport->rx_dma_chan);
 246                SSYNC();
 247        }
 248        if (sport->rx_callback)
 249                sport->rx_callback(sport->rx_data);
 250        return IRQ_HANDLED;
 251}
 252
 253static irqreturn_t sport_err_irq(int irq, void *dev_id)
 254{
 255        struct sport_device *sport = dev_id;
 256        struct device *dev = &sport->pdev->dev;
 257
 258        if (sport->tx_regs->spctl & SPORT_CTL_DERRPRI)
 259                dev_err(dev, "sport error: TUVF\n");
 260        if (sport->rx_regs->spctl & SPORT_CTL_DERRPRI)
 261                dev_err(dev, "sport error: ROVF\n");
 262
 263        return IRQ_HANDLED;
 264}
 265
 266static int sport_get_resource(struct sport_device *sport)
 267{
 268        struct platform_device *pdev = sport->pdev;
 269        struct device *dev = &pdev->dev;
 270        struct bfin_snd_platform_data *pdata = dev->platform_data;
 271        struct resource *res;
 272
 273        if (!pdata) {
 274                dev_err(dev, "No platform data\n");
 275                return -ENODEV;
 276        }
 277        sport->pin_req = pdata->pin_req;
 278
 279        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 280        if (!res) {
 281                dev_err(dev, "No tx MEM resource\n");
 282                return -ENODEV;
 283        }
 284        sport->tx_regs = (struct sport_register *)res->start;
 285
 286        res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 287        if (!res) {
 288                dev_err(dev, "No rx MEM resource\n");
 289                return -ENODEV;
 290        }
 291        sport->rx_regs = (struct sport_register *)res->start;
 292
 293        res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
 294        if (!res) {
 295                dev_err(dev, "No tx DMA resource\n");
 296                return -ENODEV;
 297        }
 298        sport->tx_dma_chan = res->start;
 299
 300        res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
 301        if (!res) {
 302                dev_err(dev, "No rx DMA resource\n");
 303                return -ENODEV;
 304        }
 305        sport->rx_dma_chan = res->start;
 306
 307        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 308        if (!res) {
 309                dev_err(dev, "No tx error irq resource\n");
 310                return -ENODEV;
 311        }
 312        sport->tx_err_irq = res->start;
 313
 314        res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
 315        if (!res) {
 316                dev_err(dev, "No rx error irq resource\n");
 317                return -ENODEV;
 318        }
 319        sport->rx_err_irq = res->start;
 320
 321        return 0;
 322}
 323
 324static int sport_request_resource(struct sport_device *sport)
 325{
 326        struct device *dev = &sport->pdev->dev;
 327        int ret;
 328
 329        ret = peripheral_request_list(sport->pin_req, "soc-audio");
 330        if (ret) {
 331                dev_err(dev, "Unable to request sport pin\n");
 332                return ret;
 333        }
 334
 335        ret = request_dma(sport->tx_dma_chan, "SPORT TX Data");
 336        if (ret) {
 337                dev_err(dev, "Unable to allocate DMA channel for sport tx\n");
 338                goto err_tx_dma;
 339        }
 340        set_dma_callback(sport->tx_dma_chan, sport_tx_irq, sport);
 341
 342        ret = request_dma(sport->rx_dma_chan, "SPORT RX Data");
 343        if (ret) {
 344                dev_err(dev, "Unable to allocate DMA channel for sport rx\n");
 345                goto err_rx_dma;
 346        }
 347        set_dma_callback(sport->rx_dma_chan, sport_rx_irq, sport);
 348
 349        ret = request_irq(sport->tx_err_irq, sport_err_irq,
 350                        0, "SPORT TX ERROR", sport);
 351        if (ret) {
 352                dev_err(dev, "Unable to allocate tx error IRQ for sport\n");
 353                goto err_tx_irq;
 354        }
 355
 356        ret = request_irq(sport->rx_err_irq, sport_err_irq,
 357                        0, "SPORT RX ERROR", sport);
 358        if (ret) {
 359                dev_err(dev, "Unable to allocate rx error IRQ for sport\n");
 360                goto err_rx_irq;
 361        }
 362
 363        return 0;
 364err_rx_irq:
 365        free_irq(sport->tx_err_irq, sport);
 366err_tx_irq:
 367        free_dma(sport->rx_dma_chan);
 368err_rx_dma:
 369        free_dma(sport->tx_dma_chan);
 370err_tx_dma:
 371        peripheral_free_list(sport->pin_req);
 372        return ret;
 373}
 374
 375static void sport_free_resource(struct sport_device *sport)
 376{
 377        free_irq(sport->rx_err_irq, sport);
 378        free_irq(sport->tx_err_irq, sport);
 379        free_dma(sport->rx_dma_chan);
 380        free_dma(sport->tx_dma_chan);
 381        peripheral_free_list(sport->pin_req);
 382}
 383
 384struct sport_device *sport_create(struct platform_device *pdev)
 385{
 386        struct device *dev = &pdev->dev;
 387        struct sport_device *sport;
 388        int ret;
 389
 390        sport = kzalloc(sizeof(*sport), GFP_KERNEL);
 391        if (!sport) {
 392                dev_err(dev, "Unable to allocate memory for sport device\n");
 393                return NULL;
 394        }
 395        sport->pdev = pdev;
 396
 397        ret = sport_get_resource(sport);
 398        if (ret) {
 399                kfree(sport);
 400                return NULL;
 401        }
 402
 403        ret = sport_request_resource(sport);
 404        if (ret) {
 405                kfree(sport);
 406                return NULL;
 407        }
 408
 409        dev_dbg(dev, "SPORT create success\n");
 410        return sport;
 411}
 412EXPORT_SYMBOL(sport_create);
 413
 414void sport_delete(struct sport_device *sport)
 415{
 416        if (sport->tx_desc)
 417                dma_free_coherent(NULL, sport->tx_desc_size,
 418                                sport->tx_desc, 0);
 419        if (sport->rx_desc)
 420                dma_free_coherent(NULL, sport->rx_desc_size,
 421                                sport->rx_desc, 0);
 422        sport_free_resource(sport);
 423        kfree(sport);
 424}
 425EXPORT_SYMBOL(sport_delete);
 426
 427MODULE_DESCRIPTION("Analog Devices BF6XX SPORT driver");
 428MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
 429MODULE_LICENSE("GPL v2");
 430