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