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 + -
显示快捷键?