Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Driver for audio on multifunction CS5535 companion device
0004  * Copyright (C) Jaya Kumar
0005  *
0006  * Based on Jaroslav Kysela and Takashi Iwai's examples.
0007  * This work was sponsored by CIS(M) Sdn Bhd.
0008  *
0009  * todo: add be fmt support, spdif, pm
0010  */
0011 
0012 #include <linux/init.h>
0013 #include <linux/pci.h>
0014 #include <sound/core.h>
0015 #include <sound/control.h>
0016 #include <sound/initval.h>
0017 #include <sound/asoundef.h>
0018 #include <sound/pcm.h>
0019 #include <sound/pcm_params.h>
0020 #include <sound/ac97_codec.h>
0021 #include "cs5535audio.h"
0022 
0023 static const struct snd_pcm_hardware snd_cs5535audio_playback =
0024 {
0025     .info =         (
0026                 SNDRV_PCM_INFO_MMAP |
0027                 SNDRV_PCM_INFO_INTERLEAVED |
0028                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
0029                 SNDRV_PCM_INFO_MMAP_VALID |
0030                 SNDRV_PCM_INFO_PAUSE |
0031                 SNDRV_PCM_INFO_RESUME
0032                 ),
0033     .formats =      (
0034                 SNDRV_PCM_FMTBIT_S16_LE
0035                 ),
0036     .rates =        (
0037                 SNDRV_PCM_RATE_CONTINUOUS |
0038                 SNDRV_PCM_RATE_8000_48000
0039                 ),
0040     .rate_min =     4000,
0041     .rate_max =     48000,
0042     .channels_min =     2,
0043     .channels_max =     2,
0044     .buffer_bytes_max = (128*1024),
0045     .period_bytes_min = 64,
0046     .period_bytes_max = (64*1024 - 16),
0047     .periods_min =      1,
0048     .periods_max =      CS5535AUDIO_MAX_DESCRIPTORS,
0049     .fifo_size =        0,
0050 };
0051 
0052 static const struct snd_pcm_hardware snd_cs5535audio_capture =
0053 {
0054     .info =         (
0055                 SNDRV_PCM_INFO_MMAP |
0056                 SNDRV_PCM_INFO_INTERLEAVED |
0057                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
0058                 SNDRV_PCM_INFO_MMAP_VALID
0059                 ),
0060     .formats =      (
0061                 SNDRV_PCM_FMTBIT_S16_LE
0062                 ),
0063     .rates =        (
0064                 SNDRV_PCM_RATE_CONTINUOUS |
0065                 SNDRV_PCM_RATE_8000_48000
0066                 ),
0067     .rate_min =     4000,
0068     .rate_max =     48000,
0069     .channels_min =     2,
0070     .channels_max =     2,
0071     .buffer_bytes_max = (128*1024),
0072     .period_bytes_min = 64,
0073     .period_bytes_max = (64*1024 - 16),
0074     .periods_min =      1,
0075     .periods_max =      CS5535AUDIO_MAX_DESCRIPTORS,
0076     .fifo_size =        0,
0077 };
0078 
0079 static int snd_cs5535audio_playback_open(struct snd_pcm_substream *substream)
0080 {
0081     int err;
0082     struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
0083     struct snd_pcm_runtime *runtime = substream->runtime;
0084 
0085     runtime->hw = snd_cs5535audio_playback;
0086     runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_FRONT_DAC];
0087     snd_pcm_limit_hw_rates(runtime);
0088     cs5535au->playback_substream = substream;
0089     runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK]);
0090     err = snd_pcm_hw_constraint_integer(runtime,
0091                         SNDRV_PCM_HW_PARAM_PERIODS);
0092     if (err < 0)
0093         return err;
0094 
0095     return 0;
0096 }
0097 
0098 static int snd_cs5535audio_playback_close(struct snd_pcm_substream *substream)
0099 {
0100     return 0;
0101 }
0102 
0103 #define CS5535AUDIO_DESC_LIST_SIZE \
0104     PAGE_ALIGN(CS5535AUDIO_MAX_DESCRIPTORS * sizeof(struct cs5535audio_dma_desc))
0105 
0106 static int cs5535audio_build_dma_packets(struct cs5535audio *cs5535au,
0107                      struct cs5535audio_dma *dma,
0108                      struct snd_pcm_substream *substream,
0109                      unsigned int periods,
0110                      unsigned int period_bytes)
0111 {
0112     unsigned int i;
0113     u32 addr, desc_addr, jmpprd_addr;
0114     struct cs5535audio_dma_desc *lastdesc;
0115 
0116     if (periods > CS5535AUDIO_MAX_DESCRIPTORS)
0117         return -ENOMEM;
0118 
0119     if (dma->desc_buf.area == NULL) {
0120         if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
0121                     &cs5535au->pci->dev,
0122                     CS5535AUDIO_DESC_LIST_SIZE+1,
0123                     &dma->desc_buf) < 0)
0124             return -ENOMEM;
0125         dma->period_bytes = dma->periods = 0;
0126     }
0127 
0128     if (dma->periods == periods && dma->period_bytes == period_bytes)
0129         return 0;
0130 
0131     /* the u32 cast is okay because in snd*create we successfully told
0132        pci alloc that we're only 32 bit capable so the upper will be 0 */
0133     addr = (u32) substream->runtime->dma_addr;
0134     desc_addr = (u32) dma->desc_buf.addr;
0135     for (i = 0; i < periods; i++) {
0136         struct cs5535audio_dma_desc *desc =
0137             &((struct cs5535audio_dma_desc *) dma->desc_buf.area)[i];
0138         desc->addr = cpu_to_le32(addr);
0139         desc->size = cpu_to_le16(period_bytes);
0140         desc->ctlreserved = cpu_to_le16(PRD_EOP);
0141         desc_addr += sizeof(struct cs5535audio_dma_desc);
0142         addr += period_bytes;
0143     }
0144     /* we reserved one dummy descriptor at the end to do the PRD jump */
0145     lastdesc = &((struct cs5535audio_dma_desc *) dma->desc_buf.area)[periods];
0146     lastdesc->addr = cpu_to_le32((u32) dma->desc_buf.addr);
0147     lastdesc->size = 0;
0148     lastdesc->ctlreserved = cpu_to_le16(PRD_JMP);
0149     jmpprd_addr = (u32)dma->desc_buf.addr +
0150         sizeof(struct cs5535audio_dma_desc) * periods;
0151 
0152     dma->substream = substream;
0153     dma->period_bytes = period_bytes;
0154     dma->periods = periods;
0155     spin_lock_irq(&cs5535au->reg_lock);
0156     dma->ops->disable_dma(cs5535au);
0157     dma->ops->setup_prd(cs5535au, jmpprd_addr);
0158     spin_unlock_irq(&cs5535au->reg_lock);
0159     return 0;
0160 }
0161 
0162 static void cs5535audio_playback_enable_dma(struct cs5535audio *cs5535au)
0163 {
0164     cs_writeb(cs5535au, ACC_BM0_CMD, BM_CTL_EN);
0165 }
0166 
0167 static void cs5535audio_playback_disable_dma(struct cs5535audio *cs5535au)
0168 {
0169     cs_writeb(cs5535au, ACC_BM0_CMD, 0);
0170 }
0171 
0172 static void cs5535audio_playback_pause_dma(struct cs5535audio *cs5535au)
0173 {
0174     cs_writeb(cs5535au, ACC_BM0_CMD, BM_CTL_PAUSE);
0175 }
0176 
0177 static void cs5535audio_playback_setup_prd(struct cs5535audio *cs5535au,
0178                        u32 prd_addr)
0179 {
0180     cs_writel(cs5535au, ACC_BM0_PRD, prd_addr);
0181 }
0182 
0183 static u32 cs5535audio_playback_read_prd(struct cs5535audio *cs5535au)
0184 {
0185     return cs_readl(cs5535au, ACC_BM0_PRD);
0186 }
0187 
0188 static u32 cs5535audio_playback_read_dma_pntr(struct cs5535audio *cs5535au)
0189 {
0190     return cs_readl(cs5535au, ACC_BM0_PNTR);
0191 }
0192 
0193 static void cs5535audio_capture_enable_dma(struct cs5535audio *cs5535au)
0194 {
0195     cs_writeb(cs5535au, ACC_BM1_CMD, BM_CTL_EN);
0196 }
0197 
0198 static void cs5535audio_capture_disable_dma(struct cs5535audio *cs5535au)
0199 {
0200     cs_writeb(cs5535au, ACC_BM1_CMD, 0);
0201 }
0202 
0203 static void cs5535audio_capture_pause_dma(struct cs5535audio *cs5535au)
0204 {
0205     cs_writeb(cs5535au, ACC_BM1_CMD, BM_CTL_PAUSE);
0206 }
0207 
0208 static void cs5535audio_capture_setup_prd(struct cs5535audio *cs5535au,
0209                       u32 prd_addr)
0210 {
0211     cs_writel(cs5535au, ACC_BM1_PRD, prd_addr);
0212 }
0213 
0214 static u32 cs5535audio_capture_read_prd(struct cs5535audio *cs5535au)
0215 {
0216     return cs_readl(cs5535au, ACC_BM1_PRD);
0217 }
0218 
0219 static u32 cs5535audio_capture_read_dma_pntr(struct cs5535audio *cs5535au)
0220 {
0221     return cs_readl(cs5535au, ACC_BM1_PNTR);
0222 }
0223 
0224 static void cs5535audio_clear_dma_packets(struct cs5535audio *cs5535au,
0225                       struct cs5535audio_dma *dma,
0226                       struct snd_pcm_substream *substream)
0227 {
0228     snd_dma_free_pages(&dma->desc_buf);
0229     dma->desc_buf.area = NULL;
0230     dma->substream = NULL;
0231 }
0232 
0233 static int snd_cs5535audio_hw_params(struct snd_pcm_substream *substream,
0234                      struct snd_pcm_hw_params *hw_params)
0235 {
0236     struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
0237     struct cs5535audio_dma *dma = substream->runtime->private_data;
0238     int err;
0239 
0240     dma->buf_addr = substream->runtime->dma_addr;
0241     dma->buf_bytes = params_buffer_bytes(hw_params);
0242 
0243     err = cs5535audio_build_dma_packets(cs5535au, dma, substream,
0244                         params_periods(hw_params),
0245                         params_period_bytes(hw_params));
0246     if (!err)
0247         dma->pcm_open_flag = 1;
0248 
0249     return err;
0250 }
0251 
0252 static int snd_cs5535audio_hw_free(struct snd_pcm_substream *substream)
0253 {
0254     struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
0255     struct cs5535audio_dma *dma = substream->runtime->private_data;
0256 
0257     if (dma->pcm_open_flag) {
0258         if (substream == cs5535au->playback_substream)
0259             snd_ac97_update_power(cs5535au->ac97,
0260                     AC97_PCM_FRONT_DAC_RATE, 0);
0261         else
0262             snd_ac97_update_power(cs5535au->ac97,
0263                     AC97_PCM_LR_ADC_RATE, 0);
0264         dma->pcm_open_flag = 0;
0265     }
0266     cs5535audio_clear_dma_packets(cs5535au, dma, substream);
0267     return 0;
0268 }
0269 
0270 static int snd_cs5535audio_playback_prepare(struct snd_pcm_substream *substream)
0271 {
0272     struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
0273     return snd_ac97_set_rate(cs5535au->ac97, AC97_PCM_FRONT_DAC_RATE,
0274                  substream->runtime->rate);
0275 }
0276 
0277 static int snd_cs5535audio_trigger(struct snd_pcm_substream *substream, int cmd)
0278 {
0279     struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
0280     struct cs5535audio_dma *dma = substream->runtime->private_data;
0281     int err = 0;
0282 
0283     spin_lock(&cs5535au->reg_lock);
0284     switch (cmd) {
0285     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
0286         dma->ops->pause_dma(cs5535au);
0287         break;
0288     case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
0289         dma->ops->enable_dma(cs5535au);
0290         break;
0291     case SNDRV_PCM_TRIGGER_START:
0292         dma->ops->enable_dma(cs5535au);
0293         break;
0294     case SNDRV_PCM_TRIGGER_RESUME:
0295         dma->ops->enable_dma(cs5535au);
0296         break;
0297     case SNDRV_PCM_TRIGGER_STOP:
0298         dma->ops->disable_dma(cs5535au);
0299         break;
0300     case SNDRV_PCM_TRIGGER_SUSPEND:
0301         dma->ops->disable_dma(cs5535au);
0302         break;
0303     default:
0304         dev_err(cs5535au->card->dev, "unhandled trigger\n");
0305         err = -EINVAL;
0306         break;
0307     }
0308     spin_unlock(&cs5535au->reg_lock);
0309     return err;
0310 }
0311 
0312 static snd_pcm_uframes_t snd_cs5535audio_pcm_pointer(struct snd_pcm_substream
0313                             *substream)
0314 {
0315     struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
0316     u32 curdma;
0317     struct cs5535audio_dma *dma;
0318 
0319     dma = substream->runtime->private_data;
0320     curdma = dma->ops->read_dma_pntr(cs5535au);
0321     if (curdma < dma->buf_addr) {
0322         dev_err(cs5535au->card->dev, "curdma=%x < %x bufaddr.\n",
0323                     curdma, dma->buf_addr);
0324         return 0;
0325     }
0326     curdma -= dma->buf_addr;
0327     if (curdma >= dma->buf_bytes) {
0328         dev_err(cs5535au->card->dev, "diff=%x >= %x buf_bytes.\n",
0329                     curdma, dma->buf_bytes);
0330         return 0;
0331     }
0332     return bytes_to_frames(substream->runtime, curdma);
0333 }
0334 
0335 static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream)
0336 {
0337     int err;
0338     struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
0339     struct snd_pcm_runtime *runtime = substream->runtime;
0340 
0341     runtime->hw = snd_cs5535audio_capture;
0342     runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_ADC];
0343     snd_pcm_limit_hw_rates(runtime);
0344     cs5535au->capture_substream = substream;
0345     runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE]);
0346     err = snd_pcm_hw_constraint_integer(runtime,
0347                         SNDRV_PCM_HW_PARAM_PERIODS);
0348     if (err < 0)
0349         return err;
0350     olpc_capture_open(cs5535au->ac97);
0351     return 0;
0352 }
0353 
0354 static int snd_cs5535audio_capture_close(struct snd_pcm_substream *substream)
0355 {
0356     struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
0357     olpc_capture_close(cs5535au->ac97);
0358     return 0;
0359 }
0360 
0361 static int snd_cs5535audio_capture_prepare(struct snd_pcm_substream *substream)
0362 {
0363     struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
0364     return snd_ac97_set_rate(cs5535au->ac97, AC97_PCM_LR_ADC_RATE,
0365                  substream->runtime->rate);
0366 }
0367 
0368 static const struct snd_pcm_ops snd_cs5535audio_playback_ops = {
0369     .open =     snd_cs5535audio_playback_open,
0370     .close =    snd_cs5535audio_playback_close,
0371     .hw_params =    snd_cs5535audio_hw_params,
0372     .hw_free =  snd_cs5535audio_hw_free,
0373     .prepare =  snd_cs5535audio_playback_prepare,
0374     .trigger =  snd_cs5535audio_trigger,
0375     .pointer =  snd_cs5535audio_pcm_pointer,
0376 };
0377 
0378 static const struct snd_pcm_ops snd_cs5535audio_capture_ops = {
0379     .open =     snd_cs5535audio_capture_open,
0380     .close =    snd_cs5535audio_capture_close,
0381     .hw_params =    snd_cs5535audio_hw_params,
0382     .hw_free =  snd_cs5535audio_hw_free,
0383     .prepare =  snd_cs5535audio_capture_prepare,
0384     .trigger =  snd_cs5535audio_trigger,
0385     .pointer =  snd_cs5535audio_pcm_pointer,
0386 };
0387 
0388 static const struct cs5535audio_dma_ops snd_cs5535audio_playback_dma_ops = {
0389         .type = CS5535AUDIO_DMA_PLAYBACK,
0390         .enable_dma = cs5535audio_playback_enable_dma,
0391         .disable_dma = cs5535audio_playback_disable_dma,
0392         .setup_prd = cs5535audio_playback_setup_prd,
0393         .read_prd = cs5535audio_playback_read_prd,
0394         .pause_dma = cs5535audio_playback_pause_dma,
0395         .read_dma_pntr = cs5535audio_playback_read_dma_pntr,
0396 };
0397 
0398 static const struct cs5535audio_dma_ops snd_cs5535audio_capture_dma_ops = {
0399         .type = CS5535AUDIO_DMA_CAPTURE,
0400         .enable_dma = cs5535audio_capture_enable_dma,
0401         .disable_dma = cs5535audio_capture_disable_dma,
0402         .setup_prd = cs5535audio_capture_setup_prd,
0403         .read_prd = cs5535audio_capture_read_prd,
0404         .pause_dma = cs5535audio_capture_pause_dma,
0405         .read_dma_pntr = cs5535audio_capture_read_dma_pntr,
0406 };
0407 
0408 int snd_cs5535audio_pcm(struct cs5535audio *cs5535au)
0409 {
0410     struct snd_pcm *pcm;
0411     int err;
0412 
0413     err = snd_pcm_new(cs5535au->card, "CS5535 Audio", 0, 1, 1, &pcm);
0414     if (err < 0)
0415         return err;
0416 
0417     cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK].ops =
0418                     &snd_cs5535audio_playback_dma_ops;
0419     cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE].ops =
0420                     &snd_cs5535audio_capture_dma_ops;
0421     snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
0422                     &snd_cs5535audio_playback_ops);
0423     snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
0424                     &snd_cs5535audio_capture_ops);
0425 
0426     pcm->private_data = cs5535au;
0427     pcm->info_flags = 0;
0428     strcpy(pcm->name, "CS5535 Audio");
0429 
0430     snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
0431                        &cs5535au->pci->dev,
0432                        64*1024, 128*1024);
0433     cs5535au->pcm = pcm;
0434 
0435     return 0;
0436 }
0437