diff options
Diffstat (limited to 'sound/isa/opti9xx')
| -rw-r--r-- | sound/isa/opti9xx/Makefile | 4 | ||||
| -rw-r--r-- | sound/isa/opti9xx/miro.c | 1662 | ||||
| -rw-r--r-- | sound/isa/opti9xx/opti92x-ad1848.c | 1796 |
3 files changed, 2104 insertions, 1358 deletions
diff --git a/sound/isa/opti9xx/Makefile b/sound/isa/opti9xx/Makefile index 28c64070cd5..b4d894db257 100644 --- a/sound/isa/opti9xx/Makefile +++ b/sound/isa/opti9xx/Makefile @@ -1,13 +1,15 @@ # # Makefile for ALSA -# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> +# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz> # snd-opti92x-ad1848-objs := opti92x-ad1848.o snd-opti92x-cs4231-objs := opti92x-cs4231.o snd-opti93x-objs := opti93x.o +snd-miro-objs := miro.o # Toplevel Module Dependency obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opti92x-ad1848.o obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opti92x-cs4231.o obj-$(CONFIG_SND_OPTI93X) += snd-opti93x.o +obj-$(CONFIG_SND_MIRO) += snd-miro.o diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c new file mode 100644 index 00000000000..c2ca681ac51 --- /dev/null +++ b/sound/isa/opti9xx/miro.c @@ -0,0 +1,1662 @@ +/* + * 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 <linux/init.h> +#include <linux/err.h> +#include <linux/isa.h> +#include <linux/pnp.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <sound/core.h> +#include <sound/wss.h> +#include <sound/mpu401.h> +#include <sound/opl4.h> +#include <sound/control.h> +#include <sound/info.h> +#define SNDRV_LEGACY_FIND_FREE_IOPORT +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#include <sound/initval.h> +#include <sound/aci.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; +#ifdef CONFIG_PNP +static bool isapnp = 1; /* Enable ISA PnP detection */ +#endif + +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"); +#ifdef CONFIG_PNP +module_param(isapnp, bool, 0444); +MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard."); +#endif + +#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) n + +struct 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_pcm *pcm; + + long wss_base; + int irq; + int dma1; + int dma2; + + long mpu_port; + int mpu_irq; + + struct snd_miro_aci *aci; +}; + +static struct snd_miro_aci aci_device; + +static char * snd_opti9xx_names[] = { + "unknown", + "82C928", "82C929", + "82C924", "82C925", + "82C930", "82C931", "82C933" +}; + +static int snd_miro_pnp_is_probed; + +#ifdef CONFIG_PNP + +static struct pnp_card_device_id snd_miro_pnpids[] = { + /* PCM20 and PCM12 in PnP mode */ + { .id = "MIR0924", + .devs = { { "MIR0000" }, { "MIR0002" }, { "MIR0005" } }, }, + { .id = "" } +}; + +MODULE_DEVICE_TABLE(pnp_card, snd_miro_pnpids); + +#endif /* CONFIG_PNP */ + +/* + * ACI control + */ + +static int aci_busy_wait(struct snd_miro_aci *aci) +{ + long timeout; + unsigned char byte; + + for (timeout = 1; timeout <= ACI_MINTIME + 30; timeout++) { + byte = inb(aci->aci_port + ACI_REG_BUSY); + if ((byte & 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_aci *aci, unsigned char byte) +{ + if (aci_busy_wait(aci) >= 0) { + outb(byte, aci->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_aci *aci) +{ + unsigned char byte; + + if (aci_busy_wait(aci) >= 0) { + byte = inb(aci->aci_port + ACI_REG_STATUS); + return byte; + } else { + snd_printk(KERN_ERR "aci busy, aci_read() stopped.\n"); + return -EBUSY; + } +} + +int snd_aci_cmd(struct snd_miro_aci *aci, int write1, int write2, int write3) +{ + int write[] = {write1, write2, write3}; + int value, i; + + if (mutex_lock_interruptible(&aci->aci_mutex)) + return -EINTR; + + for (i=0; i<3; i++) { + if (write[i]< 0 || write[i] > 255) + break; + else { + value = aci_write(aci, write[i]); + if (value < 0) + goto out; + } + } + + value = aci_read(aci); + +out: mutex_unlock(&aci->aci_mutex); + return value; +} +EXPORT_SYMBOL(snd_aci_cmd); + +static int aci_getvalue(struct snd_miro_aci *aci, unsigned char index) +{ + return snd_aci_cmd(aci, ACI_STATUS, index, -1); +} + +static int aci_setvalue(struct snd_miro_aci *aci, unsigned char index, + int value) +{ + return snd_aci_cmd(aci, index, value, -1); +} + +struct snd_miro_aci *snd_aci_get_aci(void) +{ + if (aci_device.aci_port == 0) + return NULL; + return &aci_device; +} +EXPORT_SYMBOL(snd_aci_get_aci); + +/* + * MIXER part + */ + +#define snd_miro_info_capture snd_ctl_boolean_mono_info + +static 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; + + value = aci_getvalue(miro->aci, ACI_S_GENERAL); + if (value < 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]); + + error = aci_setvalue(miro->aci, ACI_SET_SOLOMODE, value); + if (error < 0) { + snd_printk(KERN_ERR "snd_miro_put_capture() failed: %d\n", + error); + return error; + } + + change = (value != miro->aci->aci_solomode); + miro->aci->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->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->aci_preamp; + return 0; + } + + value = aci_getvalue(miro->aci, ACI_GET_PREAMP); + if (value < 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]; + + error = aci_setvalue(miro->aci, ACI_SET_PREAMP, value); + if (error < 0) { + snd_printk(KERN_ERR "snd_miro_put_preamp() failed: %d\n", + error); + return error; + } + + change = (value != miro->aci->aci_preamp); + miro->aci->aci_preamp = value; + + return change; +} + +#define snd_miro_info_amp snd_ctl_boolean_mono_info + +static 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->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]; + + error = aci_setvalue(miro->aci, ACI_SET_POWERAMP, value); + if (error < 0) { + snd_printk(KERN_ERR "snd_miro_put_amp() to %d failed: %d\n", value, error); + return error; + } + + change = (value != miro->aci->aci_amp); + miro->aci->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; + + right_val = aci_getvalue(miro->aci, right_reg); + if (right_val < 0) { + snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", right_reg, right_val); + return right_val; + } + + left_val = aci_getvalue(miro->aci, left_reg); + if (left_val < 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); + struct snd_miro_aci *aci = miro->aci; + 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; + setreg_left = setreg_right + 8; + if (setreg_right == ACI_SET_MASTER) + setreg_left -= 7; + + getreg_right = kcontrol->private_value & 0xff; + getreg_left = getreg_right + 1; + + left_old = aci_getvalue(aci, getreg_left); + if (left_old < 0) { + snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_left, left_old); + return left_old; + } + + right_old = aci_getvalue(aci, getreg_right); + if (right_old < 0) { + snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_right, right_old); + return right_old; + } + + if ((getreg_right >= ACI_GET_EQ1) && (getreg_right <= ACI_GET_EQ7)) { + + /* equalizer elements */ + + if (left < -0x7f || left > 0x7f || + right < -0x7f || right > 0x7f) + return -EINVAL; + + if (left_old > 0x80) + left_old = 0x80 - left_old; + if (right_old > 0x80) + right_old = 0x80 - right_old; + + if (left >= 0) { + error = aci_setvalue(aci, setreg_left, left); + if (error < 0) { + snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", + left, error); + return error; + } + } else { + error = aci_setvalue(aci, setreg_left, 0x80 - left); + if (error < 0) { + snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", + 0x80 - left, error); + return error; + } + } + + if (right >= 0) { + error = aci_setvalue(aci, setreg_right, right); + if (error < 0) { + snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", + right, error); + return error; + } + } else { + error = aci_setvalue(aci, setreg_right, 0x80 - right); + if (error < 0) { + snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", + 0x80 - right, error); + return error; + } + } + + } else { + + /* non-equalizer elements */ + + if (left < 0 || left > 0x20 || + right < 0 || right > 0x20) + return -EINVAL; + + left_old = 0x20 - left_old; + right_old = 0x20 - right_old; + + error = aci_setvalue(aci, setreg_left, 0x20 - left); + if (error < 0) { + snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", + 0x20 - left, error); + return error; + } + error = aci_setvalue(aci, setreg_right, 0x20 - right); + if (error < 0) { + snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", + 0x20 - right, error); + return error; + } + } + + change = (left != left_old) || (right != right_old); + + return change; +} + +static struct snd_kcontrol_new snd_miro_controls[] = { +MIRO_DOUBLE("Master Playback Volume", 0, ACI_GET_MASTER, ACI_SET_MASTER), +MIRO_DOUBLE("Mic Playback Volume", 1, ACI_GET_MIC, ACI_SET_MIC), +MIRO_DOUBLE("Line Playback Volume", 1, ACI_GET_LINE, ACI_SET_LINE), +MIRO_DOUBLE("CD Playback Volume", 0, ACI_GET_CD, ACI_SET_CD), +MIRO_DOUBLE("Synth Playback Volume", 0, ACI_GET_SYNTH, ACI_SET_SYNTH), +MIRO_DOUBLE("PCM Playback Volume", 1, ACI_GET_PCM, ACI_SET_PCM), +MIRO_DOUBLE("Aux Playback Volume", 2, ACI_GET_LINE2, ACI_SET_LINE2), +}; + +/* Equalizer with seven bands (only PCM20) + from -12dB up to +12dB on each band */ +static struct snd_kcontrol_new snd_miro_eq_controls[] = { +MIRO_DOUBLE("Tone Control - 28 Hz", 0, ACI_GET_EQ1, ACI_SET_EQ1), +MIRO_DOUBLE("Tone Control - 160 Hz", 0, ACI_GET_EQ2, ACI_SET_EQ2), +MIRO_DOUBLE("Tone Control - 400 Hz", 0, ACI_GET_EQ3, ACI_SET_EQ3), +MIRO_DOUBLE("Tone Control - 1 kHz", 0, ACI_GET_EQ4, ACI_SET_EQ4), +MIRO_DOUBLE("Tone Control - 2.5 kHz", 0, ACI_GET_EQ5, ACI_SET_EQ5), +MIRO_DOUBLE("Tone Control - 6.3 kHz", 0, ACI_GET_EQ6, ACI_SET_EQ6), +MIRO_DOUBLE("Tone Control - 16 kHz", 0, ACI_GET_EQ7, ACI_SET_EQ7), +}; + +static struct snd_kcontrol_new snd_miro_radio_control[] = { +MIRO_DOUBLE("Radio Playback Volume", 0, ACI_GET_LINE1, ACI_SET_LINE1), +}; + +static struct snd_kcontrol_new snd_miro_line_control[] = { +MIRO_DOUBLE("Line Playback Volume", 2, ACI_GET_LINE1, ACI_SET_LINE1), +}; + +static struct snd_kcontrol_new snd_miro_preamp_control[] = { +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic Boost", + .index = 1, + .info = snd_miro_info_preamp, + .get = snd_miro_get_preamp, + .put = snd_miro_put_preamp, +}}; + +static struct snd_kcontrol_new snd_miro_amp_control[] = { +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line Boost", + .index = 0, + .info = snd_miro_info_amp, + .get = snd_miro_get_amp, + .put = snd_miro_put_amp, +}}; + +static struct snd_kcontrol_new snd_miro_capture_control[] = { +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Capture Switch", + .index = 0, + .info = snd_miro_info_capture, + .get = snd_miro_get_capture, + .put = snd_miro_put_capture, +}}; + +static unsigned char aci_init_values[][2] = { + { ACI_SET_MUTE, 0x00 }, + { ACI_SET_POWERAMP, 0x00 }, + { ACI_SET_PREAMP, 0x00 }, + { ACI_SET_SOLOMODE, 0x00 }, + { ACI_SET_MIC + 0, 0x20 }, + { ACI_SET_MIC + 8, 0x20 }, + { ACI_SET_LINE + 0, 0x20 }, + { ACI_SET_LINE + 8, 0x20 }, + { ACI_SET_CD + 0, 0x20 }, + { ACI_SET_CD + 8, 0x20 }, + { ACI_SET_PCM + 0, 0x20 }, + { ACI_SET_PCM + 8, 0x20 }, + { ACI_SET_LINE1 + 0, 0x20 }, + { ACI_SET_LINE1 + 8, 0x20 }, + { ACI_SET_LINE2 + 0, 0x20 }, + { ACI_SET_LINE2 + 8, 0x20 }, + { ACI_SET_SYNTH + 0, 0x20 }, + { ACI_SET_SYNTH + 8, 0x20 }, + { ACI_SET_MASTER + 0, 0x20 }, + { ACI_SET_MASTER + 1, 0x20 }, +}; + +static int snd_set_aci_init_values(struct snd_miro *miro) +{ + int idx, error; + struct snd_miro_aci *aci = miro->aci; + + /* enable WSS on PCM1 */ + + if ((aci->aci_product == 'A') && wss) { + error = aci_setvalue(aci, ACI_SET_WSS, wss); + if (error < 0) { + snd_printk(KERN_ERR "enabling WSS mode failed\n"); + return error; + } + } + + /* enable IDE port */ + + if (ide) { + error = aci_setvalue(aci, ACI_SET_IDE, ide); + if (error < 0) { + snd_printk(KERN_ERR "enabling IDE port failed\n"); + return error; + } + } + + /* set common aci values */ + + for (idx = 0; idx < ARRAY_SIZE(aci_init_values); idx++) { + error = aci_setvalue(aci, aci_init_values[idx][0], + aci_init_values[idx][1]); + if (error < 0) { + snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", + aci_init_values[idx][0], error); + return error; + } + } + aci->aci_amp = 0; + aci->aci_preamp = 0; + aci->aci_solomode = 1; + + return 0; +} + +static int snd_miro_mixer(struct snd_card *card, + struct snd_miro *miro) +{ + unsigned int idx; + int err; + + if (snd_BUG_ON(!miro || !card)) + return -EINVAL; + + switch (miro->hardware) { + case OPTi9XX_HW_82C924: + strcpy(card->mixername, "ACI & OPTi924"); + break; + case OPTi9XX_HW_82C929: + strcpy(card->mixername, "ACI & OPTi929"); + break; + default: + snd_BUG(); + break; + } + + for (idx = 0; idx < ARRAY_SIZE(snd_miro_controls); idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_controls[idx], miro))) < 0) + return err; + } + + if ((miro->aci->aci_product == 'A') || + (miro->aci->aci_product == 'B')) { + /* PCM1/PCM12 with power-amp and Line 2 */ + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_line_control[0], miro))) < 0) + return err; + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_amp_control[0], miro))) < 0) + return err; + } + + if ((miro->aci->aci_product == 'B') || + (miro->aci->aci_product == 'C')) { + /* PCM12/PCM20 with mic-preamp */ + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_preamp_control[0], miro))) < 0) + return err; + if (miro->aci->aci_version >= 176) + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_capture_control[0], miro))) < 0) + return err; + } + + if (miro->aci->aci_product == 'C') { + /* PCM20 with radio and 7 band equalizer */ + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_radio_control[0], miro))) < 0) + return err; + for (idx = 0; idx < ARRAY_SIZE(snd_miro_eq_controls); idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_eq_controls[idx], miro))) < 0) + return err; + } + } + + return 0; +} + +static int snd_miro_init(struct snd_miro *chip, + unsigned short hardware) +{ + static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2}; + + chip->hardware = hardware; + strcpy(chip->name, snd_opti9xx_names[hardware]); + + chip->mc_base_size = opti9xx_mc_size[hardware]; + + spin_lock_init(&chip->lock); + + chip->wss_base = -1; + chip->irq = -1; + chip->dma1 = -1; + chip->dma2 = -1; + chip->mpu_port = -1; + chip->mpu_irq = -1; + + chip->pwd_reg = 3; + +#ifdef CONFIG_PNP + if (isapnp && chip->mc_base) + /* PnP resource gives the least 10 bits */ + chip->mc_base |= 0xc00; + else +#endif + chip->mc_base = 0xf8c; + + switch (hardware) { + case OPTi9XX_HW_82C929: + chip->password = 0xe3; + break; + + case OPTi9XX_HW_82C924: + chip->password = 0xe5; + break; + + default: + snd_printk(KERN_ERR "sorry, no support for %d\n", hardware); + return -ENODEV; + } + + return 0; +} + +static unsigned char snd_miro_read(struct snd_miro *chip, + unsigned char reg) +{ + unsigned long flags; + unsigned char retval = 0xff; + + spin_lock_irqsave(&chip->lock, flags); + outb(chip->password, chip->mc_base + chip->pwd_reg); + + switch (chip->hardware) { + case OPTi9XX_HW_82C924: + if (reg > 7) { + outb(reg, chip->mc_base + 8); + outb(chip->password, chip->mc_base + chip->pwd_reg); + retval = inb(chip->mc_base + 9); + break; + } + + case OPTi9XX_HW_82C929: + retval = inb(chip->mc_base + reg); + break; + + default: + snd_printk(KERN_ERR "sorry, no support for %d\n", chip->hardware); + } + + spin_unlock_irqrestore(&chip->lock, flags); + return retval; +} + +static void snd_miro_write(struct snd_miro *chip, unsigned char reg, + unsigned char value) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + outb(chip->password, chip->mc_base + chip->pwd_reg); + + switch (chip->hardware) { + case OPTi9XX_HW_82C924: + if (reg > 7) { + outb(reg, chip->mc_base + 8); + outb(chip->password, chip->mc_base + chip->pwd_reg); + outb(value, chip->mc_base + 9); + break; + } + + case OPTi9XX_HW_82C929: + outb(value, chip->mc_base + reg); + break; + + default: + snd_printk(KERN_ERR "sorry, no support for %d\n", chip->hardware); + } + + spin_unlock_irqrestore(&chip->lock, flags); +} + + +#define snd_miro_write_mask(chip, reg, value, mask) \ + snd_miro_write(chip, reg, \ + (snd_miro_read(chip, reg) & ~(mask)) | ((value) & (mask))) + +/* + * Proc Interface + */ + +static void snd_miro_proc_read(struct snd_info_entry * entry, + struct snd_info_buffer *buffer) +{ + struct snd_miro *miro = (struct snd_miro *) entry->private_data; + struct snd_miro_aci *aci = miro->aci; + char* model = "unknown"; + + /* miroSOUND PCM1 pro, early PCM12 */ + + if ((miro->hardware == OPTi9XX_HW_82C929) && + (aci->aci_vendor == 'm') && + (aci->aci_product == 'A')) { + switch (aci->aci_version) { + case 3: + model = "miroSOUND PCM1 pro"; + break; + default: + model = "miroSOUND PCM1 pro / (early) PCM12"; + break; + } + } + + /* miroSOUND PCM12, PCM12 (Rev. E), PCM12 pnp */ + + if ((miro->hardware == OPTi9XX_HW_82C924) && + (aci->aci_vendor == 'm') && + (aci->aci_product == 'B')) { + switch (aci->aci_version) { + case 4: + model = "miroSOUND PCM12"; + break; + case 176: + model = "miroSOUND PCM12 (Rev. E)"; + break; + default: + model = "miroSOUND PCM12 / PCM12 pnp"; + break; + } + } + + /* miroSOUND PCM20 radio */ + + if ((miro->hardware == OPTi9XX_HW_82C924) && + (aci->aci_vendor == 'm') && + (aci->aci_product == 'C')) { + switch (aci->aci_version) { + case 7: + model = "miroSOUND PCM20 radio (Rev. E)"; + break; + default: + model = "miroSOUND PCM20 radio"; + break; + } + } + + snd_iprintf(buffer, "\nGeneral information:\n"); + snd_iprintf(buffer, " model : %s\n", model); + snd_iprintf(buffer, " opti : %s\n", miro->name); + snd_iprintf(buffer, " codec : %s\n", miro->pcm->name); + snd_iprintf(buffer, " port : 0x%lx\n", miro->wss_base); + snd_iprintf(buffer, " irq : %d\n", miro->irq); + snd_iprintf(buffer, " dma : %d,%d\n\n", miro->dma1, miro->dma2); + + snd_iprintf(buffer, "MPU-401:\n"); + snd_iprintf(buffer, " port : 0x%lx\n", miro->mpu_port); + snd_iprintf(buffer, " irq : %d\n\n", miro->mpu_irq); + + snd_iprintf(buffer, "ACI information:\n"); + snd_iprintf(buffer, " vendor : "); + switch (aci->aci_vendor) { + case 'm': + snd_iprintf(buffer, "Miro\n"); + break; + default: + snd_iprintf(buffer, "unknown (0x%x)\n", aci->aci_vendor); + break; + } + + snd_iprintf(buffer, " product : "); + switch (aci->aci_product) { + case 'A': + snd_iprintf(buffer, "miroSOUND PCM1 pro / (early) PCM12\n"); + break; + case 'B': + snd_iprintf(buffer, "miroSOUND PCM12\n"); + break; + case 'C': + snd_iprintf(buffer, "miroSOUND PCM20 radio\n"); + break; + default: + snd_iprintf(buffer, "unknown (0x%x)\n", aci->aci_product); + break; + } + + snd_iprintf(buffer, " firmware: %d (0x%x)\n", + aci->aci_version, aci->aci_version); + snd_iprintf(buffer, " port : 0x%lx-0x%lx\n", + aci->aci_port, aci->aci_port+2); + snd_iprintf(buffer, " wss : 0x%x\n", wss); + snd_iprintf(buffer, " ide : 0x%x\n", ide); + snd_iprintf(buffer, " solomode: 0x%x\n", aci->aci_solomode); + snd_iprintf(buffer, " amp : 0x%x\n", aci->aci_amp); + snd_iprintf(buffer, " preamp : 0x%x\n", aci->aci_preamp); +} + +static void snd_miro_proc_init(struct snd_card *card, + struct snd_miro *miro) +{ + struct snd_info_entry *entry; + + if (!snd_card_proc_new(card, "miro", &entry)) + snd_info_set_text_ops(entry, miro, snd_miro_proc_read); +} + +/* + * Init + */ + +static int snd_miro_configure(struct snd_miro *chip) +{ + unsigned char wss_base_bits; + unsigned char irq_bits; + unsigned char dma_bits; + unsigned char mpu_port_bits = 0; + unsigned char mpu_irq_bits; + unsigned long flags; + + snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); + snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */ + snd_miro_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); + + switch (chip->hardware) { + case OPTi9XX_HW_82C924: + snd_miro_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02); + snd_miro_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff); + break; + case OPTi9XX_HW_82C929: + /* untested init commands for OPTi929 */ + snd_miro_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c); + break; + default: + snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); + return -EINVAL; + } + + /* PnP resource says it decodes only 10 bits of address */ + switch (chip->wss_base & 0x3ff) { + case 0x130: + chip->wss_base = 0x530; + wss_base_bits = 0x00; + break; + case 0x204: + chip->wss_base = 0x604; + wss_base_bits = 0x03; + break; + case 0x280: + chip->wss_base = 0xe80; + wss_base_bits = 0x01; + break; + case 0x340: + chip->wss_base = 0xf40; + wss_base_bits = 0x02; + break; + default: + snd_printk(KERN_ERR "WSS port 0x%lx not valid\n", chip->wss_base); + goto __skip_base; + } + snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30); + +__skip_base: + switch (chip->irq) { + case 5: + irq_bits = 0x05; + break; + case 7: + irq_bits = 0x01; + break; + case 9: + irq_bits = 0x02; + break; + case 10: + irq_bits = 0x03; + break; + case 11: + irq_bits = 0x04; + break; + default: + snd_printk(KERN_ERR "WSS irq # %d not valid\n", chip->irq); + goto __skip_resources; + } + + switch (chip->dma1) { + case 0: + dma_bits = 0x01; + break; + case 1: + dma_bits = 0x02; + break; + case 3: + dma_bits = 0x03; + break; + default: + snd_printk(KERN_ERR "WSS dma1 # %d not valid\n", chip->dma1); + goto __skip_resources; + } + + if (chip->dma1 == chip->dma2) { + snd_printk(KERN_ERR "don't want to share dmas\n"); + return -EBUSY; + } + + switch (chip->dma2) { + case 0: + case 1: + break; + default: + snd_printk(KERN_ERR "WSS dma2 # %d not valid\n", chip->dma2); + goto __skip_resources; + } + dma_bits |= 0x04; + + spin_lock_irqsave(&chip->lock, flags); + outb(irq_bits << 3 | dma_bits, chip->wss_base); + spin_unlock_irqrestore(&chip->lock, flags); + +__skip_resources: + if (chip->hardware > OPTi9XX_HW_82C928) { + switch (chip->mpu_port) { + case 0: + case -1: + break; + case 0x300: + mpu_port_bits = 0x03; + break; + case 0x310: + mpu_port_bits = 0x02; + break; + case 0x320: + mpu_port_bits = 0x01; + break; + case 0x330: + mpu_port_bits = 0x00; + break; + default: + snd_printk(KERN_ERR "MPU-401 port 0x%lx not valid\n", + chip->mpu_port); + goto __skip_mpu; + } + + switch (chip->mpu_irq) { + case 5: + mpu_irq_bits = 0x02; + break; + case 7: + mpu_irq_bits = 0x03; + break; + case 9: + mpu_irq_bits = 0x00; + break; + case 10: + mpu_irq_bits = 0x01; + break; + default: + snd_printk(KERN_ERR "MPU-401 irq # %d not valid\n", + chip->mpu_irq); + goto __skip_mpu; + } + + snd_miro_write_mask(chip, OPTi9XX_MC_REG(6), + (chip->mpu_port <= 0) ? 0x00 : + 0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3, + 0xf8); + } +__skip_mpu: + + return 0; +} + +static int snd_miro_opti_check(struct snd_miro *chip) +{ + unsigned char value; + + chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, + "OPTi9xx MC"); + if (chip->res_mc_base == NULL) + return -ENOMEM; + + value = snd_miro_read(chip, OPTi9XX_MC_REG(1)); + if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1))) + if (value == snd_miro_read(chip, OPTi9XX_MC_REG(1))) + return 0; + + release_and_free_resource(chip->res_mc_base); + chip->res_mc_base = NULL; + + return -ENODEV; +} + +static int snd_card_miro_detect(struct snd_card *card, + struct snd_miro *chip) +{ + int i, err; + + for (i = OPTi9XX_HW_82C929; i <= OPTi9XX_HW_82C924; i++) { + + if ((err = snd_miro_init(chip, i)) < 0) + return err; + + err = snd_miro_opti_check(chip); + if (err == 0) + return 1; + } + + return -ENODEV; +} + +static int snd_card_miro_aci_detect(struct snd_card *card, + struct snd_miro *miro) +{ + unsigned char regval; + int i; + struct snd_miro_aci *aci = &aci_device; + + miro->aci = aci; + + mutex_init(&aci->aci_mutex); + + /* get ACI port from OPTi9xx MC 4 */ + + regval=inb(miro->mc_base + 4); + aci->aci_port = (regval & 0x10) ? 0x344 : 0x354; + + miro->res_aci_port = request_region(aci->aci_port, 3, "miro aci"); + if (miro->res_aci_port == NULL) { + snd_printk(KERN_ERR "aci i/o area 0x%lx-0x%lx already used.\n", + aci->aci_port, aci->aci_port+2); + return -ENOMEM; + } + + /* force ACI into a known state */ + for (i = 0; i < 3; i++) + if (snd_aci_cmd(aci, ACI_ERROR_OP, -1, -1) < 0) { + snd_printk(KERN_ERR "can't force aci into known state.\n"); + return -ENXIO; + } + + aci->aci_vendor = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1); + aci->aci_product = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1); + if (aci->aci_vendor < 0 || aci->aci_product < 0) { + snd_printk(KERN_ERR "can't read aci id on 0x%lx.\n", + aci->aci_port); + return -ENXIO; + } + + aci->aci_version = snd_aci_cmd(aci, ACI_READ_VERSION, -1, -1); + if (aci->aci_version < 0) { + snd_printk(KERN_ERR "can't read aci version on 0x%lx.\n", + aci->aci_port); + return -ENXIO; + } + + if (snd_aci_cmd(aci, ACI_INIT, -1, -1) < 0 || + snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0 || + snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0) { + snd_printk(KERN_ERR "can't initialize aci.\n"); + return -ENXIO; + } + + return 0; +} + +static void snd_card_miro_free(struct snd_card *card) +{ + struct snd_miro *miro = card->private_data; + + release_and_free_resource(miro->res_aci_port); + if (miro->aci) + miro->aci->aci_port = 0; + release_and_free_resource(miro->res_mc_base); +} + +static int snd_miro_probe(struct snd_card *card) +{ + int error; + struct snd_miro *miro = card->private_data; + struct snd_wss *codec; + struct snd_timer *timer; + struct snd_pcm *pcm; + struct snd_rawmidi *rmidi; + + if (!miro->res_mc_base) { + miro->res_mc_base = request_region(miro->mc_base, + miro->mc_base_size, + "miro (OPTi9xx MC)"); + if (miro->res_mc_base == NULL) { + snd_printk(KERN_ERR "request for OPTI9xx MC failed\n"); + return -ENOMEM; + } + } + + error = snd_card_miro_aci_detect(card, miro); + if (error < 0) { + snd_printk(KERN_ERR "unable to detect aci chip\n"); + return -ENODEV; + } + + miro->wss_base = port; + miro->mpu_port = mpu_port; + miro->irq = irq; + miro->mpu_irq = mpu_irq; + miro->dma1 = dma1; + miro->dma2 = dma2; + + /* init proc interface */ + snd_miro_proc_init(card, miro); + + error = snd_miro_configure(miro); + if (error) + return error; + + error = snd_wss_create(card, miro->wss_base + 4, -1, + miro->irq, miro->dma1, miro->dma2, + WSS_HW_DETECT, 0, &codec); + if (error < 0) + return error; + + error = snd_wss_pcm(codec, 0, &pcm); + if (error < 0) + return error; + + error = snd_wss_mixer(codec); + if (error < 0) + return error; + + error = snd_wss_timer(codec, 0, &timer); + if (error < 0) + return error; + + miro->pcm = pcm; + + error = snd_miro_mixer(card, miro); + if (error < 0) + return error; + + if (miro->aci->aci_vendor == 'm') { + /* It looks like a miro sound card. */ + switch (miro->aci->aci_product) { + case 'A': + sprintf(card->shortname, + "miroSOUND PCM1 pro / PCM12"); + break; + case 'B': + sprintf(card->shortname, + "miroSOUND PCM12"); + break; + case 'C': + sprintf(card->shortname, + "miroSOUND PCM20 radio"); + break; + default: + sprintf(card->shortname, + "unknown miro"); + snd_printk(KERN_INFO "unknown miro aci id\n"); + break; + } + } else { + snd_printk(KERN_INFO "found unsupported aci card\n"); + sprintf(card->shortname, "unknown Cardinal Technologies"); + } + + strcpy(card->driver, "miro"); + sprintf(card->longname, "%s: OPTi%s, %s at 0x%lx, irq %d, dma %d&%d", + card->shortname, miro->name, pcm->name, miro->wss_base + 4, + miro->irq, miro->dma1, miro->dma2); + + if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT) + rmidi = NULL; + else { + error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + mpu_port, 0, miro->mpu_irq, &rmidi); + if (error < 0) + snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n", + mpu_port); + } + + if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) { + struct snd_opl3 *opl3 = NULL; + struct snd_opl4 *opl4; + + if (snd_opl4_create(card, fm_port, fm_port - 8, + 2, &opl3, &opl4) < 0) + snd_printk(KERN_WARNING "no OPL4 device at 0x%lx\n", + fm_port); + } + + error = snd_set_aci_init_values(miro); + if (error < 0) + return error; + + return snd_card_register(card); +} + +static int snd_miro_isa_match(struct device *devptr, unsigned int n) +{ +#ifdef CONFIG_PNP + if (snd_miro_pnp_is_probed) + return 0; + if (isapnp) + return 0; +#endif + return 1; +} + +static int snd_miro_isa_probe(struct device *devptr, unsigned int n) +{ + static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1}; + static long possible_mpu_ports[] = {0x330, 0x300, 0x310, 0x320, -1}; + static int possible_irqs[] = {11, 9, 10, 7, -1}; + static int possible_mpu_irqs[] = {10, 5, 9, 7, -1}; + static int possible_dma1s[] = {3, 1, 0, -1}; + static int possible_dma2s[][2] = { {1, -1}, {0, -1}, {-1, -1}, + {0, -1} }; + + int error; + struct snd_miro *miro; + struct snd_card *card; + + error = snd_card_new(devptr, index, id, THIS_MODULE, + sizeof(struct snd_miro), &card); + if (error < 0) + return error; + + card->private_free = snd_card_miro_free; + miro = card->private_data; + + error = snd_card_miro_detect(card, miro); + if (error < 0) { + snd_card_free(card); + snd_printk(KERN_ERR "unable to detect OPTi9xx chip\n"); + return -ENODEV; + } + + if (port == SNDRV_AUTO_PORT) { + port = snd_legacy_find_free_ioport(possible_ports, 4); + if (port < 0) { + snd_card_free(card); + snd_printk(KERN_ERR "unable to find a free WSS port\n"); + return -EBUSY; + } + } + + if (mpu_port == SNDRV_AUTO_PORT) { + mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2); + if (mpu_port < 0) { + snd_card_free(card); + snd_printk(KERN_ERR + "unable to find a free MPU401 port\n"); + return -EBUSY; + } + } + + if (irq == SNDRV_AUTO_IRQ) { + irq = snd_legacy_find_free_irq(possible_irqs); + if (irq < 0) { + snd_card_free(card); + snd_printk(KERN_ERR "unable to find a free IRQ\n"); + return -EBUSY; + } + } + if (mpu_irq == SNDRV_AUTO_IRQ) { + mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs); + if (mpu_irq < 0) { + snd_card_free(card); + snd_printk(KERN_ERR + "unable to find a free MPU401 IRQ\n"); + return -EBUSY; + } + } + if (dma1 == SNDRV_AUTO_DMA) { + dma1 = snd_legacy_find_free_dma(possible_dma1s); + if (dma1 < 0) { + snd_card_free(card); + snd_printk(KERN_ERR "unable to find a free DMA1\n"); + return -EBUSY; + } + } + if (dma2 == SNDRV_AUTO_DMA) { + dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4]); + if (dma2 < 0) { + snd_card_free(card); + snd_printk(KERN_ERR "unable to find a free DMA2\n"); + return -EBUSY; + } + } + + error = snd_miro_probe(card); + if (error < 0) { + snd_card_free(card); + return error; + } + + dev_set_drvdata(devptr, card); + return 0; +} + +static int snd_miro_isa_remove(struct device *devptr, + unsigned int dev) +{ + snd_card_free(dev_get_drvdata(devptr)); + return 0; +} + +#define DEV_NAME "miro" + +static struct isa_driver snd_miro_driver = { + .match = snd_miro_isa_match, + .probe = snd_miro_isa_probe, + .remove = snd_miro_isa_remove, + /* FIXME: suspend/resume */ + .driver = { + .name = DEV_NAME + }, +}; + +#ifdef CONFIG_PNP + +static int snd_card_miro_pnp(struct snd_miro *chip, + struct pnp_card_link *card, + const struct pnp_card_device_id *pid) +{ + struct pnp_dev *pdev; + int err; + struct pnp_dev *devmpu; + struct pnp_dev *devmc; + + pdev = pnp_request_card_device(card, pid->devs[0].id, NULL); + if (pdev == NULL) + return -EBUSY; + + devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL); + if (devmpu == NULL) + return -EBUSY; + + devmc = pnp_request_card_device(card, pid->devs[2].id, NULL); + if (devmc == NULL) + return -EBUSY; + + err = pnp_activate_dev(pdev); + if (err < 0) { + snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err); + return err; + } + + err = pnp_activate_dev(devmc); + if (err < 0) { + snd_printk(KERN_ERR "MC pnp configure failure: %d\n", + err); + return err; + } + + port = pnp_port_start(pdev, 1); + fm_port = pnp_port_start(pdev, 2) + 8; + + /* + * The MC(0) is never accessed and the miroSOUND PCM20 card does not + * include it in the PnP resource range. OPTI93x include it. + */ + chip->mc_base = pnp_port_start(devmc, 0) - 1; + chip->mc_base_size = pnp_port_len(devmc, 0) + 1; + + irq = pnp_irq(pdev, 0); + dma1 = pnp_dma(pdev, 0); + dma2 = pnp_dma(pdev, 1); + + if (mpu_port > 0) { + err = pnp_activate_dev(devmpu); + if (err < 0) { + snd_printk(KERN_ERR "MPU401 pnp configure failure\n"); + mpu_port = -1; + return err; + } + mpu_port = pnp_port_start(devmpu, 0); + mpu_irq = pnp_irq(devmpu, 0); + } + return 0; +} + +static int snd_miro_pnp_probe(struct pnp_card_link *pcard, + const struct pnp_card_device_id *pid) +{ + struct snd_card *card; + int err; + struct snd_miro *miro; + + if (snd_miro_pnp_is_probed) + return -EBUSY; + if (!isapnp) + return -ENODEV; + err = snd_card_new(&pcard->card->dev, index, id, THIS_MODULE, + sizeof(struct snd_miro), &card); + if (err < 0) + return err; + + card->private_free = snd_card_miro_free; + miro = card->private_data; + + err = snd_card_miro_pnp(miro, pcard, pid); + if (err) { + snd_card_free(card); + return err; + } + + /* only miroSOUND PCM20 and PCM12 == OPTi924 */ + err = snd_miro_init(miro, OPTi9XX_HW_82C924); + if (err) { + snd_card_free(card); + return err; + } + + err = snd_miro_opti_check(miro); + if (err) { + snd_printk(KERN_ERR "OPTI chip not found\n"); + snd_card_free(card); + return err; + } + + err = snd_miro_probe(card); + if (err < 0) { + snd_card_free(card); + return err; + } + pnp_set_card_drvdata(pcard, card); + snd_miro_pnp_is_probed = 1; + return 0; +} + +static void snd_miro_pnp_remove(struct pnp_card_link *pcard) +{ + snd_card_free(pnp_get_card_drvdata(pcard)); + pnp_set_card_drvdata(pcard, NULL); + snd_miro_pnp_is_probed = 0; +} + +static struct pnp_card_driver miro_pnpc_driver = { + .flags = PNP_DRIVER_RES_DISABLE, + .name = "miro", + .id_table = snd_miro_pnpids, + .probe = snd_miro_pnp_probe, + .remove = snd_miro_pnp_remove, +}; +#endif + +static int __init alsa_card_miro_init(void) +{ +#ifdef CONFIG_PNP + pnp_register_card_driver(&miro_pnpc_driver); + if (snd_miro_pnp_is_probed) + return 0; + pnp_unregister_card_driver(&miro_pnpc_driver); +#endif + return isa_register_driver(&snd_miro_driver, 1); +} + +static void __exit alsa_card_miro_exit(void) +{ + if (!snd_miro_pnp_is_probed) { + isa_unregister_driver(&snd_miro_driver); + return; + } +#ifdef CONFIG_PNP + pnp_unregister_card_driver(&miro_pnpc_driver); +#endif +} + +module_init(alsa_card_miro_init) +module_exit(alsa_card_miro_exit) diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c index 1ea3944ef7a..c9b58284860 100644 --- a/sound/isa/opti9xx/opti92x-ad1848.c +++ b/sound/isa/opti9xx/opti92x-ad1848.c @@ -23,32 +23,23 @@ */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> -#include <linux/platform_device.h> +#include <linux/isa.h> #include <linux/delay.h> -#include <linux/slab.h> #include <linux/pnp.h> -#include <linux/moduleparam.h> +#include <linux/module.h> #include <asm/io.h> #include <asm/dma.h> #include <sound/core.h> -#ifdef CS4231 -#include <sound/cs4231.h> -#else -#ifndef OPTi93X -#include <sound/ad1848.h> -#else -#include <sound/control.h> -#include <sound/pcm.h> -#endif /* OPTi93X */ -#endif /* CS4231 */ +#include <sound/tlv.h> +#include <sound/wss.h> #include <sound/mpu401.h> #include <sound/opl3.h> #ifndef OPTi93X #include <sound/opl4.h> #endif +#define SNDRV_LEGACY_FIND_FREE_IOPORT #define SNDRV_LEGACY_FIND_FREE_IRQ #define SNDRV_LEGACY_FIND_FREE_DMA #include <sound/initval.h> @@ -73,8 +64,10 @@ MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (AD1848)}," static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ -//static int enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */ -static int isapnp = 1; /* Enable ISA PnP detection */ +//static bool enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */ +#ifdef CONFIG_PNP +static bool isapnp = true; /* Enable ISA PnP detection */ +#endif 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 */ @@ -91,8 +84,10 @@ module_param(id, charp, 0444); MODULE_PARM_DESC(id, "ID string for opti9xx based soundcard."); //module_param(enable, bool, 0444); //MODULE_PARM_DESC(enable, "Enable opti9xx soundcard."); +#ifdef CONFIG_PNP module_param(isapnp, bool, 0444); MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard."); +#endif module_param(port, long, 0444); MODULE_PARM_DESC(port, "WSS port # for opti9xx driver."); module_param(mpu_port, long, 0444); @@ -110,7 +105,6 @@ module_param(dma2, int, 0444); MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver."); #endif /* CS4231 || OPTi93X */ -#define OPTi9XX_HW_DETECT 0 #define OPTi9XX_HW_82C928 1 #define OPTi9XX_HW_82C929 2 #define OPTi9XX_HW_82C924 3 @@ -124,105 +118,12 @@ MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver."); #ifdef OPTi93X -#define OPTi93X_INDEX 0x00 -#define OPTi93X_DATA 0x01 #define OPTi93X_STATUS 0x02 -#define OPTi93X_DDATA 0x03 #define OPTi93X_PORT(chip, r) ((chip)->port + OPTi93X_##r) -#define OPTi93X_MIXOUT_LEFT 0x00 -#define OPTi93X_MIXOUT_RIGHT 0x01 -#define OPTi93X_CD_LEFT_INPUT 0x02 -#define OPTi93X_CD_RIGHT_INPUT 0x03 -#define OPTi930_AUX_LEFT_INPUT 0x04 -#define OPTi930_AUX_RIGHT_INPUT 0x05 -#define OPTi931_FM_LEFT_INPUT 0x04 -#define OPTi931_FM_RIGHT_INPUT 0x05 -#define OPTi93X_DAC_LEFT 0x06 -#define OPTi93X_DAC_RIGHT 0x07 -#define OPTi93X_PLAY_FORMAT 0x08 -#define OPTi93X_IFACE_CONF 0x09 -#define OPTi93X_PIN_CTRL 0x0a -#define OPTi93X_ERR_INIT 0x0b -#define OPTi93X_ID 0x0c -#define OPTi93X_PLAY_UPR_CNT 0x0e -#define OPTi93X_PLAY_LWR_CNT 0x0f -#define OPTi931_AUX_LEFT_INPUT 0x10 -#define OPTi931_AUX_RIGHT_INPUT 0x11 -#define OPTi93X_LINE_LEFT_INPUT 0x12 -#define OPTi93X_LINE_RIGHT_INPUT 0x13 -#define OPTi93X_MIC_LEFT_INPUT 0x14 -#define OPTi93X_MIC_RIGHT_INPUT 0x15 -#define OPTi93X_OUT_LEFT 0x16 -#define OPTi93X_OUT_RIGHT 0x17 -#define OPTi93X_CAPT_FORMAT 0x1c -#define OPTi93X_CAPT_UPR_CNT 0x1e -#define OPTi93X_CAPT_LWR_CNT 0x1f - -#define OPTi93X_TRD 0x20 -#define OPTi93X_MCE 0x40 -#define OPTi93X_INIT 0x80 - -#define OPTi93X_MIXOUT_MIC_GAIN 0x20 -#define OPTi93X_MIXOUT_LINE 0x00 -#define OPTi93X_MIXOUT_CD 0x40 -#define OPTi93X_MIXOUT_MIC 0x80 -#define OPTi93X_MIXOUT_MIXER 0xc0 - -#define OPTi93X_STEREO 0x10 -#define OPTi93X_LINEAR_8 0x00 -#define OPTi93X_ULAW_8 0x20 -#define OPTi93X_LINEAR_16_LIT 0x40 -#define OPTi93X_ALAW_8 0x60 -#define OPTi93X_ADPCM_16 0xa0 -#define OPTi93X_LINEAR_16_BIG 0xc0 - -#define OPTi93X_CAPTURE_PIO 0x80 -#define OPTi93X_PLAYBACK_PIO 0x40 -#define OPTi93X_AUTOCALIB 0x08 -#define OPTi93X_SINGLE_DMA 0x04 -#define OPTi93X_CAPTURE_ENABLE 0x02 -#define OPTi93X_PLAYBACK_ENABLE 0x01 - -#define OPTi93X_IRQ_ENABLE 0x02 - -#define OPTi93X_DMA_REQUEST 0x10 -#define OPTi93X_CALIB_IN_PROGRESS 0x20 - #define OPTi93X_IRQ_PLAYBACK 0x04 #define OPTi93X_IRQ_CAPTURE 0x08 - -struct snd_opti93x { - unsigned long port; - struct resource *res_port; - int irq; - int dma1; - int dma2; - - struct snd_opti9xx *chip; - unsigned short hardware; - unsigned char image[32]; - - unsigned char mce_bit; - unsigned short mode; - int mute; - - spinlock_t lock; - - struct snd_card *card; - struct snd_pcm *pcm; - struct snd_pcm_substream *playback_substream; - struct snd_pcm_substream *capture_substream; - unsigned int p_dma_size; - unsigned int c_dma_size; -}; - -#define OPTi93X_MODE_NONE 0x00 -#define OPTi93X_MODE_PLAY 0x01 -#define OPTi93X_MODE_CAPTURE 0x02 -#define OPTi93X_MODE_OPEN (OPTi93X_MODE_PLAY | OPTi93X_MODE_CAPTURE) - #endif /* OPTi93X */ struct snd_opti9xx { @@ -235,43 +136,35 @@ struct snd_opti9xx { unsigned long mc_base_size; #ifdef OPTi93X unsigned long mc_indir_index; + struct resource *res_mc_indir; #endif /* OPTi93X */ + struct snd_wss *codec; unsigned long pwd_reg; spinlock_t lock; long wss_base; int irq; - int dma1; -#if defined(CS4231) || defined(OPTi93X) - int dma2; -#endif /* CS4231 || OPTi93X */ - - long fm_port; - - long mpu_port; - int mpu_irq; - -#ifdef CONFIG_PNP - struct pnp_dev *dev; - struct pnp_dev *devmpu; -#endif /* CONFIG_PNP */ }; static int snd_opti9xx_pnp_is_probed; -static struct platform_device *snd_opti9xx_platform_device; #ifdef CONFIG_PNP static struct pnp_card_device_id snd_opti9xx_pnpids[] = { #ifndef OPTi93X /* OPTi 82C924 */ - { .id = "OPT0924", .devs = { { "OPT0000" }, { "OPT0002" } }, .driver_data = 0x0924 }, + { .id = "OPT0924", + .devs = { { "OPT0000" }, { "OPT0002" }, { "OPT0005" } }, + .driver_data = 0x0924 }, /* OPTi 82C925 */ - { .id = "OPT0925", .devs = { { "OPT9250" }, { "OPT0002" } }, .driver_data = 0x0925 }, + { .id = "OPT0925", + .devs = { { "OPT9250" }, { "OPT0002" }, { "OPT0005" } }, + .driver_data = 0x0925 }, #else /* OPTi 82C931/3 */ - { .id = "OPT0931", .devs = { { "OPT9310" }, { "OPT0002" } }, .driver_data = 0x0931 }, + { .id = "OPT0931", .devs = { { "OPT9310" }, { "OPT0002" } }, + .driver_data = 0x0931 }, #endif /* OPTi93X */ { .id = "" } }; @@ -280,65 +173,52 @@ MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids); #endif /* CONFIG_PNP */ -#ifdef OPTi93X -#define DRIVER_NAME "snd-card-opti93x" -#else -#define DRIVER_NAME "snd-card-opti92x" -#endif /* OPTi93X */ +#define DEV_NAME KBUILD_MODNAME static char * snd_opti9xx_names[] = { - "unkown", + "unknown", "82C928", "82C929", "82C924", "82C925", "82C930", "82C931", "82C933" }; - -static long __init snd_legacy_find_free_ioport(long *port_table, long size) -{ - while (*port_table != -1) { - if (request_region(*port_table, size, "ALSA test")) { - release_region(*port_table, size); - return *port_table; - } - port_table++; - } - return -1; -} - -static int __init snd_opti9xx_init(struct snd_opti9xx *chip, unsigned short hardware) +static int snd_opti9xx_init(struct snd_opti9xx *chip, + unsigned short hardware) { static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2}; chip->hardware = hardware; strcpy(chip->name, snd_opti9xx_names[hardware]); - chip->mc_base_size = opti9xx_mc_size[hardware]; - spin_lock_init(&chip->lock); - chip->wss_base = -1; chip->irq = -1; - chip->dma1 = -1; -#if defined(CS4231) || defined (OPTi93X) - chip->dma2 = -1; -#endif /* CS4231 || OPTi93X */ - chip->fm_port = -1; - chip->mpu_port = -1; - chip->mpu_irq = -1; + +#ifndef OPTi93X +#ifdef CONFIG_PNP + if (isapnp && chip->mc_base) + /* PnP resource gives the least 10 bits */ + chip->mc_base |= 0xc00; + else +#endif /* CONFIG_PNP */ + { + chip->mc_base = 0xf8c; + chip->mc_base_size = opti9xx_mc_size[hardware]; + } +#else + chip->mc_base_size = opti9xx_mc_size[hardware]; +#endif switch (hardware) { #ifndef OPTi93X case OPTi9XX_HW_82C928: case OPTi9XX_HW_82C929: - chip->mc_base = 0xf8c; chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3; chip->pwd_reg = 3; break; case OPTi9XX_HW_82C924: case OPTi9XX_HW_82C925: - chip->mc_base = 0xf8c; chip->password = 0xe5; chip->pwd_reg = 3; break; @@ -348,14 +228,15 @@ static int __init snd_opti9xx_init(struct snd_opti9xx *chip, unsigned short hard case OPTi9XX_HW_82C931: case OPTi9XX_HW_82C933: chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d; - chip->mc_indir_index = 0xe0e; + if (!chip->mc_indir_index) + chip->mc_indir_index = 0xe0e; chip->password = 0xe4; chip->pwd_reg = 0; break; #endif /* OPTi93X */ default: - snd_printk("chip %d not supported\n", hardware); + snd_printk(KERN_ERR "chip %d not supported\n", hardware); return -ENODEV; } return 0; @@ -397,13 +278,13 @@ static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip, #endif /* OPTi93X */ default: - snd_printk("chip %d not supported\n", chip->hardware); + snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); } spin_unlock_irqrestore(&chip->lock, flags); return retval; } - + static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg, unsigned char value) { @@ -439,7 +320,7 @@ static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg, #endif /* OPTi93X */ default: - snd_printk("chip %d not supported\n", chip->hardware); + snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); } spin_unlock_irqrestore(&chip->lock, flags); @@ -451,7 +332,10 @@ static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg, (snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask))) -static int __init snd_opti9xx_configure(struct snd_opti9xx *chip) +static int snd_opti9xx_configure(struct snd_opti9xx *chip, + long port, + int irq, int dma1, int dma2, + long mpu_port, int mpu_irq) { unsigned char wss_base_bits; unsigned char irq_bits; @@ -462,16 +346,23 @@ static int __init snd_opti9xx_configure(struct snd_opti9xx *chip) switch (chip->hardware) { #ifndef OPTi93X case OPTi9XX_HW_82C924: + /* opti 929 mode (?), OPL3 clock output, audio enable */ snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc); + /* enable wave audio */ snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02); case OPTi9XX_HW_82C925: + /* enable WSS mode */ snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); + /* OPL3 FM synthesis */ snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20); + /* disable Sound Blaster IRQ and DMA */ snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff); #ifdef CS4231 + /* cs4231/4248 fix enabled */ snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); #else + /* cs4231/4248 fix disabled */ snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); #endif /* CS4231 */ break; @@ -492,9 +383,21 @@ static int __init snd_opti9xx_configure(struct snd_opti9xx *chip) break; #else /* OPTi93X */ - case OPTi9XX_HW_82C930: case OPTi9XX_HW_82C931: - case OPTi9XX_HW_82C933: + /* disable 3D sound (set GPIO1 as output, low) */ + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(20), 0x04, 0x0c); + case OPTi9XX_HW_82C933: /* FALL THROUGH */ + /* + * The BTC 1817DW has QS1000 wavetable which is connected + * to the serial digital input of the OPTI931. + */ + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(21), 0x82, 0xff); + /* + * This bit sets OPTI931 to automaticaly select FM + * or digital input signal. + */ + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(26), 0x01, 0x01); + case OPTi9XX_HW_82C930: /* FALL THROUGH */ snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03); snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff); snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 | @@ -505,31 +408,36 @@ static int __init snd_opti9xx_configure(struct snd_opti9xx *chip) #endif /* OPTi93X */ default: - snd_printk("chip %d not supported\n", chip->hardware); + snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); return -EINVAL; } - switch (chip->wss_base) { - case 0x530: + /* PnP resource says it decodes only 10 bits of address */ + switch (port & 0x3ff) { + case 0x130: + chip->wss_base = 0x530; wss_base_bits = 0x00; break; - case 0x604: + case 0x204: + chip->wss_base = 0x604; wss_base_bits = 0x03; break; - case 0xe80: + case 0x280: + chip->wss_base = 0xe80; wss_base_bits = 0x01; break; - case 0xf40: + case 0x340: + chip->wss_base = 0xf40; wss_base_bits = 0x02; break; default: - snd_printk("WSS port 0x%lx not valid\n", chip->wss_base); + snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n", port); goto __skip_base; } snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30); __skip_base: - switch (chip->irq) { + switch (irq) { //#ifdef OPTi93X case 5: irq_bits = 0x05; @@ -548,11 +456,11 @@ __skip_base: irq_bits = 0x04; break; default: - snd_printk("WSS irq # %d not valid\n", chip->irq); + snd_printk(KERN_WARNING "WSS irq # %d not valid\n", irq); goto __skip_resources; } - switch (chip->dma1) { + switch (dma1) { case 0: dma_bits = 0x01; break; @@ -563,22 +471,22 @@ __skip_base: dma_bits = 0x03; break; default: - snd_printk("WSS dma1 # %d not valid\n", chip->dma1); + snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n", dma1); goto __skip_resources; } #if defined(CS4231) || defined(OPTi93X) - if (chip->dma1 == chip->dma2) { - snd_printk("don't want to share dmas\n"); + if (dma1 == dma2) { + snd_printk(KERN_ERR "don't want to share dmas\n"); return -EBUSY; } - switch (chip->dma2) { + switch (dma2) { case 0: case 1: break; default: - snd_printk("WSS dma2 # %d not valid\n", chip->dma2); + snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n", dma2); goto __skip_resources; } dma_bits |= 0x04; @@ -592,7 +500,7 @@ __skip_base: __skip_resources: if (chip->hardware > OPTi9XX_HW_82C928) { - switch (chip->mpu_port) { + switch (mpu_port) { case 0: case -1: break; @@ -609,12 +517,12 @@ __skip_resources: mpu_port_bits = 0x00; break; default: - snd_printk("MPU-401 port 0x%lx not valid\n", - chip->mpu_port); + snd_printk(KERN_WARNING + "MPU-401 port 0x%lx not valid\n", mpu_port); goto __skip_mpu; } - switch (chip->mpu_irq) { + switch (mpu_irq) { case 5: mpu_irq_bits = 0x02; break; @@ -628,13 +536,13 @@ __skip_resources: mpu_irq_bits = 0x01; break; default: - snd_printk("MPU-401 irq # %d not valid\n", - chip->mpu_irq); + snd_printk(KERN_WARNING "MPU-401 irq # %d not valid\n", + mpu_irq); goto __skip_mpu; } snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), - (chip->mpu_port <= 0) ? 0x00 : + (mpu_port <= 0) ? 0x00 : 0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3, 0xf8); } @@ -645,1096 +553,227 @@ __skip_mpu: #ifdef OPTi93X -static unsigned char snd_opti93x_default_image[32] = -{ - 0x00, /* 00/00 - l_mixout_outctrl */ - 0x00, /* 01/01 - r_mixout_outctrl */ - 0x88, /* 02/02 - l_cd_inctrl */ - 0x88, /* 03/03 - r_cd_inctrl */ - 0x88, /* 04/04 - l_a1/fm_inctrl */ - 0x88, /* 05/05 - r_a1/fm_inctrl */ - 0x80, /* 06/06 - l_dac_inctrl */ - 0x80, /* 07/07 - r_dac_inctrl */ - 0x00, /* 08/08 - ply_dataform_reg */ - 0x00, /* 09/09 - if_conf */ - 0x00, /* 0a/10 - pin_ctrl */ - 0x00, /* 0b/11 - err_init_reg */ - 0x0a, /* 0c/12 - id_reg */ - 0x00, /* 0d/13 - reserved */ - 0x00, /* 0e/14 - ply_upcount_reg */ - 0x00, /* 0f/15 - ply_lowcount_reg */ - 0x88, /* 10/16 - reserved/l_a1_inctrl */ - 0x88, /* 11/17 - reserved/r_a1_inctrl */ - 0x88, /* 12/18 - l_line_inctrl */ - 0x88, /* 13/19 - r_line_inctrl */ - 0x88, /* 14/20 - l_mic_inctrl */ - 0x88, /* 15/21 - r_mic_inctrl */ - 0x80, /* 16/22 - l_out_outctrl */ - 0x80, /* 17/23 - r_out_outctrl */ - 0x00, /* 18/24 - reserved */ - 0x00, /* 19/25 - reserved */ - 0x00, /* 1a/26 - reserved */ - 0x00, /* 1b/27 - reserved */ - 0x00, /* 1c/28 - cap_dataform_reg */ - 0x00, /* 1d/29 - reserved */ - 0x00, /* 1e/30 - cap_upcount_reg */ - 0x00 /* 1f/31 - cap_lowcount_reg */ -}; - - -static int snd_opti93x_busy_wait(struct snd_opti93x *chip) -{ - int timeout; - - for (timeout = 250; timeout-- > 0; udelay(10)) - if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_INIT)) - return 0; - - snd_printk("chip still busy.\n"); - return -EBUSY; -} - -static unsigned char snd_opti93x_in(struct snd_opti93x *chip, unsigned char reg) -{ - snd_opti93x_busy_wait(chip); - outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX)); - return inb(OPTi93X_PORT(chip, DATA)); -} - -static void snd_opti93x_out(struct snd_opti93x *chip, unsigned char reg, - unsigned char value) -{ - snd_opti93x_busy_wait(chip); - outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX)); - outb(value, OPTi93X_PORT(chip, DATA)); -} - -static void snd_opti93x_out_image(struct snd_opti93x *chip, unsigned char reg, - unsigned char value) -{ - snd_opti93x_out(chip, reg, chip->image[reg] = value); -} - -static void snd_opti93x_out_mask(struct snd_opti93x *chip, unsigned char reg, - unsigned char mask, unsigned char value) -{ - snd_opti93x_out_image(chip, reg, - (chip->image[reg] & ~mask) | (value & mask)); -} - - -static void snd_opti93x_mce_up(struct snd_opti93x *chip) -{ - snd_opti93x_busy_wait(chip); - - chip->mce_bit = OPTi93X_MCE; - if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE)) - outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX)); -} - -static void snd_opti93x_mce_down(struct snd_opti93x *chip) -{ - snd_opti93x_busy_wait(chip); - - chip->mce_bit = 0; - if (inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE) - outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX)); -} - -#define snd_opti93x_mute_reg(chip, reg, mute) \ - snd_opti93x_out(chip, reg, mute ? 0x80 : chip->image[reg]); - -static void snd_opti93x_mute(struct snd_opti93x *chip, int mute) -{ - mute = mute ? 1 : 0; - if (chip->mute == mute) - return; - - chip->mute = mute; - - snd_opti93x_mute_reg(chip, OPTi93X_CD_LEFT_INPUT, mute); - snd_opti93x_mute_reg(chip, OPTi93X_CD_RIGHT_INPUT, mute); - switch (chip->hardware) { - case OPTi9XX_HW_82C930: - snd_opti93x_mute_reg(chip, OPTi930_AUX_LEFT_INPUT, mute); - snd_opti93x_mute_reg(chip, OPTi930_AUX_RIGHT_INPUT, mute); - break; - case OPTi9XX_HW_82C931: - case OPTi9XX_HW_82C933: - snd_opti93x_mute_reg(chip, OPTi931_FM_LEFT_INPUT, mute); - snd_opti93x_mute_reg(chip, OPTi931_FM_RIGHT_INPUT, mute); - snd_opti93x_mute_reg(chip, OPTi931_AUX_LEFT_INPUT, mute); - snd_opti93x_mute_reg(chip, OPTi931_AUX_RIGHT_INPUT, mute); - } - snd_opti93x_mute_reg(chip, OPTi93X_DAC_LEFT, mute); - snd_opti93x_mute_reg(chip, OPTi93X_DAC_RIGHT, mute); - snd_opti93x_mute_reg(chip, OPTi93X_LINE_LEFT_INPUT, mute); - snd_opti93x_mute_reg(chip, OPTi93X_LINE_RIGHT_INPUT, mute); - snd_opti93x_mute_reg(chip, OPTi93X_MIC_LEFT_INPUT, mute); - snd_opti93x_mute_reg(chip, OPTi93X_MIC_RIGHT_INPUT, mute); - snd_opti93x_mute_reg(chip, OPTi93X_OUT_LEFT, mute); - snd_opti93x_mute_reg(chip, OPTi93X_OUT_RIGHT, mute); -} - - -static unsigned int snd_opti93x_get_count(unsigned char format, - unsigned int size) -{ - switch (format & 0xe0) { - case OPTi93X_LINEAR_16_LIT: - case OPTi93X_LINEAR_16_BIG: - size >>= 1; - break; - case OPTi93X_ADPCM_16: - return size >> 2; - } - return (format & OPTi93X_STEREO) ? (size >> 1) : size; -} - -static unsigned int rates[] = { 5512, 6615, 8000, 9600, 11025, 16000, - 18900, 22050, 27428, 32000, 33075, 37800, - 44100, 48000 }; -#define RATES ARRAY_SIZE(rates) +static const DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_step, -9300, 300, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_4bit_12db_max, -3300, 300, 0); -static struct snd_pcm_hw_constraint_list hw_constraints_rates = { - .count = RATES, - .list = rates, - .mask = 0, +static struct snd_kcontrol_new snd_opti93x_controls[] = { +WSS_DOUBLE("Master Playback Switch", 0, + OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1), +WSS_DOUBLE_TLV("Master Playback Volume", 0, + OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1, + db_scale_5bit_3db_step), +WSS_DOUBLE_TLV("PCM Playback Volume", 0, + CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 31, 1, + db_scale_5bit), +WSS_DOUBLE_TLV("FM Playback Volume", 0, + CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 1, 1, 15, 1, + db_scale_4bit_12db_max), +WSS_DOUBLE("Line Playback Switch", 0, + CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), +WSS_DOUBLE_TLV("Line Playback Volume", 0, + CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1, + db_scale_4bit_12db_max), +WSS_DOUBLE("Mic Playback Switch", 0, + OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1), +WSS_DOUBLE_TLV("Mic Playback Volume", 0, + OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1, + db_scale_4bit_12db_max), +WSS_DOUBLE_TLV("CD Playback Volume", 0, + CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 1, 1, 15, 1, + db_scale_4bit_12db_max), +WSS_DOUBLE("Aux Playback Switch", 0, + OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1), +WSS_DOUBLE_TLV("Aux Playback Volume", 0, + OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1, + db_scale_4bit_12db_max), }; -static unsigned char bits[] = { 0x01, 0x0f, 0x00, 0x0e, 0x03, 0x02, - 0x05, 0x07, 0x04, 0x06, 0x0d, 0x09, - 0x0b, 0x0c}; - -static unsigned char snd_opti93x_get_freq(unsigned int rate) -{ - unsigned int i; - - for (i = 0; i < RATES; i++) { - if (rate == rates[i]) - return bits[i]; - } - snd_BUG(); - return bits[RATES-1]; -} - -static unsigned char snd_opti93x_get_format(struct snd_opti93x *chip, - unsigned int format, int channels) +static int snd_opti93x_mixer(struct snd_wss *chip) { - unsigned char retval = OPTi93X_LINEAR_8; - - switch (format) { - case SNDRV_PCM_FORMAT_MU_LAW: - retval = OPTi93X_ULAW_8; - break; - case SNDRV_PCM_FORMAT_A_LAW: - retval = OPTi93X_ALAW_8; - break; - case SNDRV_PCM_FORMAT_S16_LE: - retval = OPTi93X_LINEAR_16_LIT; - break; - case SNDRV_PCM_FORMAT_S16_BE: - retval = OPTi93X_LINEAR_16_BIG; - break; - case SNDRV_PCM_FORMAT_IMA_ADPCM: - retval = OPTi93X_ADPCM_16; - } - return (channels > 1) ? (retval | OPTi93X_STEREO) : retval; -} - - -static void snd_opti93x_playback_format(struct snd_opti93x *chip, unsigned char fmt) -{ - unsigned char mask; - - snd_opti93x_mute(chip, 1); - - snd_opti93x_mce_up(chip); - mask = (chip->mode & OPTi93X_MODE_CAPTURE) ? 0xf0 : 0xff; - snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, mask, fmt); - snd_opti93x_mce_down(chip); - - snd_opti93x_mute(chip, 0); -} - -static void snd_opti93x_capture_format(struct snd_opti93x *chip, unsigned char fmt) -{ - snd_opti93x_mute(chip, 1); - - snd_opti93x_mce_up(chip); - if (!(chip->mode & OPTi93X_MODE_PLAY)) - snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, 0x0f, fmt); - else - fmt = chip->image[OPTi93X_PLAY_FORMAT] & 0xf0; - snd_opti93x_out_image(chip, OPTi93X_CAPT_FORMAT, fmt); - snd_opti93x_mce_down(chip); - - snd_opti93x_mute(chip, 0); -} - + struct snd_card *card; + unsigned int idx; + struct snd_ctl_elem_id id1, id2; + int err; -static int snd_opti93x_open(struct snd_opti93x *chip, unsigned int mode) -{ - unsigned long flags; + if (snd_BUG_ON(!chip || !chip->pcm)) + return -EINVAL; - spin_lock_irqsave(&chip->lock, flags); + card = chip->card; - if (chip->mode & mode) { - spin_unlock_irqrestore(&chip->lock, flags); - return -EAGAIN; - } + strcpy(card->mixername, chip->pcm->name); - if (!(chip->mode & OPTi93X_MODE_OPEN)) { - outb(0x00, OPTi93X_PORT(chip, STATUS)); - snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL, - OPTi93X_IRQ_ENABLE, OPTi93X_IRQ_ENABLE); - chip->mode = mode; + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + /* reassign AUX0 switch to CD */ + strcpy(id1.name, "Aux Playback Switch"); + strcpy(id2.name, "CD Playback Switch"); + err = snd_ctl_rename_id(card, &id1, &id2); + if (err < 0) { + snd_printk(KERN_ERR "Cannot rename opti93x control\n"); + return err; } - else - chip->mode |= mode; - - spin_unlock_irqrestore(&chip->lock, flags); - return 0; -} - -static void snd_opti93x_close(struct snd_opti93x *chip, unsigned int mode) -{ - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - - chip->mode &= ~mode; - if (chip->mode & OPTi93X_MODE_OPEN) { - spin_unlock_irqrestore(&chip->lock, flags); - return; + /* reassign AUX1 switch to FM */ + strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strcpy(id2.name, "FM Playback Switch"); + err = snd_ctl_rename_id(card, &id1, &id2); + if (err < 0) { + snd_printk(KERN_ERR "Cannot rename opti93x control\n"); + return err; } + /* remove AUX1 volume */ + strcpy(id1.name, "Aux Playback Volume"); id1.index = 1; + snd_ctl_remove_id(card, &id1); - snd_opti93x_mute(chip, 1); - - outb(0, OPTi93X_PORT(chip, STATUS)); - snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL, OPTi93X_IRQ_ENABLE, - ~OPTi93X_IRQ_ENABLE); - - snd_opti93x_mce_up(chip); - snd_opti93x_out_image(chip, OPTi93X_IFACE_CONF, 0x00); - snd_opti93x_mce_down(chip); - chip->mode = 0; - - snd_opti93x_mute(chip, 0); - spin_unlock_irqrestore(&chip->lock, flags); -} - -static int snd_opti93x_trigger(struct snd_pcm_substream *substream, - unsigned char what, int cmd) -{ - struct snd_opti93x *chip = snd_pcm_substream_chip(substream); + /* Replace WSS volume controls with OPTi93x volume controls */ + id1.index = 0; + for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) { + strcpy(id1.name, snd_opti93x_controls[idx].name); + snd_ctl_remove_id(card, &id1); - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_STOP: - { - unsigned int what = 0; - struct list_head *pos; - struct snd_pcm_substream *s; - snd_pcm_group_for_each(pos, substream) { - s = snd_pcm_group_substream_entry(pos); - if (s == chip->playback_substream) { - what |= OPTi93X_PLAYBACK_ENABLE; - snd_pcm_trigger_done(s, substream); - } else if (s == chip->capture_substream) { - what |= OPTi93X_CAPTURE_ENABLE; - snd_pcm_trigger_done(s, substream); - } - } - spin_lock(&chip->lock); - if (cmd == SNDRV_PCM_TRIGGER_START) { - snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, what); - if (what & OPTi93X_CAPTURE_ENABLE) - udelay(50); - } else - snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, 0x00); - spin_unlock(&chip->lock); - break; - } - default: - return -EINVAL; + err = snd_ctl_add(card, + snd_ctl_new1(&snd_opti93x_controls[idx], chip)); + if (err < 0) + return err; } return 0; } -static int snd_opti93x_playback_trigger(struct snd_pcm_substream *substream, int cmd) -{ - return snd_opti93x_trigger(substream, - OPTi93X_PLAYBACK_ENABLE, cmd); -} - -static int snd_opti93x_capture_trigger(struct snd_pcm_substream *substream, int cmd) -{ - return snd_opti93x_trigger(substream, - OPTi93X_CAPTURE_ENABLE, cmd); -} - -static int snd_opti93x_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); -} - - -static int snd_opti93x_hw_free(struct snd_pcm_substream *substream) -{ - snd_pcm_lib_free_pages(substream); - return 0; -} - - -static int snd_opti93x_playback_prepare(struct snd_pcm_substream *substream) -{ - struct snd_opti93x *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned long flags; - unsigned char format; - unsigned int count = snd_pcm_lib_period_bytes(substream); - unsigned int size = snd_pcm_lib_buffer_bytes(substream); - - spin_lock_irqsave(&chip->lock, flags); - - chip->p_dma_size = size; - snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, - OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO, - ~(OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO)); - - snd_dma_program(chip->dma1, runtime->dma_addr, size, - DMA_MODE_WRITE | DMA_AUTOINIT); - - format = snd_opti93x_get_freq(runtime->rate); - format |= snd_opti93x_get_format(chip, runtime->format, - runtime->channels); - snd_opti93x_playback_format(chip, format); - format = chip->image[OPTi93X_PLAY_FORMAT]; - - count = snd_opti93x_get_count(format, count) - 1; - snd_opti93x_out_image(chip, OPTi93X_PLAY_LWR_CNT, count); - snd_opti93x_out_image(chip, OPTi93X_PLAY_UPR_CNT, count >> 8); - - spin_unlock_irqrestore(&chip->lock, flags); - return 0; -} - -static int snd_opti93x_capture_prepare(struct snd_pcm_substream *substream) -{ - struct snd_opti93x *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned long flags; - unsigned char format; - unsigned int count = snd_pcm_lib_period_bytes(substream); - unsigned int size = snd_pcm_lib_buffer_bytes(substream); - - spin_lock_irqsave(&chip->lock, flags); - - chip->c_dma_size = size; - snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, - OPTi93X_CAPTURE_ENABLE | OPTi93X_CAPTURE_PIO, 0); - - snd_dma_program(chip->dma2, runtime->dma_addr, size, - DMA_MODE_READ | DMA_AUTOINIT); - - format = snd_opti93x_get_freq(runtime->rate); - format |= snd_opti93x_get_format(chip, runtime->format, - runtime->channels); - snd_opti93x_capture_format(chip, format); - format = chip->image[OPTi93X_CAPT_FORMAT]; - - count = snd_opti93x_get_count(format, count) - 1; - snd_opti93x_out_image(chip, OPTi93X_CAPT_LWR_CNT, count); - snd_opti93x_out_image(chip, OPTi93X_CAPT_UPR_CNT, count >> 8); - - spin_unlock_irqrestore(&chip->lock, flags); - return 0; -} - -static snd_pcm_uframes_t snd_opti93x_playback_pointer(struct snd_pcm_substream *substream) +static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id) { - struct snd_opti93x *chip = snd_pcm_substream_chip(substream); - size_t ptr; - - if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_PLAYBACK_ENABLE)) - return 0; - - ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size); - return bytes_to_frames(substream->runtime, ptr); -} - -static snd_pcm_uframes_t snd_opti93x_capture_pointer(struct snd_pcm_substream *substream) -{ - struct snd_opti93x *chip = snd_pcm_substream_chip(substream); - size_t ptr; - - if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_CAPTURE_ENABLE)) - return 0; - - ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size); - return bytes_to_frames(substream->runtime, ptr); -} - - -static void snd_opti93x_overrange(struct snd_opti93x *chip) -{ - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - - if (snd_opti93x_in(chip, OPTi93X_ERR_INIT) & (0x08 | 0x02)) - chip->capture_substream->runtime->overrange++; - - spin_unlock_irqrestore(&chip->lock, flags); -} - -static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct snd_opti93x *codec = dev_id; + struct snd_opti9xx *chip = dev_id; + struct snd_wss *codec = chip->codec; unsigned char status; - status = snd_opti9xx_read(codec->chip, OPTi9XX_MC_REG(11)); + if (!codec) + return IRQ_HANDLED; + + status = snd_opti9xx_read(chip, OPTi9XX_MC_REG(11)); if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream) snd_pcm_period_elapsed(codec->playback_substream); if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) { - snd_opti93x_overrange(codec); + snd_wss_overrange(codec); snd_pcm_period_elapsed(codec->capture_substream); } outb(0x00, OPTi93X_PORT(codec, STATUS)); return IRQ_HANDLED; } +#endif /* OPTi93X */ -static struct snd_pcm_hardware snd_opti93x_playback = { - .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), - .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | - SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), - .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, - .rate_min = 5512, - .rate_max = 48000, - .channels_min = 1, - .channels_max = 2, - .buffer_bytes_max = (128*1024), - .period_bytes_min = 64, - .period_bytes_max = (128*1024), - .periods_min = 1, - .periods_max = 1024, - .fifo_size = 0, -}; - -static struct snd_pcm_hardware snd_opti93x_capture = { - .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), - .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | - SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), - .rates = SNDRV_PCM_RATE_8000_48000, - .rate_min = 5512, - .rate_max = 48000, - .channels_min = 1, - .channels_max = 2, - .buffer_bytes_max = (128*1024), - .period_bytes_min = 64, - .period_bytes_max = (128*1024), - .periods_min = 1, - .periods_max = 1024, - .fifo_size = 0, -}; - -static int snd_opti93x_playback_open(struct snd_pcm_substream *substream) -{ - int error; - struct snd_opti93x *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - - if ((error = snd_opti93x_open(chip, OPTi93X_MODE_PLAY)) < 0) - return error; - snd_pcm_set_sync(substream); - chip->playback_substream = substream; - runtime->hw = snd_opti93x_playback; - snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); - return error; -} - -static int snd_opti93x_capture_open(struct snd_pcm_substream *substream) -{ - int error; - struct snd_opti93x *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - - if ((error = snd_opti93x_open(chip, OPTi93X_MODE_CAPTURE)) < 0) - return error; - runtime->hw = snd_opti93x_capture; - snd_pcm_set_sync(substream); - chip->capture_substream = substream; - snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); - return error; -} - -static int snd_opti93x_playback_close(struct snd_pcm_substream *substream) -{ - struct snd_opti93x *chip = snd_pcm_substream_chip(substream); - - chip->playback_substream = NULL; - snd_opti93x_close(chip, OPTi93X_MODE_PLAY); - return 0; -} - -static int snd_opti93x_capture_close(struct snd_pcm_substream *substream) -{ - struct snd_opti93x *chip = snd_pcm_substream_chip(substream); - - chip->capture_substream = NULL; - snd_opti93x_close(chip, OPTi93X_MODE_CAPTURE); - return 0; -} - - -static void snd_opti93x_init(struct snd_opti93x *chip) -{ - unsigned long flags; - int i; - - spin_lock_irqsave(&chip->lock, flags); - snd_opti93x_mce_up(chip); - - for (i = 0; i < 32; i++) - snd_opti93x_out_image(chip, i, snd_opti93x_default_image[i]); - - snd_opti93x_mce_down(chip); - spin_unlock_irqrestore(&chip->lock, flags); -} - -static int snd_opti93x_probe(struct snd_opti93x *chip) +static int snd_opti9xx_read_check(struct snd_opti9xx *chip) { + unsigned char value; +#ifdef OPTi93X unsigned long flags; - unsigned char val; - - spin_lock_irqsave(&chip->lock, flags); - val = snd_opti93x_in(chip, OPTi93X_ID) & 0x0f; - spin_unlock_irqrestore(&chip->lock, flags); - - return (val == 0x0a) ? 0 : -ENODEV; -} - -static int snd_opti93x_free(struct snd_opti93x *chip) -{ - release_and_free_resource(chip->res_port); - if (chip->dma1 >= 0) { - disable_dma(chip->dma1); - free_dma(chip->dma1); - } - if (chip->dma2 >= 0) { - disable_dma(chip->dma2); - free_dma(chip->dma2); - } - if (chip->irq >= 0) { - free_irq(chip->irq, chip); - } - kfree(chip); - return 0; -} - -static int snd_opti93x_dev_free(struct snd_device *device) -{ - struct snd_opti93x *chip = device->device_data; - return snd_opti93x_free(chip); -} - -static const char *snd_opti93x_chip_id(struct snd_opti93x *codec) -{ - switch (codec->hardware) { - case OPTi9XX_HW_82C930: return "82C930"; - case OPTi9XX_HW_82C931: return "82C931"; - case OPTi9XX_HW_82C933: return "82C933"; - default: return "???"; - } -} +#endif -static int snd_opti93x_create(struct snd_card *card, struct snd_opti9xx *chip, - int dma1, int dma2, - struct snd_opti93x **rcodec) -{ - static struct snd_device_ops ops = { - .dev_free = snd_opti93x_dev_free, - }; - int error; - struct snd_opti93x *codec; - - *rcodec = NULL; - codec = kzalloc(sizeof(*codec), GFP_KERNEL); - if (codec == NULL) - return -ENOMEM; - codec->irq = -1; - codec->dma1 = -1; - codec->dma2 = -1; - - if ((codec->res_port = request_region(chip->wss_base + 4, 4, "OPTI93x CODEC")) == NULL) { - snd_printk(KERN_ERR "opti9xx: can't grab port 0x%lx\n", chip->wss_base + 4); - snd_opti93x_free(codec); - return -EBUSY; - } - if (request_dma(dma1, "OPTI93x - 1")) { - snd_printk(KERN_ERR "opti9xx: can't grab DMA1 %d\n", dma1); - snd_opti93x_free(codec); + chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, + "OPTi9xx MC"); + if (chip->res_mc_base == NULL) return -EBUSY; - } - codec->dma1 = chip->dma1; - if (request_dma(dma2, "OPTI93x - 2")) { - snd_printk(KERN_ERR "opti9xx: can't grab DMA2 %d\n", dma2); - snd_opti93x_free(codec); - return -EBUSY; - } - codec->dma2 = chip->dma2; - - if (request_irq(chip->irq, snd_opti93x_interrupt, SA_INTERRUPT, DRIVER_NAME" - WSS", codec)) { - snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", chip->irq); - snd_opti93x_free(codec); +#ifndef OPTi93X + value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)); + if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1))) + if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1))) + return 0; +#else /* OPTi93X */ + chip->res_mc_indir = request_region(chip->mc_indir_index, 2, + "OPTi93x MC"); + if (chip->res_mc_indir == NULL) return -EBUSY; - } - - codec->card = card; - codec->port = chip->wss_base + 4; - codec->irq = chip->irq; - - spin_lock_init(&codec->lock); - codec->hardware = chip->hardware; - codec->chip = chip; - - if ((error = snd_opti93x_probe(codec))) { - snd_opti93x_free(codec); - return error; - } - - snd_opti93x_init(codec); - - /* Register device */ - if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops)) < 0) { - snd_opti93x_free(codec); - return error; - } - - *rcodec = codec; - return 0; -} - -static struct snd_pcm_ops snd_opti93x_playback_ops = { - .open = snd_opti93x_playback_open, - .close = snd_opti93x_playback_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_opti93x_hw_params, - .hw_free = snd_opti93x_hw_free, - .prepare = snd_opti93x_playback_prepare, - .trigger = snd_opti93x_playback_trigger, - .pointer = snd_opti93x_playback_pointer, -}; - -static struct snd_pcm_ops snd_opti93x_capture_ops = { - .open = snd_opti93x_capture_open, - .close = snd_opti93x_capture_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_opti93x_hw_params, - .hw_free = snd_opti93x_hw_free, - .prepare = snd_opti93x_capture_prepare, - .trigger = snd_opti93x_capture_trigger, - .pointer = snd_opti93x_capture_pointer, -}; - -static int snd_opti93x_pcm(struct snd_opti93x *codec, int device, struct snd_pcm **rpcm) -{ - int error; - struct snd_pcm *pcm; - - if ((error = snd_pcm_new(codec->card, "OPTi 82C93X", device, 1, 1, &pcm))) - return error; - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_opti93x_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_opti93x_capture_ops); - - pcm->private_data = codec; - pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; - - strcpy(pcm->name, snd_opti93x_chip_id(codec)); - - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - snd_dma_isa_data(), - 64*1024, codec->dma1 > 3 || codec->dma2 > 3 ? 128*1024 : 64*1024); - - codec->pcm = pcm; - if (rpcm) - *rpcm = pcm; - return 0; -} - -/* - * MIXER part - */ - -static int snd_opti93x_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - static char *texts[4] = { - "Line1", "Aux", "Mic", "Mix" - }; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 2; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; -} - -static int snd_opti93x_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - ucontrol->value.enumerated.item[0] = (chip->image[OPTi93X_MIXOUT_LEFT] & OPTi93X_MIXOUT_MIXER) >> 6; - ucontrol->value.enumerated.item[1] = (chip->image[OPTi93X_MIXOUT_RIGHT] & OPTi93X_MIXOUT_MIXER) >> 6; - spin_unlock_irqrestore(&chip->lock, flags); - return 0; -} - -static int snd_opti93x_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; - unsigned short left, right; - int change; - - if (ucontrol->value.enumerated.item[0] > 3 || - ucontrol->value.enumerated.item[1] > 3) - return -EINVAL; - left = ucontrol->value.enumerated.item[0] << 6; - right = ucontrol->value.enumerated.item[1] << 6; - spin_lock_irqsave(&chip->lock, flags); - left = (chip->image[OPTi93X_MIXOUT_LEFT] & ~OPTi93X_MIXOUT_MIXER) | left; - right = (chip->image[OPTi93X_MIXOUT_RIGHT] & ~OPTi93X_MIXOUT_MIXER) | right; - change = left != chip->image[OPTi93X_MIXOUT_LEFT] || - right != chip->image[OPTi93X_MIXOUT_RIGHT]; - snd_opti93x_out_image(chip, OPTi93X_MIXOUT_LEFT, left); - snd_opti93x_out_image(chip, OPTi93X_MIXOUT_RIGHT, right); - spin_unlock_irqrestore(&chip->lock, flags); - return change; -} - -#if 0 - -#define OPTi93X_SINGLE(xname, xindex, reg, shift, mask, invert) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ - .info = snd_opti93x_info_single, \ - .get = snd_opti93x_get_single, .put = snd_opti93x_put_single, \ - .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } - -static int snd_opti93x_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - int mask = (kcontrol->private_value >> 16) & 0xff; - - uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = mask; - return 0; -} - -static int snd_opti93x_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; - int reg = kcontrol->private_value & 0xff; - int shift = (kcontrol->private_value >> 8) & 0xff; - int mask = (kcontrol->private_value >> 16) & 0xff; - int invert = (kcontrol->private_value >> 24) & 0xff; - - spin_lock_irqsave(&chip->lock, flags); - ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; - spin_unlock_irqrestore(&chip->lock, flags); - if (invert) - ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; - return 0; -} - -static int snd_opti93x_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; - int reg = kcontrol->private_value & 0xff; - int shift = (kcontrol->private_value >> 8) & 0xff; - int mask = (kcontrol->private_value >> 16) & 0xff; - int invert = (kcontrol->private_value >> 24) & 0xff; - int change; - unsigned short val; - - val = (ucontrol->value.integer.value[0] & mask); - if (invert) - val = mask - val; - val <<= shift; - spin_lock_irqsave(&chip->lock, flags); - val = (chip->image[reg] & ~(mask << shift)) | val; - change = val != chip->image[reg]; - snd_opti93x_out(chip, reg, val); - spin_unlock_irqrestore(&chip->lock, flags); - return change; -} - -#endif /* single */ - -#define OPTi93X_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ - .info = snd_opti93x_info_double, \ - .get = snd_opti93x_get_double, .put = snd_opti93x_put_double, \ - .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } - -#define OPTi93X_DOUBLE_INVERT_INVERT(xctl) \ - do { xctl.private_value ^= 22; } while (0) -#define OPTi93X_DOUBLE_CHANGE_REGS(xctl, left_reg, right_reg) \ - do { xctl.private_value &= ~0x0000ffff; \ - xctl.private_value |= left_reg | (right_reg << 8); } while (0) - -static int snd_opti93x_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - int mask = (kcontrol->private_value >> 24) & 0xff; - uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = mask; - return 0; -} - -static int snd_opti93x_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; - int left_reg = kcontrol->private_value & 0xff; - int right_reg = (kcontrol->private_value >> 8) & 0xff; - int shift_left = (kcontrol->private_value >> 16) & 0x07; - int shift_right = (kcontrol->private_value >> 19) & 0x07; - int mask = (kcontrol->private_value >> 24) & 0xff; - int invert = (kcontrol->private_value >> 22) & 1; - - spin_lock_irqsave(&chip->lock, flags); - ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; - ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask; - spin_unlock_irqrestore(&chip->lock, flags); - if (invert) { - ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; - ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; - } - return 0; -} - -static int snd_opti93x_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; - int left_reg = kcontrol->private_value & 0xff; - int right_reg = (kcontrol->private_value >> 8) & 0xff; - int shift_left = (kcontrol->private_value >> 16) & 0x07; - int shift_right = (kcontrol->private_value >> 19) & 0x07; - int mask = (kcontrol->private_value >> 24) & 0xff; - int invert = (kcontrol->private_value >> 22) & 1; - int change; - unsigned short val1, val2; - - val1 = ucontrol->value.integer.value[0] & mask; - val2 = ucontrol->value.integer.value[1] & mask; - if (invert) { - val1 = mask - val1; - val2 = mask - val2; - } - val1 <<= shift_left; - val2 <<= shift_right; spin_lock_irqsave(&chip->lock, flags); - val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; - val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; - change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg]; - snd_opti93x_out_image(chip, left_reg, val1); - snd_opti93x_out_image(chip, right_reg, val2); + outb(chip->password, chip->mc_base + chip->pwd_reg); + outb(((chip->mc_indir_index & 0x1f0) >> 4), chip->mc_base); spin_unlock_irqrestore(&chip->lock, flags); - return change; -} -static struct snd_kcontrol_new snd_opti93x_controls[] = { -OPTi93X_DOUBLE("Master Playback Switch", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1), -OPTi93X_DOUBLE("Master Playback Volume", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1), -OPTi93X_DOUBLE("PCM Playback Switch", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 7, 7, 1, 1), -OPTi93X_DOUBLE("PCM Playback Volume", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 0, 0, 31, 1), -OPTi93X_DOUBLE("FM Playback Switch", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 7, 7, 1, 1), -OPTi93X_DOUBLE("FM Playback Volume", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 1, 1, 15, 1), -OPTi93X_DOUBLE("Line Playback Switch", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 7, 7, 1, 1), -OPTi93X_DOUBLE("Line Playback Volume", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 1, 1, 15, 1), -OPTi93X_DOUBLE("Mic Playback Switch", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1), -OPTi93X_DOUBLE("Mic Playback Volume", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1), -OPTi93X_DOUBLE("Mic Boost", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 5, 5, 1, 1), -OPTi93X_DOUBLE("CD Playback Switch", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 7, 7, 1, 1), -OPTi93X_DOUBLE("CD Playback Volume", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 1, 1, 15, 1), -OPTi93X_DOUBLE("Aux Playback Switch", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1), -OPTi93X_DOUBLE("Aux Playback Volume", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1), -OPTi93X_DOUBLE("Capture Volume", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 0, 0, 15, 0), -{ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = snd_opti93x_info_mux, - .get = snd_opti93x_get_mux, - .put = snd_opti93x_put_mux, -} -}; - -static int snd_opti93x_mixer(struct snd_opti93x *chip) -{ - struct snd_card *card; - struct snd_kcontrol_new knew; - int err; - unsigned int idx; - - snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); - - card = chip->card; + value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)); + snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value); + if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value) + return 0; - strcpy(card->mixername, snd_opti93x_chip_id(chip)); + release_and_free_resource(chip->res_mc_indir); + chip->res_mc_indir = NULL; +#endif /* OPTi93X */ + release_and_free_resource(chip->res_mc_base); + chip->res_mc_base = NULL; - for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) { - knew = snd_opti93x_controls[idx]; - if (chip->hardware == OPTi9XX_HW_82C930) { - if (strstr(knew.name, "FM")) /* skip FM controls */ - continue; - else if (strcmp(knew.name, "Mic Playback Volume")) - OPTi93X_DOUBLE_INVERT_INVERT(knew); - else if (strstr(knew.name, "Aux")) - OPTi93X_DOUBLE_CHANGE_REGS(knew, OPTi930_AUX_LEFT_INPUT, OPTi930_AUX_RIGHT_INPUT); - else if (strcmp(knew.name, "PCM Playback Volume")) - OPTi93X_DOUBLE_INVERT_INVERT(knew); - else if (strcmp(knew.name, "Master Playback Volume")) - OPTi93X_DOUBLE_INVERT_INVERT(knew); - } - if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opti93x_controls[idx], chip))) < 0) - return err; - } - return 0; + return -ENODEV; } -#endif /* OPTi93X */ - -static int __init snd_card_opti9xx_detect(struct snd_card *card, struct snd_opti9xx *chip) +static int snd_card_opti9xx_detect(struct snd_card *card, + struct snd_opti9xx *chip) { int i, err; #ifndef OPTi93X for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) { - unsigned char value; - - if ((err = snd_opti9xx_init(chip, i)) < 0) - return err; - - if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) - continue; - - value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)); - if ((value != 0xff) && (value != inb(chip->mc_base + 1))) - if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1))) - return 1; - - release_and_free_resource(chip->res_mc_base); - chip->res_mc_base = NULL; - - } -#else /* OPTi93X */ +#else for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) { - unsigned long flags; - unsigned char value; - - if ((err = snd_opti9xx_init(chip, i)) < 0) +#endif + err = snd_opti9xx_init(chip, i); + if (err < 0) return err; - if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) - continue; - - spin_lock_irqsave(&chip->lock, flags); - outb(chip->password, chip->mc_base + chip->pwd_reg); - outb(((chip->mc_indir_index & (1 << 8)) >> 4) | - ((chip->mc_indir_index & 0xf0) >> 4), chip->mc_base); - spin_unlock_irqrestore(&chip->lock, flags); - - value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)); - snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value); - if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value) + err = snd_opti9xx_read_check(chip); + if (err == 0) return 1; - - release_and_free_resource(chip->res_mc_base); - chip->res_mc_base = NULL; +#ifdef OPTi93X + chip->mc_indir_index = 0; +#endif } -#endif /* OPTi93X */ - return -ENODEV; } #ifdef CONFIG_PNP -static int __init snd_card_opti9xx_pnp(struct snd_opti9xx *chip, struct pnp_card_link *card, - const struct pnp_card_device_id *pid) +static int snd_card_opti9xx_pnp(struct snd_opti9xx *chip, + struct pnp_card_link *card, + const struct pnp_card_device_id *pid) { struct pnp_dev *pdev; - struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); int err; + struct pnp_dev *devmpu; +#ifndef OPTi93X + struct pnp_dev *devmc; +#endif - chip->dev = pnp_request_card_device(card, pid->devs[0].id, NULL); - if (chip->dev == NULL) { - kfree(cfg); + pdev = pnp_request_card_device(card, pid->devs[0].id, NULL); + if (pdev == NULL) return -EBUSY; - } - chip->devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL); - pdev = chip->dev; - pnp_init_resource_table(cfg); - -#ifdef OPTi93X - if (port != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], port + 4, 4); -#else - if (pid->driver_data != 0x0924 && port != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[1], port, 4); -#endif /* OPTi93X */ - if (irq != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], irq, 1); - if (dma1 != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[0], dma1, 1); -#if defined(CS4231) || defined(OPTi93X) - if (dma2 != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[1], dma2, 1); -#else -#ifdef snd_opti9xx_fixup_dma2 - snd_opti9xx_fixup_dma2(pdev); -#endif -#endif /* CS4231 || OPTi93X */ -#ifdef OPTi93X - if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[1], fm_port, 4); -#else - if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[2], fm_port, 4); -#endif - if (pnp_manual_config_dev(pdev, cfg, 0) < 0) - snd_printk(KERN_ERR "AUDIO the requested resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) { snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err); - kfree(cfg); return err; } #ifdef OPTi93X port = pnp_port_start(pdev, 0) - 4; - fm_port = pnp_port_start(pdev, 1); + fm_port = pnp_port_start(pdev, 1) + 8; + /* adjust mc_indir_index - some cards report it at 0xe?d, + other at 0xe?c but it really is always at 0xe?e */ + chip->mc_indir_index = (pnp_port_start(pdev, 3) & ~0xf) | 0xe; #else - if (pid->driver_data != 0x0924) - port = pnp_port_start(pdev, 1); - fm_port = pnp_port_start(pdev, 2); + devmc = pnp_request_card_device(card, pid->devs[2].id, NULL); + if (devmc == NULL) + return -EBUSY; + + err = pnp_activate_dev(devmc); + if (err < 0) { + snd_printk(KERN_ERR "MC pnp configure failure: %d\n", err); + return err; + } + + port = pnp_port_start(pdev, 1); + fm_port = pnp_port_start(pdev, 2) + 8; + /* + * The MC(0) is never accessed and card does not + * include it in the PnP resource range. OPTI93x include it. + */ + chip->mc_base = pnp_port_start(devmc, 0) - 1; + chip->mc_base_size = pnp_port_len(devmc, 0) + 1; #endif /* OPTi93X */ irq = pnp_irq(pdev, 0); dma1 = pnp_dma(pdev, 0); @@ -1742,28 +781,18 @@ static int __init snd_card_opti9xx_pnp(struct snd_opti9xx *chip, struct pnp_card dma2 = pnp_dma(pdev, 1); #endif /* CS4231 || OPTi93X */ - pdev = chip->devmpu; - if (pdev && mpu_port > 0) { - pnp_init_resource_table(cfg); + devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL); - if (mpu_port != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], mpu_port, 2); - if (mpu_irq != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], mpu_irq, 1); - - if (pnp_manual_config_dev(pdev, cfg, 0) < 0) - snd_printk(KERN_ERR "AUDIO the requested resources are invalid, using auto config\n"); - err = pnp_activate_dev(pdev); + if (devmpu && mpu_port > 0) { + err = pnp_activate_dev(devmpu); if (err < 0) { - snd_printk(KERN_ERR "AUDIO pnp configure failure\n"); + snd_printk(KERN_ERR "MPU401 pnp configure failure\n"); mpu_port = -1; - chip->devmpu = NULL; } else { - mpu_port = pnp_port_start(pdev, 0); - mpu_irq = pnp_irq(pdev, 0); + mpu_port = pnp_port_start(devmpu, 0); + mpu_irq = pnp_irq(devmpu, 0); } } - kfree(cfg); return pid->driver_data; } #endif /* CONFIG_PNP */ @@ -1771,104 +800,108 @@ static int __init snd_card_opti9xx_pnp(struct snd_opti9xx *chip, struct pnp_card static void snd_card_opti9xx_free(struct snd_card *card) { struct snd_opti9xx *chip = card->private_data; - - if (chip) + + if (chip) { +#ifdef OPTi93X + if (chip->irq > 0) { + disable_irq(chip->irq); + free_irq(chip->irq, chip); + } + release_and_free_resource(chip->res_mc_indir); +#endif release_and_free_resource(chip->res_mc_base); + } } -static int __init snd_opti9xx_probe(struct snd_card *card) +static int snd_opti9xx_probe(struct snd_card *card) { static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1}; int error; + int xdma2; struct snd_opti9xx *chip = card->private_data; -#if defined(OPTi93X) - struct snd_opti93x *codec; -#elif defined(CS4231) - struct snd_cs4231 *codec; + struct snd_wss *codec; +#ifdef CS4231 struct snd_timer *timer; -#else - struct snd_ad1848 *codec; #endif struct snd_pcm *pcm; struct snd_rawmidi *rmidi; struct snd_hwdep *synth; - if (! chip->res_mc_base && - (chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, - "OPTi9xx MC")) == NULL) - return -ENOMEM; - - chip->wss_base = port; - chip->fm_port = fm_port; - chip->mpu_port = mpu_port; - chip->irq = irq; - chip->mpu_irq = mpu_irq; - chip->dma1 = dma1; #if defined(CS4231) || defined(OPTi93X) - chip->dma2 = dma2; + xdma2 = dma2; +#else + xdma2 = -1; #endif - if (chip->wss_base == SNDRV_AUTO_PORT) { - if ((chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4)) < 0) { - snd_printk("unable to find a free WSS port\n"); + if (port == SNDRV_AUTO_PORT) { + port = snd_legacy_find_free_ioport(possible_ports, 4); + if (port < 0) { + snd_printk(KERN_ERR "unable to find a free WSS port\n"); return -EBUSY; } } - if ((error = snd_opti9xx_configure(chip))) + error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2, + mpu_port, mpu_irq); + if (error) return error; -#if defined(OPTi93X) - if ((error = snd_opti93x_create(card, chip, chip->dma1, chip->dma2, &codec))) - return error; - if ((error = snd_opti93x_pcm(codec, 0, &pcm)) < 0) - return error; - if ((error = snd_opti93x_mixer(codec)) < 0) - return error; -#elif defined(CS4231) - if ((error = snd_cs4231_create(card, chip->wss_base + 4, -1, - chip->irq, chip->dma1, chip->dma2, - CS4231_HW_DETECT, - 0, - &codec)) < 0) - return error; - if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0) + error = snd_wss_create(card, chip->wss_base + 4, -1, irq, dma1, xdma2, +#ifdef OPTi93X + WSS_HW_OPTI93X, WSS_HWSHARE_IRQ, +#else + WSS_HW_DETECT, 0, +#endif + &codec); + if (error < 0) return error; - if ((error = snd_cs4231_mixer(codec)) < 0) + chip->codec = codec; + error = snd_wss_pcm(codec, 0, &pcm); + if (error < 0) return error; - if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0) + error = snd_wss_mixer(codec); + if (error < 0) return error; -#else - if ((error = snd_ad1848_create(card, chip->wss_base + 4, - chip->irq, chip->dma1, - AD1848_HW_DETECT, &codec)) < 0) +#ifdef OPTi93X + error = snd_opti93x_mixer(codec); + if (error < 0) return error; - if ((error = snd_ad1848_pcm(codec, 0, &pcm)) < 0) +#endif +#ifdef CS4231 + error = snd_wss_timer(codec, 0, &timer); + if (error < 0) return error; - if ((error = snd_ad1848_mixer(codec)) < 0) +#endif +#ifdef OPTi93X + error = request_irq(irq, snd_opti93x_interrupt, + 0, DEV_NAME" - WSS", chip); + if (error < 0) { + snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", irq); return error; + } #endif + chip->irq = irq; strcpy(card->driver, chip->name); sprintf(card->shortname, "OPTi %s", card->driver); #if defined(CS4231) || defined(OPTi93X) sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d", - card->shortname, pcm->name, chip->wss_base + 4, - chip->irq, chip->dma1, chip->dma2); + card->shortname, pcm->name, + chip->wss_base + 4, irq, dma1, xdma2); #else sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d", - card->shortname, pcm->name, chip->wss_base + 4, - chip->irq, chip->dma1); + card->shortname, pcm->name, chip->wss_base + 4, irq, dma1); #endif /* CS4231 || OPTi93X */ - if (chip->mpu_port <= 0 || chip->mpu_port == SNDRV_AUTO_PORT) + if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT) rmidi = NULL; - else - if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, - chip->mpu_port, 0, chip->mpu_irq, SA_INTERRUPT, - &rmidi))) + else { + error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + mpu_port, 0, mpu_irq, &rmidi); + if (error) snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n", - chip->mpu_port); + mpu_port); + } - if (chip->fm_port > 0 && chip->fm_port != SNDRV_AUTO_PORT) { + if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) { struct snd_opl3 *opl3 = NULL; #ifndef OPTi93X if (chip->hardware == OPTi9XX_HW_82C928 || @@ -1878,9 +911,7 @@ static int __init snd_opti9xx_probe(struct snd_card *card) /* assume we have an OPL4 */ snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); - if (snd_opl4_create(card, - chip->fm_port, - chip->fm_port - 8, + if (snd_opl4_create(card, fm_port, fm_port - 8, 2, &opl3, &opl4) < 0) { /* no luck, use OPL3 instead */ snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), @@ -1888,22 +919,14 @@ static int __init snd_opti9xx_probe(struct snd_card *card) } } #endif /* !OPTi93X */ - if (!opl3 && snd_opl3_create(card, - chip->fm_port, - chip->fm_port + 2, + if (!opl3 && snd_opl3_create(card, fm_port, fm_port + 2, OPL3_HW_AUTO, 0, &opl3) < 0) { snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n", - chip->fm_port, chip->fm_port + 4 - 1); + fm_port, fm_port + 4 - 1); } if (opl3) { -#ifdef CS4231 - const int t1dev = 1; -#else - const int t1dev = 0; -#endif - if ((error = snd_opl3_timer_new(opl3, t1dev, t1dev+1)) < 0) - return error; - if ((error = snd_opl3_hwdep_new(opl3, 0, 1, &synth)) < 0) + error = snd_opl3_hwdep_new(opl3, 0, 1, &synth); + if (error < 0) return error; } } @@ -1911,18 +934,34 @@ static int __init snd_opti9xx_probe(struct snd_card *card) return snd_card_register(card); } -static struct snd_card *snd_opti9xx_card_new(void) +static int snd_opti9xx_card_new(struct device *pdev, struct snd_card **cardp) { struct snd_card *card; + int err; - card = snd_card_new(index, id, THIS_MODULE, sizeof(struct snd_opti9xx)); - if (! card) - return NULL; + err = snd_card_new(pdev, index, id, THIS_MODULE, + sizeof(struct snd_opti9xx), &card); + if (err < 0) + return err; card->private_free = snd_card_opti9xx_free; - return card; + *cardp = card; + return 0; } -static int __init snd_opti9xx_nonpnp_probe(struct platform_device *devptr) +static int snd_opti9xx_isa_match(struct device *devptr, + unsigned int dev) +{ +#ifdef CONFIG_PNP + if (snd_opti9xx_pnp_is_probed) + return 0; + if (isapnp) + return 0; +#endif + return 1; +} + +static int snd_opti9xx_isa_probe(struct device *devptr, + unsigned int dev) { struct snd_card *card; int error; @@ -1938,9 +977,6 @@ static int __init snd_opti9xx_nonpnp_probe(struct platform_device *devptr) static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}}; #endif /* CS4231 || OPTi93X */ - if (snd_opti9xx_pnp_is_probed) - return -EBUSY; - if (mpu_port == SNDRV_AUTO_PORT) { if ((mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) { snd_printk(KERN_ERR "unable to find a free MPU401 port\n"); @@ -1968,48 +1004,92 @@ static int __init snd_opti9xx_nonpnp_probe(struct platform_device *devptr) #if defined(CS4231) || defined(OPTi93X) if (dma2 == SNDRV_AUTO_DMA) { if ((dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4])) < 0) { - snd_printk("unable to find a free DMA2\n"); + snd_printk(KERN_ERR "unable to find a free DMA2\n"); return -EBUSY; } } #endif - card = snd_opti9xx_card_new(); - if (! card) - return -ENOMEM; + error = snd_opti9xx_card_new(devptr, &card); + if (error < 0) + return error; if ((error = snd_card_opti9xx_detect(card, card->private_data)) < 0) { snd_card_free(card); return error; } - snd_card_set_dev(card, &devptr->dev); if ((error = snd_opti9xx_probe(card)) < 0) { snd_card_free(card); return error; } - platform_set_drvdata(devptr, card); + dev_set_drvdata(devptr, card); return 0; } -static int __devexit snd_opti9xx_nonpnp_remove(struct platform_device *devptr) +static int snd_opti9xx_isa_remove(struct device *devptr, + unsigned int dev) { - snd_card_free(platform_get_drvdata(devptr)); - platform_set_drvdata(devptr, NULL); + snd_card_free(dev_get_drvdata(devptr)); + return 0; +} + +#ifdef CONFIG_PM +static int snd_opti9xx_suspend(struct snd_card *card) +{ + struct snd_opti9xx *chip = card->private_data; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + chip->codec->suspend(chip->codec); + return 0; +} + +static int snd_opti9xx_resume(struct snd_card *card) +{ + struct snd_opti9xx *chip = card->private_data; + int error, xdma2; +#if defined(CS4231) || defined(OPTi93X) + xdma2 = dma2; +#else + xdma2 = -1; +#endif + + error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2, + mpu_port, mpu_irq); + if (error) + return error; + chip->codec->resume(chip->codec); + snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -static struct platform_driver snd_opti9xx_driver = { - .probe = snd_opti9xx_nonpnp_probe, - .remove = __devexit_p(snd_opti9xx_nonpnp_remove), - /* FIXME: suspend/resume */ +static int snd_opti9xx_isa_suspend(struct device *dev, unsigned int n, + pm_message_t state) +{ + return snd_opti9xx_suspend(dev_get_drvdata(dev)); +} + +static int snd_opti9xx_isa_resume(struct device *dev, unsigned int n) +{ + return snd_opti9xx_resume(dev_get_drvdata(dev)); +} +#endif + +static struct isa_driver snd_opti9xx_driver = { + .match = snd_opti9xx_isa_match, + .probe = snd_opti9xx_isa_probe, + .remove = snd_opti9xx_isa_remove, +#ifdef CONFIG_PM + .suspend = snd_opti9xx_isa_suspend, + .resume = snd_opti9xx_isa_resume, +#endif .driver = { - .name = DRIVER_NAME + .name = DEV_NAME }, }; #ifdef CONFIG_PNP -static int __init snd_opti9xx_pnp_probe(struct pnp_card_link *pcard, - const struct pnp_card_device_id *pid) +static int snd_opti9xx_pnp_probe(struct pnp_card_link *pcard, + const struct pnp_card_device_id *pid) { struct snd_card *card; int error, hw; @@ -2019,9 +1099,9 @@ static int __init snd_opti9xx_pnp_probe(struct pnp_card_link *pcard, return -EBUSY; if (! isapnp) return -ENODEV; - card = snd_opti9xx_card_new(); - if (! card) - return -ENOMEM; + error = snd_opti9xx_card_new(&pcard->card->dev, &card); + if (error < 0) + return error; chip = card->private_data; hw = snd_card_opti9xx_pnp(chip, pcard, pid); @@ -2044,9 +1124,12 @@ static int __init snd_opti9xx_pnp_probe(struct pnp_card_link *pcard, snd_card_free(card); return error; } - if (hw <= OPTi9XX_HW_82C930) - chip->mc_base -= 0x80; - snd_card_set_dev(card, &pcard->card->dev); + error = snd_opti9xx_read_check(chip); + if (error) { + snd_printk(KERN_ERR "OPTI chip not found\n"); + snd_card_free(card); + return error; + } if ((error = snd_opti9xx_probe(card)) < 0) { snd_card_free(card); return error; @@ -2056,27 +1139,39 @@ static int __init snd_opti9xx_pnp_probe(struct pnp_card_link *pcard, return 0; } -static void __devexit snd_opti9xx_pnp_remove(struct pnp_card_link * pcard) +static void snd_opti9xx_pnp_remove(struct pnp_card_link *pcard) { snd_card_free(pnp_get_card_drvdata(pcard)); pnp_set_card_drvdata(pcard, NULL); snd_opti9xx_pnp_is_probed = 0; } +#ifdef CONFIG_PM +static int snd_opti9xx_pnp_suspend(struct pnp_card_link *pcard, + pm_message_t state) +{ + return snd_opti9xx_suspend(pnp_get_card_drvdata(pcard)); +} + +static int snd_opti9xx_pnp_resume(struct pnp_card_link *pcard) +{ + return snd_opti9xx_resume(pnp_get_card_drvdata(pcard)); +} +#endif + static struct pnp_card_driver opti9xx_pnpc_driver = { .flags = PNP_DRIVER_RES_DISABLE, - .name = "opti9xx", + .name = DEV_NAME, .id_table = snd_opti9xx_pnpids, .probe = snd_opti9xx_pnp_probe, - .remove = __devexit_p(snd_opti9xx_pnp_remove), + .remove = snd_opti9xx_pnp_remove, +#ifdef CONFIG_PM + .suspend = snd_opti9xx_pnp_suspend, + .resume = snd_opti9xx_pnp_resume, +#endif }; #endif -#ifdef CONFIG_PNP -#define is_isapnp_selected() isapnp -#else -#define is_isapnp_selected() 0 -#endif #ifdef OPTi93X #define CHIP_NAME "82C93x" #else @@ -2085,37 +1180,24 @@ static struct pnp_card_driver opti9xx_pnpc_driver = { static int __init alsa_card_opti9xx_init(void) { - int error; - struct platform_device *device; - +#ifdef CONFIG_PNP pnp_register_card_driver(&opti9xx_pnpc_driver); if (snd_opti9xx_pnp_is_probed) return 0; - if (! is_isapnp_selected()) { - error = platform_driver_register(&snd_opti9xx_driver); - if (error < 0) - return error; - device = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); - if (!IS_ERR(device)) { - snd_opti9xx_platform_device = device; - return 0; - } - platform_driver_unregister(&snd_opti9xx_driver); - } pnp_unregister_card_driver(&opti9xx_pnpc_driver); -#ifdef MODULE - printk(KERN_ERR "no OPTi " CHIP_NAME " soundcard found\n"); #endif - return -ENODEV; + return isa_register_driver(&snd_opti9xx_driver, 1); } static void __exit alsa_card_opti9xx_exit(void) { if (!snd_opti9xx_pnp_is_probed) { - platform_device_unregister(snd_opti9xx_platform_device); - platform_driver_unregister(&snd_opti9xx_driver); + isa_unregister_driver(&snd_opti9xx_driver); + return; } +#ifdef CONFIG_PNP pnp_unregister_card_driver(&opti9xx_pnpc_driver); +#endif } module_init(alsa_card_opti9xx_init) |
