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