Path: blob/master/sound/isa/opti9xx/opti92x-ad1848.c
10817 views
/*1card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards.2Copyright (C) 1998-2000 by Massimo Piccioni <[email protected]>34Part of this code was developed at the Italian Ministry of Air Defence,5Sixth Division (oh, che pace ...), Rome.67Thanks to Maria Grazia Pollarini, Salvatore Vassallo.89This program is free software; you can redistribute it and/or modify10it under the terms of the GNU General Public License as published by11the Free Software Foundation; either version 2 of the License, or12(at your option) any later version.1314This program is distributed in the hope that it will be useful,15but WITHOUT ANY WARRANTY; without even the implied warranty of16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the17GNU General Public License for more details.1819You should have received a copy of the GNU General Public License20along with this program; if not, write to the Free Software21Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA22*/232425#include <linux/init.h>26#include <linux/err.h>27#include <linux/isa.h>28#include <linux/delay.h>29#include <linux/pnp.h>30#include <linux/moduleparam.h>31#include <asm/io.h>32#include <asm/dma.h>33#include <sound/core.h>34#include <sound/tlv.h>35#include <sound/wss.h>36#include <sound/mpu401.h>37#include <sound/opl3.h>38#ifndef OPTi93X39#include <sound/opl4.h>40#endif41#define SNDRV_LEGACY_FIND_FREE_IRQ42#define SNDRV_LEGACY_FIND_FREE_DMA43#include <sound/initval.h>4445MODULE_AUTHOR("Massimo Piccioni <[email protected]>");46MODULE_LICENSE("GPL");47#ifdef OPTi93X48MODULE_DESCRIPTION("OPTi93X");49MODULE_SUPPORTED_DEVICE("{{OPTi,82C931/3}}");50#else /* OPTi93X */51#ifdef CS423152MODULE_DESCRIPTION("OPTi92X - CS4231");53MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (CS4231)},"54"{OPTi,82C925 (CS4231)}}");55#else /* CS4231 */56MODULE_DESCRIPTION("OPTi92X - AD1848");57MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (AD1848)},"58"{OPTi,82C925 (AD1848)},"59"{OAK,Mozart}}");60#endif /* CS4231 */61#endif /* OPTi93X */6263static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */64static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */65//static int enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */66#ifdef CONFIG_PNP67static int isapnp = 1; /* Enable ISA PnP detection */68#endif69static long port = SNDRV_DEFAULT_PORT1; /* 0x530,0xe80,0xf40,0x604 */70static long mpu_port = SNDRV_DEFAULT_PORT1; /* 0x300,0x310,0x320,0x330 */71static long fm_port = SNDRV_DEFAULT_PORT1; /* 0x388 */72static int irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10,11 */73static int mpu_irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10 */74static int dma1 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */75#if defined(CS4231) || defined(OPTi93X)76static int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */77#endif /* CS4231 || OPTi93X */7879module_param(index, int, 0444);80MODULE_PARM_DESC(index, "Index value for opti9xx based soundcard.");81module_param(id, charp, 0444);82MODULE_PARM_DESC(id, "ID string for opti9xx based soundcard.");83//module_param(enable, bool, 0444);84//MODULE_PARM_DESC(enable, "Enable opti9xx soundcard.");85#ifdef CONFIG_PNP86module_param(isapnp, bool, 0444);87MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");88#endif89module_param(port, long, 0444);90MODULE_PARM_DESC(port, "WSS port # for opti9xx driver.");91module_param(mpu_port, long, 0444);92MODULE_PARM_DESC(mpu_port, "MPU-401 port # for opti9xx driver.");93module_param(fm_port, long, 0444);94MODULE_PARM_DESC(fm_port, "FM port # for opti9xx driver.");95module_param(irq, int, 0444);96MODULE_PARM_DESC(irq, "WSS irq # for opti9xx driver.");97module_param(mpu_irq, int, 0444);98MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for opti9xx driver.");99module_param(dma1, int, 0444);100MODULE_PARM_DESC(dma1, "1st dma # for opti9xx driver.");101#if defined(CS4231) || defined(OPTi93X)102module_param(dma2, int, 0444);103MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver.");104#endif /* CS4231 || OPTi93X */105106#define OPTi9XX_HW_82C928 1107#define OPTi9XX_HW_82C929 2108#define OPTi9XX_HW_82C924 3109#define OPTi9XX_HW_82C925 4110#define OPTi9XX_HW_82C930 5111#define OPTi9XX_HW_82C931 6112#define OPTi9XX_HW_82C933 7113#define OPTi9XX_HW_LAST OPTi9XX_HW_82C933114115#define OPTi9XX_MC_REG(n) n116117#ifdef OPTi93X118119#define OPTi93X_STATUS 0x02120#define OPTi93X_PORT(chip, r) ((chip)->port + OPTi93X_##r)121122#define OPTi93X_IRQ_PLAYBACK 0x04123#define OPTi93X_IRQ_CAPTURE 0x08124125#endif /* OPTi93X */126127struct snd_opti9xx {128unsigned short hardware;129unsigned char password;130char name[7];131132unsigned long mc_base;133struct resource *res_mc_base;134unsigned long mc_base_size;135#ifdef OPTi93X136unsigned long mc_indir_index;137unsigned long mc_indir_size;138struct resource *res_mc_indir;139struct snd_wss *codec;140#endif /* OPTi93X */141unsigned long pwd_reg;142143spinlock_t lock;144145long wss_base;146int irq;147};148149static int snd_opti9xx_pnp_is_probed;150151#ifdef CONFIG_PNP152153static struct pnp_card_device_id snd_opti9xx_pnpids[] = {154#ifndef OPTi93X155/* OPTi 82C924 */156{ .id = "OPT0924",157.devs = { { "OPT0000" }, { "OPT0002" }, { "OPT0005" } },158.driver_data = 0x0924 },159/* OPTi 82C925 */160{ .id = "OPT0925",161.devs = { { "OPT9250" }, { "OPT0002" }, { "OPT0005" } },162.driver_data = 0x0925 },163#else164/* OPTi 82C931/3 */165{ .id = "OPT0931", .devs = { { "OPT9310" }, { "OPT0002" } },166.driver_data = 0x0931 },167#endif /* OPTi93X */168{ .id = "" }169};170171MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids);172173#endif /* CONFIG_PNP */174175#ifdef OPTi93X176#define DEV_NAME "opti93x"177#else178#define DEV_NAME "opti92x"179#endif180181static char * snd_opti9xx_names[] = {182"unknown",183"82C928", "82C929",184"82C924", "82C925",185"82C930", "82C931", "82C933"186};187188189static long __devinit snd_legacy_find_free_ioport(long *port_table, long size)190{191while (*port_table != -1) {192if (request_region(*port_table, size, "ALSA test")) {193release_region(*port_table, size);194return *port_table;195}196port_table++;197}198return -1;199}200201static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip,202unsigned short hardware)203{204static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};205206chip->hardware = hardware;207strcpy(chip->name, snd_opti9xx_names[hardware]);208209spin_lock_init(&chip->lock);210211chip->irq = -1;212213#ifndef OPTi93X214#ifdef CONFIG_PNP215if (isapnp && chip->mc_base)216/* PnP resource gives the least 10 bits */217chip->mc_base |= 0xc00;218else219#endif /* CONFIG_PNP */220{221chip->mc_base = 0xf8c;222chip->mc_base_size = opti9xx_mc_size[hardware];223}224#else225chip->mc_base_size = opti9xx_mc_size[hardware];226#endif227228switch (hardware) {229#ifndef OPTi93X230case OPTi9XX_HW_82C928:231case OPTi9XX_HW_82C929:232chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3;233chip->pwd_reg = 3;234break;235236case OPTi9XX_HW_82C924:237case OPTi9XX_HW_82C925:238chip->password = 0xe5;239chip->pwd_reg = 3;240break;241#else /* OPTi93X */242243case OPTi9XX_HW_82C930:244case OPTi9XX_HW_82C931:245case OPTi9XX_HW_82C933:246chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d;247if (!chip->mc_indir_index) {248chip->mc_indir_index = 0xe0e;249chip->mc_indir_size = 2;250}251chip->password = 0xe4;252chip->pwd_reg = 0;253break;254#endif /* OPTi93X */255256default:257snd_printk(KERN_ERR "chip %d not supported\n", hardware);258return -ENODEV;259}260return 0;261}262263static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip,264unsigned char reg)265{266unsigned long flags;267unsigned char retval = 0xff;268269spin_lock_irqsave(&chip->lock, flags);270outb(chip->password, chip->mc_base + chip->pwd_reg);271272switch (chip->hardware) {273#ifndef OPTi93X274case OPTi9XX_HW_82C924:275case OPTi9XX_HW_82C925:276if (reg > 7) {277outb(reg, chip->mc_base + 8);278outb(chip->password, chip->mc_base + chip->pwd_reg);279retval = inb(chip->mc_base + 9);280break;281}282283case OPTi9XX_HW_82C928:284case OPTi9XX_HW_82C929:285retval = inb(chip->mc_base + reg);286break;287#else /* OPTi93X */288289case OPTi9XX_HW_82C930:290case OPTi9XX_HW_82C931:291case OPTi9XX_HW_82C933:292outb(reg, chip->mc_indir_index);293outb(chip->password, chip->mc_base + chip->pwd_reg);294retval = inb(chip->mc_indir_index + 1);295break;296#endif /* OPTi93X */297298default:299snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);300}301302spin_unlock_irqrestore(&chip->lock, flags);303return retval;304}305306static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,307unsigned char value)308{309unsigned long flags;310311spin_lock_irqsave(&chip->lock, flags);312outb(chip->password, chip->mc_base + chip->pwd_reg);313314switch (chip->hardware) {315#ifndef OPTi93X316case OPTi9XX_HW_82C924:317case OPTi9XX_HW_82C925:318if (reg > 7) {319outb(reg, chip->mc_base + 8);320outb(chip->password, chip->mc_base + chip->pwd_reg);321outb(value, chip->mc_base + 9);322break;323}324325case OPTi9XX_HW_82C928:326case OPTi9XX_HW_82C929:327outb(value, chip->mc_base + reg);328break;329#else /* OPTi93X */330331case OPTi9XX_HW_82C930:332case OPTi9XX_HW_82C931:333case OPTi9XX_HW_82C933:334outb(reg, chip->mc_indir_index);335outb(chip->password, chip->mc_base + chip->pwd_reg);336outb(value, chip->mc_indir_index + 1);337break;338#endif /* OPTi93X */339340default:341snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);342}343344spin_unlock_irqrestore(&chip->lock, flags);345}346347348#define snd_opti9xx_write_mask(chip, reg, value, mask) \349snd_opti9xx_write(chip, reg, \350(snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask)))351352353static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip,354long port,355int irq, int dma1, int dma2,356long mpu_port, int mpu_irq)357{358unsigned char wss_base_bits;359unsigned char irq_bits;360unsigned char dma_bits;361unsigned char mpu_port_bits = 0;362unsigned char mpu_irq_bits;363364switch (chip->hardware) {365#ifndef OPTi93X366case OPTi9XX_HW_82C924:367/* opti 929 mode (?), OPL3 clock output, audio enable */368snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc);369/* enable wave audio */370snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);371372case OPTi9XX_HW_82C925:373/* enable WSS mode */374snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);375/* OPL3 FM synthesis */376snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);377/* disable Sound Blaster IRQ and DMA */378snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);379#ifdef CS4231380/* cs4231/4248 fix enabled */381snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);382#else383/* cs4231/4248 fix disabled */384snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);385#endif /* CS4231 */386break;387388case OPTi9XX_HW_82C928:389case OPTi9XX_HW_82C929:390snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);391snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);392/*393snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae);394*/395snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);396#ifdef CS4231397snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);398#else399snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);400#endif /* CS4231 */401break;402403#else /* OPTi93X */404case OPTi9XX_HW_82C931:405case OPTi9XX_HW_82C933:406/*407* The BTC 1817DW has QS1000 wavetable which is connected408* to the serial digital input of the OPTI931.409*/410snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(21), 0x82, 0xff);411/*412* This bit sets OPTI931 to automaticaly select FM413* or digital input signal.414*/415snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(26), 0x01, 0x01);416case OPTi9XX_HW_82C930: /* FALL THROUGH */417snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03);418snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff);419snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 |420(chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04),4210x34);422snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf);423break;424#endif /* OPTi93X */425426default:427snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);428return -EINVAL;429}430431/* PnP resource says it decodes only 10 bits of address */432switch (port & 0x3ff) {433case 0x130:434chip->wss_base = 0x530;435wss_base_bits = 0x00;436break;437case 0x204:438chip->wss_base = 0x604;439wss_base_bits = 0x03;440break;441case 0x280:442chip->wss_base = 0xe80;443wss_base_bits = 0x01;444break;445case 0x340:446chip->wss_base = 0xf40;447wss_base_bits = 0x02;448break;449default:450snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n", port);451goto __skip_base;452}453snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);454455__skip_base:456switch (irq) {457//#ifdef OPTi93X458case 5:459irq_bits = 0x05;460break;461//#endif /* OPTi93X */462case 7:463irq_bits = 0x01;464break;465case 9:466irq_bits = 0x02;467break;468case 10:469irq_bits = 0x03;470break;471case 11:472irq_bits = 0x04;473break;474default:475snd_printk(KERN_WARNING "WSS irq # %d not valid\n", irq);476goto __skip_resources;477}478479switch (dma1) {480case 0:481dma_bits = 0x01;482break;483case 1:484dma_bits = 0x02;485break;486case 3:487dma_bits = 0x03;488break;489default:490snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n", dma1);491goto __skip_resources;492}493494#if defined(CS4231) || defined(OPTi93X)495if (dma1 == dma2) {496snd_printk(KERN_ERR "don't want to share dmas\n");497return -EBUSY;498}499500switch (dma2) {501case 0:502case 1:503break;504default:505snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n", dma2);506goto __skip_resources;507}508dma_bits |= 0x04;509#endif /* CS4231 || OPTi93X */510511#ifndef OPTi93X512outb(irq_bits << 3 | dma_bits, chip->wss_base);513#else /* OPTi93X */514snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits));515#endif /* OPTi93X */516517__skip_resources:518if (chip->hardware > OPTi9XX_HW_82C928) {519switch (mpu_port) {520case 0:521case -1:522break;523case 0x300:524mpu_port_bits = 0x03;525break;526case 0x310:527mpu_port_bits = 0x02;528break;529case 0x320:530mpu_port_bits = 0x01;531break;532case 0x330:533mpu_port_bits = 0x00;534break;535default:536snd_printk(KERN_WARNING537"MPU-401 port 0x%lx not valid\n", mpu_port);538goto __skip_mpu;539}540541switch (mpu_irq) {542case 5:543mpu_irq_bits = 0x02;544break;545case 7:546mpu_irq_bits = 0x03;547break;548case 9:549mpu_irq_bits = 0x00;550break;551case 10:552mpu_irq_bits = 0x01;553break;554default:555snd_printk(KERN_WARNING "MPU-401 irq # %d not valid\n",556mpu_irq);557goto __skip_mpu;558}559560snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6),561(mpu_port <= 0) ? 0x00 :5620x80 | mpu_port_bits << 5 | mpu_irq_bits << 3,5630xf8);564}565__skip_mpu:566567return 0;568}569570#ifdef OPTi93X571572static const DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_step, -9300, 300, 0);573static const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);574static const DECLARE_TLV_DB_SCALE(db_scale_4bit_12db_max, -3300, 300, 0);575576static struct snd_kcontrol_new snd_opti93x_controls[] = {577WSS_DOUBLE("Master Playback Switch", 0,578OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),579WSS_DOUBLE_TLV("Master Playback Volume", 0,580OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1,581db_scale_5bit_3db_step),582WSS_DOUBLE_TLV("PCM Playback Volume", 0,583CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 31, 1,584db_scale_5bit),585WSS_DOUBLE_TLV("FM Playback Volume", 0,586CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 1, 1, 15, 1,587db_scale_4bit_12db_max),588WSS_DOUBLE("Line Playback Switch", 0,589CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),590WSS_DOUBLE_TLV("Line Playback Volume", 0,591CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1,592db_scale_4bit_12db_max),593WSS_DOUBLE("Mic Playback Switch", 0,594OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1),595WSS_DOUBLE_TLV("Mic Playback Volume", 0,596OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1,597db_scale_4bit_12db_max),598WSS_DOUBLE_TLV("CD Playback Volume", 0,599CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 1, 1, 15, 1,600db_scale_4bit_12db_max),601WSS_DOUBLE("Aux Playback Switch", 0,602OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1),603WSS_DOUBLE_TLV("Aux Playback Volume", 0,604OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1,605db_scale_4bit_12db_max),606};607608static int __devinit snd_opti93x_mixer(struct snd_wss *chip)609{610struct snd_card *card;611unsigned int idx;612struct snd_ctl_elem_id id1, id2;613int err;614615if (snd_BUG_ON(!chip || !chip->pcm))616return -EINVAL;617618card = chip->card;619620strcpy(card->mixername, chip->pcm->name);621622memset(&id1, 0, sizeof(id1));623memset(&id2, 0, sizeof(id2));624id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;625/* reassign AUX0 switch to CD */626strcpy(id1.name, "Aux Playback Switch");627strcpy(id2.name, "CD Playback Switch");628err = snd_ctl_rename_id(card, &id1, &id2);629if (err < 0) {630snd_printk(KERN_ERR "Cannot rename opti93x control\n");631return err;632}633/* reassign AUX1 switch to FM */634strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;635strcpy(id2.name, "FM Playback Switch");636err = snd_ctl_rename_id(card, &id1, &id2);637if (err < 0) {638snd_printk(KERN_ERR "Cannot rename opti93x control\n");639return err;640}641/* remove AUX1 volume */642strcpy(id1.name, "Aux Playback Volume"); id1.index = 1;643snd_ctl_remove_id(card, &id1);644645/* Replace WSS volume controls with OPTi93x volume controls */646id1.index = 0;647for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) {648strcpy(id1.name, snd_opti93x_controls[idx].name);649snd_ctl_remove_id(card, &id1);650651err = snd_ctl_add(card,652snd_ctl_new1(&snd_opti93x_controls[idx], chip));653if (err < 0)654return err;655}656return 0;657}658659static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)660{661struct snd_opti9xx *chip = dev_id;662struct snd_wss *codec = chip->codec;663unsigned char status;664665if (!codec)666return IRQ_HANDLED;667668status = snd_opti9xx_read(chip, OPTi9XX_MC_REG(11));669if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream)670snd_pcm_period_elapsed(codec->playback_substream);671if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) {672snd_wss_overrange(codec);673snd_pcm_period_elapsed(codec->capture_substream);674}675outb(0x00, OPTi93X_PORT(codec, STATUS));676return IRQ_HANDLED;677}678679#endif /* OPTi93X */680681static int __devinit snd_opti9xx_read_check(struct snd_opti9xx *chip)682{683unsigned char value;684#ifdef OPTi93X685unsigned long flags;686#endif687688chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size,689"OPTi9xx MC");690if (chip->res_mc_base == NULL)691return -EBUSY;692#ifndef OPTi93X693value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1));694if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1)))695if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)))696return 0;697#else /* OPTi93X */698chip->res_mc_indir = request_region(chip->mc_indir_index,699chip->mc_indir_size,700"OPTi93x MC");701if (chip->res_mc_indir == NULL)702return -EBUSY;703704spin_lock_irqsave(&chip->lock, flags);705outb(chip->password, chip->mc_base + chip->pwd_reg);706outb(((chip->mc_indir_index & 0x1f0) >> 4), chip->mc_base);707spin_unlock_irqrestore(&chip->lock, flags);708709value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7));710snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value);711if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value)712return 0;713714release_and_free_resource(chip->res_mc_indir);715chip->res_mc_indir = NULL;716#endif /* OPTi93X */717release_and_free_resource(chip->res_mc_base);718chip->res_mc_base = NULL;719720return -ENODEV;721}722723static int __devinit snd_card_opti9xx_detect(struct snd_card *card,724struct snd_opti9xx *chip)725{726int i, err;727728#ifndef OPTi93X729for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) {730#else731for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) {732#endif733err = snd_opti9xx_init(chip, i);734if (err < 0)735return err;736737err = snd_opti9xx_read_check(chip);738if (err == 0)739return 1;740#ifdef OPTi93X741chip->mc_indir_index = 0;742#endif743}744return -ENODEV;745}746747#ifdef CONFIG_PNP748static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,749struct pnp_card_link *card,750const struct pnp_card_device_id *pid)751{752struct pnp_dev *pdev;753int err;754struct pnp_dev *devmpu;755#ifndef OPTi93X756struct pnp_dev *devmc;757#endif758759pdev = pnp_request_card_device(card, pid->devs[0].id, NULL);760if (pdev == NULL)761return -EBUSY;762763err = pnp_activate_dev(pdev);764if (err < 0) {765snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);766return err;767}768769#ifdef OPTi93X770port = pnp_port_start(pdev, 0) - 4;771fm_port = pnp_port_start(pdev, 1) + 8;772chip->mc_indir_index = pnp_port_start(pdev, 3) + 2;773chip->mc_indir_size = pnp_port_len(pdev, 3) - 2;774#else775devmc = pnp_request_card_device(card, pid->devs[2].id, NULL);776if (devmc == NULL)777return -EBUSY;778779err = pnp_activate_dev(devmc);780if (err < 0) {781snd_printk(KERN_ERR "MC pnp configure failure: %d\n", err);782return err;783}784785port = pnp_port_start(pdev, 1);786fm_port = pnp_port_start(pdev, 2) + 8;787/*788* The MC(0) is never accessed and card does not789* include it in the PnP resource range. OPTI93x include it.790*/791chip->mc_base = pnp_port_start(devmc, 0) - 1;792chip->mc_base_size = pnp_port_len(devmc, 0) + 1;793#endif /* OPTi93X */794irq = pnp_irq(pdev, 0);795dma1 = pnp_dma(pdev, 0);796#if defined(CS4231) || defined(OPTi93X)797dma2 = pnp_dma(pdev, 1);798#endif /* CS4231 || OPTi93X */799800devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);801802if (devmpu && mpu_port > 0) {803err = pnp_activate_dev(devmpu);804if (err < 0) {805snd_printk(KERN_ERR "MPU401 pnp configure failure\n");806mpu_port = -1;807} else {808mpu_port = pnp_port_start(devmpu, 0);809mpu_irq = pnp_irq(devmpu, 0);810}811}812return pid->driver_data;813}814#endif /* CONFIG_PNP */815816static void snd_card_opti9xx_free(struct snd_card *card)817{818struct snd_opti9xx *chip = card->private_data;819820if (chip) {821#ifdef OPTi93X822if (chip->irq > 0) {823disable_irq(chip->irq);824free_irq(chip->irq, chip);825}826release_and_free_resource(chip->res_mc_indir);827#endif828release_and_free_resource(chip->res_mc_base);829}830}831832static int __devinit snd_opti9xx_probe(struct snd_card *card)833{834static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};835int error;836int xdma2;837struct snd_opti9xx *chip = card->private_data;838struct snd_wss *codec;839#ifdef CS4231840struct snd_timer *timer;841#endif842struct snd_pcm *pcm;843struct snd_rawmidi *rmidi;844struct snd_hwdep *synth;845846#if defined(CS4231) || defined(OPTi93X)847xdma2 = dma2;848#else849xdma2 = -1;850#endif851852if (port == SNDRV_AUTO_PORT) {853port = snd_legacy_find_free_ioport(possible_ports, 4);854if (port < 0) {855snd_printk(KERN_ERR "unable to find a free WSS port\n");856return -EBUSY;857}858}859error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2,860mpu_port, mpu_irq);861if (error)862return error;863864error = snd_wss_create(card, chip->wss_base + 4, -1, irq, dma1, xdma2,865#ifdef OPTi93X866WSS_HW_OPTI93X, WSS_HWSHARE_IRQ,867#else868WSS_HW_DETECT, 0,869#endif870&codec);871if (error < 0)872return error;873#ifdef OPTi93X874chip->codec = codec;875#endif876error = snd_wss_pcm(codec, 0, &pcm);877if (error < 0)878return error;879error = snd_wss_mixer(codec);880if (error < 0)881return error;882#ifdef OPTi93X883error = snd_opti93x_mixer(codec);884if (error < 0)885return error;886#endif887#ifdef CS4231888error = snd_wss_timer(codec, 0, &timer);889if (error < 0)890return error;891#endif892#ifdef OPTi93X893error = request_irq(irq, snd_opti93x_interrupt,894IRQF_DISABLED, DEV_NAME" - WSS", chip);895if (error < 0) {896snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", irq);897return error;898}899#endif900chip->irq = irq;901strcpy(card->driver, chip->name);902sprintf(card->shortname, "OPTi %s", card->driver);903#if defined(CS4231) || defined(OPTi93X)904sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d",905card->shortname, pcm->name,906chip->wss_base + 4, irq, dma1, xdma2);907#else908sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d",909card->shortname, pcm->name, chip->wss_base + 4, irq, dma1);910#endif /* CS4231 || OPTi93X */911912if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT)913rmidi = NULL;914else {915error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,916mpu_port, 0, mpu_irq, IRQF_DISABLED, &rmidi);917if (error)918snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",919mpu_port);920}921922if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {923struct snd_opl3 *opl3 = NULL;924#ifndef OPTi93X925if (chip->hardware == OPTi9XX_HW_82C928 ||926chip->hardware == OPTi9XX_HW_82C929 ||927chip->hardware == OPTi9XX_HW_82C924) {928struct snd_opl4 *opl4;929/* assume we have an OPL4 */930snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),9310x20, 0x20);932if (snd_opl4_create(card, fm_port, fm_port - 8,9332, &opl3, &opl4) < 0) {934/* no luck, use OPL3 instead */935snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),9360x00, 0x20);937}938}939#endif /* !OPTi93X */940if (!opl3 && snd_opl3_create(card, fm_port, fm_port + 2,941OPL3_HW_AUTO, 0, &opl3) < 0) {942snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n",943fm_port, fm_port + 4 - 1);944}945if (opl3) {946error = snd_opl3_hwdep_new(opl3, 0, 1, &synth);947if (error < 0)948return error;949}950}951952return snd_card_register(card);953}954955static int snd_opti9xx_card_new(struct snd_card **cardp)956{957struct snd_card *card;958int err;959960err = snd_card_create(index, id, THIS_MODULE,961sizeof(struct snd_opti9xx), &card);962if (err < 0)963return err;964card->private_free = snd_card_opti9xx_free;965*cardp = card;966return 0;967}968969static int __devinit snd_opti9xx_isa_match(struct device *devptr,970unsigned int dev)971{972#ifdef CONFIG_PNP973if (snd_opti9xx_pnp_is_probed)974return 0;975if (isapnp)976return 0;977#endif978return 1;979}980981static int __devinit snd_opti9xx_isa_probe(struct device *devptr,982unsigned int dev)983{984struct snd_card *card;985int error;986static long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1};987#ifdef OPTi93X988static int possible_irqs[] = {5, 9, 10, 11, 7, -1};989#else990static int possible_irqs[] = {9, 10, 11, 7, -1};991#endif /* OPTi93X */992static int possible_mpu_irqs[] = {5, 9, 10, 7, -1};993static int possible_dma1s[] = {3, 1, 0, -1};994#if defined(CS4231) || defined(OPTi93X)995static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};996#endif /* CS4231 || OPTi93X */997998if (mpu_port == SNDRV_AUTO_PORT) {999if ((mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) {1000snd_printk(KERN_ERR "unable to find a free MPU401 port\n");1001return -EBUSY;1002}1003}1004if (irq == SNDRV_AUTO_IRQ) {1005if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {1006snd_printk(KERN_ERR "unable to find a free IRQ\n");1007return -EBUSY;1008}1009}1010if (mpu_irq == SNDRV_AUTO_IRQ) {1011if ((mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) {1012snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n");1013return -EBUSY;1014}1015}1016if (dma1 == SNDRV_AUTO_DMA) {1017if ((dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) {1018snd_printk(KERN_ERR "unable to find a free DMA1\n");1019return -EBUSY;1020}1021}1022#if defined(CS4231) || defined(OPTi93X)1023if (dma2 == SNDRV_AUTO_DMA) {1024if ((dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4])) < 0) {1025snd_printk(KERN_ERR "unable to find a free DMA2\n");1026return -EBUSY;1027}1028}1029#endif10301031error = snd_opti9xx_card_new(&card);1032if (error < 0)1033return error;10341035if ((error = snd_card_opti9xx_detect(card, card->private_data)) < 0) {1036snd_card_free(card);1037return error;1038}1039snd_card_set_dev(card, devptr);1040if ((error = snd_opti9xx_probe(card)) < 0) {1041snd_card_free(card);1042return error;1043}1044dev_set_drvdata(devptr, card);1045return 0;1046}10471048static int __devexit snd_opti9xx_isa_remove(struct device *devptr,1049unsigned int dev)1050{1051snd_card_free(dev_get_drvdata(devptr));1052dev_set_drvdata(devptr, NULL);1053return 0;1054}10551056static struct isa_driver snd_opti9xx_driver = {1057.match = snd_opti9xx_isa_match,1058.probe = snd_opti9xx_isa_probe,1059.remove = __devexit_p(snd_opti9xx_isa_remove),1060/* FIXME: suspend/resume */1061.driver = {1062.name = DEV_NAME1063},1064};10651066#ifdef CONFIG_PNP1067static int __devinit snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,1068const struct pnp_card_device_id *pid)1069{1070struct snd_card *card;1071int error, hw;1072struct snd_opti9xx *chip;10731074if (snd_opti9xx_pnp_is_probed)1075return -EBUSY;1076if (! isapnp)1077return -ENODEV;1078error = snd_opti9xx_card_new(&card);1079if (error < 0)1080return error;1081chip = card->private_data;10821083hw = snd_card_opti9xx_pnp(chip, pcard, pid);1084switch (hw) {1085case 0x0924:1086hw = OPTi9XX_HW_82C924;1087break;1088case 0x0925:1089hw = OPTi9XX_HW_82C925;1090break;1091case 0x0931:1092hw = OPTi9XX_HW_82C931;1093break;1094default:1095snd_card_free(card);1096return -ENODEV;1097}10981099if ((error = snd_opti9xx_init(chip, hw))) {1100snd_card_free(card);1101return error;1102}1103error = snd_opti9xx_read_check(chip);1104if (error) {1105snd_printk(KERN_ERR "OPTI chip not found\n");1106snd_card_free(card);1107return error;1108}1109snd_card_set_dev(card, &pcard->card->dev);1110if ((error = snd_opti9xx_probe(card)) < 0) {1111snd_card_free(card);1112return error;1113}1114pnp_set_card_drvdata(pcard, card);1115snd_opti9xx_pnp_is_probed = 1;1116return 0;1117}11181119static void __devexit snd_opti9xx_pnp_remove(struct pnp_card_link * pcard)1120{1121snd_card_free(pnp_get_card_drvdata(pcard));1122pnp_set_card_drvdata(pcard, NULL);1123snd_opti9xx_pnp_is_probed = 0;1124}11251126static struct pnp_card_driver opti9xx_pnpc_driver = {1127.flags = PNP_DRIVER_RES_DISABLE,1128.name = "opti9xx",1129.id_table = snd_opti9xx_pnpids,1130.probe = snd_opti9xx_pnp_probe,1131.remove = __devexit_p(snd_opti9xx_pnp_remove),1132};1133#endif11341135#ifdef OPTi93X1136#define CHIP_NAME "82C93x"1137#else1138#define CHIP_NAME "82C92x"1139#endif11401141static int __init alsa_card_opti9xx_init(void)1142{1143#ifdef CONFIG_PNP1144pnp_register_card_driver(&opti9xx_pnpc_driver);1145if (snd_opti9xx_pnp_is_probed)1146return 0;1147pnp_unregister_card_driver(&opti9xx_pnpc_driver);1148#endif1149return isa_register_driver(&snd_opti9xx_driver, 1);1150}11511152static void __exit alsa_card_opti9xx_exit(void)1153{1154if (!snd_opti9xx_pnp_is_probed) {1155isa_unregister_driver(&snd_opti9xx_driver);1156return;1157}1158#ifdef CONFIG_PNP1159pnp_unregister_card_driver(&opti9xx_pnpc_driver);1160#endif1161}11621163module_init(alsa_card_opti9xx_init)1164module_exit(alsa_card_opti9xx_exit)116511661167