2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards.
|
|
|
|
Copyright (C) 1998-2000 by Massimo Piccioni <dafastidio@libero.it>
|
|
|
|
|
|
|
|
Part of this code was developed at the Italian Ministry of Air Defence,
|
|
|
|
Sixth Division (oh, che pace ...), Rome.
|
|
|
|
|
|
|
|
Thanks to Maria Grazia Pollarini, Salvatore Vassallo.
|
|
|
|
|
|
|
|
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>
|
2005-11-18 00:13:59 +08:00
|
|
|
#include <linux/err.h>
|
2007-02-22 19:50:54 +08:00
|
|
|
#include <linux/isa.h>
|
2005-11-18 00:13:59 +08:00
|
|
|
#include <linux/delay.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/pnp.h>
|
2011-07-16 01:13:37 +08:00
|
|
|
#include <linux/module.h>
|
2005-11-18 00:13:59 +08:00
|
|
|
#include <asm/io.h>
|
|
|
|
#include <asm/dma.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <sound/core.h>
|
2009-12-11 03:40:18 +08:00
|
|
|
#include <sound/tlv.h>
|
2008-08-01 03:02:42 +08:00
|
|
|
#include <sound/wss.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <sound/mpu401.h>
|
|
|
|
#include <sound/opl3.h>
|
|
|
|
#ifndef OPTi93X
|
|
|
|
#include <sound/opl4.h>
|
|
|
|
#endif
|
2012-08-01 22:05:39 +08:00
|
|
|
#define SNDRV_LEGACY_FIND_FREE_IOPORT
|
2005-04-17 06:20:36 +08:00
|
|
|
#define SNDRV_LEGACY_FIND_FREE_IRQ
|
|
|
|
#define SNDRV_LEGACY_FIND_FREE_DMA
|
|
|
|
#include <sound/initval.h>
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
#ifdef OPTi93X
|
|
|
|
MODULE_DESCRIPTION("OPTi93X");
|
|
|
|
MODULE_SUPPORTED_DEVICE("{{OPTi,82C931/3}}");
|
|
|
|
#else /* OPTi93X */
|
|
|
|
#ifdef CS4231
|
|
|
|
MODULE_DESCRIPTION("OPTi92X - CS4231");
|
|
|
|
MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (CS4231)},"
|
|
|
|
"{OPTi,82C925 (CS4231)}}");
|
|
|
|
#else /* CS4231 */
|
|
|
|
MODULE_DESCRIPTION("OPTi92X - AD1848");
|
|
|
|
MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (AD1848)},"
|
|
|
|
"{OPTi,82C925 (AD1848)},"
|
|
|
|
"{OAK,Mozart}}");
|
|
|
|
#endif /* CS4231 */
|
|
|
|
#endif /* OPTi93X */
|
|
|
|
|
|
|
|
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
|
|
|
|
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
|
2011-12-15 11:19:36 +08:00
|
|
|
//static bool enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */
|
2008-07-18 17:15:12 +08:00
|
|
|
#ifdef CONFIG_PNP
|
2012-03-29 09:26:17 +08:00
|
|
|
static bool isapnp = true; /* Enable ISA PnP detection */
|
2008-07-18 17:15:12 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
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 */
|
|
|
|
#if defined(CS4231) || defined(OPTi93X)
|
|
|
|
static int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */
|
|
|
|
#endif /* CS4231 || OPTi93X */
|
|
|
|
|
|
|
|
module_param(index, int, 0444);
|
|
|
|
MODULE_PARM_DESC(index, "Index value for opti9xx based soundcard.");
|
|
|
|
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.");
|
2008-07-18 17:15:12 +08:00
|
|
|
#ifdef CONFIG_PNP
|
2005-04-17 06:20:36 +08:00
|
|
|
module_param(isapnp, bool, 0444);
|
|
|
|
MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");
|
2008-07-18 17:15:12 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
module_param(port, long, 0444);
|
|
|
|
MODULE_PARM_DESC(port, "WSS port # for opti9xx driver.");
|
|
|
|
module_param(mpu_port, long, 0444);
|
|
|
|
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for opti9xx driver.");
|
|
|
|
module_param(fm_port, long, 0444);
|
|
|
|
MODULE_PARM_DESC(fm_port, "FM port # for opti9xx driver.");
|
|
|
|
module_param(irq, int, 0444);
|
|
|
|
MODULE_PARM_DESC(irq, "WSS irq # for opti9xx driver.");
|
|
|
|
module_param(mpu_irq, int, 0444);
|
|
|
|
MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for opti9xx driver.");
|
|
|
|
module_param(dma1, int, 0444);
|
|
|
|
MODULE_PARM_DESC(dma1, "1st dma # for opti9xx driver.");
|
|
|
|
#if defined(CS4231) || defined(OPTi93X)
|
|
|
|
module_param(dma2, int, 0444);
|
|
|
|
MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver.");
|
|
|
|
#endif /* CS4231 || OPTi93X */
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
|
|
|
#ifdef OPTi93X
|
|
|
|
|
|
|
|
#define OPTi93X_STATUS 0x02
|
|
|
|
#define OPTi93X_PORT(chip, r) ((chip)->port + OPTi93X_##r)
|
|
|
|
|
|
|
|
#define OPTi93X_IRQ_PLAYBACK 0x04
|
|
|
|
#define OPTi93X_IRQ_CAPTURE 0x08
|
|
|
|
|
|
|
|
#endif /* OPTi93X */
|
|
|
|
|
2005-11-17 21:37:56 +08:00
|
|
|
struct snd_opti9xx {
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned short hardware;
|
|
|
|
unsigned char password;
|
|
|
|
char name[7];
|
|
|
|
|
|
|
|
unsigned long mc_base;
|
|
|
|
struct resource *res_mc_base;
|
|
|
|
unsigned long mc_base_size;
|
|
|
|
#ifdef OPTi93X
|
|
|
|
unsigned long mc_indir_index;
|
2009-12-05 01:30:18 +08:00
|
|
|
struct resource *res_mc_indir;
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif /* OPTi93X */
|
2012-07-17 15:16:44 +08:00
|
|
|
struct snd_wss *codec;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long pwd_reg;
|
|
|
|
|
|
|
|
spinlock_t lock;
|
|
|
|
|
2010-03-04 02:41:44 +08:00
|
|
|
long wss_base;
|
2005-04-17 06:20:36 +08:00
|
|
|
int irq;
|
|
|
|
};
|
|
|
|
|
2005-11-18 00:13:59 +08:00
|
|
|
static int snd_opti9xx_pnp_is_probed;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_PNP
|
|
|
|
|
|
|
|
static struct pnp_card_device_id snd_opti9xx_pnpids[] = {
|
|
|
|
#ifndef OPTi93X
|
|
|
|
/* OPTi 82C924 */
|
2010-03-04 02:41:44 +08:00
|
|
|
{ .id = "OPT0924",
|
|
|
|
.devs = { { "OPT0000" }, { "OPT0002" }, { "OPT0005" } },
|
|
|
|
.driver_data = 0x0924 },
|
2005-04-17 06:20:36 +08:00
|
|
|
/* OPTi 82C925 */
|
2010-03-04 02:41:44 +08:00
|
|
|
{ .id = "OPT0925",
|
|
|
|
.devs = { { "OPT9250" }, { "OPT0002" }, { "OPT0005" } },
|
|
|
|
.driver_data = 0x0925 },
|
2005-04-17 06:20:36 +08:00
|
|
|
#else
|
|
|
|
/* OPTi 82C931/3 */
|
2010-03-04 02:41:44 +08:00
|
|
|
{ .id = "OPT0931", .devs = { { "OPT9310" }, { "OPT0002" } },
|
|
|
|
.driver_data = 0x0931 },
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif /* OPTi93X */
|
|
|
|
{ .id = "" }
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids);
|
|
|
|
|
|
|
|
#endif /* CONFIG_PNP */
|
|
|
|
|
2013-08-27 18:03:01 +08:00
|
|
|
#define DEV_NAME KBUILD_MODNAME
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static char * snd_opti9xx_names[] = {
|
tree-wide: fix assorted typos all over the place
That is "success", "unknown", "through", "performance", "[re|un]mapping"
, "access", "default", "reasonable", "[con]currently", "temperature"
, "channel", "[un]used", "application", "example","hierarchy", "therefore"
, "[over|under]flow", "contiguous", "threshold", "enough" and others.
Signed-off-by: André Goddard Rosa <andre.goddard@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2009-11-14 23:09:05 +08:00
|
|
|
"unknown",
|
2005-04-17 06:20:36 +08:00
|
|
|
"82C928", "82C929",
|
|
|
|
"82C924", "82C925",
|
|
|
|
"82C930", "82C931", "82C933"
|
|
|
|
};
|
|
|
|
|
2012-12-07 01:35:21 +08:00
|
|
|
static int snd_opti9xx_init(struct snd_opti9xx *chip,
|
|
|
|
unsigned short hardware)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
|
|
|
|
|
|
|
|
chip->hardware = hardware;
|
|
|
|
strcpy(chip->name, snd_opti9xx_names[hardware]);
|
|
|
|
|
|
|
|
spin_lock_init(&chip->lock);
|
|
|
|
|
|
|
|
chip->irq = -1;
|
|
|
|
|
2010-03-04 02:41:44 +08:00
|
|
|
#ifndef OPTi93X
|
|
|
|
#ifdef CONFIG_PNP
|
|
|
|
if (isapnp && chip->mc_base)
|
|
|
|
/* PnP resource gives the least 10 bits */
|
|
|
|
chip->mc_base |= 0xc00;
|
2010-03-09 01:32:42 +08:00
|
|
|
else
|
2010-03-04 02:41:44 +08:00
|
|
|
#endif /* CONFIG_PNP */
|
2010-03-09 01:32:42 +08:00
|
|
|
{
|
2010-03-04 02:41:44 +08:00
|
|
|
chip->mc_base = 0xf8c;
|
|
|
|
chip->mc_base_size = opti9xx_mc_size[hardware];
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
chip->mc_base_size = opti9xx_mc_size[hardware];
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
switch (hardware) {
|
|
|
|
#ifndef OPTi93X
|
|
|
|
case OPTi9XX_HW_82C928:
|
|
|
|
case OPTi9XX_HW_82C929:
|
|
|
|
chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3;
|
|
|
|
chip->pwd_reg = 3;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OPTi9XX_HW_82C924:
|
|
|
|
case OPTi9XX_HW_82C925:
|
|
|
|
chip->password = 0xe5;
|
|
|
|
chip->pwd_reg = 3;
|
|
|
|
break;
|
|
|
|
#else /* OPTi93X */
|
|
|
|
|
|
|
|
case OPTi9XX_HW_82C930:
|
|
|
|
case OPTi9XX_HW_82C931:
|
|
|
|
case OPTi9XX_HW_82C933:
|
|
|
|
chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d;
|
2012-06-16 02:55:54 +08:00
|
|
|
if (!chip->mc_indir_index)
|
2009-12-05 01:30:18 +08:00
|
|
|
chip->mc_indir_index = 0xe0e;
|
2005-04-17 06:20:36 +08:00
|
|
|
chip->password = 0xe4;
|
|
|
|
chip->pwd_reg = 0;
|
|
|
|
break;
|
|
|
|
#endif /* OPTi93X */
|
|
|
|
|
|
|
|
default:
|
2009-02-05 22:47:51 +08:00
|
|
|
snd_printk(KERN_ERR "chip %d not supported\n", hardware);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:37:56 +08:00
|
|
|
static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip,
|
2005-04-17 06:20:36 +08:00
|
|
|
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) {
|
|
|
|
#ifndef OPTi93X
|
|
|
|
case OPTi9XX_HW_82C924:
|
|
|
|
case OPTi9XX_HW_82C925:
|
|
|
|
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_82C928:
|
|
|
|
case OPTi9XX_HW_82C929:
|
|
|
|
retval = inb(chip->mc_base + reg);
|
|
|
|
break;
|
|
|
|
#else /* OPTi93X */
|
|
|
|
|
|
|
|
case OPTi9XX_HW_82C930:
|
|
|
|
case OPTi9XX_HW_82C931:
|
|
|
|
case OPTi9XX_HW_82C933:
|
|
|
|
outb(reg, chip->mc_indir_index);
|
|
|
|
outb(chip->password, chip->mc_base + chip->pwd_reg);
|
|
|
|
retval = inb(chip->mc_indir_index + 1);
|
|
|
|
break;
|
|
|
|
#endif /* OPTi93X */
|
|
|
|
|
|
|
|
default:
|
2009-02-05 22:47:51 +08:00
|
|
|
snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&chip->lock, flags);
|
|
|
|
return retval;
|
|
|
|
}
|
2010-03-04 02:41:44 +08:00
|
|
|
|
2005-11-17 21:37:56 +08:00
|
|
|
static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned char value)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&chip->lock, flags);
|
|
|
|
outb(chip->password, chip->mc_base + chip->pwd_reg);
|
|
|
|
|
|
|
|
switch (chip->hardware) {
|
|
|
|
#ifndef OPTi93X
|
|
|
|
case OPTi9XX_HW_82C924:
|
|
|
|
case OPTi9XX_HW_82C925:
|
|
|
|
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_82C928:
|
|
|
|
case OPTi9XX_HW_82C929:
|
|
|
|
outb(value, chip->mc_base + reg);
|
|
|
|
break;
|
|
|
|
#else /* OPTi93X */
|
|
|
|
|
|
|
|
case OPTi9XX_HW_82C930:
|
|
|
|
case OPTi9XX_HW_82C931:
|
|
|
|
case OPTi9XX_HW_82C933:
|
|
|
|
outb(reg, chip->mc_indir_index);
|
|
|
|
outb(chip->password, chip->mc_base + chip->pwd_reg);
|
|
|
|
outb(value, chip->mc_indir_index + 1);
|
|
|
|
break;
|
|
|
|
#endif /* OPTi93X */
|
|
|
|
|
|
|
|
default:
|
2009-02-05 22:47:51 +08:00
|
|
|
snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&chip->lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define snd_opti9xx_write_mask(chip, reg, value, mask) \
|
|
|
|
snd_opti9xx_write(chip, reg, \
|
|
|
|
(snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask)))
|
|
|
|
|
|
|
|
|
2012-07-17 18:14:17 +08:00
|
|
|
static int snd_opti9xx_configure(struct snd_opti9xx *chip,
|
2010-03-04 02:41:44 +08:00
|
|
|
long port,
|
2009-12-03 06:27:12 +08:00
|
|
|
int irq, int dma1, int dma2,
|
|
|
|
long mpu_port, int mpu_irq)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned char wss_base_bits;
|
|
|
|
unsigned char irq_bits;
|
|
|
|
unsigned char dma_bits;
|
|
|
|
unsigned char mpu_port_bits = 0;
|
|
|
|
unsigned char mpu_irq_bits;
|
|
|
|
|
|
|
|
switch (chip->hardware) {
|
|
|
|
#ifndef OPTi93X
|
|
|
|
case OPTi9XX_HW_82C924:
|
2010-03-04 02:41:44 +08:00
|
|
|
/* opti 929 mode (?), OPL3 clock output, audio enable */
|
2005-04-17 06:20:36 +08:00
|
|
|
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc);
|
2010-03-04 02:41:44 +08:00
|
|
|
/* enable wave audio */
|
2005-04-17 06:20:36 +08:00
|
|
|
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);
|
|
|
|
|
|
|
|
case OPTi9XX_HW_82C925:
|
2010-03-04 02:41:44 +08:00
|
|
|
/* enable WSS mode */
|
2005-04-17 06:20:36 +08:00
|
|
|
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
|
2010-03-04 02:41:44 +08:00
|
|
|
/* OPL3 FM synthesis */
|
2005-04-17 06:20:36 +08:00
|
|
|
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
|
2010-03-04 02:41:44 +08:00
|
|
|
/* disable Sound Blaster IRQ and DMA */
|
2005-04-17 06:20:36 +08:00
|
|
|
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);
|
|
|
|
#ifdef CS4231
|
2010-03-04 02:41:44 +08:00
|
|
|
/* cs4231/4248 fix enabled */
|
2005-04-17 06:20:36 +08:00
|
|
|
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
|
|
|
|
#else
|
2010-03-04 02:41:44 +08:00
|
|
|
/* cs4231/4248 fix disabled */
|
2005-04-17 06:20:36 +08:00
|
|
|
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
|
|
|
|
#endif /* CS4231 */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OPTi9XX_HW_82C928:
|
|
|
|
case OPTi9XX_HW_82C929:
|
|
|
|
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
|
|
|
|
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
|
|
|
|
/*
|
|
|
|
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae);
|
|
|
|
*/
|
|
|
|
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);
|
|
|
|
#ifdef CS4231
|
|
|
|
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
|
|
|
|
#else
|
|
|
|
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
|
|
|
|
#endif /* CS4231 */
|
|
|
|
break;
|
|
|
|
|
|
|
|
#else /* OPTi93X */
|
|
|
|
case OPTi9XX_HW_82C931:
|
2012-06-16 02:55:54 +08:00
|
|
|
/* 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 */
|
2008-06-08 13:57:53 +08:00
|
|
|
/*
|
2007-10-16 20:54:37 +08:00
|
|
|
* 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);
|
2008-06-08 13:57:53 +08:00
|
|
|
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 |
|
|
|
|
(chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04),
|
|
|
|
0x34);
|
|
|
|
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
#endif /* OPTi93X */
|
|
|
|
|
|
|
|
default:
|
2009-02-05 22:47:51 +08:00
|
|
|
snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2010-03-04 02:41:44 +08:00
|
|
|
/* PnP resource says it decodes only 10 bits of address */
|
|
|
|
switch (port & 0x3ff) {
|
|
|
|
case 0x130:
|
|
|
|
chip->wss_base = 0x530;
|
2005-04-17 06:20:36 +08:00
|
|
|
wss_base_bits = 0x00;
|
|
|
|
break;
|
2010-03-04 02:41:44 +08:00
|
|
|
case 0x204:
|
|
|
|
chip->wss_base = 0x604;
|
2005-04-17 06:20:36 +08:00
|
|
|
wss_base_bits = 0x03;
|
|
|
|
break;
|
2010-03-04 02:41:44 +08:00
|
|
|
case 0x280:
|
|
|
|
chip->wss_base = 0xe80;
|
2005-04-17 06:20:36 +08:00
|
|
|
wss_base_bits = 0x01;
|
|
|
|
break;
|
2010-03-04 02:41:44 +08:00
|
|
|
case 0x340:
|
|
|
|
chip->wss_base = 0xf40;
|
2005-04-17 06:20:36 +08:00
|
|
|
wss_base_bits = 0x02;
|
|
|
|
break;
|
|
|
|
default:
|
2010-03-04 02:41:44 +08:00
|
|
|
snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n", port);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto __skip_base;
|
|
|
|
}
|
|
|
|
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
|
|
|
|
|
|
|
|
__skip_base:
|
2009-12-03 06:27:12 +08:00
|
|
|
switch (irq) {
|
2005-04-17 06:20:36 +08:00
|
|
|
//#ifdef OPTi93X
|
|
|
|
case 5:
|
|
|
|
irq_bits = 0x05;
|
|
|
|
break;
|
|
|
|
//#endif /* OPTi93X */
|
|
|
|
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:
|
2009-12-03 06:27:12 +08:00
|
|
|
snd_printk(KERN_WARNING "WSS irq # %d not valid\n", irq);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto __skip_resources;
|
|
|
|
}
|
|
|
|
|
2009-12-03 06:27:12 +08:00
|
|
|
switch (dma1) {
|
2005-04-17 06:20:36 +08:00
|
|
|
case 0:
|
|
|
|
dma_bits = 0x01;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
dma_bits = 0x02;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
dma_bits = 0x03;
|
|
|
|
break;
|
|
|
|
default:
|
2009-12-03 06:27:12 +08:00
|
|
|
snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n", dma1);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto __skip_resources;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(CS4231) || defined(OPTi93X)
|
2009-12-03 06:27:12 +08:00
|
|
|
if (dma1 == dma2) {
|
2009-02-05 22:47:51 +08:00
|
|
|
snd_printk(KERN_ERR "don't want to share dmas\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2009-12-03 06:27:12 +08:00
|
|
|
switch (dma2) {
|
2005-04-17 06:20:36 +08:00
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
break;
|
|
|
|
default:
|
2009-12-03 06:27:12 +08:00
|
|
|
snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n", dma2);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto __skip_resources;
|
|
|
|
}
|
|
|
|
dma_bits |= 0x04;
|
|
|
|
#endif /* CS4231 || OPTi93X */
|
|
|
|
|
|
|
|
#ifndef OPTi93X
|
2010-03-04 02:41:44 +08:00
|
|
|
outb(irq_bits << 3 | dma_bits, chip->wss_base);
|
2005-04-17 06:20:36 +08:00
|
|
|
#else /* OPTi93X */
|
|
|
|
snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits));
|
|
|
|
#endif /* OPTi93X */
|
|
|
|
|
|
|
|
__skip_resources:
|
|
|
|
if (chip->hardware > OPTi9XX_HW_82C928) {
|
2009-12-03 06:27:12 +08:00
|
|
|
switch (mpu_port) {
|
2005-04-17 06:20:36 +08:00
|
|
|
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:
|
2009-02-05 22:47:51 +08:00
|
|
|
snd_printk(KERN_WARNING
|
2009-12-03 06:27:12 +08:00
|
|
|
"MPU-401 port 0x%lx not valid\n", mpu_port);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto __skip_mpu;
|
|
|
|
}
|
|
|
|
|
2009-12-03 06:27:12 +08:00
|
|
|
switch (mpu_irq) {
|
2005-04-17 06:20:36 +08:00
|
|
|
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:
|
2009-02-05 22:47:51 +08:00
|
|
|
snd_printk(KERN_WARNING "MPU-401 irq # %d not valid\n",
|
2009-12-03 06:27:12 +08:00
|
|
|
mpu_irq);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto __skip_mpu;
|
|
|
|
}
|
|
|
|
|
|
|
|
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6),
|
2009-12-03 06:27:12 +08:00
|
|
|
(mpu_port <= 0) ? 0x00 :
|
2005-04-17 06:20:36 +08:00
|
|
|
0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3,
|
|
|
|
0xf8);
|
|
|
|
}
|
|
|
|
__skip_mpu:
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef OPTi93X
|
|
|
|
|
2009-12-12 16:51:03 +08:00
|
|
|
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);
|
2009-12-11 03:40:18 +08:00
|
|
|
|
|
|
|
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,
|
2009-12-12 16:51:03 +08:00
|
|
|
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),
|
2009-12-11 03:40:18 +08:00
|
|
|
WSS_DOUBLE("Line Playback Switch", 0,
|
|
|
|
CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
|
2009-12-12 16:51:03 +08:00
|
|
|
WSS_DOUBLE_TLV("Line Playback Volume", 0,
|
|
|
|
CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1,
|
|
|
|
db_scale_4bit_12db_max),
|
2009-12-11 03:40:18 +08:00
|
|
|
WSS_DOUBLE("Mic Playback Switch", 0,
|
|
|
|
OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1),
|
2009-12-12 16:51:03 +08:00
|
|
|
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),
|
2009-12-11 03:40:18 +08:00
|
|
|
WSS_DOUBLE("Aux Playback Switch", 0,
|
|
|
|
OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1),
|
2009-12-12 16:51:03 +08:00
|
|
|
WSS_DOUBLE_TLV("Aux Playback Volume", 0,
|
|
|
|
OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1,
|
|
|
|
db_scale_4bit_12db_max),
|
2009-12-11 03:40:18 +08:00
|
|
|
};
|
|
|
|
|
2012-12-07 01:35:21 +08:00
|
|
|
static int snd_opti93x_mixer(struct snd_wss *chip)
|
2009-12-11 03:40:18 +08:00
|
|
|
{
|
|
|
|
struct snd_card *card;
|
|
|
|
unsigned int idx;
|
|
|
|
struct snd_ctl_elem_id id1, id2;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (snd_BUG_ON(!chip || !chip->pcm))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
card = chip->card;
|
|
|
|
|
|
|
|
strcpy(card->mixername, chip->pcm->name);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
err = snd_ctl_add(card,
|
|
|
|
snd_ctl_new1(&snd_opti93x_controls[idx], chip));
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2009-12-10 03:12:43 +08:00
|
|
|
struct snd_opti9xx *chip = dev_id;
|
|
|
|
struct snd_wss *codec = chip->codec;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned char status;
|
|
|
|
|
2009-12-10 03:12:43 +08:00
|
|
|
if (!codec)
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
|
2008-06-11 18:26:32 +08:00
|
|
|
status = snd_opti9xx_read(chip, OPTi9XX_MC_REG(11));
|
2005-04-17 06:20:36 +08:00
|
|
|
if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream)
|
|
|
|
snd_pcm_period_elapsed(codec->playback_substream);
|
|
|
|
if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) {
|
2008-08-01 03:03:41 +08:00
|
|
|
snd_wss_overrange(codec);
|
2005-04-17 06:20:36 +08:00
|
|
|
snd_pcm_period_elapsed(codec->capture_substream);
|
|
|
|
}
|
|
|
|
outb(0x00, OPTi93X_PORT(codec, STATUS));
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* OPTi93X */
|
|
|
|
|
2012-12-07 01:35:21 +08:00
|
|
|
static int snd_opti9xx_read_check(struct snd_opti9xx *chip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2009-12-05 01:30:18 +08:00
|
|
|
unsigned char value;
|
|
|
|
#ifdef OPTi93X
|
|
|
|
unsigned long flags;
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-12-05 01:30:18 +08:00
|
|
|
chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size,
|
|
|
|
"OPTi9xx MC");
|
|
|
|
if (chip->res_mc_base == NULL)
|
|
|
|
return -EBUSY;
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifndef OPTi93X
|
2009-12-05 01:30:18 +08:00
|
|
|
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 */
|
2012-06-16 02:55:54 +08:00
|
|
|
chip->res_mc_indir = request_region(chip->mc_indir_index, 2,
|
2009-12-05 01:30:18 +08:00
|
|
|
"OPTi93x MC");
|
|
|
|
if (chip->res_mc_indir == NULL)
|
|
|
|
return -EBUSY;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-12-05 01:30:18 +08:00
|
|
|
spin_lock_irqsave(&chip->lock, flags);
|
|
|
|
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);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-12-05 01:30:18 +08:00
|
|
|
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;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-12-05 01:30:18 +08:00
|
|
|
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;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-12-05 01:30:18 +08:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-12-07 01:35:21 +08:00
|
|
|
static int snd_card_opti9xx_detect(struct snd_card *card,
|
|
|
|
struct snd_opti9xx *chip)
|
2009-12-05 01:30:18 +08:00
|
|
|
{
|
|
|
|
int i, err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-12-05 01:30:18 +08:00
|
|
|
#ifndef OPTi93X
|
|
|
|
for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) {
|
|
|
|
#else
|
|
|
|
for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) {
|
|
|
|
#endif
|
|
|
|
err = snd_opti9xx_init(chip, i);
|
|
|
|
if (err < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return err;
|
|
|
|
|
2009-12-05 01:30:18 +08:00
|
|
|
err = snd_opti9xx_read_check(chip);
|
|
|
|
if (err == 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return 1;
|
2009-12-05 01:30:18 +08:00
|
|
|
#ifdef OPTi93X
|
|
|
|
chip->mc_indir_index = 0;
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_PNP
|
2012-12-07 01:35:21 +08:00
|
|
|
static int snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
|
|
|
|
struct pnp_card_link *card,
|
|
|
|
const struct pnp_card_device_id *pid)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct pnp_dev *pdev;
|
|
|
|
int err;
|
2010-03-04 02:41:44 +08:00
|
|
|
struct pnp_dev *devmpu;
|
|
|
|
#ifndef OPTi93X
|
|
|
|
struct pnp_dev *devmc;
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-03-04 02:41:44 +08:00
|
|
|
pdev = pnp_request_card_device(card, pid->devs[0].id, NULL);
|
|
|
|
if (pdev == NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EBUSY;
|
2007-12-01 00:59:25 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
err = pnp_activate_dev(pdev);
|
|
|
|
if (err < 0) {
|
|
|
|
snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef OPTi93X
|
|
|
|
port = pnp_port_start(pdev, 0) - 4;
|
2007-09-17 22:44:06 +08:00
|
|
|
fm_port = pnp_port_start(pdev, 1) + 8;
|
2012-06-16 02:55:54 +08:00
|
|
|
/* 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;
|
2005-04-17 06:20:36 +08:00
|
|
|
#else
|
2010-03-04 02:41:44 +08:00
|
|
|
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);
|
2007-09-17 22:44:06 +08:00
|
|
|
fm_port = pnp_port_start(pdev, 2) + 8;
|
2010-03-04 02:41:44 +08:00
|
|
|
/*
|
|
|
|
* 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;
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif /* OPTi93X */
|
|
|
|
irq = pnp_irq(pdev, 0);
|
|
|
|
dma1 = pnp_dma(pdev, 0);
|
|
|
|
#if defined(CS4231) || defined(OPTi93X)
|
|
|
|
dma2 = pnp_dma(pdev, 1);
|
|
|
|
#endif /* CS4231 || OPTi93X */
|
|
|
|
|
2010-03-04 02:41:44 +08:00
|
|
|
devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
|
|
|
|
|
|
|
|
if (devmpu && mpu_port > 0) {
|
|
|
|
err = pnp_activate_dev(devmpu);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (err < 0) {
|
2010-03-04 02:41:44 +08:00
|
|
|
snd_printk(KERN_ERR "MPU401 pnp configure failure\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
mpu_port = -1;
|
|
|
|
} else {
|
2010-03-04 02:41:44 +08:00
|
|
|
mpu_port = pnp_port_start(devmpu, 0);
|
|
|
|
mpu_irq = pnp_irq(devmpu, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return pid->driver_data;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_PNP */
|
|
|
|
|
2005-11-17 21:37:56 +08:00
|
|
|
static void snd_card_opti9xx_free(struct snd_card *card)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-18 00:13:59 +08:00
|
|
|
struct snd_opti9xx *chip = card->private_data;
|
2009-12-05 01:30:18 +08:00
|
|
|
|
2008-06-11 18:26:32 +08:00
|
|
|
if (chip) {
|
|
|
|
#ifdef OPTi93X
|
2009-12-10 03:12:43 +08:00
|
|
|
if (chip->irq > 0) {
|
|
|
|
disable_irq(chip->irq);
|
|
|
|
free_irq(chip->irq, chip);
|
2008-06-11 18:26:32 +08:00
|
|
|
}
|
2009-12-05 01:30:18 +08:00
|
|
|
release_and_free_resource(chip->res_mc_indir);
|
2008-06-11 18:26:32 +08:00
|
|
|
#endif
|
2005-10-10 17:56:31 +08:00
|
|
|
release_and_free_resource(chip->res_mc_base);
|
2008-06-11 18:26:32 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2012-12-07 01:35:21 +08:00
|
|
|
static int snd_opti9xx_probe(struct snd_card *card)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
|
|
|
|
int error;
|
2009-12-03 06:27:12 +08:00
|
|
|
int xdma2;
|
2005-11-18 00:13:59 +08:00
|
|
|
struct snd_opti9xx *chip = card->private_data;
|
2008-08-01 03:03:41 +08:00
|
|
|
struct snd_wss *codec;
|
2008-06-11 18:26:32 +08:00
|
|
|
#ifdef CS4231
|
2005-11-17 21:37:56 +08:00
|
|
|
struct snd_timer *timer;
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
2005-11-17 21:37:56 +08:00
|
|
|
struct snd_pcm *pcm;
|
|
|
|
struct snd_rawmidi *rmidi;
|
|
|
|
struct snd_hwdep *synth;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#if defined(CS4231) || defined(OPTi93X)
|
2009-12-03 06:27:12 +08:00
|
|
|
xdma2 = dma2;
|
2008-08-04 11:26:26 +08:00
|
|
|
#else
|
2009-12-03 06:27:12 +08:00
|
|
|
xdma2 = -1;
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
|
|
|
|
2009-12-03 06:27:12 +08:00
|
|
|
if (port == SNDRV_AUTO_PORT) {
|
|
|
|
port = snd_legacy_find_free_ioport(possible_ports, 4);
|
|
|
|
if (port < 0) {
|
2009-02-05 22:47:51 +08:00
|
|
|
snd_printk(KERN_ERR "unable to find a free WSS port\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
}
|
2009-12-03 06:27:12 +08:00
|
|
|
error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2,
|
|
|
|
mpu_port, mpu_irq);
|
2008-08-01 03:03:41 +08:00
|
|
|
if (error)
|
2005-04-17 06:20:36 +08:00
|
|
|
return error;
|
|
|
|
|
2010-03-04 02:41:44 +08:00
|
|
|
error = snd_wss_create(card, chip->wss_base + 4, -1, irq, dma1, xdma2,
|
2008-08-04 11:26:26 +08:00
|
|
|
#ifdef OPTi93X
|
2008-08-01 03:03:41 +08:00
|
|
|
WSS_HW_OPTI93X, WSS_HWSHARE_IRQ,
|
2008-08-04 11:26:26 +08:00
|
|
|
#else
|
|
|
|
WSS_HW_DETECT, 0,
|
2008-06-11 18:26:32 +08:00
|
|
|
#endif
|
2008-08-01 03:03:41 +08:00
|
|
|
&codec);
|
|
|
|
if (error < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return error;
|
2008-06-11 18:26:32 +08:00
|
|
|
chip->codec = codec;
|
2008-08-01 03:09:32 +08:00
|
|
|
error = snd_wss_pcm(codec, 0, &pcm);
|
2008-08-01 03:08:32 +08:00
|
|
|
if (error < 0)
|
|
|
|
return error;
|
2008-08-01 03:03:41 +08:00
|
|
|
error = snd_wss_mixer(codec);
|
|
|
|
if (error < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return error;
|
2009-12-11 03:40:18 +08:00
|
|
|
#ifdef OPTi93X
|
|
|
|
error = snd_opti93x_mixer(codec);
|
|
|
|
if (error < 0)
|
|
|
|
return error;
|
|
|
|
#endif
|
2008-06-11 18:26:32 +08:00
|
|
|
#ifdef CS4231
|
2008-08-01 03:03:41 +08:00
|
|
|
error = snd_wss_timer(codec, 0, &timer);
|
|
|
|
if (error < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return error;
|
2008-08-01 03:08:32 +08:00
|
|
|
#endif
|
|
|
|
#ifdef OPTi93X
|
2009-12-03 06:27:12 +08:00
|
|
|
error = request_irq(irq, snd_opti93x_interrupt,
|
2011-09-22 16:59:20 +08:00
|
|
|
0, DEV_NAME" - WSS", chip);
|
2008-06-11 18:26:32 +08:00
|
|
|
if (error < 0) {
|
2009-12-10 03:12:43 +08:00
|
|
|
snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", irq);
|
2008-06-11 18:26:32 +08:00
|
|
|
return error;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
2009-12-03 06:27:12 +08:00
|
|
|
chip->irq = irq;
|
2005-04-17 06:20:36 +08:00
|
|
|
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",
|
2010-03-04 02:41:44 +08:00
|
|
|
card->shortname, pcm->name,
|
|
|
|
chip->wss_base + 4, irq, dma1, xdma2);
|
2005-04-17 06:20:36 +08:00
|
|
|
#else
|
|
|
|
sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d",
|
2010-03-04 02:41:44 +08:00
|
|
|
card->shortname, pcm->name, chip->wss_base + 4, irq, dma1);
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif /* CS4231 || OPTi93X */
|
|
|
|
|
2009-12-03 06:27:12 +08:00
|
|
|
if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT)
|
2005-04-17 06:20:36 +08:00
|
|
|
rmidi = NULL;
|
2009-12-03 06:27:12 +08:00
|
|
|
else {
|
|
|
|
error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
|
ALSA: mpu401: clean up interrupt specification
The semantics of snd_mpu401_uart_new()'s interrupt parameters are
somewhat counterintuitive: To prevent the function from allocating its
own interrupt, either the irq number must be invalid, or the irq_flags
parameter must be zero. At the same time, the irq parameter being
invalid specifies that the mpu401 code has to work without an interrupt
allocated by the caller. This implies that, if there is an interrupt
and it is allocated by the caller, the irq parameter must be set to
a valid-looking number which then isn't actually used.
With the removal of IRQF_DISABLED, zero becomes a valid irq_flags value,
which forces us to handle the parameters differently.
This patch introduces a new flag MPU401_INFO_IRQ_HOOK for when the
device interrupt is handled by the caller, and makes the allocation of
the interrupt to depend only on the irq parameter. As suggested by
Takashi, the irq_flags parameter was dropped because, when used, it had
the constant value IRQF_DISABLED.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2011-09-13 17:24:41 +08:00
|
|
|
mpu_port, 0, mpu_irq, &rmidi);
|
2009-12-03 06:27:12 +08:00
|
|
|
if (error)
|
2005-11-18 00:13:59 +08:00
|
|
|
snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
|
2009-12-03 06:27:12 +08:00
|
|
|
mpu_port);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-12-03 06:27:12 +08:00
|
|
|
if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
|
2005-11-17 21:37:56 +08:00
|
|
|
struct snd_opl3 *opl3 = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifndef OPTi93X
|
|
|
|
if (chip->hardware == OPTi9XX_HW_82C928 ||
|
|
|
|
chip->hardware == OPTi9XX_HW_82C929 ||
|
|
|
|
chip->hardware == OPTi9XX_HW_82C924) {
|
2005-11-17 21:37:56 +08:00
|
|
|
struct snd_opl4 *opl4;
|
2005-04-17 06:20:36 +08:00
|
|
|
/* assume we have an OPL4 */
|
|
|
|
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
|
|
|
|
0x20, 0x20);
|
2009-12-03 06:27:12 +08:00
|
|
|
if (snd_opl4_create(card, fm_port, fm_port - 8,
|
2005-04-17 06:20:36 +08:00
|
|
|
2, &opl3, &opl4) < 0) {
|
|
|
|
/* no luck, use OPL3 instead */
|
|
|
|
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
|
|
|
|
0x00, 0x20);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* !OPTi93X */
|
2009-12-03 06:27:12 +08:00
|
|
|
if (!opl3 && snd_opl3_create(card, fm_port, fm_port + 2,
|
2005-04-17 06:20:36 +08:00
|
|
|
OPL3_HW_AUTO, 0, &opl3) < 0) {
|
2005-11-18 00:13:59 +08:00
|
|
|
snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n",
|
2009-12-03 06:27:12 +08:00
|
|
|
fm_port, fm_port + 4 - 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
if (opl3) {
|
2009-01-21 22:08:03 +08:00
|
|
|
error = snd_opl3_hwdep_new(opl3, 0, 1, &synth);
|
|
|
|
if (error < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-11-18 00:13:59 +08:00
|
|
|
return snd_card_register(card);
|
|
|
|
}
|
|
|
|
|
2008-12-28 23:47:30 +08:00
|
|
|
static int snd_opti9xx_card_new(struct snd_card **cardp)
|
2005-11-18 00:13:59 +08:00
|
|
|
{
|
|
|
|
struct snd_card *card;
|
2009-01-14 16:34:06 +08:00
|
|
|
int err;
|
2005-11-18 00:13:59 +08:00
|
|
|
|
2008-12-28 23:43:35 +08:00
|
|
|
err = snd_card_create(index, id, THIS_MODULE,
|
|
|
|
sizeof(struct snd_opti9xx), &card);
|
|
|
|
if (err < 0)
|
2008-12-28 23:47:30 +08:00
|
|
|
return err;
|
2005-11-18 00:13:59 +08:00
|
|
|
card->private_free = snd_card_opti9xx_free;
|
2008-12-28 23:47:30 +08:00
|
|
|
*cardp = card;
|
|
|
|
return 0;
|
2005-11-18 00:13:59 +08:00
|
|
|
}
|
|
|
|
|
2012-12-07 01:35:21 +08:00
|
|
|
static int snd_opti9xx_isa_match(struct device *devptr,
|
|
|
|
unsigned int dev)
|
2007-02-22 19:50:54 +08:00
|
|
|
{
|
2007-06-20 18:03:09 +08:00
|
|
|
#ifdef CONFIG_PNP
|
2007-02-22 19:50:54 +08:00
|
|
|
if (snd_opti9xx_pnp_is_probed)
|
|
|
|
return 0;
|
|
|
|
if (isapnp)
|
|
|
|
return 0;
|
2007-06-20 18:03:09 +08:00
|
|
|
#endif
|
2007-02-22 19:50:54 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-12-07 01:35:21 +08:00
|
|
|
static int snd_opti9xx_isa_probe(struct device *devptr,
|
|
|
|
unsigned int dev)
|
2005-11-18 00:13:59 +08:00
|
|
|
{
|
|
|
|
struct snd_card *card;
|
|
|
|
int error;
|
|
|
|
static long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1};
|
|
|
|
#ifdef OPTi93X
|
|
|
|
static int possible_irqs[] = {5, 9, 10, 11, 7, -1};
|
|
|
|
#else
|
|
|
|
static int possible_irqs[] = {9, 10, 11, 7, -1};
|
|
|
|
#endif /* OPTi93X */
|
|
|
|
static int possible_mpu_irqs[] = {5, 9, 10, 7, -1};
|
|
|
|
static int possible_dma1s[] = {3, 1, 0, -1};
|
|
|
|
#if defined(CS4231) || defined(OPTi93X)
|
|
|
|
static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};
|
|
|
|
#endif /* CS4231 || OPTi93X */
|
|
|
|
|
|
|
|
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");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (irq == SNDRV_AUTO_IRQ) {
|
|
|
|
if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
|
|
|
|
snd_printk(KERN_ERR "unable to find a free IRQ\n");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (mpu_irq == SNDRV_AUTO_IRQ) {
|
|
|
|
if ((mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) {
|
|
|
|
snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (dma1 == SNDRV_AUTO_DMA) {
|
|
|
|
if ((dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) {
|
|
|
|
snd_printk(KERN_ERR "unable to find a free DMA1\n");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#if defined(CS4231) || defined(OPTi93X)
|
|
|
|
if (dma2 == SNDRV_AUTO_DMA) {
|
|
|
|
if ((dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4])) < 0) {
|
2009-02-05 22:47:51 +08:00
|
|
|
snd_printk(KERN_ERR "unable to find a free DMA2\n");
|
2005-11-18 00:13:59 +08:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-12-28 23:47:30 +08:00
|
|
|
error = snd_opti9xx_card_new(&card);
|
|
|
|
if (error < 0)
|
|
|
|
return error;
|
2005-11-18 00:13:59 +08:00
|
|
|
|
|
|
|
if ((error = snd_card_opti9xx_detect(card, card->private_data)) < 0) {
|
2005-04-17 06:20:36 +08:00
|
|
|
snd_card_free(card);
|
|
|
|
return error;
|
|
|
|
}
|
2007-02-22 19:50:54 +08:00
|
|
|
snd_card_set_dev(card, devptr);
|
2005-11-18 00:13:59 +08:00
|
|
|
if ((error = snd_opti9xx_probe(card)) < 0) {
|
|
|
|
snd_card_free(card);
|
|
|
|
return error;
|
|
|
|
}
|
2007-02-22 19:50:54 +08:00
|
|
|
dev_set_drvdata(devptr, card);
|
2005-11-18 00:13:59 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-07 01:35:21 +08:00
|
|
|
static int snd_opti9xx_isa_remove(struct device *devptr,
|
|
|
|
unsigned int dev)
|
2005-11-18 00:13:59 +08:00
|
|
|
{
|
2007-02-22 19:50:54 +08:00
|
|
|
snd_card_free(dev_get_drvdata(devptr));
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-17 15:16:44 +08:00
|
|
|
#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 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
|
|
|
|
|
2007-02-22 19:50:54 +08:00
|
|
|
static struct isa_driver snd_opti9xx_driver = {
|
|
|
|
.match = snd_opti9xx_isa_match,
|
|
|
|
.probe = snd_opti9xx_isa_probe,
|
2012-12-07 01:35:21 +08:00
|
|
|
.remove = snd_opti9xx_isa_remove,
|
2012-07-17 15:16:44 +08:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
.suspend = snd_opti9xx_isa_suspend,
|
|
|
|
.resume = snd_opti9xx_isa_resume,
|
|
|
|
#endif
|
2005-11-18 00:13:59 +08:00
|
|
|
.driver = {
|
2007-03-20 18:33:46 +08:00
|
|
|
.name = DEV_NAME
|
2005-11-18 00:13:59 +08:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifdef CONFIG_PNP
|
2012-12-07 01:35:21 +08:00
|
|
|
static int snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
|
|
|
|
const struct pnp_card_device_id *pid)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-18 00:13:59 +08:00
|
|
|
struct snd_card *card;
|
|
|
|
int error, hw;
|
|
|
|
struct snd_opti9xx *chip;
|
|
|
|
|
|
|
|
if (snd_opti9xx_pnp_is_probed)
|
|
|
|
return -EBUSY;
|
|
|
|
if (! isapnp)
|
|
|
|
return -ENODEV;
|
2008-12-28 23:47:30 +08:00
|
|
|
error = snd_opti9xx_card_new(&card);
|
|
|
|
if (error < 0)
|
|
|
|
return error;
|
2005-11-18 00:13:59 +08:00
|
|
|
chip = card->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-11-18 00:13:59 +08:00
|
|
|
hw = snd_card_opti9xx_pnp(chip, pcard, pid);
|
|
|
|
switch (hw) {
|
|
|
|
case 0x0924:
|
|
|
|
hw = OPTi9XX_HW_82C924;
|
|
|
|
break;
|
|
|
|
case 0x0925:
|
|
|
|
hw = OPTi9XX_HW_82C925;
|
|
|
|
break;
|
|
|
|
case 0x0931:
|
|
|
|
hw = OPTi9XX_HW_82C931;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
snd_card_free(card);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = snd_opti9xx_init(chip, hw))) {
|
|
|
|
snd_card_free(card);
|
|
|
|
return error;
|
|
|
|
}
|
2009-12-05 01:30:18 +08:00
|
|
|
error = snd_opti9xx_read_check(chip);
|
|
|
|
if (error) {
|
|
|
|
snd_printk(KERN_ERR "OPTI chip not found\n");
|
|
|
|
snd_card_free(card);
|
|
|
|
return error;
|
|
|
|
}
|
2005-11-18 00:13:59 +08:00
|
|
|
snd_card_set_dev(card, &pcard->card->dev);
|
|
|
|
if ((error = snd_opti9xx_probe(card)) < 0) {
|
|
|
|
snd_card_free(card);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
pnp_set_card_drvdata(pcard, card);
|
|
|
|
snd_opti9xx_pnp_is_probed = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-07 01:35:21 +08:00
|
|
|
static void snd_opti9xx_pnp_remove(struct pnp_card_link *pcard)
|
2005-11-18 00:13:59 +08:00
|
|
|
{
|
|
|
|
snd_card_free(pnp_get_card_drvdata(pcard));
|
|
|
|
pnp_set_card_drvdata(pcard, NULL);
|
|
|
|
snd_opti9xx_pnp_is_probed = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2012-07-17 15:16:44 +08:00
|
|
|
#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
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static struct pnp_card_driver opti9xx_pnpc_driver = {
|
|
|
|
.flags = PNP_DRIVER_RES_DISABLE,
|
2013-08-27 18:03:01 +08:00
|
|
|
.name = DEV_NAME,
|
2005-04-17 06:20:36 +08:00
|
|
|
.id_table = snd_opti9xx_pnpids,
|
2005-11-18 00:13:59 +08:00
|
|
|
.probe = snd_opti9xx_pnp_probe,
|
2012-12-07 01:35:21 +08:00
|
|
|
.remove = snd_opti9xx_pnp_remove,
|
2012-07-17 15:16:44 +08:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
.suspend = snd_opti9xx_pnp_suspend,
|
|
|
|
.resume = snd_opti9xx_pnp_resume,
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef OPTi93X
|
2005-11-18 00:13:59 +08:00
|
|
|
#define CHIP_NAME "82C93x"
|
2005-04-17 06:20:36 +08:00
|
|
|
#else
|
2005-11-18 00:13:59 +08:00
|
|
|
#define CHIP_NAME "82C92x"
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
2005-11-18 00:13:59 +08:00
|
|
|
|
|
|
|
static int __init alsa_card_opti9xx_init(void)
|
|
|
|
{
|
2006-02-28 00:23:46 +08:00
|
|
|
#ifdef CONFIG_PNP
|
2005-11-18 00:13:59 +08:00
|
|
|
pnp_register_card_driver(&opti9xx_pnpc_driver);
|
|
|
|
if (snd_opti9xx_pnp_is_probed)
|
|
|
|
return 0;
|
2007-06-20 18:03:09 +08:00
|
|
|
pnp_unregister_card_driver(&opti9xx_pnpc_driver);
|
2006-02-28 00:23:46 +08:00
|
|
|
#endif
|
2007-02-22 19:50:54 +08:00
|
|
|
return isa_register_driver(&snd_opti9xx_driver, 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit alsa_card_opti9xx_exit(void)
|
|
|
|
{
|
2005-12-07 16:13:42 +08:00
|
|
|
if (!snd_opti9xx_pnp_is_probed) {
|
2007-02-22 19:50:54 +08:00
|
|
|
isa_unregister_driver(&snd_opti9xx_driver);
|
|
|
|
return;
|
2005-12-07 16:13:42 +08:00
|
|
|
}
|
2006-02-28 00:23:46 +08:00
|
|
|
#ifdef CONFIG_PNP
|
2005-04-17 06:20:36 +08:00
|
|
|
pnp_unregister_card_driver(&opti9xx_pnpc_driver);
|
2006-02-28 00:23:46 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(alsa_card_opti9xx_init)
|
|
|
|
module_exit(alsa_card_opti9xx_exit)
|