diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/oss/maestro.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'sound/oss/maestro.c')
-rw-r--r-- | sound/oss/maestro.c | 3832 |
1 files changed, 3832 insertions, 0 deletions
diff --git a/sound/oss/maestro.c b/sound/oss/maestro.c new file mode 100644 index 00000000000..52d2db4bc31 --- /dev/null +++ b/sound/oss/maestro.c @@ -0,0 +1,3832 @@ +/***************************************************************************** + * + * ESS Maestro/Maestro-2/Maestro-2E driver for Linux 2.[23].x + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * (c) Copyright 1999 Alan Cox <alan.cox@linux.org> + * + * Based heavily on SonicVibes.c: + * Copyright (C) 1998-1999 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * Heavily modified by Zach Brown <zab@zabbo.net> based on lunch + * with ESS engineers. Many thanks to Howard Kim for providing + * contacts and hardware. Honorable mention goes to Eric + * Brombaugh for all sorts of things. Best regards to the + * proprietors of Hack Central for fine lodging. + * + * Supported devices: + * /dev/dsp0-3 standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * + * Hardware Description + * + * A working Maestro setup contains the Maestro chip wired to a + * codec or 2. In the Maestro we have the APUs, the ASSP, and the + * Wavecache. The APUs can be though of as virtual audio routing + * channels. They can take data from a number of sources and perform + * basic encodings of the data. The wavecache is a storehouse for + * PCM data. Typically it deals with PCI and interracts with the + * APUs. The ASSP is a wacky DSP like device that ESS is loth + * to release docs on. Thankfully it isn't required on the Maestro + * until you start doing insane things like FM emulation and surround + * encoding. The codecs are almost always AC-97 compliant codecs, + * but it appears that early Maestros may have had PT101 (an ESS + * part?) wired to them. The only real difference in the Maestro + * families is external goop like docking capability, memory for + * the ASSP, and initialization differences. + * + * Driver Operation + * + * We only drive the APU/Wavecache as typical DACs and drive the + * mixers in the codecs. There are 64 APUs. We assign 6 to each + * /dev/dsp? device. 2 channels for output, and 4 channels for + * input. + * + * Each APU can do a number of things, but we only really use + * 3 basic functions. For playback we use them to convert PCM + * data fetched over PCI by the wavecahche into analog data that + * is handed to the codec. One APU for mono, and a pair for stereo. + * When in stereo, the combination of smarts in the APU and Wavecache + * decide which wavecache gets the left or right channel. + * + * For record we still use the old overly mono system. For each in + * coming channel the data comes in from the codec, through a 'input' + * APU, through another rate converter APU, and then into memory via + * the wavecache and PCI. If its stereo, we mash it back into LRLR in + * software. The pass between the 2 APUs is supposedly what requires us + * to have a 512 byte buffer sitting around in wavecache/memory. + * + * The wavecache makes our life even more fun. First off, it can + * only address the first 28 bits of PCI address space, making it + * useless on quite a few architectures. Secondly, its insane. + * It claims to fetch from 4 regions of PCI space, each 4 meg in length. + * But that doesn't really work. You can only use 1 region. So all our + * allocations have to be in 4meg of each other. Booo. Hiss. + * So we have a module parameter, dsps_order, that is the order of + * the number of dsps to provide. All their buffer space is allocated + * on open time. The sonicvibes OSS routines we inherited really want + * power of 2 buffers, so we have all those next to each other, then + * 512 byte regions for the recording wavecaches. This ends up + * wasting quite a bit of memory. The only fixes I can see would be + * getting a kernel allocator that could work in zones, or figuring out + * just how to coerce the WP into doing what we want. + * + * The indirection of the various registers means we have to spinlock + * nearly all register accesses. We have the main register indirection + * like the wave cache, maestro registers, etc. Then we have beasts + * like the APU interface that is indirect registers gotten at through + * the main maestro indirection. Ouch. We spinlock around the actual + * ports on a per card basis. This means spinlock activity at each IO + * operation, but the only IO operation clusters are in non critical + * paths and it makes the code far easier to follow. Interrupts are + * blocked while holding the locks because the int handler has to + * get at some of them :(. The mixer interface doesn't, however. + * We also have an OSS state lock that is thrown around in a few + * places. + * + * This driver has brute force APM suspend support. We catch suspend + * notifications and stop all work being done on the chip. Any people + * that try between this shutdown and the real suspend operation will + * be put to sleep. When we resume we restore our software state on + * the chip and wake up the people that were using it. The code thats + * being used now is quite dirty and assumes we're on a uni-processor + * machine. Much of it will need to be cleaned up for SMP ACPI or + * similar. + * + * We also pay attention to PCI power management now. The driver + * will power down units of the chip that it knows aren't needed. + * The WaveProcessor and company are only powered on when people + * have /dev/dsp*s open. On removal the driver will + * power down the maestro entirely. There could still be + * trouble with BIOSen that magically change power states + * themselves, but we'll see. + * + * History + * v0.15 - May 21 2001 - Marcus Meissner <mm@caldera.de> + * Ported to Linux 2.4 PCI API. Some clean ups, global devs list + * removed (now using pci device driver data). + * PM needs to be polished still. Bumped version. + * (still kind of v0.14) May 13 2001 - Ben Pfaff <pfaffben@msu.edu> + * Add support for 978 docking and basic hardware volume control + * (still kind of v0.14) Nov 23 - Alan Cox <alan@redhat.com> + * Add clocking= for people with seriously warped hardware + * (still v0.14) Nov 10 2000 - Bartlomiej Zolnierkiewicz <bkz@linux-ide.org> + * add __init to maestro_ac97_init() and maestro_install() + * (still based on v0.14) Mar 29 2000 - Zach Brown <zab@redhat.com> + * move to 2.3 power management interface, which + * required hacking some suspend/resume/check paths + * make static compilation work + * v0.14 - Jan 28 2000 - Zach Brown <zab@redhat.com> + * add PCI power management through ACPI regs. + * we now shut down on machine reboot/halt + * leave scary PCI config items alone (isa stuff, mostly) + * enable 1921s, it seems only mine was broke. + * fix swapped left/right pcm dac. har har. + * up bob freq, increase buffers, fix pointers at underflow + * silly compilation problems + * v0.13 - Nov 18 1999 - Zach Brown <zab@redhat.com> + * fix nec Versas? man would that be cool. + * v0.12 - Nov 12 1999 - Zach Brown <zab@redhat.com> + * brown bag volume max fix.. + * v0.11 - Nov 11 1999 - Zach Brown <zab@redhat.com> + * use proper stereo apu decoding, mmap/write should work. + * make volume sliders more useful, tweak rate calculation. + * fix lame 8bit format reporting bug. duh. apm apu saving buglet also + * fix maestro 1 clock freq "bug", remove pt101 support + * v0.10 - Oct 28 1999 - Zach Brown <zab@redhat.com> + * aha, so, sometimes the WP writes a status word to offset 0 + * from one of the PCMBARs. rearrange allocation accordingly.. + * cheers again to Eric for being a good hacker in investigating this. + * Jeroen Hoogervorst submits 7500 fix out of nowhere. yay. :) + * v0.09 - Oct 23 1999 - Zach Brown <zab@redhat.com> + * added APM support. + * re-order something such that some 2Es now work. Magic! + * new codec reset routine. made some codecs come to life. + * fix clear_advance, sync some control with ESS. + * now write to all base regs to be paranoid. + * v0.08 - Oct 20 1999 - Zach Brown <zab@redhat.com> + * Fix initial buflen bug. I am so smart. also smp compiling.. + * I owe Eric yet another beer: fixed recmask, igain, + * muting, and adc sync consistency. Go Team. + * v0.07 - Oct 4 1999 - Zach Brown <zab@redhat.com> + * tweak adc/dac, formating, and stuff to allow full duplex + * allocate dsps memory at open() so we can fit in the wavecache window + * fix wavecache braindamage. again. no more scribbling? + * fix ess 1921 codec bug on some laptops. + * fix dumb pci scanning bug + * started 2.3 cleanup, redid spinlocks, little cleanups + * v0.06 - Sep 20 1999 - Zach Brown <zab@redhat.com> + * fix wavecache thinkos. limit to 1 /dev/dsp. + * eric is wearing his thinking toque this week. + * spotted apu mode bugs and gain ramping problem + * don't touch weird mixer regs, make recmask optional + * fixed igain inversion, defaults for mixers, clean up rec_start + * make mono recording work. + * report subsystem stuff, please send reports. + * littles: parallel out, amp now + * v0.05 - Sep 17 1999 - Zach Brown <zab@redhat.com> + * merged and fixed up Eric's initial recording code + * munged format handling to catch misuse, needs rewrite. + * revert ring bus init, fixup shared int, add pci busmaster setting + * fix mixer oss interface, fix mic mute and recmask + * mask off unsupported mixers, reset with all 1s, modularize defaults + * make sure bob is running while we need it + * got rid of device limit, initial minimal apm hooks + * pull out dead code/includes, only allow multimedia/audio maestros + * v0.04 - Sep 01 1999 - Zach Brown <zab@redhat.com> + * copied memory leak fix from sonicvibes driver + * different ac97 reset, play with 2.0 ac97, simplify ring bus setup + * bob freq code, region sanity, jitter sync fix; all from Eric + * + * TODO + * fix bob frequency + * endianness + * do smart things with ac97 2.0 bits. + * dual codecs + * leave 54->61 open + * + * it also would be fun to have a mode that would not use pci dma at all + * but would copy into the wavecache on board memory and use that + * on architectures that don't like the maestro's pci dma ickiness. + */ + +/*****************************************************************************/ + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/smp_lock.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/sound.h> +#include <linux/slab.h> +#include <linux/soundcard.h> +#include <linux/pci.h> +#include <linux/spinlock.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/poll.h> +#include <linux/reboot.h> +#include <linux/bitops.h> +#include <linux/wait.h> + +#include <asm/current.h> +#include <asm/dma.h> +#include <asm/io.h> +#include <asm/page.h> +#include <asm/uaccess.h> + +#include <linux/pm.h> +static int maestro_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *d); + +#include "maestro.h" + +static struct pci_driver maestro_pci_driver; + +/* --------------------------------------------------------------------- */ + +#define M_DEBUG 1 + +#ifdef M_DEBUG +static int debug; +#define M_printk(args...) {if (debug) printk(args);} +#else +#define M_printk(x) +#endif + +/* we try to setup 2^(dsps_order) /dev/dsp devices */ +static int dsps_order; +/* whether or not we mess around with power management */ +static int use_pm=2; /* set to 1 for force */ +/* clocking for broken hardware - a few laptops seem to use a 50Khz clock + ie insmod with clocking=50000 or so */ + +static int clocking=48000; + +MODULE_AUTHOR("Zach Brown <zab@zabbo.net>, Alan Cox <alan@redhat.com>"); +MODULE_DESCRIPTION("ESS Maestro Driver"); +MODULE_LICENSE("GPL"); + +#ifdef M_DEBUG +module_param(debug, bool, 0644); +#endif +module_param(dsps_order, int, 0); +module_param(use_pm, int, 0); +module_param(clocking, int, 0); + +/* --------------------------------------------------------------------- */ +#define DRIVER_VERSION "0.15" + +#ifndef PCI_VENDOR_ESS +#define PCI_VENDOR_ESS 0x125D +#define PCI_DEVICE_ID_ESS_ESS1968 0x1968 /* Maestro 2 */ +#define PCI_DEVICE_ID_ESS_ESS1978 0x1978 /* Maestro 2E */ + +#define PCI_VENDOR_ESS_OLD 0x1285 /* Platform Tech, + the people the maestro + was bought from */ +#define PCI_DEVICE_ID_ESS_ESS0100 0x0100 /* maestro 1 */ +#endif /* PCI_VENDOR_ESS */ + +#define ESS_CHAN_HARD 0x100 + +/* NEC Versas ? */ +#define NEC_VERSA_SUBID1 0x80581033 +#define NEC_VERSA_SUBID2 0x803c1033 + + +/* changed so that I could actually find all the + references and fix them up. it's a little more readable now. */ +#define ESS_FMT_STEREO 0x01 +#define ESS_FMT_16BIT 0x02 +#define ESS_FMT_MASK 0x03 +#define ESS_DAC_SHIFT 0 +#define ESS_ADC_SHIFT 4 + +#define ESS_STATE_MAGIC 0x125D1968 +#define ESS_CARD_MAGIC 0x19283746 + +#define DAC_RUNNING 1 +#define ADC_RUNNING 2 + +#define MAX_DSP_ORDER 2 +#define MAX_DSPS (1<<MAX_DSP_ORDER) +#define NR_DSPS (1<<dsps_order) +#define NR_IDRS 32 + +#define NR_APUS 64 +#define NR_APU_REGS 16 + +/* acpi states */ +enum { + ACPI_D0=0, + ACPI_D1, + ACPI_D2, + ACPI_D3 +}; + +/* bits in the acpi masks */ +#define ACPI_12MHZ ( 1 << 15) +#define ACPI_24MHZ ( 1 << 14) +#define ACPI_978 ( 1 << 13) +#define ACPI_SPDIF ( 1 << 12) +#define ACPI_GLUE ( 1 << 11) +#define ACPI__10 ( 1 << 10) /* reserved */ +#define ACPI_PCIINT ( 1 << 9) +#define ACPI_HV ( 1 << 8) /* hardware volume */ +#define ACPI_GPIO ( 1 << 7) +#define ACPI_ASSP ( 1 << 6) +#define ACPI_SB ( 1 << 5) /* sb emul */ +#define ACPI_FM ( 1 << 4) /* fm emul */ +#define ACPI_RB ( 1 << 3) /* ringbus / aclink */ +#define ACPI_MIDI ( 1 << 2) +#define ACPI_GP ( 1 << 1) /* game port */ +#define ACPI_WP ( 1 << 0) /* wave processor */ + +#define ACPI_ALL (0xffff) +#define ACPI_SLEEP (~(ACPI_SPDIF|ACPI_ASSP|ACPI_SB|ACPI_FM| \ + ACPI_MIDI|ACPI_GP|ACPI_WP)) +#define ACPI_NONE (ACPI__10) + +/* these masks indicate which units we care about at + which states */ +static u16 acpi_state_mask[] = { + [ACPI_D0] = ACPI_ALL, + [ACPI_D1] = ACPI_SLEEP, + [ACPI_D2] = ACPI_SLEEP, + [ACPI_D3] = ACPI_NONE +}; + +static char version[] __devinitdata = +KERN_INFO "maestro: version " DRIVER_VERSION " time " __TIME__ " " __DATE__ "\n"; + + + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +enum card_types_t { + TYPE_MAESTRO, + TYPE_MAESTRO2, + TYPE_MAESTRO2E +}; + +static const char *card_names[]={ + [TYPE_MAESTRO] = "ESS Maestro", + [TYPE_MAESTRO2] = "ESS Maestro 2", + [TYPE_MAESTRO2E] = "ESS Maestro 2E" +}; + +static int clock_freq[]={ + [TYPE_MAESTRO] = (49152000L / 1024L), + [TYPE_MAESTRO2] = (50000000L / 1024L), + [TYPE_MAESTRO2E] = (50000000L / 1024L) +}; + +static int maestro_notifier(struct notifier_block *nb, unsigned long event, void *buf); + +static struct notifier_block maestro_nb = {maestro_notifier, NULL, 0}; + +/* --------------------------------------------------------------------- */ + +struct ess_state { + unsigned int magic; + /* FIXME: we probably want submixers in here, but only one record pair */ + u8 apu[6]; /* l/r output, l/r intput converters, l/r input apus */ + u8 apu_mode[6]; /* Running mode for this APU */ + u8 apu_pan[6]; /* Panning setup for this APU */ + u32 apu_base[6]; /* base address for this apu */ + struct ess_card *card; /* Card info */ + /* wave stuff */ + unsigned int rateadc, ratedac; + unsigned char fmt, enable; + + int index; + + /* this locks around the oss state in the driver */ + spinlock_t lock; + /* only let 1 be opening at a time */ + struct semaphore open_sem; + wait_queue_head_t open_wait; + mode_t open_mode; + + /* soundcore stuff */ + int dev_audio; + + struct dmabuf { + void *rawbuf; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + /* XXX zab - swptr only in here so that it can be referenced by + clear_advance, as far as I can tell :( */ + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; /* our oss buffers are ready to go */ + unsigned endcleared:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + u16 base; /* Offset for ptr */ + } dma_dac, dma_adc; + + /* pointer to each dsp?s piece of the apu->src buffer page */ + void *mixbuf; + +}; + +struct ess_card { + unsigned int magic; + + /* We keep maestro cards in a linked list */ + struct ess_card *next; + + int dev_mixer; + + int card_type; + + /* as most of this is static, + perhaps it should be a pointer to a global struct */ + struct mixer_goo { + int modcnt; + int supported_mixers; + int stereo_mixers; + int record_sources; + /* the caller must guarantee arg sanity before calling these */ +/* int (*read_mixer)(struct ess_card *card, int index);*/ + void (*write_mixer)(struct ess_card *card,int mixer, unsigned int left,unsigned int right); + int (*recmask_io)(struct ess_card *card,int rw,int mask); + unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; + } mix; + + int power_regs; + + int in_suspend; + wait_queue_head_t suspend_queue; + + struct ess_state channels[MAX_DSPS]; + u16 maestro_map[NR_IDRS]; /* Register map */ + /* we have to store this junk so that we can come back from a + suspend */ + u16 apu_map[NR_APUS][NR_APU_REGS]; /* contents of apu regs */ + + /* this locks around the physical registers on the card */ + spinlock_t lock; + + /* memory for this card.. wavecache limited :(*/ + void *dmapages; + int dmaorder; + + /* hardware resources */ + struct pci_dev *pcidev; + u32 iobase; + u32 irq; + + int bob_freq; + char dsps_open; + + int dock_mute_vol; +}; + +static void set_mixer(struct ess_card *card,unsigned int mixer, unsigned int val ); + +static unsigned +ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + + +/* --------------------------------------------------------------------- */ + +static void check_suspend(struct ess_card *card); + +/* --------------------------------------------------------------------- */ + + +/* + * ESS Maestro AC97 codec programming interface. + */ + +static void maestro_ac97_set(struct ess_card *card, u8 cmd, u16 val) +{ + int io = card->iobase; + int i; + /* + * Wait for the codec bus to be free + */ + + check_suspend(card); + + for(i=0;i<10000;i++) + { + if(!(inb(io+ESS_AC97_INDEX)&1)) + break; + } + /* + * Write the bus + */ + outw(val, io+ESS_AC97_DATA); + mdelay(1); + outb(cmd, io+ESS_AC97_INDEX); + mdelay(1); +} + +static u16 maestro_ac97_get(struct ess_card *card, u8 cmd) +{ + int io = card->iobase; + int sanity=10000; + u16 data; + int i; + + check_suspend(card); + /* + * Wait for the codec bus to be free + */ + + for(i=0;i<10000;i++) + { + if(!(inb(io+ESS_AC97_INDEX)&1)) + break; + } + + outb(cmd|0x80, io+ESS_AC97_INDEX); + mdelay(1); + + while(inb(io+ESS_AC97_INDEX)&1) + { + sanity--; + if(!sanity) + { + printk(KERN_ERR "maestro: ac97 codec timeout reading 0x%x.\n",cmd); + return 0; + } + } + data=inw(io+ESS_AC97_DATA); + mdelay(1); + return data; +} + +/* OSS interface to the ac97s.. */ + +#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|\ + SOUND_MASK_PCM|SOUND_MASK_LINE|SOUND_MASK_CD|\ + SOUND_MASK_VIDEO|SOUND_MASK_LINE1|SOUND_MASK_IGAIN) + +#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \ + SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_MIC|\ + SOUND_MASK_SPEAKER) + +#define AC97_RECORD_MASK (SOUND_MASK_MIC|\ + SOUND_MASK_CD| SOUND_MASK_VIDEO| SOUND_MASK_LINE1| SOUND_MASK_LINE|\ + SOUND_MASK_PHONEIN) + +#define supported_mixer(CARD,FOO) ( CARD->mix.supported_mixers & (1<<FOO) ) + +/* this table has default mixer values for all OSS mixers. + be sure to fill it in if you add oss mixers + to anyone's supported mixer defines */ + +static unsigned int mixer_defaults[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_VOLUME] = 0x3232, + [SOUND_MIXER_BASS] = 0x3232, + [SOUND_MIXER_TREBLE] = 0x3232, + [SOUND_MIXER_SPEAKER] = 0x3232, + [SOUND_MIXER_MIC] = 0x8000, /* annoying */ + [SOUND_MIXER_LINE] = 0x3232, + [SOUND_MIXER_CD] = 0x3232, + [SOUND_MIXER_VIDEO] = 0x3232, + [SOUND_MIXER_LINE1] = 0x3232, + [SOUND_MIXER_PCM] = 0x3232, + [SOUND_MIXER_IGAIN] = 0x3232 +}; + +static struct ac97_mixer_hw { + unsigned char offset; + int scale; +} ac97_hw[SOUND_MIXER_NRDEVICES]= { + [SOUND_MIXER_VOLUME] = {0x02,63}, + [SOUND_MIXER_BASS] = {0x08,15}, + [SOUND_MIXER_TREBLE] = {0x08,15}, + [SOUND_MIXER_SPEAKER] = {0x0a,15}, + [SOUND_MIXER_MIC] = {0x0e,31}, + [SOUND_MIXER_LINE] = {0x10,31}, + [SOUND_MIXER_CD] = {0x12,31}, + [SOUND_MIXER_VIDEO] = {0x14,31}, + [SOUND_MIXER_LINE1] = {0x16,31}, + [SOUND_MIXER_PCM] = {0x18,31}, + [SOUND_MIXER_IGAIN] = {0x1c,15} +}; + +#if 0 /* *shrug* removed simply because we never used it. + feel free to implement again if needed */ + +/* reads the given OSS mixer from the ac97 + the caller must have insured that the ac97 knows + about that given mixer, and should be holding a + spinlock for the card */ +static int ac97_read_mixer(struct ess_card *card, int mixer) +{ + u16 val; + int ret=0; + struct ac97_mixer_hw *mh = &ac97_hw[mixer]; + + val = maestro_ac97_get(card, mh->offset); + + if(AC97_STEREO_MASK & (1<<mixer)) { + /* nice stereo mixers .. */ + int left,right; + + left = (val >> 8) & 0x7f; + right = val & 0x7f; + + if (mixer == SOUND_MIXER_IGAIN) { + right = (right * 100) / mh->scale; + left = (left * 100) / mh->scale; + } else { + right = 100 - ((right * 100) / mh->scale); + left = 100 - ((left * 100) / mh->scale); + } + + ret = left | (right << 8); + } else if (mixer == SOUND_MIXER_SPEAKER) { + ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale); + } else if (mixer == SOUND_MIXER_MIC) { + ret = 100 - (((val & 0x1f) * 100) / mh->scale); + /* the low bit is optional in the tone sliders and masking + it lets is avoid the 0xf 'bypass'.. */ + } else if (mixer == SOUND_MIXER_BASS) { + ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale); + } else if (mixer == SOUND_MIXER_TREBLE) { + ret = 100 - (((val & 0xe) * 100) / mh->scale); + } + + M_printk("read mixer %d (0x%x) %x -> %x\n",mixer,mh->offset,val,ret); + + return ret; +} +#endif + +/* write the OSS encoded volume to the given OSS encoded mixer, + again caller's job to make sure all is well in arg land, + call with spinlock held */ + +/* linear scale -> log */ +static unsigned char lin2log[101] = +{ +0, 0 , 15 , 23 , 30 , 34 , 38 , 42 , 45 , 47 , +50 , 52 , 53 , 55 , 57 , 58 , 60 , 61 , 62 , +63 , 65 , 66 , 67 , 68 , 69 , 69 , 70 , 71 , +72 , 73 , 73 , 74 , 75 , 75 , 76 , 77 , 77 , +78 , 78 , 79 , 80 , 80 , 81 , 81 , 82 , 82 , +83 , 83 , 84 , 84 , 84 , 85 , 85 , 86 , 86 , +87 , 87 , 87 , 88 , 88 , 88 , 89 , 89 , 89 , +90 , 90 , 90 , 91 , 91 , 91 , 92 , 92 , 92 , +93 , 93 , 93 , 94 , 94 , 94 , 94 , 95 , 95 , +95 , 95 , 96 , 96 , 96 , 96 , 97 , 97 , 97 , +97 , 98 , 98 , 98 , 98 , 99 , 99 , 99 , 99 , 99 +}; + +static void ac97_write_mixer(struct ess_card *card,int mixer, unsigned int left, unsigned int right) +{ + u16 val=0; + struct ac97_mixer_hw *mh = &ac97_hw[mixer]; + + M_printk("wrote mixer %d (0x%x) %d,%d",mixer,mh->offset,left,right); + + if(AC97_STEREO_MASK & (1<<mixer)) { + /* stereo mixers, mute them if we can */ + + if (mixer == SOUND_MIXER_IGAIN) { + /* igain's slider is reversed.. */ + right = (right * mh->scale) / 100; + left = (left * mh->scale) / 100; + if ((left == 0) && (right == 0)) + val |= 0x8000; + } else if (mixer == SOUND_MIXER_PCM || mixer == SOUND_MIXER_CD) { + /* log conversion seems bad for them */ + if ((left == 0) && (right == 0)) + val = 0x8000; + right = ((100 - right) * mh->scale) / 100; + left = ((100 - left) * mh->scale) / 100; + } else { + /* log conversion for the stereo controls */ + if((left == 0) && (right == 0)) + val = 0x8000; + right = ((100 - lin2log[right]) * mh->scale) / 100; + left = ((100 - lin2log[left]) * mh->scale) / 100; + } + + val |= (left << 8) | right; + + } else if (mixer == SOUND_MIXER_SPEAKER) { + val = (((100 - left) * mh->scale) / 100) << 1; + } else if (mixer == SOUND_MIXER_MIC) { + val = maestro_ac97_get(card, mh->offset) & ~0x801f; + val |= (((100 - left) * mh->scale) / 100); + /* the low bit is optional in the tone sliders and masking + it lets is avoid the 0xf 'bypass'.. */ + } else if (mixer == SOUND_MIXER_BASS) { + val = maestro_ac97_get(card , mh->offset) & ~0x0f00; + val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00; + } else if (mixer == SOUND_MIXER_TREBLE) { + val = maestro_ac97_get(card , mh->offset) & ~0x000f; + val |= (((100 - left) * mh->scale) / 100) & 0x000e; + } + + maestro_ac97_set(card , mh->offset, val); + + M_printk(" -> %x\n",val); +} + +/* the following tables allow us to go from + OSS <-> ac97 quickly. */ + +enum ac97_recsettings { + AC97_REC_MIC=0, + AC97_REC_CD, + AC97_REC_VIDEO, + AC97_REC_AUX, + AC97_REC_LINE, + AC97_REC_STEREO, /* combination of all enabled outputs.. */ + AC97_REC_MONO, /*.. or the mono equivalent */ + AC97_REC_PHONE +}; + +static unsigned int ac97_oss_mask[] = { + [AC97_REC_MIC] = SOUND_MASK_MIC, + [AC97_REC_CD] = SOUND_MASK_CD, + [AC97_REC_VIDEO] = SOUND_MASK_VIDEO, + [AC97_REC_AUX] = SOUND_MASK_LINE1, + [AC97_REC_LINE] = SOUND_MASK_LINE, + [AC97_REC_PHONE] = SOUND_MASK_PHONEIN +}; + +/* indexed by bit position */ +static unsigned int ac97_oss_rm[] = { + [SOUND_MIXER_MIC] = AC97_REC_MIC, + [SOUND_MIXER_CD] = AC97_REC_CD, + [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO, + [SOUND_MIXER_LINE1] = AC97_REC_AUX, + [SOUND_MIXER_LINE] = AC97_REC_LINE, + [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE +}; + +/* read or write the recmask + the ac97 can really have left and right recording + inputs independently set, but OSS doesn't seem to + want us to express that to the user. + the caller guarantees that we have a supported bit set, + and they must be holding the card's spinlock */ +static int +ac97_recmask_io(struct ess_card *card, int read, int mask) +{ + unsigned int val = ac97_oss_mask[ maestro_ac97_get(card, 0x1a) & 0x7 ]; + + if (read) return val; + + /* oss can have many inputs, maestro can't. try + to pick the 'new' one */ + + if (mask != val) mask &= ~val; + + val = ffs(mask) - 1; + val = ac97_oss_rm[val]; + val |= val << 8; /* set both channels */ + + M_printk("maestro: setting ac97 recmask to 0x%x\n",val); + + maestro_ac97_set(card,0x1a,val); + + return 0; +}; + +/* + * The Maestro can be wired to a standard AC97 compliant codec + * (see www.intel.com for the pdf's on this), or to a PT101 codec + * which appears to be the ES1918 (data sheet on the esstech.com.tw site) + * + * The PT101 setup is untested. + */ + +static u16 __init maestro_ac97_init(struct ess_card *card) +{ + u16 vend1, vend2, caps; + + card->mix.supported_mixers = AC97_SUPPORTED_MASK; + card->mix.stereo_mixers = AC97_STEREO_MASK; + card->mix.record_sources = AC97_RECORD_MASK; +/* card->mix.read_mixer = ac97_read_mixer;*/ + card->mix.write_mixer = ac97_write_mixer; + card->mix.recmask_io = ac97_recmask_io; + + vend1 = maestro_ac97_get(card, 0x7c); + vend2 = maestro_ac97_get(card, 0x7e); + + caps = maestro_ac97_get(card, 0x00); + + printk(KERN_INFO "maestro: AC97 Codec detected: v: 0x%2x%2x caps: 0x%x pwr: 0x%x\n", + vend1,vend2,caps,maestro_ac97_get(card,0x26) & 0xf); + + if (! (caps & 0x4) ) { + /* no bass/treble nobs */ + card->mix.supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE); + } + + /* XXX endianness, dork head. */ + /* vendor specifc bits.. */ + switch ((long)(vend1 << 16) | vend2) { + case 0x545200ff: /* TriTech */ + /* no idea what this does */ + maestro_ac97_set(card,0x2a,0x0001); + maestro_ac97_set(card,0x2c,0x0000); + maestro_ac97_set(card,0x2c,0xffff); + break; +#if 0 /* i thought the problems I was seeing were with + the 1921, but apparently they were with the pci board + it was on, so this code is commented out. + lets see if this holds true. */ + case 0x83847609: /* ESS 1921 */ + /* writing to 0xe (mic) or 0x1a (recmask) seems + to hang this codec */ + card->mix.supported_mixers &= ~(SOUND_MASK_MIC); + card->mix.record_sources = 0; + card->mix.recmask_io = NULL; +#if 0 /* don't ask. I have yet to see what these actually do. */ + maestro_ac97_set(card,0x76,0xABBA); /* o/~ Take a chance on me o/~ */ + udelay(20); + maestro_ac97_set(card,0x78,0x3002); + udelay(20); + maestro_ac97_set(card,0x78,0x3802); + udelay(20); +#endif + break; +#endif + default: break; + } + + maestro_ac97_set(card, 0x1E, 0x0404); + /* null misc stuff */ + maestro_ac97_set(card, 0x20, 0x0000); + + return 0; +} + +#if 0 /* there has been 1 person on the planet with a pt101 that we + know of. If they care, they can put this back in :) */ +static u16 maestro_pt101_init(struct ess_card *card,int iobase) +{ + printk(KERN_INFO "maestro: PT101 Codec detected, initializing but _not_ installing mixer device.\n"); + /* who knows.. */ + maestro_ac97_set(iobase, 0x2A, 0x0001); + maestro_ac97_set(iobase, 0x2C, 0x0000); + maestro_ac97_set(iobase, 0x2C, 0xFFFF); + maestro_ac97_set(iobase, 0x10, 0x9F1F); + maestro_ac97_set(iobase, 0x12, 0x0808); + maestro_ac97_set(iobase, 0x14, 0x9F1F); + maestro_ac97_set(iobase, 0x16, 0x9F1F); + maestro_ac97_set(iobase, 0x18, 0x0404); + maestro_ac97_set(iobase, 0x1A, 0x0000); + maestro_ac97_set(iobase, 0x1C, 0x0000); + maestro_ac97_set(iobase, 0x02, 0x0404); + maestro_ac97_set(iobase, 0x04, 0x0808); + maestro_ac97_set(iobase, 0x0C, 0x801F); + maestro_ac97_set(iobase, 0x0E, 0x801F); + return 0; +} +#endif + +/* this is very magic, and very slow.. */ +static void +maestro_ac97_reset(int ioaddr, struct pci_dev *pcidev) +{ + u16 save_68; + u16 w; + u32 vend; + + outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); + outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); + outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); + + /* reset the first codec */ + outw(0x0000, ioaddr+0x36); + save_68 = inw(ioaddr+0x68); + pci_read_config_word(pcidev, 0x58, &w); /* something magical with gpio and bus arb. */ + pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &vend); + if( w & 0x1) + save_68 |= 0x10; + outw(0xfffe, ioaddr + 0x64); /* tickly gpio 0.. */ + outw(0x0001, ioaddr + 0x68); + outw(0x0000, ioaddr + 0x60); + udelay(20); + outw(0x0001, ioaddr + 0x60); + mdelay(20); + + outw(save_68 | 0x1, ioaddr + 0x68); /* now restore .. */ + outw( (inw(ioaddr + 0x38) & 0xfffc)|0x1, ioaddr + 0x38); + outw( (inw(ioaddr + 0x3a) & 0xfffc)|0x1, ioaddr + 0x3a); + outw( (inw(ioaddr + 0x3c) & 0xfffc)|0x1, ioaddr + 0x3c); + + /* now the second codec */ + outw(0x0000, ioaddr+0x36); + outw(0xfff7, ioaddr + 0x64); + save_68 = inw(ioaddr+0x68); + outw(0x0009, ioaddr + 0x68); + outw(0x0001, ioaddr + 0x60); + udelay(20); + outw(0x0009, ioaddr + 0x60); + mdelay(500); /* .. ouch.. */ + outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); + outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); + outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); + +#if 0 /* the loop here needs to be much better if we want it.. */ + M_printk("trying software reset\n"); + /* try and do a software reset */ + outb(0x80|0x7c, ioaddr + 0x30); + for (w=0; ; w++) { + if ((inw(ioaddr+ 0x30) & 1) == 0) { + if(inb(ioaddr + 0x32) !=0) break; + + outb(0x80|0x7d, ioaddr + 0x30); + if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break; + outb(0x80|0x7f, ioaddr + 0x30); + if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break; + } + + if( w > 10000) { + outb( inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37); /* do a software reset */ + mdelay(500); /* oh my.. */ + outb( inb(ioaddr + 0x37) & ~0x08, ioaddr + 0x37); + udelay(1); + outw( 0x80, ioaddr+0x30); + for(w = 0 ; w < 10000; w++) { + if((inw(ioaddr + 0x30) & 1) ==0) break; + } + } + } +#endif + if ( vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) { + /* turn on external amp? */ + outw(0xf9ff, ioaddr + 0x64); + outw(inw(ioaddr+0x68) | 0x600, ioaddr + 0x68); + outw(0x0209, ioaddr + 0x60); + } + + /* Turn on the 978 docking chip. + First frob the "master output enable" bit, + then set most of the playback volume control registers to max. */ + outb(inb(ioaddr+0xc0)|(1<<5), ioaddr+0xc0); + outb(0xff, ioaddr+0xc3); + outb(0xff, ioaddr+0xc4); + outb(0xff, ioaddr+0xc6); + outb(0xff, ioaddr+0xc8); + outb(0x3f, ioaddr+0xcf); + outb(0x3f, ioaddr+0xd0); +} +/* + * Indirect register access. Not all registers are readable so we + * need to keep register state ourselves + */ + +#define WRITEABLE_MAP 0xEFFFFF +#define READABLE_MAP 0x64003F + +/* + * The Maestro engineers were a little indirection happy. These indirected + * registers themselves include indirect registers at another layer + */ + +static void __maestro_write(struct ess_card *card, u16 reg, u16 data) +{ + long ioaddr = card->iobase; + + outw(reg, ioaddr+0x02); + outw(data, ioaddr+0x00); + if( reg >= NR_IDRS) printk("maestro: IDR %d out of bounds!\n",reg); + else card->maestro_map[reg]=data; + +} + +static void maestro_write(struct ess_state *s, u16 reg, u16 data) +{ + unsigned long flags; + + check_suspend(s->card); + spin_lock_irqsave(&s->card->lock,flags); + + __maestro_write(s->card,reg,data); + + spin_unlock_irqrestore(&s->card->lock,flags); +} + +static u16 __maestro_read(struct ess_card *card, u16 reg) +{ + long ioaddr = card->iobase; + + outw(reg, ioaddr+0x02); + return card->maestro_map[reg]=inw(ioaddr+0x00); +} |