linux/sound/pci/cs5535audio/cs5535audio_pcm.c
<<
>>
Prefs
   1/*
   2 * Driver for audio on multifunction CS5535 companion device
   3 * Copyright (C) Jaya Kumar
   4 *
   5 * Based on Jaroslav Kysela and Takashi Iwai's examples.
   6 * This work was sponsored by CIS(M) Sdn Bhd.
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  21 *
  22 * todo: add be fmt support, spdif, pm
  23 */
  24
  25#include <linux/init.h>
  26#include <linux/pci.h>
  27#include <sound/core.h>
  28#include <sound/control.h>
  29#include <sound/initval.h>
  30#include <sound/asoundef.h>
  31#include <sound/pcm.h>
  32#include <sound/pcm_params.h>
  33#include <sound/ac97_codec.h>
  34#include "cs5535audio.h"
  35
  36static struct snd_pcm_hardware snd_cs5535audio_playback =
  37{
  38        .info =                 (
  39                                SNDRV_PCM_INFO_MMAP |
  40                                SNDRV_PCM_INFO_INTERLEAVED |
  41                                SNDRV_PCM_INFO_BLOCK_TRANSFER |
  42                                SNDRV_PCM_INFO_MMAP_VALID |
  43                                SNDRV_PCM_INFO_PAUSE |
  44                                SNDRV_PCM_INFO_RESUME
  45                                ),
  46        .formats =              (
  47                                SNDRV_PCM_FMTBIT_S16_LE
  48                                ),
  49        .rates =                (
  50                                SNDRV_PCM_RATE_CONTINUOUS |
  51                                SNDRV_PCM_RATE_8000_48000
  52                                ),
  53        .rate_min =             4000,
  54        .rate_max =             48000,
  55        .channels_min =         2,
  56        .channels_max =         2,
  57        .buffer_bytes_max =     (128*1024),
  58        .period_bytes_min =     64,
  59        .period_bytes_max =     (64*1024 - 16),
  60        .periods_min =          1,
  61        .periods_max =          CS5535AUDIO_MAX_DESCRIPTORS,
  62        .fifo_size =            0,
  63};
  64
  65static struct snd_pcm_hardware snd_cs5535audio_capture =
  66{
  67        .info =                 (
  68                                SNDRV_PCM_INFO_MMAP |
  69                                SNDRV_PCM_INFO_INTERLEAVED |
  70                                SNDRV_PCM_INFO_BLOCK_TRANSFER |
  71                                SNDRV_PCM_INFO_MMAP_VALID
  72                                ),
  73        .formats =              (
  74                                SNDRV_PCM_FMTBIT_S16_LE
  75                                ),
  76        .rates =                (
  77                                SNDRV_PCM_RATE_CONTINUOUS |
  78                                SNDRV_PCM_RATE_8000_48000
  79                                ),
  80        .rate_min =             4000,
  81        .rate_max =             48000,
  82        .channels_min =         2,
  83        .channels_max =         2,
  84        .buffer_bytes_max =     (128*1024),
  85        .period_bytes_min =     64,
  86        .period_bytes_max =     (64*1024 - 16),
  87        .periods_min =          1,
  88        .periods_max =          CS5535AUDIO_MAX_DESCRIPTORS,
  89        .fifo_size =            0,
  90};
  91
  92static int snd_cs5535audio_playback_open(struct snd_pcm_substream *substream)
  93{
  94        int err;
  95        struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
  96        struct snd_pcm_runtime *runtime = substream->runtime;
  97
  98        runtime->hw = snd_cs5535audio_playback;
  99        runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_FRONT_DAC];
 100        snd_pcm_limit_hw_rates(runtime);
 101        cs5535au->playback_substream = substream;
 102        runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK]);
 103        if ((err = snd_pcm_hw_constraint_integer(runtime,
 104                                SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
 105                return err;
 106
 107        return 0;
 108}
 109
 110static int snd_cs5535audio_playback_close(struct snd_pcm_substream *substream)
 111{
 112        return 0;
 113}
 114
 115#define CS5535AUDIO_DESC_LIST_SIZE \
 116        PAGE_ALIGN(CS5535AUDIO_MAX_DESCRIPTORS * sizeof(struct cs5535audio_dma_desc))
 117
 118static int cs5535audio_build_dma_packets(struct cs5535audio *cs5535au,
 119                                         struct cs5535audio_dma *dma,
 120                                         struct snd_pcm_substream *substream,
 121                                         unsigned int periods,
 122                                         unsigned int period_bytes)
 123{
 124        unsigned int i;
 125        u32 addr, desc_addr, jmpprd_addr;
 126        struct cs5535audio_dma_desc *lastdesc;
 127
 128        if (periods > CS5535AUDIO_MAX_DESCRIPTORS)
 129                return -ENOMEM;
 130
 131        if (dma->desc_buf.area == NULL) {
 132                if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
 133                                        snd_dma_pci_data(cs5535au->pci),
 134                                        CS5535AUDIO_DESC_LIST_SIZE+1,
 135                                        &dma->desc_buf) < 0)
 136                        return -ENOMEM;
 137                dma->period_bytes = dma->periods = 0;
 138        }
 139
 140        if (dma->periods == periods && dma->period_bytes == period_bytes)
 141                return 0;
 142
 143        /* the u32 cast is okay because in snd*create we successfully told
 144           pci alloc that we're only 32 bit capable so the uppper will be 0 */
 145        addr = (u32) substream->runtime->dma_addr;
 146        desc_addr = (u32) dma->desc_buf.addr;
 147        for (i = 0; i < periods; i++) {
 148                struct cs5535audio_dma_desc *desc =
 149                        &((struct cs5535audio_dma_desc *) dma->desc_buf.area)[i];
 150                desc->addr = cpu_to_le32(addr);
 151                desc->size = cpu_to_le32(period_bytes);
 152                desc->ctlreserved = cpu_to_le32(PRD_EOP);
 153                desc_addr += sizeof(struct cs5535audio_dma_desc);
 154                addr += period_bytes;
 155        }
 156        /* we reserved one dummy descriptor at the end to do the PRD jump */
 157        lastdesc = &((struct cs5535audio_dma_desc *) dma->desc_buf.area)[periods];
 158        lastdesc->addr = cpu_to_le32((u32) dma->desc_buf.addr);
 159        lastdesc->size = 0;
 160        lastdesc->ctlreserved = cpu_to_le32(PRD_JMP);
 161        jmpprd_addr = cpu_to_le32(lastdesc->addr +
 162                                  (sizeof(struct cs5535audio_dma_desc)*periods));
 163
 164        dma->substream = substream;
 165        dma->period_bytes = period_bytes;
 166        dma->periods = periods;
 167        spin_lock_irq(&cs5535au->reg_lock);
 168        dma->ops->disable_dma(cs5535au);
 169        dma->ops->setup_prd(cs5535au, jmpprd_addr);
 170        spin_unlock_irq(&cs5535au->reg_lock);
 171        return 0;
 172}
 173
 174static void cs5535audio_playback_enable_dma(struct cs5535audio *cs5535au)
 175{
 176        cs_writeb(cs5535au, ACC_BM0_CMD, BM_CTL_EN);
 177}
 178
 179static void cs5535audio_playback_disable_dma(struct cs5535audio *cs5535au)
 180{
 181        cs_writeb(cs5535au, ACC_BM0_CMD, 0);
 182}
 183
 184static void cs5535audio_playback_pause_dma(struct cs5535audio *cs5535au)
 185{
 186        cs_writeb(cs5535au, ACC_BM0_CMD, BM_CTL_PAUSE);
 187}
 188
 189static void cs5535audio_playback_setup_prd(struct cs5535audio *cs5535au,
 190                                           u32 prd_addr)
 191{
 192        cs_writel(cs5535au, ACC_BM0_PRD, prd_addr);
 193}
 194
 195static u32 cs5535audio_playback_read_prd(struct cs5535audio *cs5535au)
 196{
 197        return cs_readl(cs5535au, ACC_BM0_PRD);
 198}
 199
 200static u32 cs5535audio_playback_read_dma_pntr(struct cs5535audio *cs5535au)
 201{
 202        return cs_readl(cs5535au, ACC_BM0_PNTR);
 203}
 204
 205static void cs5535audio_capture_enable_dma(struct cs5535audio *cs5535au)
 206{
 207        cs_writeb(cs5535au, ACC_BM1_CMD, BM_CTL_EN);
 208}
 209
 210static void cs5535audio_capture_disable_dma(struct cs5535audio *cs5535au)
 211{
 212        cs_writeb(cs5535au, ACC_BM1_CMD, 0);
 213}
 214
 215static void cs5535audio_capture_pause_dma(struct cs5535audio *cs5535au)
 216{
 217        cs_writeb(cs5535au, ACC_BM1_CMD, BM_CTL_PAUSE);
 218}
 219
 220static void cs5535audio_capture_setup_prd(struct cs5535audio *cs5535au,
 221                                          u32 prd_addr)
 222{
 223        cs_writel(cs5535au, ACC_BM1_PRD, prd_addr);
 224}
 225
 226static u32 cs5535audio_capture_read_prd(struct cs5535audio *cs5535au)
 227{
 228        return cs_readl(cs5535au, ACC_BM1_PRD);
 229}
 230
 231static u32 cs5535audio_capture_read_dma_pntr(struct cs5535audio *cs5535au)
 232{
 233        return cs_readl(cs5535au, ACC_BM1_PNTR);
 234}
 235
 236static void cs5535audio_clear_dma_packets(struct cs5535audio *cs5535au,
 237                                          struct cs5535audio_dma *dma,
 238                                          struct snd_pcm_substream *substream)
 239{
 240        snd_dma_free_pages(&dma->desc_buf);
 241        dma->desc_buf.area = NULL;
 242        dma->substream = NULL;
 243}
 244
 245static int snd_cs5535audio_hw_params(struct snd_pcm_substream *substream,
 246                                     struct snd_pcm_hw_params *hw_params)
 247{
 248        struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
 249        struct cs5535audio_dma *dma = substream->runtime->private_data;
 250        int err;
 251
 252        err = snd_pcm_lib_malloc_pages(substream,
 253                                        params_buffer_bytes(hw_params));
 254        if (err < 0)
 255                return err;
 256        dma->buf_addr = substream->runtime->dma_addr;
 257        dma->buf_bytes = params_buffer_bytes(hw_params);
 258
 259        err = cs5535audio_build_dma_packets(cs5535au, dma, substream,
 260                                            params_periods(hw_params),
 261                                            params_period_bytes(hw_params));
 262        if (!err)
 263                dma->pcm_open_flag = 1;
 264
 265        return err;
 266}
 267
 268static int snd_cs5535audio_hw_free(struct snd_pcm_substream *substream)
 269{
 270        struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
 271        struct cs5535audio_dma *dma = substream->runtime->private_data;
 272
 273        if (dma->pcm_open_flag) {
 274                if (substream == cs5535au->playback_substream)
 275                        snd_ac97_update_power(cs5535au->ac97,
 276                                        AC97_PCM_FRONT_DAC_RATE, 0);
 277                else
 278                        snd_ac97_update_power(cs5535au->ac97,
 279                                        AC97_PCM_LR_ADC_RATE, 0);
 280                dma->pcm_open_flag = 0;
 281        }
 282        cs5535audio_clear_dma_packets(cs5535au, dma, substream);
 283        return snd_pcm_lib_free_pages(substream);
 284}
 285
 286static int snd_cs5535audio_playback_prepare(struct snd_pcm_substream *substream)
 287{
 288        struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
 289        return snd_ac97_set_rate(cs5535au->ac97, AC97_PCM_FRONT_DAC_RATE,
 290                                 substream->runtime->rate);
 291}
 292
 293static int snd_cs5535audio_trigger(struct snd_pcm_substream *substream, int cmd)
 294{
 295        struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
 296        struct cs5535audio_dma *dma = substream->runtime->private_data;
 297        int err = 0;
 298
 299        spin_lock(&cs5535au->reg_lock);
 300        switch (cmd) {
 301        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 302                dma->ops->pause_dma(cs5535au);
 303                break;
 304        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 305                dma->ops->enable_dma(cs5535au);
 306                break;
 307        case SNDRV_PCM_TRIGGER_START:
 308                dma->ops->enable_dma(cs5535au);
 309                break;
 310        case SNDRV_PCM_TRIGGER_RESUME:
 311                dma->ops->enable_dma(cs5535au);
 312                break;
 313        case SNDRV_PCM_TRIGGER_STOP:
 314                dma->ops->disable_dma(cs5535au);
 315                break;
 316        case SNDRV_PCM_TRIGGER_SUSPEND:
 317                dma->ops->disable_dma(cs5535au);
 318                break;
 319        default:
 320                snd_printk(KERN_ERR "unhandled trigger\n");
 321                err = -EINVAL;
 322                break;
 323        }
 324        spin_unlock(&cs5535au->reg_lock);
 325        return err;
 326}
 327
 328static snd_pcm_uframes_t snd_cs5535audio_pcm_pointer(struct snd_pcm_substream
 329                                                        *substream)
 330{
 331        struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
 332        u32 curdma;
 333        struct cs5535audio_dma *dma;
 334
 335        dma = substream->runtime->private_data;
 336        curdma = dma->ops->read_dma_pntr(cs5535au);
 337        if (curdma < dma->buf_addr) {
 338                snd_printk(KERN_ERR "curdma=%x < %x bufaddr.\n",
 339                                        curdma, dma->buf_addr);
 340                return 0;
 341        }
 342        curdma -= dma->buf_addr;
 343        if (curdma >= dma->buf_bytes) {
 344                snd_printk(KERN_ERR "diff=%x >= %x buf_bytes.\n",
 345                                        curdma, dma->buf_bytes);
 346                return 0;
 347        }
 348        return bytes_to_frames(substream->runtime, curdma);
 349}
 350
 351static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream)
 352{
 353        int err;
 354        struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
 355        struct snd_pcm_runtime *runtime = substream->runtime;
 356
 357        runtime->hw = snd_cs5535audio_capture;
 358        runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_ADC];
 359        snd_pcm_limit_hw_rates(runtime);
 360        cs5535au->capture_substream = substream;
 361        runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE]);
 362        if ((err = snd_pcm_hw_constraint_integer(runtime,
 363                                         SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
 364                return err;
 365        olpc_capture_open(cs5535au->ac97);
 366        return 0;
 367}
 368
 369static int snd_cs5535audio_capture_close(struct snd_pcm_substream *substream)
 370{
 371        struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
 372        olpc_capture_close(cs5535au->ac97);
 373        return 0;
 374}
 375
 376static int snd_cs5535audio_capture_prepare(struct snd_pcm_substream *substream)
 377{
 378        struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
 379        return snd_ac97_set_rate(cs5535au->ac97, AC97_PCM_LR_ADC_RATE,
 380                                 substream->runtime->rate);
 381}
 382
 383static struct snd_pcm_ops snd_cs5535audio_playback_ops = {
 384        .open =         snd_cs5535audio_playback_open,
 385        .close =        snd_cs5535audio_playback_close,
 386        .ioctl =        snd_pcm_lib_ioctl,
 387        .hw_params =    snd_cs5535audio_hw_params,
 388        .hw_free =      snd_cs5535audio_hw_free,
 389        .prepare =      snd_cs5535audio_playback_prepare,
 390        .trigger =      snd_cs5535audio_trigger,
 391        .pointer =      snd_cs5535audio_pcm_pointer,
 392};
 393
 394static struct snd_pcm_ops snd_cs5535audio_capture_ops = {
 395        .open =         snd_cs5535audio_capture_open,
 396        .close =        snd_cs5535audio_capture_close,
 397        .ioctl =        snd_pcm_lib_ioctl,
 398        .hw_params =    snd_cs5535audio_hw_params,
 399        .hw_free =      snd_cs5535audio_hw_free,
 400        .prepare =      snd_cs5535audio_capture_prepare,
 401        .trigger =      snd_cs5535audio_trigger,
 402        .pointer =      snd_cs5535audio_pcm_pointer,
 403};
 404
 405static struct cs5535audio_dma_ops snd_cs5535audio_playback_dma_ops = {
 406        .type = CS5535AUDIO_DMA_PLAYBACK,
 407        .enable_dma = cs5535audio_playback_enable_dma,
 408        .disable_dma = cs5535audio_playback_disable_dma,
 409        .setup_prd = cs5535audio_playback_setup_prd,
 410        .read_prd = cs5535audio_playback_read_prd,
 411        .pause_dma = cs5535audio_playback_pause_dma,
 412        .read_dma_pntr = cs5535audio_playback_read_dma_pntr,
 413};
 414
 415static struct cs5535audio_dma_ops snd_cs5535audio_capture_dma_ops = {
 416        .type = CS5535AUDIO_DMA_CAPTURE,
 417        .enable_dma = cs5535audio_capture_enable_dma,
 418        .disable_dma = cs5535audio_capture_disable_dma,
 419        .setup_prd = cs5535audio_capture_setup_prd,
 420        .read_prd = cs5535audio_capture_read_prd,
 421        .pause_dma = cs5535audio_capture_pause_dma,
 422        .read_dma_pntr = cs5535audio_capture_read_dma_pntr,
 423};
 424
 425int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535au)
 426{
 427        struct snd_pcm *pcm;
 428        int err;
 429
 430        err = snd_pcm_new(cs5535au->card, "CS5535 Audio", 0, 1, 1, &pcm);
 431        if (err < 0)
 432                return err;
 433
 434        cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK].ops =
 435                                        &snd_cs5535audio_playback_dma_ops;
 436        cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE].ops =
 437                                        &snd_cs5535audio_capture_dma_ops;
 438        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
 439                                        &snd_cs5535audio_playback_ops);
 440        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
 441                                        &snd_cs5535audio_capture_ops);
 442
 443        pcm->private_data = cs5535au;
 444        pcm->info_flags = 0;
 445        strcpy(pcm->name, "CS5535 Audio");
 446
 447        snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
 448                                        snd_dma_pci_data(cs5535au->pci),
 449                                        64*1024, 128*1024);
 450        cs5535au->pcm = pcm;
 451
 452        return 0;
 453}
 454
 455