⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cx88-alsa.c

📁 V4l driver for DVB HD
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * *  Support for audio capture *  PCI function #1 of the cx2388x. * *    (c) 2005,2006 Ricardo Cerqueira <v4l@cerqueira.org> *    (c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org> *    Based on a dummy cx88 module by Gerd Knorr <kraxel@bytesex.org> *    Based on dummy.c by Jaroslav Kysela <perex@suse.cz> * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2 of the License, or *  (at your option) any later version. * *  This program is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with this program; if not, write to the Free Software *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/module.h>#include <linux/init.h>#include <linux/device.h>#include <linux/interrupt.h>#include <asm/delay.h>#include <sound/driver.h>#include <sound/core.h>#include <sound/pcm.h>#include <sound/pcm_params.h>#include <sound/control.h>#include <sound/initval.h>#include "compat.h"#include "cx88.h"#include "cx88-reg.h"#define dprintk(level,fmt, arg...)	if (debug >= level) \	printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg)#define dprintk_core(level,fmt, arg...)	if (debug >= level) \	printk(KERN_DEBUG "%s/1: " fmt, chip->core->name , ## arg)/****************************************************************************	Data type declarations - Can be moded to a header file later ****************************************************************************//* These can be replaced after done */#define MIXER_ADDR_LAST MAX_CX88_INPUTstruct cx88_audio_dev {	struct cx88_core           *core;	struct cx88_dmaqueue       q;	/* pci i/o */	struct pci_dev             *pci;	unsigned char              pci_rev,pci_lat;	/* audio controls */	int                        irq;#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)	snd_card_t                 *card;#else	struct snd_card            *card;#endif	spinlock_t                 reg_lock;	unsigned int               dma_size;	unsigned int               period_size;	unsigned int               num_periods;	struct videobuf_dmabuf dma_risc;	int                        mixer_volume[MIXER_ADDR_LAST+1][2];	int                        capture_source[MIXER_ADDR_LAST+1][2];	long int read_count;	long int read_offset;	struct cx88_buffer   *buf;	long opened;#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)	snd_pcm_substream_t *substream;#else	struct snd_pcm_substream *substream;#endif};typedef struct cx88_audio_dev snd_cx88_card_t;#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,8)#define chip_t snd_cx88_card_t#endif/****************************************************************************			Module global static vars ****************************************************************************/static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1};#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)static snd_card_t *snd_cx88_cards[SNDRV_CARDS];#elsestatic struct snd_card *snd_cx88_cards[SNDRV_CARDS];#endif#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)static unsigned int dummy;module_param_array(enable, bool, dummy, 0444);#elsemodule_param_array(enable, bool, NULL, 0444);#endifMODULE_PARM_DESC(enable, "Enable cx88x soundcard. default enabled.");#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)module_param_array(index, int, dummy, 0444);#elsemodule_param_array(index, int, NULL, 0444);#endifMODULE_PARM_DESC(index, "Index value for cx88x capture interface(s).");/****************************************************************************				Module macros ****************************************************************************/MODULE_DESCRIPTION("ALSA driver module for cx2388x based TV cards");MODULE_AUTHOR("Ricardo Cerqueira");MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{{Conexant,23881},"			"{{Conexant,23882},"			"{{Conexant,23883}");static unsigned int debug;module_param(debug,int,0644);MODULE_PARM_DESC(debug,"enable debug messages");/****************************************************************************			Module specific funtions ****************************************************************************//* * BOARD Specific: Sets audio DMA */static int _cx88_start_audio_dma(snd_cx88_card_t *chip){	struct cx88_buffer   *buf = chip->buf;	struct cx88_core *core=chip->core;	struct sram_channel *audio_ch = &cx88_sram_channels[SRAM_CH25];	dprintk(1, "Starting audio DMA for %i bytes/line and %i (%i) lines at address %08x\n",buf->bpl, chip->num_periods, audio_ch->fifo_size / buf->bpl, audio_ch->fifo_start);	/* setup fifo + format - out channel */	cx88_sram_channel_setup(chip->core, &cx88_sram_channels[SRAM_CH25],				buf->bpl, buf->risc.dma);	/* sets bpl size */	cx_write(MO_AUDD_LNGTH, buf->bpl);	/* reset counter */	cx_write(MO_AUDD_GPCNTRL,GP_COUNT_CONTROL_RESET);	dprintk(1,"Enabling IRQ, setting mask from 0x%x to 0x%x\n",chip->core->pci_irqmask,(chip->core->pci_irqmask | 0x02));	/* enable irqs */	cx_set(MO_PCI_INTMSK, chip->core->pci_irqmask | 0x02);	/* Enables corresponding bits at AUD_INT_STAT */	cx_write(MO_AUD_INTMSK,			(1<<16)|			(1<<12)|			(1<<4)|			(1<<0)			);	/* start dma */	cx_set(MO_DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */	cx_set(MO_AUD_DMACNTRL, 0x11); /* audio downstream FIFO and RISC enable */	if (debug)		cx88_sram_channel_dump(chip->core, &cx88_sram_channels[SRAM_CH25]);	return 0;}/* * BOARD Specific: Resets audio DMA */static int _cx88_stop_audio_dma(snd_cx88_card_t *chip){	struct cx88_core *core=chip->core;	dprintk(1, "Stopping audio DMA\n");	/* stop dma */	cx_clear(MO_AUD_DMACNTRL, 0x11);	/* disable irqs */	cx_clear(MO_PCI_INTMSK, 0x02);	cx_clear(MO_AUD_INTMSK,			(1<<16)|			(1<<12)|			(1<<4)|			(1<<0)			);	if (debug)		cx88_sram_channel_dump(chip->core, &cx88_sram_channels[SRAM_CH25]);	return 0;}#define MAX_IRQ_LOOP 10/* * BOARD Specific: IRQ dma bits */static char *cx88_aud_irqs[32] = {	"dn_risci1", "up_risci1", "rds_dn_risc1", /* 0-2 */	NULL,					  /* reserved */	"dn_risci2", "up_risci2", "rds_dn_risc2", /* 4-6 */	NULL,					  /* reserved */	"dnf_of", "upf_uf", "rds_dnf_uf",	  /* 8-10 */	NULL,					  /* reserved */	"dn_sync", "up_sync", "rds_dn_sync",	  /* 12-14 */	NULL,					  /* reserved */	"opc_err", "par_err", "rip_err",	  /* 16-18 */	"pci_abort", "ber_irq", "mchg_irq"	  /* 19-21 */};/* * BOARD Specific: Threats IRQ audio specific calls */static void cx8801_aud_irq(snd_cx88_card_t *chip){	struct cx88_core *core = chip->core;	u32 status, mask;	u32 count;	status = cx_read(MO_AUD_INTSTAT);	mask   = cx_read(MO_AUD_INTMSK);	if (0 == (status & mask)) {		spin_unlock(&chip->reg_lock);		return;	}	cx_write(MO_AUD_INTSTAT, status);	if (debug > 1  ||  (status & mask & ~0xff))		cx88_print_irqbits(core->name, "irq aud",				   cx88_aud_irqs, status, mask);	/* risc op code error */	if (status & (1 << 16)) {		printk(KERN_WARNING "%s/0: audio risc op code error\n",core->name);		cx_clear(MO_AUD_DMACNTRL, 0x11);		cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH25]);	}	/* risc1 downstream */	if (status & 0x01) {		spin_lock(&chip->reg_lock);		count = cx_read(MO_AUDD_GPCNT);		spin_unlock(&chip->reg_lock);		if (chip->read_count == 0)			chip->read_count += chip->dma_size;	}	if  (chip->read_count >= chip->period_size) {		dprintk(2, "Elapsing period\n");		snd_pcm_period_elapsed(chip->substream);	}	dprintk(3,"Leaving audio IRQ handler...\n");	/* FIXME: Any other status should deserve a special handling? */}/* * BOARD Specific: Handles IRQ calls */static irqreturn_t cx8801_irq(int irq, void *dev_id, struct pt_regs *regs){	snd_cx88_card_t *chip = dev_id;	struct cx88_core *core = chip->core;	u32 status;	int loop, handled = 0;	for (loop = 0; loop < MAX_IRQ_LOOP; loop++) {		status = cx_read(MO_PCI_INTSTAT) & (core->pci_irqmask | 0x02);		if (0 == status)			goto out;		dprintk( 3, "cx8801_irq\n" );		dprintk( 3, "    loop: %d/%d\n", loop, MAX_IRQ_LOOP );		dprintk( 3, "    status: %d\n", status );		handled = 1;		cx_write(MO_PCI_INTSTAT, status);		if (status & 0x02)		{			dprintk( 2, "    ALSA IRQ handling\n" );			cx8801_aud_irq(chip);		}	};	if (MAX_IRQ_LOOP == loop) {		dprintk( 0, "clearing mask\n" );		dprintk(1,"%s/0: irq loop -- clearing mask\n",		       core->name);		cx_clear(MO_PCI_INTMSK,0x02);	} out:	return IRQ_RETVAL(handled);}static int dsp_buffer_free(snd_cx88_card_t *chip){	BUG_ON(!chip->dma_size);	dprintk(2,"Freeing buffer\n");	videobuf_pci_dma_unmap(chip->pci, &chip->dma_risc);	videobuf_dma_free(&chip->dma_risc);	btcx_riscmem_free(chip->pci,&chip->buf->risc);	kfree(chip->buf);	chip->dma_size = 0;       return 0;}/****************************************************************************				ALSA PCM Interface ****************************************************************************//* * Digital hardware definition */#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)static snd_pcm_hardware_t snd_cx88_digital_hw = {#elsestatic struct snd_pcm_hardware snd_cx88_digital_hw = {#endif	.info = SNDRV_PCM_INFO_MMAP |		SNDRV_PCM_INFO_INTERLEAVED |		SNDRV_PCM_INFO_BLOCK_TRANSFER |		SNDRV_PCM_INFO_MMAP_VALID,	.formats = SNDRV_PCM_FMTBIT_S16_LE,	.rates =		SNDRV_PCM_RATE_48000,	.rate_min =		48000,	.rate_max =		48000,	.channels_min = 1,	.channels_max = 2,	.buffer_bytes_max = (2*2048),	.period_bytes_min = 2048,	.period_bytes_max = 2048,	.periods_min = 2,	.periods_max = 2,};/* * audio pcm capture runtime free */#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)static void snd_card_cx88_runtime_free(snd_pcm_runtime_t *runtime)#elsestatic void snd_card_cx88_runtime_free(struct snd_pcm_runtime *runtime)#endif{}/* * audio pcm capture open callback */#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)static int snd_cx88_pcm_open(snd_pcm_substream_t *substream)#elsestatic int snd_cx88_pcm_open(struct snd_pcm_substream *substream)#endif{	snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)	snd_pcm_runtime_t *runtime = substream->runtime;#else	struct snd_pcm_runtime *runtime = substream->runtime;#endif	int err;	if (test_and_set_bit(0, &chip->opened))		return -EBUSY;	err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);	if (err < 0)		goto _error;	chip->substream = substream;	chip->read_count = 0;	chip->read_offset = 0;	runtime->private_free = snd_card_cx88_runtime_free;	runtime->hw = snd_cx88_digital_hw;	return 0;_error:	dprintk(1,"Error opening PCM!\n");	clear_bit(0, &chip->opened);	smp_mb__after_clear_bit();	return err;}/* * audio close callback */#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)static int snd_cx88_close(snd_pcm_substream_t *substream)#elsestatic int snd_cx88_close(struct snd_pcm_substream *substream)#endif{	snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);	clear_bit(0, &chip->opened);	smp_mb__after_clear_bit();	return 0;}/* * hw_params callback */#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)static int snd_cx88_hw_params(snd_pcm_substream_t * substream,				 snd_pcm_hw_params_t * hw_params)#elsestatic int snd_cx88_hw_params(struct snd_pcm_substream * substream,			      struct snd_pcm_hw_params * hw_params)#endif{	snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);	struct cx88_buffer *buf;	if (substream->runtime->dma_area) {		dsp_buffer_free(chip);		substream->runtime->dma_area = NULL;	}	chip->period_size = params_period_bytes(hw_params);	chip->num_periods = params_periods(hw_params);	chip->dma_size = chip->period_size * params_periods(hw_params);	BUG_ON(!chip->dma_size);	dprintk(1,"Setting buffer\n");	buf = kmalloc(sizeof(*buf),GFP_KERNEL);	if (NULL == buf)		return -ENOMEM;	memset(buf,0,sizeof(*buf));	buf->vb.memory = V4L2_MEMORY_MMAP;	buf->vb.width  = chip->period_size;	buf->vb.height = chip->num_periods;	buf->vb.size   = chip->dma_size;	buf->vb.field  = V4L2_FIELD_NONE;	videobuf_dma_init(&buf->vb.dma);	videobuf_dma_init_kernel(&buf->vb.dma,PCI_DMA_FROMDEVICE,			(PAGE_ALIGN(buf->vb.size) >> PAGE_SHIFT));	videobuf_pci_dma_map(chip->pci,&buf->vb.dma);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -