0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include "cx88.h"
0014 #include "cx88-reg.h"
0015
0016 #include <linux/module.h>
0017 #include <linux/init.h>
0018 #include <linux/delay.h>
0019 #include <linux/device.h>
0020 #include <linux/interrupt.h>
0021 #include <linux/vmalloc.h>
0022 #include <linux/dma-mapping.h>
0023 #include <linux/pci.h>
0024 #include <linux/slab.h>
0025
0026 #include <sound/core.h>
0027 #include <sound/pcm.h>
0028 #include <sound/pcm_params.h>
0029 #include <sound/control.h>
0030 #include <sound/initval.h>
0031 #include <sound/tlv.h>
0032 #include <media/i2c/wm8775.h>
0033
0034 #define dprintk(level, fmt, arg...) do { \
0035 if (debug + 1 > level) \
0036 printk(KERN_DEBUG pr_fmt("%s: alsa: " fmt), \
0037 chip->core->name, ##arg); \
0038 } while (0)
0039
0040
0041
0042
0043
0044 struct cx88_audio_buffer {
0045 unsigned int bpl;
0046 struct cx88_riscmem risc;
0047 void *vaddr;
0048 struct scatterlist *sglist;
0049 int sglen;
0050 unsigned long nr_pages;
0051 };
0052
0053 struct cx88_audio_dev {
0054 struct cx88_core *core;
0055 struct cx88_dmaqueue q;
0056
0057
0058 struct pci_dev *pci;
0059
0060
0061 int irq;
0062
0063 struct snd_card *card;
0064
0065 spinlock_t reg_lock;
0066 atomic_t count;
0067
0068 unsigned int dma_size;
0069 unsigned int period_size;
0070 unsigned int num_periods;
0071
0072 struct cx88_audio_buffer *buf;
0073
0074 struct snd_pcm_substream *substream;
0075 };
0076
0077
0078
0079
0080
0081 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
0082 static const char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
0083 static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
0084
0085 module_param_array(enable, bool, NULL, 0444);
0086 MODULE_PARM_DESC(enable, "Enable cx88x soundcard. default enabled.");
0087
0088 module_param_array(index, int, NULL, 0444);
0089 MODULE_PARM_DESC(index, "Index value for cx88x capture interface(s).");
0090
0091
0092
0093
0094
0095 MODULE_DESCRIPTION("ALSA driver module for cx2388x based TV cards");
0096 MODULE_AUTHOR("Ricardo Cerqueira");
0097 MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>");
0098 MODULE_LICENSE("GPL v2");
0099 MODULE_VERSION(CX88_VERSION);
0100
0101 static unsigned int debug;
0102 module_param(debug, int, 0644);
0103 MODULE_PARM_DESC(debug, "enable debug messages");
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113 static int _cx88_start_audio_dma(struct cx88_audio_dev *chip)
0114 {
0115 struct cx88_audio_buffer *buf = chip->buf;
0116 struct cx88_core *core = chip->core;
0117 const struct sram_channel *audio_ch = &cx88_sram_channels[SRAM_CH25];
0118
0119
0120 cx_clear(MO_AUD_DMACNTRL, 0x11);
0121
0122
0123 cx88_sram_channel_setup(chip->core, audio_ch, buf->bpl, buf->risc.dma);
0124
0125
0126 cx_write(MO_AUDD_LNGTH, buf->bpl);
0127
0128
0129 cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET);
0130 atomic_set(&chip->count, 0);
0131
0132 dprintk(1,
0133 "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d byte buffer\n",
0134 buf->bpl, cx_read(audio_ch->cmds_start + 8) >> 1,
0135 chip->num_periods, buf->bpl * chip->num_periods);
0136
0137
0138 cx_write(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
0139 AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1);
0140
0141
0142 cx_write(MO_AUD_INTSTAT, ~0);
0143
0144
0145 cx_set(MO_PCI_INTMSK, chip->core->pci_irqmask | PCI_INT_AUDINT);
0146
0147
0148
0149
0150 cx_set(MO_DEV_CNTRL2, (1 << 5));
0151
0152 cx_set(MO_AUD_DMACNTRL, 0x11);
0153
0154 if (debug)
0155 cx88_sram_channel_dump(chip->core, audio_ch);
0156
0157 return 0;
0158 }
0159
0160
0161
0162
0163 static int _cx88_stop_audio_dma(struct cx88_audio_dev *chip)
0164 {
0165 struct cx88_core *core = chip->core;
0166
0167 dprintk(1, "Stopping audio DMA\n");
0168
0169
0170 cx_clear(MO_AUD_DMACNTRL, 0x11);
0171
0172
0173 cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT);
0174 cx_clear(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
0175 AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1);
0176
0177 if (debug)
0178 cx88_sram_channel_dump(chip->core,
0179 &cx88_sram_channels[SRAM_CH25]);
0180
0181 return 0;
0182 }
0183
0184 #define MAX_IRQ_LOOP 50
0185
0186
0187
0188
0189 static const char *cx88_aud_irqs[32] = {
0190 "dn_risci1", "up_risci1", "rds_dn_risc1",
0191 NULL,
0192 "dn_risci2", "up_risci2", "rds_dn_risc2",
0193 NULL,
0194 "dnf_of", "upf_uf", "rds_dnf_uf",
0195 NULL,
0196 "dn_sync", "up_sync", "rds_dn_sync",
0197 NULL,
0198 "opc_err", "par_err", "rip_err",
0199 "pci_abort", "ber_irq", "mchg_irq"
0200 };
0201
0202
0203
0204
0205 static void cx8801_aud_irq(struct cx88_audio_dev *chip)
0206 {
0207 struct cx88_core *core = chip->core;
0208 u32 status, mask;
0209
0210 status = cx_read(MO_AUD_INTSTAT);
0211 mask = cx_read(MO_AUD_INTMSK);
0212 if (0 == (status & mask))
0213 return;
0214 cx_write(MO_AUD_INTSTAT, status);
0215 if (debug > 1 || (status & mask & ~0xff))
0216 cx88_print_irqbits("irq aud",
0217 cx88_aud_irqs, ARRAY_SIZE(cx88_aud_irqs),
0218 status, mask);
0219
0220 if (status & AUD_INT_OPC_ERR) {
0221 pr_warn("Audio risc op code error\n");
0222 cx_clear(MO_AUD_DMACNTRL, 0x11);
0223 cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH25]);
0224 }
0225 if (status & AUD_INT_DN_SYNC) {
0226 dprintk(1, "Downstream sync error\n");
0227 cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET);
0228 return;
0229 }
0230
0231 if (status & AUD_INT_DN_RISCI1) {
0232 atomic_set(&chip->count, cx_read(MO_AUDD_GPCNT));
0233 snd_pcm_period_elapsed(chip->substream);
0234 }
0235
0236 }
0237
0238
0239
0240
0241 static irqreturn_t cx8801_irq(int irq, void *dev_id)
0242 {
0243 struct cx88_audio_dev *chip = dev_id;
0244 struct cx88_core *core = chip->core;
0245 u32 status;
0246 int loop, handled = 0;
0247
0248 for (loop = 0; loop < MAX_IRQ_LOOP; loop++) {
0249 status = cx_read(MO_PCI_INTSTAT) &
0250 (core->pci_irqmask | PCI_INT_AUDINT);
0251 if (status == 0)
0252 goto out;
0253 dprintk(3, "cx8801_irq loop %d/%d, status %x\n",
0254 loop, MAX_IRQ_LOOP, status);
0255 handled = 1;
0256 cx_write(MO_PCI_INTSTAT, status);
0257
0258 if (status & core->pci_irqmask)
0259 cx88_core_irq(core, status);
0260 if (status & PCI_INT_AUDINT)
0261 cx8801_aud_irq(chip);
0262 }
0263
0264 if (loop == MAX_IRQ_LOOP) {
0265 pr_err("IRQ loop detected, disabling interrupts\n");
0266 cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT);
0267 }
0268
0269 out:
0270 return IRQ_RETVAL(handled);
0271 }
0272
0273 static int cx88_alsa_dma_init(struct cx88_audio_dev *chip,
0274 unsigned long nr_pages)
0275 {
0276 struct cx88_audio_buffer *buf = chip->buf;
0277 struct page *pg;
0278 int i;
0279
0280 buf->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT);
0281 if (!buf->vaddr) {
0282 dprintk(1, "vmalloc_32(%lu pages) failed\n", nr_pages);
0283 return -ENOMEM;
0284 }
0285
0286 dprintk(1, "vmalloc is at addr %p, size=%lu\n",
0287 buf->vaddr, nr_pages << PAGE_SHIFT);
0288
0289 memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT);
0290 buf->nr_pages = nr_pages;
0291
0292 buf->sglist = vzalloc(array_size(sizeof(*buf->sglist), buf->nr_pages));
0293 if (!buf->sglist)
0294 goto vzalloc_err;
0295
0296 sg_init_table(buf->sglist, buf->nr_pages);
0297 for (i = 0; i < buf->nr_pages; i++) {
0298 pg = vmalloc_to_page(buf->vaddr + i * PAGE_SIZE);
0299 if (!pg)
0300 goto vmalloc_to_page_err;
0301 sg_set_page(&buf->sglist[i], pg, PAGE_SIZE, 0);
0302 }
0303 return 0;
0304
0305 vmalloc_to_page_err:
0306 vfree(buf->sglist);
0307 buf->sglist = NULL;
0308 vzalloc_err:
0309 vfree(buf->vaddr);
0310 buf->vaddr = NULL;
0311 return -ENOMEM;
0312 }
0313
0314 static int cx88_alsa_dma_map(struct cx88_audio_dev *dev)
0315 {
0316 struct cx88_audio_buffer *buf = dev->buf;
0317
0318 buf->sglen = dma_map_sg(&dev->pci->dev, buf->sglist,
0319 buf->nr_pages, DMA_FROM_DEVICE);
0320
0321 if (buf->sglen == 0) {
0322 pr_warn("%s: cx88_alsa_map_sg failed\n", __func__);
0323 return -ENOMEM;
0324 }
0325 return 0;
0326 }
0327
0328 static int cx88_alsa_dma_unmap(struct cx88_audio_dev *dev)
0329 {
0330 struct cx88_audio_buffer *buf = dev->buf;
0331
0332 if (!buf->sglen)
0333 return 0;
0334
0335 dma_unmap_sg(&dev->pci->dev, buf->sglist, buf->nr_pages,
0336 DMA_FROM_DEVICE);
0337 buf->sglen = 0;
0338 return 0;
0339 }
0340
0341 static int cx88_alsa_dma_free(struct cx88_audio_buffer *buf)
0342 {
0343 vfree(buf->sglist);
0344 buf->sglist = NULL;
0345 vfree(buf->vaddr);
0346 buf->vaddr = NULL;
0347 return 0;
0348 }
0349
0350 static int dsp_buffer_free(struct cx88_audio_dev *chip)
0351 {
0352 struct cx88_riscmem *risc = &chip->buf->risc;
0353
0354 WARN_ON(!chip->dma_size);
0355
0356 dprintk(2, "Freeing buffer\n");
0357 cx88_alsa_dma_unmap(chip);
0358 cx88_alsa_dma_free(chip->buf);
0359 if (risc->cpu)
0360 dma_free_coherent(&chip->pci->dev, risc->size, risc->cpu,
0361 risc->dma);
0362 kfree(chip->buf);
0363
0364 chip->buf = NULL;
0365
0366 return 0;
0367 }
0368
0369
0370
0371
0372
0373
0374
0375
0376 #define DEFAULT_FIFO_SIZE 4096
0377 static const struct snd_pcm_hardware snd_cx88_digital_hw = {
0378 .info = SNDRV_PCM_INFO_MMAP |
0379 SNDRV_PCM_INFO_INTERLEAVED |
0380 SNDRV_PCM_INFO_BLOCK_TRANSFER |
0381 SNDRV_PCM_INFO_MMAP_VALID,
0382 .formats = SNDRV_PCM_FMTBIT_S16_LE,
0383
0384 .rates = SNDRV_PCM_RATE_48000,
0385 .rate_min = 48000,
0386 .rate_max = 48000,
0387 .channels_min = 2,
0388 .channels_max = 2,
0389
0390
0391
0392
0393 .period_bytes_min = DEFAULT_FIFO_SIZE / 4,
0394 .period_bytes_max = DEFAULT_FIFO_SIZE / 4,
0395 .periods_min = 1,
0396 .periods_max = 1024,
0397 .buffer_bytes_max = (1024 * 1024),
0398 };
0399
0400
0401
0402
0403 static int snd_cx88_pcm_open(struct snd_pcm_substream *substream)
0404 {
0405 struct cx88_audio_dev *chip = snd_pcm_substream_chip(substream);
0406 struct snd_pcm_runtime *runtime = substream->runtime;
0407 int err;
0408
0409 if (!chip) {
0410 pr_err("BUG: cx88 can't find device struct. Can't proceed with open\n");
0411 return -ENODEV;
0412 }
0413
0414 err = snd_pcm_hw_constraint_pow2(runtime, 0,
0415 SNDRV_PCM_HW_PARAM_PERIODS);
0416 if (err < 0)
0417 goto _error;
0418
0419 chip->substream = substream;
0420
0421 runtime->hw = snd_cx88_digital_hw;
0422
0423 if (cx88_sram_channels[SRAM_CH25].fifo_size != DEFAULT_FIFO_SIZE) {
0424 unsigned int bpl = cx88_sram_channels[SRAM_CH25].fifo_size / 4;
0425
0426 bpl &= ~7;
0427 runtime->hw.period_bytes_min = bpl;
0428 runtime->hw.period_bytes_max = bpl;
0429 }
0430
0431 return 0;
0432 _error:
0433 dprintk(1, "Error opening PCM!\n");
0434 return err;
0435 }
0436
0437
0438
0439
0440 static int snd_cx88_close(struct snd_pcm_substream *substream)
0441 {
0442 return 0;
0443 }
0444
0445
0446
0447
0448 static int snd_cx88_hw_params(struct snd_pcm_substream *substream,
0449 struct snd_pcm_hw_params *hw_params)
0450 {
0451 struct cx88_audio_dev *chip = snd_pcm_substream_chip(substream);
0452
0453 struct cx88_audio_buffer *buf;
0454 int ret;
0455
0456 if (substream->runtime->dma_area) {
0457 dsp_buffer_free(chip);
0458 substream->runtime->dma_area = NULL;
0459 }
0460
0461 chip->period_size = params_period_bytes(hw_params);
0462 chip->num_periods = params_periods(hw_params);
0463 chip->dma_size = chip->period_size * params_periods(hw_params);
0464
0465 WARN_ON(!chip->dma_size);
0466 WARN_ON(chip->num_periods & (chip->num_periods - 1));
0467
0468 buf = kzalloc(sizeof(*buf), GFP_KERNEL);
0469 if (!buf)
0470 return -ENOMEM;
0471
0472 chip->buf = buf;
0473 buf->bpl = chip->period_size;
0474
0475 ret = cx88_alsa_dma_init(chip,
0476 (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT));
0477 if (ret < 0)
0478 goto error;
0479
0480 ret = cx88_alsa_dma_map(chip);
0481 if (ret < 0)
0482 goto error;
0483
0484 ret = cx88_risc_databuffer(chip->pci, &buf->risc, buf->sglist,
0485 chip->period_size, chip->num_periods, 1);
0486 if (ret < 0)
0487 goto error;
0488
0489
0490 buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
0491 buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
0492
0493 substream->runtime->dma_area = chip->buf->vaddr;
0494 substream->runtime->dma_bytes = chip->dma_size;
0495 substream->runtime->dma_addr = 0;
0496 return 0;
0497
0498 error:
0499 kfree(buf);
0500 return ret;
0501 }
0502
0503
0504
0505
0506 static int snd_cx88_hw_free(struct snd_pcm_substream *substream)
0507 {
0508 struct cx88_audio_dev *chip = snd_pcm_substream_chip(substream);
0509
0510 if (substream->runtime->dma_area) {
0511 dsp_buffer_free(chip);
0512 substream->runtime->dma_area = NULL;
0513 }
0514
0515 return 0;
0516 }
0517
0518
0519
0520
0521 static int snd_cx88_prepare(struct snd_pcm_substream *substream)
0522 {
0523 return 0;
0524 }
0525
0526
0527
0528
0529 static int snd_cx88_card_trigger(struct snd_pcm_substream *substream, int cmd)
0530 {
0531 struct cx88_audio_dev *chip = snd_pcm_substream_chip(substream);
0532 int err;
0533
0534
0535 spin_lock(&chip->reg_lock);
0536
0537 switch (cmd) {
0538 case SNDRV_PCM_TRIGGER_START:
0539 err = _cx88_start_audio_dma(chip);
0540 break;
0541 case SNDRV_PCM_TRIGGER_STOP:
0542 err = _cx88_stop_audio_dma(chip);
0543 break;
0544 default:
0545 err = -EINVAL;
0546 break;
0547 }
0548
0549 spin_unlock(&chip->reg_lock);
0550
0551 return err;
0552 }
0553
0554
0555
0556
0557 static snd_pcm_uframes_t snd_cx88_pointer(struct snd_pcm_substream *substream)
0558 {
0559 struct cx88_audio_dev *chip = snd_pcm_substream_chip(substream);
0560 struct snd_pcm_runtime *runtime = substream->runtime;
0561 u16 count;
0562
0563 count = atomic_read(&chip->count);
0564
0565
0566
0567
0568 return runtime->period_size * (count & (runtime->periods - 1));
0569 }
0570
0571
0572
0573
0574 static struct page *snd_cx88_page(struct snd_pcm_substream *substream,
0575 unsigned long offset)
0576 {
0577 void *pageptr = substream->runtime->dma_area + offset;
0578
0579 return vmalloc_to_page(pageptr);
0580 }
0581
0582
0583
0584
0585 static const struct snd_pcm_ops snd_cx88_pcm_ops = {
0586 .open = snd_cx88_pcm_open,
0587 .close = snd_cx88_close,
0588 .hw_params = snd_cx88_hw_params,
0589 .hw_free = snd_cx88_hw_free,
0590 .prepare = snd_cx88_prepare,
0591 .trigger = snd_cx88_card_trigger,
0592 .pointer = snd_cx88_pointer,
0593 .page = snd_cx88_page,
0594 };
0595
0596
0597
0598
0599 static int snd_cx88_pcm(struct cx88_audio_dev *chip, int device,
0600 const char *name)
0601 {
0602 int err;
0603 struct snd_pcm *pcm;
0604
0605 err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm);
0606 if (err < 0)
0607 return err;
0608 pcm->private_data = chip;
0609 strscpy(pcm->name, name, sizeof(pcm->name));
0610 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx88_pcm_ops);
0611
0612 return 0;
0613 }
0614
0615
0616
0617
0618 static int snd_cx88_volume_info(struct snd_kcontrol *kcontrol,
0619 struct snd_ctl_elem_info *info)
0620 {
0621 info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
0622 info->count = 2;
0623 info->value.integer.min = 0;
0624 info->value.integer.max = 0x3f;
0625
0626 return 0;
0627 }
0628
0629 static int snd_cx88_volume_get(struct snd_kcontrol *kcontrol,
0630 struct snd_ctl_elem_value *value)
0631 {
0632 struct cx88_audio_dev *chip = snd_kcontrol_chip(kcontrol);
0633 struct cx88_core *core = chip->core;
0634 int vol = 0x3f - (cx_read(AUD_VOL_CTL) & 0x3f),
0635 bal = cx_read(AUD_BAL_CTL);
0636
0637 value->value.integer.value[(bal & 0x40) ? 0 : 1] = vol;
0638 vol -= (bal & 0x3f);
0639 value->value.integer.value[(bal & 0x40) ? 1 : 0] = vol < 0 ? 0 : vol;
0640
0641 return 0;
0642 }
0643
0644 static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol,
0645 struct snd_ctl_elem_value *value)
0646 {
0647 struct cx88_audio_dev *chip = snd_kcontrol_chip(kcontrol);
0648 struct cx88_core *core = chip->core;
0649 u16 left = value->value.integer.value[0];
0650 u16 right = value->value.integer.value[1];
0651 int v, b;
0652
0653
0654 if (left >= right) {
0655 v = left << 10;
0656 b = left ? (0x8000 * right) / left : 0x8000;
0657 } else {
0658 v = right << 10;
0659 b = right ? 0xffff - (0x8000 * left) / right : 0x8000;
0660 }
0661 wm8775_s_ctrl(core, V4L2_CID_AUDIO_VOLUME, v);
0662 wm8775_s_ctrl(core, V4L2_CID_AUDIO_BALANCE, b);
0663 }
0664
0665
0666 static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol,
0667 struct snd_ctl_elem_value *value)
0668 {
0669 struct cx88_audio_dev *chip = snd_kcontrol_chip(kcontrol);
0670 struct cx88_core *core = chip->core;
0671 int left, right, v, b;
0672 int changed = 0;
0673 u32 old;
0674
0675 if (core->sd_wm8775)
0676 snd_cx88_wm8775_volume_put(kcontrol, value);
0677
0678 left = value->value.integer.value[0] & 0x3f;
0679 right = value->value.integer.value[1] & 0x3f;
0680 b = right - left;
0681 if (b < 0) {
0682 v = 0x3f - left;
0683 b = (-b) | 0x40;
0684 } else {
0685 v = 0x3f - right;
0686 }
0687
0688 spin_lock_irq(&chip->reg_lock);
0689 old = cx_read(AUD_VOL_CTL);
0690 if (v != (old & 0x3f)) {
0691 cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, (old & ~0x3f) | v);
0692 changed = 1;
0693 }
0694 if ((cx_read(AUD_BAL_CTL) & 0x7f) != b) {
0695 cx_write(AUD_BAL_CTL, b);
0696 changed = 1;
0697 }
0698 spin_unlock_irq(&chip->reg_lock);
0699
0700 return changed;
0701 }
0702
0703 static const DECLARE_TLV_DB_SCALE(snd_cx88_db_scale, -6300, 100, 0);
0704
0705 static const struct snd_kcontrol_new snd_cx88_volume = {
0706 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
0707 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
0708 SNDRV_CTL_ELEM_ACCESS_TLV_READ,
0709 .name = "Analog-TV Volume",
0710 .info = snd_cx88_volume_info,
0711 .get = snd_cx88_volume_get,
0712 .put = snd_cx88_volume_put,
0713 .tlv.p = snd_cx88_db_scale,
0714 };
0715
0716 static int snd_cx88_switch_get(struct snd_kcontrol *kcontrol,
0717 struct snd_ctl_elem_value *value)
0718 {
0719 struct cx88_audio_dev *chip = snd_kcontrol_chip(kcontrol);
0720 struct cx88_core *core = chip->core;
0721 u32 bit = kcontrol->private_value;
0722
0723 value->value.integer.value[0] = !(cx_read(AUD_VOL_CTL) & bit);
0724 return 0;
0725 }
0726
0727 static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol,
0728 struct snd_ctl_elem_value *value)
0729 {
0730 struct cx88_audio_dev *chip = snd_kcontrol_chip(kcontrol);
0731 struct cx88_core *core = chip->core;
0732 u32 bit = kcontrol->private_value;
0733 int ret = 0;
0734 u32 vol;
0735
0736 spin_lock_irq(&chip->reg_lock);
0737 vol = cx_read(AUD_VOL_CTL);
0738 if (value->value.integer.value[0] != !(vol & bit)) {
0739 vol ^= bit;
0740 cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, vol);
0741
0742 if (core->sd_wm8775 && ((1 << 6) == bit))
0743 wm8775_s_ctrl(core,
0744 V4L2_CID_AUDIO_MUTE, 0 != (vol & bit));
0745 ret = 1;
0746 }
0747 spin_unlock_irq(&chip->reg_lock);
0748 return ret;
0749 }
0750
0751 static const struct snd_kcontrol_new snd_cx88_dac_switch = {
0752 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
0753 .name = "Audio-Out Switch",
0754 .info = snd_ctl_boolean_mono_info,
0755 .get = snd_cx88_switch_get,
0756 .put = snd_cx88_switch_put,
0757 .private_value = (1 << 8),
0758 };
0759
0760 static const struct snd_kcontrol_new snd_cx88_source_switch = {
0761 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
0762 .name = "Analog-TV Switch",
0763 .info = snd_ctl_boolean_mono_info,
0764 .get = snd_cx88_switch_get,
0765 .put = snd_cx88_switch_put,
0766 .private_value = (1 << 6),
0767 };
0768
0769 static int snd_cx88_alc_get(struct snd_kcontrol *kcontrol,
0770 struct snd_ctl_elem_value *value)
0771 {
0772 struct cx88_audio_dev *chip = snd_kcontrol_chip(kcontrol);
0773 struct cx88_core *core = chip->core;
0774 s32 val;
0775
0776 val = wm8775_g_ctrl(core, V4L2_CID_AUDIO_LOUDNESS);
0777 value->value.integer.value[0] = val ? 1 : 0;
0778 return 0;
0779 }
0780
0781 static int snd_cx88_alc_put(struct snd_kcontrol *kcontrol,
0782 struct snd_ctl_elem_value *value)
0783 {
0784 struct cx88_audio_dev *chip = snd_kcontrol_chip(kcontrol);
0785 struct cx88_core *core = chip->core;
0786
0787 wm8775_s_ctrl(core, V4L2_CID_AUDIO_LOUDNESS,
0788 value->value.integer.value[0] != 0);
0789 return 0;
0790 }
0791
0792 static const struct snd_kcontrol_new snd_cx88_alc_switch = {
0793 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
0794 .name = "Line-In ALC Switch",
0795 .info = snd_ctl_boolean_mono_info,
0796 .get = snd_cx88_alc_get,
0797 .put = snd_cx88_alc_put,
0798 };
0799
0800
0801
0802
0803
0804
0805
0806
0807
0808
0809 static const struct pci_device_id cx88_audio_pci_tbl[] = {
0810 {0x14f1, 0x8801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
0811 {0x14f1, 0x8811, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
0812 {0, }
0813 };
0814 MODULE_DEVICE_TABLE(pci, cx88_audio_pci_tbl);
0815
0816
0817
0818
0819
0820 static int snd_cx88_free(struct cx88_audio_dev *chip)
0821 {
0822 if (chip->irq >= 0)
0823 free_irq(chip->irq, chip);
0824
0825 cx88_core_put(chip->core, chip->pci);
0826
0827 pci_disable_device(chip->pci);
0828 return 0;
0829 }
0830
0831
0832
0833
0834 static void snd_cx88_dev_free(struct snd_card *card)
0835 {
0836 struct cx88_audio_dev *chip = card->private_data;
0837
0838 snd_cx88_free(chip);
0839 }
0840
0841
0842
0843
0844
0845 static int devno;
0846 static int snd_cx88_create(struct snd_card *card, struct pci_dev *pci,
0847 struct cx88_audio_dev **rchip,
0848 struct cx88_core **core_ptr)
0849 {
0850 struct cx88_audio_dev *chip;
0851 struct cx88_core *core;
0852 int err;
0853 unsigned char pci_lat;
0854
0855 *rchip = NULL;
0856
0857 err = pci_enable_device(pci);
0858 if (err < 0)
0859 return err;
0860
0861 pci_set_master(pci);
0862
0863 chip = card->private_data;
0864
0865 core = cx88_core_get(pci);
0866 if (!core) {
0867 err = -EINVAL;
0868 return err;
0869 }
0870
0871 err = dma_set_mask(&pci->dev, DMA_BIT_MASK(32));
0872 if (err) {
0873 dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n", core->name);
0874 cx88_core_put(core, pci);
0875 return err;
0876 }
0877
0878
0879 chip->card = card;
0880 chip->pci = pci;
0881 chip->irq = -1;
0882 spin_lock_init(&chip->reg_lock);
0883
0884 chip->core = core;
0885
0886
0887 err = request_irq(chip->pci->irq, cx8801_irq,
0888 IRQF_SHARED, chip->core->name, chip);
0889 if (err < 0) {
0890 dprintk(0, "%s: can't get IRQ %d\n",
0891 chip->core->name, chip->pci->irq);
0892 return err;
0893 }
0894
0895
0896 pci_read_config_byte(pci, PCI_LATENCY_TIMER, &pci_lat);
0897
0898 dprintk(1,
0899 "ALSA %s/%i: found at %s, rev: %d, irq: %d, latency: %d, mmio: 0x%llx\n",
0900 core->name, devno,
0901 pci_name(pci), pci->revision, pci->irq,
0902 pci_lat, (unsigned long long)pci_resource_start(pci, 0));
0903
0904 chip->irq = pci->irq;
0905 synchronize_irq(chip->irq);
0906
0907 *rchip = chip;
0908 *core_ptr = core;
0909
0910 return 0;
0911 }
0912
0913 static int cx88_audio_initdev(struct pci_dev *pci,
0914 const struct pci_device_id *pci_id)
0915 {
0916 struct snd_card *card;
0917 struct cx88_audio_dev *chip;
0918 struct cx88_core *core = NULL;
0919 int err;
0920
0921 if (devno >= SNDRV_CARDS)
0922 return (-ENODEV);
0923
0924 if (!enable[devno]) {
0925 ++devno;
0926 return (-ENOENT);
0927 }
0928
0929 err = snd_card_new(&pci->dev, index[devno], id[devno], THIS_MODULE,
0930 sizeof(struct cx88_audio_dev), &card);
0931 if (err < 0)
0932 return err;
0933
0934 card->private_free = snd_cx88_dev_free;
0935
0936 err = snd_cx88_create(card, pci, &chip, &core);
0937 if (err < 0)
0938 goto error;
0939
0940 err = snd_cx88_pcm(chip, 0, "CX88 Digital");
0941 if (err < 0)
0942 goto error;
0943
0944 err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_volume, chip));
0945 if (err < 0)
0946 goto error;
0947 err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_dac_switch, chip));
0948 if (err < 0)
0949 goto error;
0950 err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_source_switch, chip));
0951 if (err < 0)
0952 goto error;
0953
0954
0955 if (core->sd_wm8775) {
0956 err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_alc_switch, chip));
0957 if (err < 0)
0958 goto error;
0959 }
0960
0961 strscpy(card->driver, "CX88x", sizeof(card->driver));
0962 sprintf(card->shortname, "Conexant CX%x", pci->device);
0963 sprintf(card->longname, "%s at %#llx",
0964 card->shortname,
0965 (unsigned long long)pci_resource_start(pci, 0));
0966 strscpy(card->mixername, "CX88", sizeof(card->mixername));
0967
0968 dprintk(0, "%s/%i: ALSA support for cx2388x boards\n",
0969 card->driver, devno);
0970
0971 err = snd_card_register(card);
0972 if (err < 0)
0973 goto error;
0974 pci_set_drvdata(pci, card);
0975
0976 devno++;
0977 return 0;
0978
0979 error:
0980 snd_card_free(card);
0981 return err;
0982 }
0983
0984
0985
0986
0987 static void cx88_audio_finidev(struct pci_dev *pci)
0988 {
0989 struct snd_card *card = pci_get_drvdata(pci);
0990
0991 snd_card_free(card);
0992
0993 devno--;
0994 }
0995
0996
0997
0998
0999
1000 static struct pci_driver cx88_audio_pci_driver = {
1001 .name = "cx88_audio",
1002 .id_table = cx88_audio_pci_tbl,
1003 .probe = cx88_audio_initdev,
1004 .remove = cx88_audio_finidev,
1005 };
1006
1007 module_pci_driver(cx88_audio_pci_driver);