0001
0002
0003
0004
0005
0006
0007
0008
0009
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
0132
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
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