📄 cx88-alsa.c
字号:
/* * * 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 + -