Commit 1841f613 authored by Martin Langer's avatar Martin Langer Committed by Jaroslav Kysela

[ALSA] Add snd-miro driver

Added snd-miro driver for miroSOUND PCM by Martin Langer.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent cbac4b0c
...@@ -1014,6 +1014,23 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. ...@@ -1014,6 +1014,23 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
The power-management is supported. The power-management is supported.
Module snd-miro
---------------
Module for Miro soundcards: miroSOUND PCM 1 pro,
miroSOUND PCM 12,
miroSOUND PCM 20 Radio.
port - Port # (0x530,0x604,0xe80,0xf40)
irq - IRQ # (5,7,9,10,11)
dma1 - 1st dma # (0,1,3)
dma2 - 2nd dma # (0,1)
mpu_port - MPU-401 port # (0x300,0x310,0x320,0x330)
mpu_irq - MPU-401 irq # (5,7,9,10)
fm_port - FM Port # (0x388)
wss - enable WSS mode
ide - enable onboard ide support
Module snd-mixart Module snd-mixart
----------------- -----------------
......
...@@ -292,6 +292,20 @@ config SND_OPTI93X ...@@ -292,6 +292,20 @@ config SND_OPTI93X
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called snd-opti93x. will be called snd-opti93x.
config SND_MIRO
tristate "Miro miroSOUND PCM1pro/PCM12/PCM20radio driver"
depends on SND
select SND_OPL4_LIB
select SND_CS4231_LIB
select SND_MPU401_UART
select SND_PCM
help
Say 'Y' or 'M' to include support for Miro miroSOUND PCM1 pro,
miroSOUND PCM12 and miroSOUND PCM20 Radio soundcards.
To compile this driver as a module, choose M here: the module
will be called snd-miro.
config SND_SB8 config SND_SB8
tristate "Sound Blaster 1.0/2.0/Pro (8-bit)" tristate "Sound Blaster 1.0/2.0/Pro (8-bit)"
depends on SND depends on SND
......
...@@ -6,8 +6,10 @@ ...@@ -6,8 +6,10 @@
snd-opti92x-ad1848-objs := opti92x-ad1848.o snd-opti92x-ad1848-objs := opti92x-ad1848.o
snd-opti92x-cs4231-objs := opti92x-cs4231.o snd-opti92x-cs4231-objs := opti92x-cs4231.o
snd-opti93x-objs := opti93x.o snd-opti93x-objs := opti93x.o
snd-miro-objs := miro.o
# Toplevel Module Dependency # Toplevel Module Dependency
obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opti92x-ad1848.o obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opti92x-ad1848.o
obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opti92x-cs4231.o obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opti92x-cs4231.o
obj-$(CONFIG_SND_OPTI93X) += snd-opti93x.o obj-$(CONFIG_SND_OPTI93X) += snd-opti93x.o
obj-$(CONFIG_SND_MIRO) += snd-miro.o
/*
* ALSA soundcard driver for Miro miroSOUND PCM1 pro
* miroSOUND PCM12
* miroSOUND PCM20 Radio
*
* Copyright (C) 2004-2005 Martin Langer <martin-langer@gmx.de>
*
* Based on OSS ACI and ALSA OPTi9xx drivers
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sound/driver.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/moduleparam.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/cs4231.h>
#include <sound/mpu401.h>
#include <sound/opl4.h>
#include <sound/control.h>
#include <sound/info.h>
#define SNDRV_LEGACY_FIND_FREE_IRQ
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>
#include "miro.h"
MODULE_AUTHOR("Martin Langer <martin-langer@gmx.de>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Miro miroSOUND PCM1 pro, PCM12, PCM20 Radio");
MODULE_SUPPORTED_DEVICE("{{Miro,miroSOUND PCM1 pro}, "
"{Miro,miroSOUND PCM12}, "
"{Miro,miroSOUND PCM20 Radio}}");
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
static long port = SNDRV_DEFAULT_PORT1; /* 0x530,0xe80,0xf40,0x604 */
static long mpu_port = SNDRV_DEFAULT_PORT1; /* 0x300,0x310,0x320,0x330 */
static long fm_port = SNDRV_DEFAULT_PORT1; /* 0x388 */
static int irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10,11 */
static int mpu_irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10 */
static int dma1 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */
static int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */
static int wss;
static int ide;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for miro soundcard.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for miro soundcard.");
module_param(port, long, 0444);
MODULE_PARM_DESC(port, "WSS port # for miro driver.");
module_param(mpu_port, long, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for miro driver.");
module_param(fm_port, long, 0444);
MODULE_PARM_DESC(fm_port, "FM Port # for miro driver.");
module_param(irq, int, 0444);
MODULE_PARM_DESC(irq, "WSS irq # for miro driver.");
module_param(mpu_irq, int, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for miro driver.");
module_param(dma1, int, 0444);
MODULE_PARM_DESC(dma1, "1st dma # for miro driver.");
module_param(dma2, int, 0444);
MODULE_PARM_DESC(dma2, "2nd dma # for miro driver.");
module_param(wss, int, 0444);
MODULE_PARM_DESC(wss, "wss mode");
module_param(ide, int, 0444);
MODULE_PARM_DESC(ide, "enable ide port");
#define OPTi9XX_HW_DETECT 0
#define OPTi9XX_HW_82C928 1
#define OPTi9XX_HW_82C929 2
#define OPTi9XX_HW_82C924 3
#define OPTi9XX_HW_82C925 4
#define OPTi9XX_HW_82C930 5
#define OPTi9XX_HW_82C931 6
#define OPTi9XX_HW_82C933 7
#define OPTi9XX_HW_LAST OPTi9XX_HW_82C933
#define OPTi9XX_MC_REG(n) 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_card *card;
struct snd_pcm *pcm;
long wss_base;
int irq;
int dma1;
int dma2;
long fm_port;
long mpu_port;
int mpu_irq;
unsigned long aci_port;
int aci_vendor;
int aci_product;
int aci_version;
int aci_amp;
int aci_preamp;
int aci_solomode;
struct mutex aci_mutex;
};
static void snd_miro_proc_init(struct snd_miro * miro);
#define DRIVER_NAME "snd-miro"
static struct platform_device *device;
static char * snd_opti9xx_names[] = {
"unkown",
"82C928", "82C929",
"82C924", "82C925",
"82C930", "82C931", "82C933"
};
/*
* ACI control
*/
static int aci_busy_wait(struct snd_miro * miro)
{
long timeout;
unsigned char byte;
for (timeout = 1; timeout <= ACI_MINTIME+30; timeout++) {
if (((byte=inb(miro->aci_port + ACI_REG_BUSY)) & 1) == 0) {
if (timeout >= ACI_MINTIME)
snd_printd("aci ready in round %ld.\n",
timeout-ACI_MINTIME);
return byte;
}
if (timeout >= ACI_MINTIME) {
long out=10*HZ;
switch (timeout-ACI_MINTIME) {
case 0 ... 9:
out /= 10;
case 10 ... 19:
out /= 10;
case 20 ... 30:
out /= 10;
default:
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(out);
break;
}
}
}
snd_printk(KERN_ERR "aci_busy_wait() time out\n");
return -EBUSY;
}
static inline int aci_write(struct snd_miro * miro, unsigned char byte)
{
if (aci_busy_wait(miro) >= 0) {
outb(byte, miro->aci_port + ACI_REG_COMMAND);
return 0;
} else {
snd_printk(KERN_ERR "aci busy, aci_write(0x%x) stopped.\n", byte);
return -EBUSY;
}
}
static inline int aci_read(struct snd_miro * miro)
{
unsigned char byte;
if (aci_busy_wait(miro) >= 0) {
byte=inb(miro->aci_port + ACI_REG_STATUS);
return byte;
} else {
snd_printk(KERN_ERR "aci busy, aci_read() stopped.\n");
return -EBUSY;
}
}
static int aci_cmd(struct snd_miro * miro, int write1, int write2, int write3)
{
int write[] = {write1, write2, write3};
int value, i;
if (mutex_lock_interruptible(&miro->aci_mutex))
return -EINTR;
for (i=0; i<3; i++) {
if (write[i]< 0 || write[i] > 255)
break;
else {
value = aci_write(miro, write[i]);
if (value < 0)
goto out;
}
}
value = aci_read(miro);
out: mutex_unlock(&miro->aci_mutex);
return value;
}
static int aci_getvalue(struct snd_miro * miro, unsigned char index)
{
return aci_cmd(miro, ACI_STATUS, index, -1);
}
static int aci_setvalue(struct snd_miro * miro, unsigned char index, int value)
{
return aci_cmd(miro, index, value, -1);
}
/*
* MIXER part
*/
static int snd_miro_info_capture(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
return 0;
}
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;
if ((value = aci_getvalue(miro, ACI_S_GENERAL)) < 0) {
snd_printk(KERN_ERR "snd_miro_get_capture() failed: %d\n", value);
return value;
}
ucontrol->value.integer.value[0] = value & 0x20;
return 0;
}
static int snd_miro_put_capture(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
int change, value, error;
value = !(ucontrol->value.integer.value[0]);
if ((error = aci_setvalue(miro, ACI_SET_SOLOMODE, value)) < 0) {
snd_printk(KERN_ERR "snd_miro_put_capture() failed: %d\n", error);
return error;
}
change = (value != miro->aci_solomode);
miro->aci_solomode = value;
return change;
}
static int snd_miro_info_preamp(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 3;
return 0;
}
static int snd_miro_get_preamp(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
int value;
if (miro->aci_version <= 176) {
/*
OSS says it's not readable with versions < 176.
But it doesn't work on my card,
which is a PCM12 with aci_version = 176.
*/
ucontrol->value.integer.value[0] = miro->aci_preamp;
return 0;
}
if ((value = aci_getvalue(miro, ACI_GET_PREAMP)) < 0) {
snd_printk(KERN_ERR "snd_miro_get_preamp() failed: %d\n", value);
return value;
}
ucontrol->value.integer.value[0] = value;
return 0;
}
static int snd_miro_put_preamp(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
int error, value, change;
value = ucontrol->value.integer.value[0];
if ((error = aci_setvalue(miro, ACI_SET_PREAMP, value)) < 0) {
snd_printk(KERN_ERR "snd_miro_put_preamp() failed: %d\n", error);
return error;
}
change = (value != miro->aci_preamp);
miro->aci_preamp = value;
return change;
}
static int snd_miro_info_amp(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
return 0;
}
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_amp;
return 0;
}
static int snd_miro_put_amp(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
int error, value, change;
value = ucontrol->value.integer.value[0];
if ((error = aci_setvalue(miro, ACI_SET_POWERAMP, value)) < 0) {
snd_printk(KERN_ERR "snd_miro_put_amp() to %d failed: %d\n", value, error);
return error;
}
change = (value != miro->aci_amp);
miro->aci_amp = value;
return change;
}
#define MIRO_DOUBLE(ctl_name, ctl_index, get_right_reg, set_right_reg) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = ctl_name, \
.index = ctl_index, \
.info = snd_miro_info_double, \
.get = snd_miro_get_double, \
.put = snd_miro_put_double, \
.private_value = get_right_reg | (set_right_reg << 8) \
}
static int snd_miro_info_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
int reg = kcontrol->private_value & 0xff;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
if ((reg >= ACI_GET_EQ1) && (reg <= ACI_GET_EQ7)) {
/* equalizer elements */
uinfo->value.integer.min = - 0x7f;
uinfo->value.integer.max = 0x7f;
} else {
/* non-equalizer elements */
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 0x20;
}
return 0;
}
static int snd_miro_get_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uinfo)
{
struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
int left_val, right_val;
int right_reg = kcontrol->private_value & 0xff;
int left_reg = right_reg + 1;
if ((right_val = aci_getvalue(miro, right_reg)) < 0) {
snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", right_reg, right_val);
return right_val;
}
if ((left_val = aci_getvalue(miro, left_reg)) < 0) {
snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", left_reg, left_val);
return left_val;
}
if ((right_reg >= ACI_GET_EQ1) && (right_reg <= ACI_GET_EQ7)) {
/* equalizer elements */
if (left_val < 0x80) {
uinfo->value.integer.value[0] = left_val;
} else {
uinfo->value.integer.value[0] = 0x80 - left_val;
}
if (right_val < 0x80) {
uinfo->value.integer.value[1] = right_val;
} else {
uinfo->value.integer.value[1] = 0x80 - right_val;
}
} else {
/* non-equalizer elements */
uinfo->value.integer.value[0] = 0x20 - left_val;
uinfo->value.integer.value[1] = 0x20 - right_val;
}
return 0;
}
static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
int left, right, left_old, right_old;
int setreg_left, setreg_right, getreg_left, getreg_right;
int change, error;
left = ucontrol->value.integer.value[0];
right = ucontrol->value.integer.value[1];
setreg_right = (kcontrol->private_value >> 8) & 0xff;
if (setreg_right == ACI_SET_MASTER) {
setreg_left = setreg_right + 1;
} else {
setreg_left = setreg_right + 8;
}
getreg_right = kcontrol->private_value & 0xff;
getreg_left = getreg_right + 1;
if ((left_old = aci_getvalue(miro, getreg_left)) < 0) {
snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_left, left_old);
return left_old;
}
if ((right_old = aci_getvalue(miro, getreg_right)) < 0) {
snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_right, right_old);
return right_old;
}
if ((getreg_right >= ACI_GET_EQ1) && (getreg_right <= ACI_GET_EQ7)) {
/* equalizer elements */
if (left_old > 0x80)
left_old = 0x80 - left_old;
if (right_old > 0x80)
right_old = 0x80 - right_old;
if (left >= 0) {
if ((error = aci_setvalue(miro, setreg_left, left)) < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
left, error);
return error;
}
} else {
if ((error = aci_setvalue(miro, setreg_left, 0x80 - left)) < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
0x80 - left, error);
return error;
}
}
if (right >= 0) {
if ((error = aci_setvalue(miro, setreg_right, right)) < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
right, error);
return error;
}
} else {
if ((error = aci_setvalue(miro, setreg_right, 0x80 - right)) < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
0x80 - right, error);
return error;
}
}
} else {
/* non-equalizer elements */
left_old = 0x20 - left_old;
right_old = 0x20 - right_old;
if ((error = aci_setvalue(miro, setreg_left, 0x20 - left)) < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
0x20 - left, error);
return error;
}
if ((error = aci_setvalue(miro, setreg_right, 0x20 - right)) < 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] __initdata = {
{ 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 __init snd_set_aci_init_values(struct snd_miro *miro)
{
int idx, error;
/* enable WSS on PCM1 */
if ((miro->aci_product == 'A') && wss) {
if ((error = aci_setvalue(miro, ACI_SET_WSS, wss)) < 0) {
snd_printk(KERN_ERR "enabling WSS mode failed\n");
return error;
}
}
/* enable IDE port */
if (ide) {
if ((error = aci_setvalue(miro, ACI_SET_IDE, ide)) < 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++)
if ((error = aci_setvalue(miro, aci_init_values[idx][0],
aci_init_values[idx][1])) < 0) {
snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
aci_init_values[idx][0], error);
return error;
}
miro->aci_amp = 0;
miro->aci_preamp = 0;
miro->aci_solomode = 1;
return 0;
}
static int snd_miro_mixer(struct snd_miro *miro)
{
struct snd_card *card;
unsigned int idx;
int err;
snd_assert(miro != NULL && miro->card != NULL, return -EINVAL);
card = miro->card;
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_product == 'A') || (miro->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_product == 'B') || (miro->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_version >= 176)
if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_capture_control[0], miro))) < 0)
return err;
}
if (miro->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 long snd_legacy_find_free_ioport(long *port_table, long size)
{
while (*port_table != -1) {
struct resource *res;
if ((res = request_region(*port_table, size,
"ALSA test")) != NULL) {
release_resource(res);
kfree_nocheck(res);
return *port_table;
}
port_table++;
}
return -1;
}
static int __init 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->fm_port = -1;
chip->mpu_port = -1;
chip->mpu_irq = -1;
switch (hardware) {
case OPTi9XX_HW_82C929:
chip->mc_base = 0xf8c;
chip->password = 0xe3;
chip->pwd_reg = 3;
break;
case OPTi9XX_HW_82C924:
chip->mc_base = 0xf8c;
chip->password = 0xe5;
chip->pwd_reg = 3;
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;
char* model = "unknown";
/* miroSOUND PCM1 pro, early PCM12 */
if ((miro->hardware == OPTi9XX_HW_82C929) &&
(miro->aci_vendor == 'm') &&
(miro->aci_product == 'A')) {
switch(miro->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) &&
(miro->aci_vendor == 'm') &&
(miro->aci_product == 'B')) {
switch(miro->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) &&
(miro->aci_vendor == 'm') &&
(miro->aci_product == 'C')) {
switch(miro->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(miro->aci_vendor) {
case 'm':
snd_iprintf(buffer, "Miro\n");
break;
default:
snd_iprintf(buffer, "unknown (0x%x)\n", miro->aci_vendor);
break;
}
snd_iprintf(buffer, " product : ");
switch(miro->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", miro->aci_product);
break;
}
snd_iprintf(buffer, " firmware: %d (0x%x)\n",
miro->aci_version, miro->aci_version);
snd_iprintf(buffer, " port : 0x%lx-0x%lx\n",
miro->aci_port, miro->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", miro->aci_solomode);
snd_iprintf(buffer, " amp : 0x%x\n", miro->aci_amp);
snd_iprintf(buffer, " preamp : 0x%x\n", miro->aci_preamp);
}
static void __init snd_miro_proc_init(struct snd_miro * miro)
{
struct snd_info_entry *entry;
if (! snd_card_proc_new(miro->card, "miro", &entry))
snd_info_set_text_ops(entry, miro, 1024, snd_miro_proc_read);
}
/*
* Init
*/
static int __init 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;
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(1), 0x80, 0x80);
snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */
snd_miro_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);
snd_miro_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
break;
case OPTi9XX_HW_82C929:
/* untested init commands for OPTi929 */
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(4), 0x00, 0x0c);
snd_miro_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
break;
default:
snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
return -EINVAL;
}
switch (chip->wss_base) {
case 0x530:
wss_base_bits = 0x00;
break;
case 0x604:
wss_base_bits = 0x03;
break;
case 0xe80:
wss_base_bits = 0x01;
break;
case 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 __init snd_card_miro_detect(struct snd_card *card, struct snd_miro *chip)
{
int i, err;
unsigned char value;
for (i = OPTi9XX_HW_82C929; i <= OPTi9XX_HW_82C924; i++) {
if ((err = snd_miro_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_miro_read(chip, OPTi9XX_MC_REG(1));
if ((value != 0xff) && (value != inb(chip->mc_base + 1)))
if (value == snd_miro_read(chip, OPTi9XX_MC_REG(1)))
return 1;
release_resource(chip->res_mc_base);
kfree_nocheck(chip->res_mc_base);
chip->res_mc_base = NULL;
}
return -ENODEV;
}
static int __init snd_card_miro_aci_detect(struct snd_card *card, struct snd_miro * miro)
{
unsigned char regval;
int i;
mutex_init(&miro->aci_mutex);
/* get ACI port from OPTi9xx MC 4 */
miro->mc_base = 0xf8c;
regval=inb(miro->mc_base + 4);
miro->aci_port = (regval & 0x10) ? 0x344: 0x354;
if ((miro->res_aci_port = request_region(miro->aci_port, 3, "miro aci")) == NULL) {
snd_printk(KERN_ERR "aci i/o area 0x%lx-0x%lx already used.\n",
miro->aci_port, miro->aci_port+2);
return -ENOMEM;
}
/* force ACI into a known state */
for (i = 0; i < 3; i++)
if (aci_cmd(miro, ACI_ERROR_OP, -1, -1) < 0) {
snd_card_free(card);
snd_printk(KERN_ERR "can't force aci into known state.\n");
return -ENXIO;
}
if ((miro->aci_vendor=aci_cmd(miro, ACI_READ_IDCODE, -1, -1)) < 0 ||
(miro->aci_product=aci_cmd(miro, ACI_READ_IDCODE, -1, -1)) < 0) {
snd_card_free(card);
snd_printk(KERN_ERR "can't read aci id on 0x%lx.\n", miro->aci_port);
return -ENXIO;
}
if ((miro->aci_version=aci_cmd(miro, ACI_READ_VERSION, -1, -1)) < 0) {
snd_card_free(card);
snd_printk(KERN_ERR "can't read aci version on 0x%lx.\n",
miro->aci_port);
return -ENXIO;
}
if (aci_cmd(miro, ACI_INIT, -1, -1) < 0 ||
aci_cmd(miro, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0 ||
aci_cmd(miro, 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);
release_and_free_resource(miro->res_mc_base);
}
static int __init snd_miro_probe(struct platform_device *devptr)
{
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_cs4231 *codec;
struct snd_timer *timer;
struct snd_card *card;
struct snd_pcm *pcm;
struct snd_rawmidi *rmidi;
if (!(card = snd_card_new(index, id, THIS_MODULE,
sizeof(struct snd_miro))))
return -ENOMEM;
card->private_free = snd_card_miro_free;
miro = card->private_data;
miro->card = card;
if ((error = snd_card_miro_aci_detect(card, miro)) < 0) {
snd_card_free(card);
snd_printk(KERN_ERR "unable to detect aci chip\n");
return -ENODEV;
}
/* init proc interface */
snd_miro_proc_init(miro);
if ((error = snd_card_miro_detect(card, miro)) < 0) {
snd_card_free(card);
snd_printk(KERN_ERR "unable to detect OPTi9xx chip\n");
return -ENODEV;
}
if (! miro->res_mc_base &&
(miro->res_mc_base = request_region(miro->mc_base, miro->mc_base_size,
"miro (OPTi9xx MC)")) == NULL) {
snd_card_free(card);
snd_printk(KERN_ERR "request for OPTI9xx MC failed\n");
return -ENOMEM;
}
miro->wss_base = port;
miro->fm_port = fm_port;
miro->mpu_port = mpu_port;
miro->irq = irq;
miro->mpu_irq = mpu_irq;
miro->dma1 = dma1;
miro->dma2 = dma2;
if (miro->wss_base == SNDRV_AUTO_PORT) {
if ((miro->wss_base = snd_legacy_find_free_ioport(possible_ports, 4)) < 0) {
snd_card_free(card);
snd_printk(KERN_ERR "unable to find a free WSS port\n");
return -EBUSY;
}
}
if (miro->mpu_port == SNDRV_AUTO_PORT) {
if ((miro->mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) {
snd_card_free(card);
snd_printk(KERN_ERR "unable to find a free MPU401 port\n");
return -EBUSY;
}
}
if (miro->irq == SNDRV_AUTO_IRQ) {
if ((miro->irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
snd_card_free(card);
snd_printk(KERN_ERR "unable to find a free IRQ\n");
return -EBUSY;
}
}
if (miro->mpu_irq == SNDRV_AUTO_IRQ) {
if ((miro->mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) {
snd_card_free(card);
snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n");
return -EBUSY;
}
}
if (miro->dma1 == SNDRV_AUTO_DMA) {
if ((miro->dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) {
snd_card_free(card);
snd_printk(KERN_ERR "unable to find a free DMA1\n");
return -EBUSY;
}
}
if (miro->dma2 == SNDRV_AUTO_DMA) {
if ((miro->dma2 = snd_legacy_find_free_dma(possible_dma2s[miro->dma1 % 4])) < 0) {
snd_card_free(card);
snd_printk(KERN_ERR "unable to find a free DMA2\n");
return -EBUSY;
}
}
if ((error = snd_miro_configure(miro))) {
snd_card_free(card);
return error;
}
if ((error = snd_cs4231_create(card, miro->wss_base + 4, -1,
miro->irq, miro->dma1, miro->dma2,
CS4231_HW_AD1845,
0,
&codec)) < 0) {
snd_card_free(card);
return error;
}
if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0) {
snd_card_free(card);
return error;
}
if ((error = snd_cs4231_mixer(codec)) < 0) {
snd_card_free(card);
return error;
}
if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0) {
snd_card_free(card);
return error;
}
miro->pcm = pcm;
if ((error = snd_miro_mixer(miro)) < 0) {
snd_card_free(card);
return error;
}
if (miro->aci_vendor == 'm') {
/* It looks like a miro sound card. */
switch (miro->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 (miro->mpu_port <= 0 || miro->mpu_port == SNDRV_AUTO_PORT)
rmidi = NULL;
else
if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
miro->mpu_port, 0, miro->mpu_irq, SA_INTERRUPT,
&rmidi)))
snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n", miro->mpu_port);
if (miro->fm_port > 0 && miro->fm_port != SNDRV_AUTO_PORT) {
struct snd_opl3 *opl3 = NULL;
struct snd_opl4 *opl4;
if (snd_opl4_create(card, miro->fm_port, miro->fm_port - 8,
2, &opl3, &opl4) < 0)
snd_printk(KERN_WARNING "no OPL4 device at 0x%lx\n", miro->fm_port);
}
if ((error = snd_set_aci_init_values(miro)) < 0) {
snd_card_free(card);
return error;
}
snd_card_set_dev(card, &devptr->dev);
if ((error = snd_card_register(card))) {
snd_card_free(card);
return error;
}
platform_set_drvdata(devptr, card);
return 0;
}
static int __devexit snd_miro_remove(struct platform_device *devptr)
{
snd_card_free(platform_get_drvdata(devptr));
platform_set_drvdata(devptr, NULL);
return 0;
}
static struct platform_driver snd_miro_driver = {
.probe = snd_miro_probe,
.remove = __devexit_p(snd_miro_remove),
/* FIXME: suspend/resume */
.driver = {
.name = DRIVER_NAME
},
};
static int __init alsa_card_miro_init(void)
{
int error;
if ((error = platform_driver_register(&snd_miro_driver)) < 0)
return error;
device = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
if (! IS_ERR(device))
return 0;
#ifdef MODULE
printk(KERN_ERR "no miro soundcard found\n");
#endif
platform_driver_unregister(&snd_miro_driver);
return PTR_ERR(device);
}
static void __exit alsa_card_miro_exit(void)
{
platform_device_unregister(device);
platform_driver_unregister(&snd_miro_driver);
}
module_init(alsa_card_miro_init)
module_exit(alsa_card_miro_exit)
#ifndef _MIRO_H_
#define _MIRO_H_
#define ACI_REG_COMMAND 0 /* write register offset */
#define ACI_REG_STATUS 1 /* read register offset */
#define ACI_REG_BUSY 2 /* busy register offset */
#define ACI_REG_RDS 2 /* PCM20: RDS register offset */
#define ACI_MINTIME 500 /* ACI time out limit */
#define ACI_SET_MUTE 0x0d
#define ACI_SET_POWERAMP 0x0f
#define ACI_SET_TUNERMUTE 0xa3
#define ACI_SET_TUNERMONO 0xa4
#define ACI_SET_IDE 0xd0
#define ACI_SET_WSS 0xd1
#define ACI_SET_SOLOMODE 0xd2
#define ACI_SET_PREAMP 0x03
#define ACI_GET_PREAMP 0x21
#define ACI_WRITE_TUNE 0xa7
#define ACI_READ_TUNERSTEREO 0xa8
#define ACI_READ_TUNERSTATION 0xa9
#define ACI_READ_VERSION 0xf1
#define ACI_READ_IDCODE 0xf2
#define ACI_INIT 0xff
#define ACI_STATUS 0xf0
#define ACI_S_GENERAL 0x00
#define ACI_ERROR_OP 0xdf
/* ACI Mixer */
/* These are the values for the right channel GET registers.
Add an offset of 0x01 for the left channel register.
(left=right+0x01) */
#define ACI_GET_MASTER 0x03
#define ACI_GET_MIC 0x05
#define ACI_GET_LINE 0x07
#define ACI_GET_CD 0x09
#define ACI_GET_SYNTH 0x0b
#define ACI_GET_PCM 0x0d
#define ACI_GET_LINE1 0x10 /* Radio on PCM20 */
#define ACI_GET_LINE2 0x12
#define ACI_GET_EQ1 0x22 /* from Bass ... */
#define ACI_GET_EQ2 0x24
#define ACI_GET_EQ3 0x26
#define ACI_GET_EQ4 0x28
#define ACI_GET_EQ5 0x2a
#define ACI_GET_EQ6 0x2c
#define ACI_GET_EQ7 0x2e /* ... to Treble */
/* And these are the values for the right channel SET registers.
For left channel access you have to add an offset of 0x08.
MASTER is an exception, which needs an offset of 0x01 */
#define ACI_SET_MASTER 0x00
#define ACI_SET_MIC 0x30
#define ACI_SET_LINE 0x31
#define ACI_SET_CD 0x34
#define ACI_SET_SYNTH 0x33
#define ACI_SET_PCM 0x32
#define ACI_SET_LINE1 0x35 /* Radio on PCM20 */
#define ACI_SET_LINE2 0x36
#define ACI_SET_EQ1 0x40 /* from Bass ... */
#define ACI_SET_EQ2 0x41
#define ACI_SET_EQ3 0x42
#define ACI_SET_EQ4 0x43
#define ACI_SET_EQ5 0x44
#define ACI_SET_EQ6 0x45
#define ACI_SET_EQ7 0x46 /* ... to Treble */
#endif /* _MIRO_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment