cx88-alsa.c
来自「trident tm5600的linux驱动」· C语言 代码 · 共 979 行 · 第 1/2 页
C
979 行
/* * * Support for audio capture * PCI function #1 of the cx2388x. * * (c) 2007 Trent Piepho <xyzzy@speakeasy.org> * (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@perex.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 <linux/vmalloc.h>#include <linux/dma-mapping.h>#include <linux/pci.h>#include <asm/delay.h>#include "compat.h"#include <sound/core.h>#include <sound/pcm.h>#include <sound/pcm_params.h>#include <sound/control.h>#include <sound/initval.h>#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)#include <sound/tlv.h>#endif#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)#if 0#include <linux/calc64.h>#define MHZ 1544.426#define INT (unsigned int)(MHZ * (1<<12) + 0.5)#define FRAC (unsigned int)(MHZ * (1<<21) / 1000.0 + 0.5)#define timestamp() if(debug>=2) { u64 time; u32 rem; rdtscll(time); \ time = (time - chip->starttime)<<12; \ rem = do_div(time, INT); \ dprintk(2, "%s - called at %u.%03u us\n", __func__, \ (unsigned int)time, (rem << 9) / FRAC); }#endif/**************************************************************************** Data type declarations - Can be moded to a header file later ****************************************************************************/struct cx88_audio_dev { struct cx88_core *core; struct cx88_dmaqueue q;#if 0 u64 starttime;#endif /* pci i/o */ struct pci_dev *pci; /* audio controls */ int irq; struct snd_card *card; spinlock_t reg_lock; atomic_t count; unsigned int dma_size; unsigned int period_size; unsigned int num_periods; struct videobuf_dmabuf *dma_risc; struct cx88_buffer *buf; struct snd_pcm_substream *substream;};typedef struct cx88_audio_dev snd_cx88_card_t;#ifdef COMPAT_SND_CTL_BOOLEAN_MONOstatic int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 1; return 0;}#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};module_param_array(enable, bool, NULL, 0444);MODULE_PARM_DESC(enable, "Enable cx88x soundcard. default enabled.");module_param_array(index, int, NULL, 0444);MODULE_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]; /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */ cx_clear(MO_AUD_DMACNTRL, 0x11); /* setup fifo + format - out channel */ cx88_sram_channel_setup(chip->core, audio_ch, 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); atomic_set(&chip->count, 0); dprintk(1, "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d " "byte buffer\n", buf->bpl, cx_read(audio_ch->cmds_start + 8)>>1, chip->num_periods, buf->bpl * chip->num_periods); /* Enables corresponding bits at AUD_INT_STAT */ cx_write(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1); /* Clean any pending interrupt bits already set */ cx_write(MO_AUD_INTSTAT, ~0); /* enable audio irqs */ cx_set(MO_PCI_INTMSK, chip->core->pci_irqmask | PCI_INT_AUDINT); /* start dma */ cx_set(MO_DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */#if 0 rdtscll(chip->starttime);#endif cx_set(MO_AUD_DMACNTRL, 0x11); /* audio downstream FIFO and RISC enable */ if (debug) cx88_sram_channel_dump(chip->core, audio_ch); 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, PCI_INT_AUDINT); cx_clear(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1); if (debug) cx88_sram_channel_dump(chip->core, &cx88_sram_channels[SRAM_CH25]); return 0;}#define MAX_IRQ_LOOP 50/* * 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; status = cx_read(MO_AUD_INTSTAT); mask = cx_read(MO_AUD_INTMSK); if (0 == (status & mask)) return; cx_write(MO_AUD_INTSTAT, status); if (debug > 1 || (status & mask & ~0xff)) cx88_print_irqbits(core->name, "irq aud", cx88_aud_irqs, ARRAY_SIZE(cx88_aud_irqs), status, mask); /* risc op code error */ if (status & AUD_INT_OPC_ERR) { printk(KERN_WARNING "%s/1: Audio risc op code error\n",core->name); cx_clear(MO_AUD_DMACNTRL, 0x11); cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH25]); } if (status & AUD_INT_DN_SYNC) {#if 0 timestamp();#endif dprintk(1, "Downstream sync error\n"); cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET); return; } /* risc1 downstream */ if (status & AUD_INT_DN_RISCI1) {#if 0 timestamp();#endif atomic_set(&chip->count, cx_read(MO_AUDD_GPCNT)); snd_pcm_period_elapsed(chip->substream); } /* FIXME: Any other status should deserve a special handling? */}/* * BOARD Specific: Handles IRQ calls */#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)static irqreturn_t cx8801_irq(int irq, void *dev_id, struct pt_regs *regs)#elsestatic irqreturn_t cx8801_irq(int irq, void *dev_id)#endif{ 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 | PCI_INT_AUDINT); if (0 == status) goto out; dprintk(3, "cx8801_irq loop %d/%d, status %x\n", loop, MAX_IRQ_LOOP, status); handled = 1; cx_write(MO_PCI_INTSTAT, status); if (status & core->pci_irqmask) cx88_core_irq(core, status); if (status & PCI_INT_AUDINT) cx8801_aud_irq(chip); } if (MAX_IRQ_LOOP == loop) { printk(KERN_ERR "%s/1: IRQ loop detected, disabling interrupts\n", core->name); cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT); } 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_sg_dma_unmap(&chip->pci->dev, chip->dma_risc); videobuf_dma_free(chip->dma_risc); btcx_riscmem_free(chip->pci,&chip->buf->risc); kfree(chip->buf); chip->dma_risc = NULL; chip->dma_size = 0; return 0;}/**************************************************************************** ALSA PCM Interface ****************************************************************************//* * Digital hardware definition */#define DEFAULT_FIFO_SIZE 4096static struct snd_pcm_hardware snd_cx88_digital_hw = { .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 = 2, .channels_max = 2, /* Analog audio output will be full of clicks and pops if there are not exactly four lines in the SRAM FIFO buffer. */ .period_bytes_min = DEFAULT_FIFO_SIZE/4, .period_bytes_max = DEFAULT_FIFO_SIZE/4, .periods_min = 1, .periods_max = 1024, .buffer_bytes_max = (1024*1024),};/* * audio pcm capture open callback */static int snd_cx88_pcm_open(struct snd_pcm_substream *substream){ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; int err; if (!chip) { printk(KERN_ERR "BUG: cx88 can't find device struct." " Can't proceed with open\n"); return -ENODEV; } err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS); if (err < 0) goto _error; chip->substream = substream; runtime->hw = snd_cx88_digital_hw; if (cx88_sram_channels[SRAM_CH25].fifo_size != DEFAULT_FIFO_SIZE) { unsigned int bpl = cx88_sram_channels[SRAM_CH25].fifo_size / 4; bpl &= ~7; /* must be multiple of 8 */ runtime->hw.period_bytes_min = bpl; runtime->hw.period_bytes_max = bpl; } return 0;_error: dprintk(1,"Error opening PCM!\n"); return err;}/* * audio close callback */static int snd_cx88_close(struct snd_pcm_substream *substream){ return 0;}/* * hw_params callback */static int snd_cx88_hw_params(struct snd_pcm_substream * substream, struct snd_pcm_hw_params * hw_params){ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); struct videobuf_dmabuf *dma; struct cx88_buffer *buf; int ret; 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); BUG_ON(chip->num_periods & (chip->num_periods-1)); buf = videobuf_sg_alloc(sizeof(*buf)); if (NULL == buf) return -ENOMEM; buf->vb.memory = V4L2_MEMORY_MMAP; buf->vb.field = V4L2_FIELD_NONE; buf->vb.width = chip->period_size; buf->bpl = chip->period_size; buf->vb.height = chip->num_periods; buf->vb.size = chip->dma_size; dma = videobuf_to_dma(&buf->vb); videobuf_dma_init(dma); ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE, (PAGE_ALIGN(buf->vb.size) >> PAGE_SHIFT)); if (ret < 0) goto error; ret = videobuf_sg_dma_map(&chip->pci->dev, dma); if (ret < 0) goto error; ret = cx88_risc_databuffer(chip->pci, &buf->risc, dma->sglist, buf->vb.width, buf->vb.height, 1); if (ret < 0) goto error; /* Loop back to start of program */ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC); buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); buf->vb.state = VIDEOBUF_PREPARED; chip->buf = buf; chip->dma_risc = dma; substream->runtime->dma_area = chip->dma_risc->vmalloc; substream->runtime->dma_bytes = chip->dma_size; substream->runtime->dma_addr = 0; return 0;error: kfree(buf); return ret;}/* * hw free callback */static int snd_cx88_hw_free(struct snd_pcm_substream * substream){ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); if (substream->runtime->dma_area) { dsp_buffer_free(chip); substream->runtime->dma_area = NULL;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?