cx88-alsa.c
来自「trident tm5600的linux驱动」· C语言 代码 · 共 979 行 · 第 1/2 页
C
979 行
} return 0;}/* * prepare callback */static int snd_cx88_prepare(struct snd_pcm_substream *substream){ return 0;}/* * trigger callback */static int snd_cx88_card_trigger(struct snd_pcm_substream *substream, int cmd){ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); int err;#if 0 timestamp();#endif /* Local interrupts are already disabled by ALSA */ spin_lock(&chip->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: err=_cx88_start_audio_dma(chip); break; case SNDRV_PCM_TRIGGER_STOP: err=_cx88_stop_audio_dma(chip); break; default: err=-EINVAL; break; } spin_unlock(&chip->reg_lock); return err;}/* * pointer callback */static snd_pcm_uframes_t snd_cx88_pointer(struct snd_pcm_substream *substream){ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; u16 count;#if 0 timestamp();#endif count = atomic_read(&chip->count);// dprintk(2, "%s - count %d (+%u), period %d, frame %lu\n", __func__,// count, new, count & (runtime->periods-1),// runtime->period_size * (count & (runtime->periods-1))); return runtime->period_size * (count & (runtime->periods-1));}/* * page callback (needed for mmap) */static struct page *snd_cx88_page(struct snd_pcm_substream *substream, unsigned long offset){ void *pageptr = substream->runtime->dma_area + offset; return vmalloc_to_page(pageptr);}/* * operators */static struct snd_pcm_ops snd_cx88_pcm_ops = { .open = snd_cx88_pcm_open, .close = snd_cx88_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_cx88_hw_params, .hw_free = snd_cx88_hw_free, .prepare = snd_cx88_prepare, .trigger = snd_cx88_card_trigger, .pointer = snd_cx88_pointer, .page = snd_cx88_page,};/* * create a PCM device */static int __devinit snd_cx88_pcm(snd_cx88_card_t *chip, int device, char *name){ int err; struct snd_pcm *pcm; err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm); if (err < 0) return err; pcm->private_data = chip; strcpy(pcm->name, name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx88_pcm_ops); return 0;}/**************************************************************************** CONTROL INTERFACE ****************************************************************************/static int snd_cx88_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *info){ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; info->count = 2; info->value.integer.min = 0; info->value.integer.max = 0x3f; return 0;}static int snd_cx88_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value){ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); struct cx88_core *core=chip->core; int vol = 0x3f - (cx_read(AUD_VOL_CTL) & 0x3f), bal = cx_read(AUD_BAL_CTL); value->value.integer.value[(bal & 0x40) ? 0 : 1] = vol; vol -= (bal & 0x3f); value->value.integer.value[(bal & 0x40) ? 1 : 0] = vol < 0 ? 0 : vol; return 0;}/* OK - TODO: test it */static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value){ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); struct cx88_core *core=chip->core; int v, b; int changed = 0; u32 old; b = value->value.integer.value[1] - value->value.integer.value[0]; if (b < 0) { v = 0x3f - value->value.integer.value[0]; b = (-b) | 0x40; } else { v = 0x3f - value->value.integer.value[1]; } /* Do we really know this will always be called with IRQs on? */ spin_lock_irq(&chip->reg_lock); old = cx_read(AUD_VOL_CTL); if (v != (old & 0x3f)) { cx_write(AUD_VOL_CTL, (old & ~0x3f) | v); changed = 1; } if (cx_read(AUD_BAL_CTL) != b) { cx_write(AUD_BAL_CTL, b); changed = 1; } spin_unlock_irq(&chip->reg_lock); return changed;}#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)static const DECLARE_TLV_DB_SCALE(snd_cx88_db_scale, -6300, 100, 0);#endifstatic struct snd_kcontrol_new snd_cx88_volume = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,#endif .name = "Playback Volume", .info = snd_cx88_volume_info, .get = snd_cx88_volume_get, .put = snd_cx88_volume_put,#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) .tlv.p = snd_cx88_db_scale,#endif};static int snd_cx88_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value){ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); struct cx88_core *core = chip->core; u32 bit = kcontrol->private_value; value->value.integer.value[0] = !(cx_read(AUD_VOL_CTL) & bit); return 0;}static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value){ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); struct cx88_core *core = chip->core; u32 bit = kcontrol->private_value; int ret = 0; u32 vol; spin_lock_irq(&chip->reg_lock); vol = cx_read(AUD_VOL_CTL); if (value->value.integer.value[0] != !(vol & bit)) { vol ^= bit; cx_write(AUD_VOL_CTL, vol); ret = 1; } spin_unlock_irq(&chip->reg_lock); return ret;}static struct snd_kcontrol_new snd_cx88_dac_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Playback Switch", .info = snd_ctl_boolean_mono_info, .get = snd_cx88_switch_get, .put = snd_cx88_switch_put, .private_value = (1<<8),};static struct snd_kcontrol_new snd_cx88_source_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Switch", .info = snd_ctl_boolean_mono_info, .get = snd_cx88_switch_get, .put = snd_cx88_switch_put, .private_value = (1<<6),};/**************************************************************************** Basic Flow for Sound Devices ****************************************************************************//* * PCI ID Table - 14f1:8801 and 14f1:8811 means function 1: Audio * Only boards with eeprom and byte 1 at eeprom=1 have it */static struct pci_device_id cx88_audio_pci_tbl[] __devinitdata = { {0x14f1,0x8801,PCI_ANY_ID,PCI_ANY_ID,0,0,0}, {0x14f1,0x8811,PCI_ANY_ID,PCI_ANY_ID,0,0,0}, {0, }};MODULE_DEVICE_TABLE(pci, cx88_audio_pci_tbl);/* * Chip-specific destructor */static int snd_cx88_free(snd_cx88_card_t *chip){ if (chip->irq >= 0) free_irq(chip->irq, chip); cx88_core_put(chip->core,chip->pci); pci_disable_device(chip->pci); return 0;}/* * Component Destructor */static void snd_cx88_dev_free(struct snd_card * card){ snd_cx88_card_t *chip = card->private_data; snd_cx88_free(chip);}/* * Alsa Constructor - Component probe */static int devno;static int __devinit snd_cx88_create(struct snd_card *card, struct pci_dev *pci, snd_cx88_card_t **rchip){ snd_cx88_card_t *chip; struct cx88_core *core; int err; unsigned char pci_lat; *rchip = NULL; err = pci_enable_device(pci); if (err < 0) return err; pci_set_master(pci); chip = (snd_cx88_card_t *) card->private_data; core = cx88_core_get(pci); if (NULL == core) { err = -EINVAL; kfree (chip); return err; } if (!pci_dma_supported(pci,DMA_32BIT_MASK)) { dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name); err = -EIO; cx88_core_put(core,pci); return err; } /* pci init */ chip->card = card; chip->pci = pci; chip->irq = -1; spin_lock_init(&chip->reg_lock); chip->core = core; /* get irq */ err = request_irq(chip->pci->irq, cx8801_irq, IRQF_SHARED | IRQF_DISABLED, chip->core->name, chip); if (err < 0) { dprintk(0, "%s: can't get IRQ %d\n", chip->core->name, chip->pci->irq); return err; } /* print pci info */ pci_read_config_byte(pci, PCI_LATENCY_TIMER, &pci_lat);#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) dprintk(1,"ALSA %s/%i: found at %s, rev: %d, irq: %d, " "latency: %d, mmio: 0x%llx\n", core->name, devno, pci_name(pci), pci->revision, pci->irq, pci_lat, (unsigned long long)pci_resource_start(pci,0));#else dprintk(1,"ALSA %s/%i: found at %s, rev: %d, irq: %d, " "latency: %d, mmio: 0x%llx\n", core->name, devno, pci_name(pci), v4l_compat_pci_rev(pci), pci->irq, pci_lat, (unsigned long long)pci_resource_start(pci,0));#endif chip->irq = pci->irq; synchronize_irq(chip->irq); snd_card_set_dev(card, &pci->dev); *rchip = chip; return 0;}static int __devinit cx88_audio_initdev(struct pci_dev *pci, const struct pci_device_id *pci_id){ struct snd_card *card; snd_cx88_card_t *chip; int err; if (devno >= SNDRV_CARDS) return (-ENODEV); if (!enable[devno]) { ++devno; return (-ENOENT); } card = snd_card_new(index[devno], id[devno], THIS_MODULE, sizeof(snd_cx88_card_t)); if (!card) return (-ENOMEM); card->private_free = snd_cx88_dev_free; err = snd_cx88_create(card, pci, &chip); if (err < 0) return (err); err = snd_cx88_pcm(chip, 0, "CX88 Digital"); if (err < 0) goto error; err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_volume, chip)); if (err < 0) goto error; err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_dac_switch, chip)); if (err < 0) goto error; err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_source_switch, chip)); if (err < 0) goto error; strcpy (card->driver, "CX88x"); sprintf(card->shortname, "Conexant CX%x", pci->device); sprintf(card->longname, "%s at %#llx", card->shortname,(unsigned long long)pci_resource_start(pci, 0)); strcpy (card->mixername, "CX88"); dprintk (0, "%s/%i: ALSA support for cx2388x boards\n", card->driver,devno); err = snd_card_register(card); if (err < 0) goto error; pci_set_drvdata(pci,card); devno++; return 0;error: snd_card_free(card); return err;}/* * ALSA destructor */static void __devexit cx88_audio_finidev(struct pci_dev *pci){ struct cx88_audio_dev *card = pci_get_drvdata(pci); snd_card_free((void *)card); pci_set_drvdata(pci, NULL); devno--;}#if 0 .suspend = cx88_audio_suspend, .resume = cx88_audio_resume,#endif/* * PCI driver definition */static struct pci_driver cx88_audio_pci_driver = { .name = "cx88_audio", .id_table = cx88_audio_pci_tbl, .probe = cx88_audio_initdev, .remove = cx88_audio_finidev,};/**************************************************************************** LINUX MODULE INIT ****************************************************************************//* * module init */static int cx88_audio_init(void){ printk(KERN_INFO "cx2388x alsa driver version %d.%d.%d loaded\n", (CX88_VERSION_CODE >> 16) & 0xff, (CX88_VERSION_CODE >> 8) & 0xff, CX88_VERSION_CODE & 0xff);#ifdef SNAPSHOT printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n", SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);#endif return pci_register_driver(&cx88_audio_pci_driver);}/* * module remove */static void cx88_audio_fini(void){ pci_unregister_driver(&cx88_audio_pci_driver);}module_init(cx88_audio_init);module_exit(cx88_audio_fini);/* ----------------------------------------------------------- *//* * Local variables: * c-basic-offset: 8 * End: */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?