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