sc6000.c
来自「linux 内核源代码」· C语言 代码 · 共 657 行 · 第 1/2 页
C
657 行
/* * Driver for Gallant SC-6000 soundcard. This card is also known as * Audio Excel DSP 16 or Zoltrix AV302. * These cards use CompuMedia ASC-9308 chip + AD1848 codec. * * Copyright (C) 2007 Krzysztof Helt <krzysztof.h1@wp.pl> * * I don't have documentation for this card. I used the driver * for OSS/Free included in the kernel source as reference. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <sound/driver.h>#include <linux/module.h>#include <linux/delay.h>#include <linux/isa.h>#include <linux/io.h>#include <asm/dma.h>#include <sound/core.h>#include <sound/ad1848.h>#include <sound/opl3.h>#include <sound/mpu401.h>#include <sound/control.h>#define SNDRV_LEGACY_FIND_FREE_IRQ#define SNDRV_LEGACY_FIND_FREE_DMA#include <sound/initval.h>MODULE_AUTHOR("Krzysztof Helt");MODULE_DESCRIPTION("Gallant SC-6000");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{{Gallant, SC-6000}," "{AudioExcel, Audio Excel DSP 16}," "{Zoltrix, AV302}}");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] = SNDRV_DEFAULT_ENABLE; /* Enable this card */static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220, 0x240 */static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5, 7, 9, 10, 11 */static long mss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530, 0xe80 */static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x300, 0x310, 0x320, 0x330 */static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5, 7, 9, 10, 0 */static int dma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0, 1, 3 */module_param_array(index, int, NULL, 0444);MODULE_PARM_DESC(index, "Index value for sc-6000 based soundcard.");module_param_array(id, charp, NULL, 0444);MODULE_PARM_DESC(id, "ID string for sc-6000 based soundcard.");module_param_array(enable, bool, NULL, 0444);MODULE_PARM_DESC(enable, "Enable sc-6000 based soundcard.");module_param_array(port, long, NULL, 0444);MODULE_PARM_DESC(port, "Port # for sc-6000 driver.");module_param_array(mss_port, long, NULL, 0444);MODULE_PARM_DESC(mss_port, "MSS Port # for sc-6000 driver.");module_param_array(mpu_port, long, NULL, 0444);MODULE_PARM_DESC(mpu_port, "MPU-401 port # for sc-6000 driver.");module_param_array(irq, int, NULL, 0444);MODULE_PARM_DESC(irq, "IRQ # for sc-6000 driver.");module_param_array(mpu_irq, int, NULL, 0444);MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for sc-6000 driver.");module_param_array(dma, int, NULL, 0444);MODULE_PARM_DESC(dma, "DMA # for sc-6000 driver.");/* * Commands of SC6000's DSP (SBPRO+special). * Some of them are COMMAND_xx, in the future they may change. */#define WRITE_MDIRQ_CFG 0x50 /* Set M&I&DRQ mask (the real config) */#define COMMAND_52 0x52 /* */#define READ_HARD_CFG 0x58 /* Read Hardware Config (I/O base etc) */#define COMMAND_5C 0x5c /* */#define COMMAND_60 0x60 /* */#define COMMAND_66 0x66 /* */#define COMMAND_6C 0x6c /* */#define COMMAND_6E 0x6e /* */#define COMMAND_88 0x88 /* Unknown command */#define DSP_INIT_MSS 0x8c /* Enable Microsoft Sound System mode */#define COMMAND_C5 0xc5 /* */#define GET_DSP_VERSION 0xe1 /* Get DSP Version */#define GET_DSP_COPYRIGHT 0xe3 /* Get DSP Copyright *//* * Offsets of SC6000 DSP I/O ports. The offset is added to base I/O port * to have the actual I/O port. * Register permissions are: * (wo) == Write Only * (ro) == Read Only * (w-) == Write * (r-) == Read */#define DSP_RESET 0x06 /* offset of DSP RESET (wo) */#define DSP_READ 0x0a /* offset of DSP READ (ro) */#define DSP_WRITE 0x0c /* offset of DSP WRITE (w-) */#define DSP_COMMAND 0x0c /* offset of DSP COMMAND (w-) */#define DSP_STATUS 0x0c /* offset of DSP STATUS (r-) */#define DSP_DATAVAIL 0x0e /* offset of DSP DATA AVAILABLE (ro) */#define PFX "sc6000: "#define DRV_NAME "SC-6000"/* hardware dependent functions *//* * sc6000_irq_to_softcfg - Decode irq number into cfg code. */static __devinit unsigned char sc6000_irq_to_softcfg(int irq){ unsigned char val = 0; switch (irq) { case 5: val = 0x28; break; case 7: val = 0x8; break; case 9: val = 0x10; break; case 10: val = 0x18; break; case 11: val = 0x20; break; default: break; } return val;}/* * sc6000_dma_to_softcfg - Decode dma number into cfg code. */static __devinit unsigned char sc6000_dma_to_softcfg(int dma){ unsigned char val = 0; switch (dma) { case 0: val = 1; break; case 1: val = 2; break; case 3: val = 3; break; default: break; } return val;}/* * sc6000_mpu_irq_to_softcfg - Decode MPU-401 irq number into cfg code. */static __devinit unsigned char sc6000_mpu_irq_to_softcfg(int mpu_irq){ unsigned char val = 0; switch (mpu_irq) { case 5: val = 4; break; case 7: val = 0x44; break; case 9: val = 0x84; break; case 10: val = 0xc4; break; default: break; } return val;}static __devinit int sc6000_wait_data(char __iomem *vport){ int loop = 1000; unsigned char val = 0; do { val = ioread8(vport + DSP_DATAVAIL); if (val & 0x80) return 0; cpu_relax(); } while (loop--); return -EAGAIN;}static __devinit int sc6000_read(char __iomem *vport){ if (sc6000_wait_data(vport)) return -EBUSY; return ioread8(vport + DSP_READ);}static __devinit int sc6000_write(char __iomem *vport, int cmd){ unsigned char val; int loop = 500000; do { val = ioread8(vport + DSP_STATUS); /* * DSP ready to receive data if bit 7 of val == 0 */ if (!(val & 0x80)) { iowrite8(cmd, vport + DSP_COMMAND); return 0; } cpu_relax(); } while (loop--); snd_printk(KERN_ERR "DSP Command (0x%x) timeout.\n", cmd); return -EIO;}static int __devinit sc6000_dsp_get_answer(char __iomem *vport, int command, char *data, int data_len){ int len = 0; if (sc6000_write(vport, command)) { snd_printk(KERN_ERR "CMD 0x%x: failed!\n", command); return -EIO; } do { int val = sc6000_read(vport); if (val < 0) break; data[len++] = val; } while (len < data_len); /* * If no more data available, return to the caller, no error if len>0. * We have no other way to know when the string is finished. */ return len ? len : -EIO;}static int __devinit sc6000_dsp_reset(char __iomem *vport){ iowrite8(1, vport + DSP_RESET); udelay(10); iowrite8(0, vport + DSP_RESET); udelay(20); if (sc6000_read(vport) == 0xaa) return 0; return -ENODEV;}/* detection and initialization */static int __devinit sc6000_cfg_write(char __iomem *vport, unsigned char softcfg){ if (sc6000_write(vport, WRITE_MDIRQ_CFG)) { snd_printk(KERN_ERR "CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG); return -EIO; } if (sc6000_write(vport, softcfg)) { snd_printk(KERN_ERR "sc6000_cfg_write: failed!\n"); return -EIO; } return 0;}static int __devinit sc6000_setup_board(char __iomem *vport, int config){ int loop = 10; do { if (sc6000_write(vport, COMMAND_88)) { snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_88); return -EIO; } } while ((sc6000_wait_data(vport) < 0) && loop--); if (sc6000_read(vport) < 0) { snd_printk(KERN_ERR "sc6000_read after CMD 0x%x: failed\n", COMMAND_88); return -EIO; } if (sc6000_cfg_write(vport, config)) return -ENODEV; return 0;}static int __devinit sc6000_init_mss(char __iomem *vport, int config, char __iomem *vmss_port, int mss_config){ if (sc6000_write(vport, DSP_INIT_MSS)) { snd_printk(KERN_ERR "sc6000_init_mss [0x%x]: failed!\n", DSP_INIT_MSS); return -EIO; } msleep(10);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?