?? miro.c
字號:
/* * ALSA soundcard driver for Miro miroSOUND PCM1 pro * miroSOUND PCM12 * miroSOUND PCM20 Radio * * Copyright (C) 2004-2005 Martin Langer <martin-langer@gmx.de> * * Based on OSS ACI and ALSA OPTi9xx drivers * * 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/init.h>#include <linux/err.h>#include <linux/isa.h>#include <linux/delay.h>#include <linux/slab.h>#include <linux/ioport.h>#include <linux/moduleparam.h>#include <asm/io.h>#include <asm/dma.h>#include <sound/core.h>#include <sound/cs4231.h>#include <sound/mpu401.h>#include <sound/opl4.h>#include <sound/control.h>#include <sound/info.h>#define SNDRV_LEGACY_FIND_FREE_IRQ#define SNDRV_LEGACY_FIND_FREE_DMA#include <sound/initval.h>#include "miro.h"MODULE_AUTHOR("Martin Langer <martin-langer@gmx.de>");MODULE_LICENSE("GPL");MODULE_DESCRIPTION("Miro miroSOUND PCM1 pro, PCM12, PCM20 Radio");MODULE_SUPPORTED_DEVICE("{{Miro,miroSOUND PCM1 pro}, " "{Miro,miroSOUND PCM12}, " "{Miro,miroSOUND PCM20 Radio}}");static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */static long port = SNDRV_DEFAULT_PORT1; /* 0x530,0xe80,0xf40,0x604 */static long mpu_port = SNDRV_DEFAULT_PORT1; /* 0x300,0x310,0x320,0x330 */static long fm_port = SNDRV_DEFAULT_PORT1; /* 0x388 */static int irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10,11 */static int mpu_irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10 */static int dma1 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */static int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */static int wss;static int ide;module_param(index, int, 0444);MODULE_PARM_DESC(index, "Index value for miro soundcard.");module_param(id, charp, 0444);MODULE_PARM_DESC(id, "ID string for miro soundcard.");module_param(port, long, 0444);MODULE_PARM_DESC(port, "WSS port # for miro driver.");module_param(mpu_port, long, 0444);MODULE_PARM_DESC(mpu_port, "MPU-401 port # for miro driver.");module_param(fm_port, long, 0444);MODULE_PARM_DESC(fm_port, "FM Port # for miro driver.");module_param(irq, int, 0444);MODULE_PARM_DESC(irq, "WSS irq # for miro driver.");module_param(mpu_irq, int, 0444);MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for miro driver.");module_param(dma1, int, 0444);MODULE_PARM_DESC(dma1, "1st dma # for miro driver.");module_param(dma2, int, 0444);MODULE_PARM_DESC(dma2, "2nd dma # for miro driver.");module_param(wss, int, 0444);MODULE_PARM_DESC(wss, "wss mode");module_param(ide, int, 0444);MODULE_PARM_DESC(ide, "enable ide port");#define OPTi9XX_HW_DETECT 0#define OPTi9XX_HW_82C928 1#define OPTi9XX_HW_82C929 2#define OPTi9XX_HW_82C924 3#define OPTi9XX_HW_82C925 4#define OPTi9XX_HW_82C930 5#define OPTi9XX_HW_82C931 6#define OPTi9XX_HW_82C933 7#define OPTi9XX_HW_LAST OPTi9XX_HW_82C933#define OPTi9XX_MC_REG(n) nstruct snd_miro { unsigned short hardware; unsigned char password; char name[7]; struct resource *res_mc_base; struct resource *res_aci_port; unsigned long mc_base; unsigned long mc_base_size; unsigned long pwd_reg; spinlock_t lock; struct snd_card *card; struct snd_pcm *pcm; long wss_base; int irq; int dma1; int dma2; long fm_port; long mpu_port; int mpu_irq; unsigned long aci_port; int aci_vendor; int aci_product; int aci_version; int aci_amp; int aci_preamp; int aci_solomode; struct mutex aci_mutex;};static void snd_miro_proc_init(struct snd_miro * miro);static char * snd_opti9xx_names[] = { "unkown", "82C928", "82C929", "82C924", "82C925", "82C930", "82C931", "82C933"};/* * ACI control */static int aci_busy_wait(struct snd_miro * miro){ long timeout; unsigned char byte; for (timeout = 1; timeout <= ACI_MINTIME+30; timeout++) { if (((byte=inb(miro->aci_port + ACI_REG_BUSY)) & 1) == 0) { if (timeout >= ACI_MINTIME) snd_printd("aci ready in round %ld.\n", timeout-ACI_MINTIME); return byte; } if (timeout >= ACI_MINTIME) { long out=10*HZ; switch (timeout-ACI_MINTIME) { case 0 ... 9: out /= 10; case 10 ... 19: out /= 10; case 20 ... 30: out /= 10; default: set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(out); break; } } } snd_printk(KERN_ERR "aci_busy_wait() time out\n"); return -EBUSY;}static inline int aci_write(struct snd_miro * miro, unsigned char byte){ if (aci_busy_wait(miro) >= 0) { outb(byte, miro->aci_port + ACI_REG_COMMAND); return 0; } else { snd_printk(KERN_ERR "aci busy, aci_write(0x%x) stopped.\n", byte); return -EBUSY; }}static inline int aci_read(struct snd_miro * miro){ unsigned char byte; if (aci_busy_wait(miro) >= 0) { byte=inb(miro->aci_port + ACI_REG_STATUS); return byte; } else { snd_printk(KERN_ERR "aci busy, aci_read() stopped.\n"); return -EBUSY; }}static int aci_cmd(struct snd_miro * miro, int write1, int write2, int write3){ int write[] = {write1, write2, write3}; int value, i; if (mutex_lock_interruptible(&miro->aci_mutex)) return -EINTR; for (i=0; i<3; i++) { if (write[i]< 0 || write[i] > 255) break; else { value = aci_write(miro, write[i]); if (value < 0) goto out; } } value = aci_read(miro);out: mutex_unlock(&miro->aci_mutex); return value;}static int aci_getvalue(struct snd_miro * miro, unsigned char index){ return aci_cmd(miro, ACI_STATUS, index, -1);}static int aci_setvalue(struct snd_miro * miro, unsigned char index, int value){ return aci_cmd(miro, index, value, -1);}/* * MIXER part */#define snd_miro_info_capture snd_ctl_boolean_mono_infostatic int snd_miro_get_capture(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_miro *miro = snd_kcontrol_chip(kcontrol); int value; if ((value = aci_getvalue(miro, ACI_S_GENERAL)) < 0) { snd_printk(KERN_ERR "snd_miro_get_capture() failed: %d\n", value); return value; } ucontrol->value.integer.value[0] = value & 0x20; return 0;}static int snd_miro_put_capture(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_miro *miro = snd_kcontrol_chip(kcontrol); int change, value, error; value = !(ucontrol->value.integer.value[0]); if ((error = aci_setvalue(miro, ACI_SET_SOLOMODE, value)) < 0) { snd_printk(KERN_ERR "snd_miro_put_capture() failed: %d\n", error); return error; } change = (value != miro->aci_solomode); miro->aci_solomode = value; return change;}static int snd_miro_info_preamp(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 3; return 0;}static int snd_miro_get_preamp(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_miro *miro = snd_kcontrol_chip(kcontrol); int value; if (miro->aci_version <= 176) { /* OSS says it's not readable with versions < 176. But it doesn't work on my card, which is a PCM12 with aci_version = 176. */ ucontrol->value.integer.value[0] = miro->aci_preamp; return 0; } if ((value = aci_getvalue(miro, ACI_GET_PREAMP)) < 0) { snd_printk(KERN_ERR "snd_miro_get_preamp() failed: %d\n", value); return value; } ucontrol->value.integer.value[0] = value; return 0;}static int snd_miro_put_preamp(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_miro *miro = snd_kcontrol_chip(kcontrol); int error, value, change; value = ucontrol->value.integer.value[0]; if ((error = aci_setvalue(miro, ACI_SET_PREAMP, value)) < 0) { snd_printk(KERN_ERR "snd_miro_put_preamp() failed: %d\n", error); return error; } change = (value != miro->aci_preamp); miro->aci_preamp = value; return change;}#define snd_miro_info_amp snd_ctl_boolean_mono_infostatic int snd_miro_get_amp(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_miro *miro = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = miro->aci_amp; return 0;}static int snd_miro_put_amp(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_miro *miro = snd_kcontrol_chip(kcontrol); int error, value, change; value = ucontrol->value.integer.value[0]; if ((error = aci_setvalue(miro, ACI_SET_POWERAMP, value)) < 0) { snd_printk(KERN_ERR "snd_miro_put_amp() to %d failed: %d\n", value, error); return error; } change = (value != miro->aci_amp); miro->aci_amp = value; return change;}#define MIRO_DOUBLE(ctl_name, ctl_index, get_right_reg, set_right_reg) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = ctl_name, \ .index = ctl_index, \ .info = snd_miro_info_double, \ .get = snd_miro_get_double, \ .put = snd_miro_put_double, \ .private_value = get_right_reg | (set_right_reg << 8) \}static int snd_miro_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ int reg = kcontrol->private_value & 0xff; uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; if ((reg >= ACI_GET_EQ1) && (reg <= ACI_GET_EQ7)) { /* equalizer elements */ uinfo->value.integer.min = - 0x7f; uinfo->value.integer.max = 0x7f; } else { /* non-equalizer elements */ uinfo->value.integer.min = 0; uinfo->value.integer.max = 0x20; } return 0;}static int snd_miro_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *uinfo){ struct snd_miro *miro = snd_kcontrol_chip(kcontrol); int left_val, right_val; int right_reg = kcontrol->private_value & 0xff; int left_reg = right_reg + 1; if ((right_val = aci_getvalue(miro, right_reg)) < 0) { snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", right_reg, right_val); return right_val; } if ((left_val = aci_getvalue(miro, left_reg)) < 0) { snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", left_reg, left_val); return left_val; } if ((right_reg >= ACI_GET_EQ1) && (right_reg <= ACI_GET_EQ7)) { /* equalizer elements */ if (left_val < 0x80) { uinfo->value.integer.value[0] = left_val; } else { uinfo->value.integer.value[0] = 0x80 - left_val; } if (right_val < 0x80) { uinfo->value.integer.value[1] = right_val; } else { uinfo->value.integer.value[1] = 0x80 - right_val; } } else { /* non-equalizer elements */ uinfo->value.integer.value[0] = 0x20 - left_val; uinfo->value.integer.value[1] = 0x20 - right_val; } return 0;}static int snd_miro_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_miro *miro = snd_kcontrol_chip(kcontrol); int left, right, left_old, right_old; int setreg_left, setreg_right, getreg_left, getreg_right; int change, error; left = ucontrol->value.integer.value[0]; right = ucontrol->value.integer.value[1]; setreg_right = (kcontrol->private_value >> 8) & 0xff; if (setreg_right == ACI_SET_MASTER) { setreg_left = setreg_right + 1; } else { setreg_left = setreg_right + 8; } getreg_right = kcontrol->private_value & 0xff; getreg_left = getreg_right + 1; if ((left_old = aci_getvalue(miro, getreg_left)) < 0) { snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_left, left_old); return left_old; } if ((right_old = aci_getvalue(miro, getreg_right)) < 0) { snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_right, right_old);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -