Hi, Here is another patch that moves towards addressing some of the issues raised about the last patch. Also I had trouble mailing the last patch to the linux mips list, mainly because I attached the patch as opposed to putting inline, so this time its pasted in. (I guess we'll find out if thats safe with Gmail). This patch was built against Linux-mips.org 2.6.21.6 On 03/07/07, Takashi Iwai <tiwai@xxxxxxx> wrote: > At Mon, 2 Jul 2007 12:27:58 +0100, > The patch includes old typdefed structs which were already removed > from the upstream, such as, snd_card_t. Please replace them > appropriately. You can find the replacement in > include/sound/typedefs.h (in the old kernel tree). Fixed, does not use typedefs for ALSA structs. > Similarly, I'd recommend to avoid typedefs in your own code, too. I still have two typedef structs of my own. However these aren't used outside the single C source file. > > Other things I noticed through a quick glance: > > - Follow the standard coding style, e.g. 80 chars in a line, don't put > if-block in a single line, etc. I tidily hard wrapped lines that i spotted over 80. How many people still using 80 column terminals? Maybe 132 would be a better choice these days? Is something like: if (0 > err) goto ERROR_EXIT; really that bad? In two lines it isn't much clearer and just user more space. > - Avoid non-ASCII letters, especially outside the comments Outside of the umlaut in my own name, I think the only place I had UTF-8 chars was in ad1843_dump_reg strings, I have fixed this, please let me know if there are more. > - You don't need *_irqsave() in get/put callbacks of the control API. > It's always schedulable, so, spin_lock_irq() suffices. Done > - ad1843_lock could be better implemented with mutex if you have long > delays inside the spinlock (except for the calls from irq handler) Doing this would be something new to me, so it's on my research list/todo list. > - please remove uneeded debug printks. If they are useful, keep it in > another macro form. Substituted for snd_printd (which requires CONFIG_SND_DEBUG), but still with no indentation so I can find and delete them in future. > Could you fix these and repost? Yes =) It still plays audio too fast. Please let me know about your experience with it, and tips/sugestions/bug fixes/etc you have. Regards, Thorben Patch can also be found here: http://www.linux-mips.org/pub/linux/mips/people/trevelyan/mace_audio/mace_audio-20070709.patch ------------------------------------- --- linux-2.6.21.6-b/include/sound/ad1843.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21.6/include/sound/ad1843.h 2007-07-09 19:14:18.000000000 +0100 @@ -0,0 +1,48 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright 2003 Vivien Chappelier <vivien.chappelier@xxxxxxxxxxxxxx> + */ + +#ifndef __SOUND_AD1843_H +#define __SOUND_AD1843_H + +typedef struct { + void *chip; + int (* read)(void *chip, int reg); + int (* write)(void *chip, int reg, int val); +} ad1843_t; + +#define AD1843_GAIN_RECLEV 0 +#define AD1843_GAIN_LINE 1 +#define AD1843_GAIN_CD 2 +#define AD1843_GAIN_MIC 3 +#define AD1843_GAIN_PCM_0 4 +#define AD1843_GAIN_PCM_1 5 +#define AD1843_GAIN_SIZE AD1843_GAIN_PCM_1+1 + +int ad1843_get_gain(ad1843_t *ad1843, int id); +int ad1843_set_gain(ad1843_t *ad1843, int id, int newval); +int ad1843_get_recsrc(ad1843_t *ad1843); +void ad1843_set_resample_mode(ad1843_t *ad1843, int onoff); +int ad1843_set_recsrc(ad1843_t *ad1843, int newsrc); +int ad1843_get_outsrc(ad1843_t *ad1843); +int ad1843_set_outsrc(ad1843_t *ad1843, int mask); +void ad1843_setup_dac(ad1843_t *ad1843, + unsigned int id, + unsigned int framerate, + snd_pcm_format_t fmt, + unsigned int channels); +void ad1843_shutdown_dac(ad1843_t *ad1843, + unsigned int id); +void ad1843_setup_adc(ad1843_t *ad1843, + unsigned int framerate, + snd_pcm_format_t fmt, + unsigned int channels); +void ad1843_shutdown_adc(ad1843_t *ad1843); +int ad1843_init(ad1843_t *ad1843); +char *ad1843_dump_reg(ad1843_t *ad1843, int reg); + +#endif /* __SOUND_AD1843_H */ --- linux-2.6.21.6-b/sound/mips/ad1843.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21.6/sound/mips/ad1843.c 2007-07-09 20:09:15.000000000 +0100 @@ -0,0 +1,911 @@ +/* + * AD1843 low level driver + * + * Copyright 2003 Vivien Chappelier <vivien.chappelier@xxxxxxxxxxxxxx> + * + * inspired from vwsnd.c (SGI VW audio driver) + * Copyright 1999 Silicon Graphics, Inc. All rights reserved. + * + * 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/slab.h> + +#include <linux/init.h> +#include <linux/jiffies.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/vmalloc.h> +#include <sound/driver.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/initval.h> +#include <sound/ad1843.h> + +// find and remove any OSS stuff +#include <linux/soundcard.h> + +/* + * AD1843 bitfield definitions. All are named as in the AD1843 data + * sheet, with ad1843_ prepended and individual bit numbers removed. + * + * E.g., bits LSS0 through LSS2 become ad1843_LSS. + * + * Only the bitfields we need are defined. + */ + +typedef struct ad1843_bitfield { + char reg; + char lo_bit; + char nbits; +} ad1843_bitfield_t; + +static const ad1843_bitfield_t + ad1843_PDNO = { 0, 14, 1 }, /* Converter Power-Down Flag */ + ad1843_INIT = { 0, 15, 1 }, /* Clock Initialization Flag */ + ad1843_RIG = { 2, 0, 4 }, /* Right ADC Input Gain */ + ad1843_RMGE = { 2, 4, 1 }, /* Right ADC Mic Gain Enable */ + ad1843_RSS = { 2, 5, 3 }, /* Right ADC Source Select */ + ad1843_LIG = { 2, 8, 4 }, /* Left ADC Input Gain */ + ad1843_LMGE = { 2, 12, 1 }, /* Left ADC Mic Gain Enable */ + ad1843_LSS = { 2, 13, 3 }, /* Left ADC Source Select */ + ad1843_RX1M = { 4, 0, 5 }, /* Right Aux 1 Mix Gain/Atten */ + ad1843_RX1MM = { 4, 7, 1 }, /* Right Aux 1 Mix Mute */ + ad1843_LX1M = { 4, 8, 5 }, /* Left Aux 1 Mix Gain/Atten */ + ad1843_LX1MM = { 4, 15, 1 }, /* Left Aux 1 Mix Mute */ + ad1843_RX2M = { 5, 0, 5 }, /* Right Aux 2 Mix Gain/Atten */ + ad1843_RX2MM = { 5, 7, 1 }, /* Right Aux 2 Mix Mute */ + ad1843_LX2M = { 5, 8, 5 }, /* Left Aux 2 Mix Gain/Atten */ + ad1843_LX2MM = { 5, 15, 1 }, /* Left Aux 2 Mix Mute */ + ad1843_RMCM = { 7, 0, 5 }, /* Right Mic Mix Gain/Atten */ + ad1843_RMCMM = { 7, 7, 1 }, /* Right Mic Mix Mute */ + ad1843_LMCM = { 7, 8, 5 }, /* Left Mic Mix Gain/Atten */ + ad1843_LMCMM = { 7, 15, 1 }, /* Left Mic Mix Mute */ + ad1843_HPOS = { 8, 4, 1 }, /* Headphone Output Voltage Swing */ + ad1843_HPOM = { 8, 5, 1 }, /* Headphone Output Mute */ + ad1843_MPOM = { 8, 6, 1 }, /* Mono Output Mute */ + ad1843_RDA1G = { 9, 0, 6 }, /* Right DAC1 Analog/Digital Gain */ + ad1843_RDA1GM = { 9, 7, 1 }, /* Right DAC1 Analog Mute */ + ad1843_LDA1G = { 9, 8, 6 }, /* Left DAC1 Analog/Digital Gain */ + ad1843_LDA1GM = { 9, 15, 1 }, /* Left DAC1 Analog Mute */ + ad1843_RDA2G = { 10, 0, 6 }, /* Right DAC2 Analog/Digital Gain */ + ad1843_RDA2GM = { 10, 7, 1 }, /* Right DAC2 Analog Mute */ + ad1843_LDA2G = { 10, 8, 6 }, /* Left DAC2 Analog/Digital Gain */ + ad1843_LDA2GM = { 10, 15, 1 }, /* Left DAC2 Analog Mute */ + ad1843_RDA1AM = { 11, 7, 1 }, /* Right DAC1 Digital Mute */ + ad1843_LDA1AM = { 11, 15, 1 }, /* Left DAC1 Digital Mute */ + ad1843_RDA2AM = { 12, 7, 1 }, /* Right DAC1 Digital Mute */ + ad1843_LDA2AM = { 12, 15, 1 }, /* Left DAC1 Digital Mute */ + ad1843_ADLC = { 15, 0, 2 }, /* ADC Left Sample Rate Source */ + ad1843_ADRC = { 15, 2, 2 }, /* ADC Right Sample Rate Source */ + ad1843_DA1C = { 15, 8, 2 }, /* DAC1 Sample Rate Source */ + ad1843_DA2C = { 15, 10, 2 }, /* DAC2 Sample Rate Source */ + ad1843_C1M = { 16, 0, 8 }, /* Clock 1 Rate Modifier */ + ad1843_C1C128 = { 16, 10, 1 }, /* Clock 1 CONV1 Freq select */ + ad1843_C1C = { 17, 0, 16 }, /* Clock 1 Sample Rate Select */ + ad1843_C2M = { 19, 0, 8 }, /* Clock 2 Rate Modifier */ + ad1843_C2C = { 20, 0, 16 }, /* Clock 2 Sample Rate Select */ + ad1843_C3M = { 22, 0, 8 }, /* Clock 3 Rate Modifier */ + ad1843_C3C = { 23, 0, 16 }, /* Clock 3 Sample Rate Select */ + ad1843_DAADL = { 25, 4, 2 }, /* Digital ADC Left Source Select */ + ad1843_DAADR = { 25, 6, 2 }, /* Digital ADC Right Source Select */ + ad1843_DRSFLT = { 25, 15, 1 }, /* Digital Reampler Filter Mode */ + ad1843_ADLF = { 26, 0, 2 }, /* ADC Left Channel Data Format */ + ad1843_ADRF = { 26, 2, 2 }, /* ADC Right Channel Data Format */ + ad1843_ADTLK = { 26, 4, 1 }, /* ADC Transmit Lock Mode Select */ + ad1843_FRS = { 26, 6, 1 }, /* Frame size select */ + ad1843_SCF = { 26, 7, 1 }, /* SCLK Frequency Select */ + ad1843_DA1F = { 26, 8, 2 }, /* DAC1 Data Format Select */ + ad1843_DA2F = { 26, 10, 2 }, /* DAC2 Data Format Select */ + ad1843_DA1SM = { 26, 14, 1 }, /* DAC1 Stereo/Mono Mode Select */ + ad1843_DA2SM = { 26, 15, 1 }, /* DAC2 Stereo/Mono Mode Select */ + ad1843_ADLEN = { 27, 0, 1 }, /* ADC Left Channel Enable */ + ad1843_ADREN = { 27, 1, 1 }, /* ADC Right Channel Enable */ + ad1843_AAMEN = { 27, 4, 1 }, /* Analog to Analog Mix Enable */ + ad1843_ANAEN = { 27, 7, 1 }, /* Analog Channel Enable */ + ad1843_DA1EN = { 27, 8, 1 }, /* DAC1 Enable */ + ad1843_DA2EN = { 27, 9, 1 }, /* DAC2 Enable */ + ad1843_ENBT1 = { 28, 2, 1 }, /* BIT1 Pin Enable */ + ad1843_ENCV1 = { 28, 3, 1 }, /* CONV1 Pin Enable */ + ad1843_ENBT2 = { 28, 4, 1 }, /* BIT2 Pin Enable */ + ad1843_ENCV2 = { 28, 5, 1 }, /* CONV2 Pin Enable */ + ad1843_ENBT3 = { 28, 6, 1 }, /* BIT3 Pin Enable */ + ad1843_ENCV3 = { 28, 7, 1 }, /* CONV3 Pin Enable */ + ad1843_ENCLKO = { 28, 10, 1 }, /* ClockOut Pin Enable */ + ad1843_C1EN = { 28, 11, 1 }, /* Clock Generator 1 Enable */ + ad1843_C2EN = { 28, 12, 1 }, /* Clock Generator 2 Enable */ + ad1843_C3EN = { 28, 13, 1 }, /* Clock Generator 3 Enable */ + ad1843_ACEN = { 28, 14, 1 }, /* Autocalibration enable */ + ad1843_PDNI = { 28, 15, 1 }; /* Converter Power Down */ + + +/* + * The various registers of the AD1843 use three different formats for + * specifying gain. The ad1843_gain structure parameterizes the + * formats. + */ + +typedef struct ad1843_gain { + int negative; /* nonzero if gain is negative. */ + const ad1843_bitfield_t *lfield; + const ad1843_bitfield_t *rfield; +} ad1843_gain_t; + +const ad1843_gain_t ad1843_gain_RECLEV + = { 0, &ad1843_LIG, &ad1843_RIG }; +const ad1843_gain_t ad1843_gain_LINE + = { 1, &ad1843_LX1M, &ad1843_RX1M }; +const ad1843_gain_t ad1843_gain_CD + = { 1, &ad1843_LX2M, &ad1843_RX2M }; +const ad1843_gain_t ad1843_gain_MIC + = { 1, &ad1843_LMCM, &ad1843_RMCM }; +const ad1843_gain_t ad1843_gain_PCM_0 + = { 1, &ad1843_LDA1G, &ad1843_RDA1G }; +const ad1843_gain_t ad1843_gain_PCM_1 + = { 1, &ad1843_LDA2G, &ad1843_RDA2G }; + +const ad1843_gain_t *ad1843_gain[AD1843_GAIN_SIZE] = { + &ad1843_gain_RECLEV, + &ad1843_gain_LINE, + &ad1843_gain_CD, + &ad1843_gain_MIC, + &ad1843_gain_PCM_0, + &ad1843_gain_PCM_1, +}; + +/* read the current value of an AD1843 bitfield. */ + +static int ad1843_read_bits(ad1843_t *ad1843, const ad1843_bitfield_t *field) +{ + int w = ad1843->read(ad1843->chip, field->reg); + int val = w >> field->lo_bit & ((1 << field->nbits) - 1); + + return val; +} + +/* + * write a new value to an AD1843 bitfield and return the old value. + */ + +static int ad1843_write_bits(ad1843_t *ad1843, + const ad1843_bitfield_t *field, + int newval) +{ + int w = ad1843->read(ad1843->chip, field->reg); + int mask = ((1 << field->nbits) - 1) << field->lo_bit; + int oldval = (w & mask) >> field->lo_bit; + int newbits = (newval << field->lo_bit) & mask; + w = (w & ~mask) | newbits; + (void) ad1843->write(ad1843->chip, field->reg, w); + + return oldval; +} + +/* + * ad1843_read_multi reads multiple bitfields from the same AD1843 + * register. It uses a single read cycle to do it. (Reading the + * ad1843 requires 256 bit times at 12.288 MHz, or nearly 20 + * microseconds.) + * + * Called like this. + * + * ad1843_read_multi(ad1843, nfields, + * &ad1843_FIELD1, &val1, + * &ad1843_FIELD2, &val2, ...); + */ + +static void ad1843_read_multi(ad1843_t *ad1843, int argcount, ...) +{ + va_list ap; + const ad1843_bitfield_t *fp; + int w = 0, mask, *value, reg = -1; + + va_start(ap, argcount); + while (--argcount >= 0) { + fp = va_arg(ap, const ad1843_bitfield_t *); + value = va_arg(ap, int *); + + if (reg == -1) { + reg = fp->reg; + w = ad1843->read(ad1843->chip, reg); + } + + mask = (1 << fp->nbits) - 1; + *value = w >> fp->lo_bit & mask; + } + va_end(ap); + +} + +/* + * ad1843_write_multi stores multiple bitfields into the same AD1843 + * register. It uses one read and one write cycle to do it. + * + * Called like this. + * + * ad1843_write_multi(ad1843, nfields, + * &ad1843_FIELD1, val1, + * &ad1843_FIELF2, val2, ...); + */ + +static void ad1843_write_multi(ad1843_t *ad1843, int argcount, ...) +{ + va_list ap; + int reg; + const ad1843_bitfield_t *fp; + int value; + int w, m, mask, bits; + + mask = 0; + bits = 0; + reg = -1; + + va_start(ap, argcount); + while (--argcount >= 0) { + fp = va_arg(ap, const ad1843_bitfield_t *); + value = va_arg(ap, int); + if (reg == -1) + reg = fp->reg; + + m = ((1 << fp->nbits) - 1) << fp->lo_bit; + mask |= m; + bits |= (value << fp->lo_bit) & m; + } + va_end(ap); + + if (~mask & 0xFFFF) + w = ad1843->read(ad1843->chip, reg); + else + w = 0; + w = (w & ~mask) | bits; + (void) ad1843->write(ad1843->chip, reg, w); +} + +/* + * ad1843_get_gain reads the specified register and extracts the gain value + * using the supplied gain type. It returns the gain in OSS format. + */ + +int ad1843_get_gain(ad1843_t *ad1843, int id) +{ + int lg, rg; + const ad1843_gain_t *gp = ad1843_gain[id]; + unsigned short mask = (1 << gp->lfield->nbits) - 1; + + ad1843_read_multi(ad1843, 2, gp->lfield, &lg, gp->rfield, &rg); + if (gp->negative) { + lg = mask - lg; + rg = mask - rg; + } + lg = (lg * 100 + (mask >> 1)) / mask; + rg = (rg * 100 + (mask >> 1)) / mask; + return lg << 0 | rg << 8; +} + +/* + * Set an audio channel's gain. Converts from OSS format to AD1843's + * format. + * + * Returns the new gain, which may be lower than the old gain. + */ + +int ad1843_set_gain(ad1843_t *ad1843, + int id, + int newval) +{ + const ad1843_gain_t *gp = ad1843_gain[id]; + unsigned short mask = (1 << gp->lfield->nbits) - 1; + + int lg = newval >> 0 & 0xFF; + int rg = newval >> 8; + if (lg < 0 || lg > 100 || rg < 0 || rg > 100) + return -EINVAL; + lg = (lg * mask + (mask >> 1)) / 100; + rg = (rg * mask + (mask >> 1)) / 100; + if (gp->negative) { + lg = mask - lg; + rg = mask - rg; + } + ad1843_write_multi(ad1843, 2, gp->lfield, lg, gp->rfield, rg); + return ad1843_get_gain(ad1843, id); +} + +/* Returns the current recording source, in OSS format. */ + +int ad1843_get_recsrc(ad1843_t *ad1843) +{ + int ls = ad1843_read_bits(ad1843, &ad1843_LSS); + + switch (ls) { + case 1: + return SOUND_MASK_MIC; + case 2: + return SOUND_MASK_LINE; + case 3: + return SOUND_MASK_CD; + case 6: + return SOUND_MASK_PCM; + default: + return -1; + } +} + +/* + * Enable/disable digital resample mode in the AD1843. + * + * The AD1843 requires that ADL, ADR, DA1 and DA2 be powered down + * while switching modes. So we save DA's state, power them down, + * switch into/out of resample mode, power them up, and restore state. + * + * This will cause audible glitches if D/A or A/D is going on, so the + * driver disallows that (in mixer_write_ioctl()). + * + * The open question is, is this worth doing? I'm leaving it in, + * because it's written, but... + */ + +void ad1843_set_resample_mode(ad1843_t *ad1843, int onoff) +{ + /* Save DA's mute and gain (addr 9/10 is analog gain/attenuation) */ + int save_da1 = ad1843->read(ad1843->chip, 9); + int save_da2 = ad1843->read(ad1843->chip, 10); + + /* Power down A/D and D/A. */ + ad1843_write_multi(ad1843, 4, + &ad1843_DA1EN, 0, + &ad1843_DA2EN, 0, + &ad1843_ADLEN, 0, + &ad1843_ADREN, 0); + + /* Switch mode */ + ad1843_write_bits(ad1843, &ad1843_DRSFLT, onoff); + + /* Power up A/D and D/A. */ + ad1843_write_multi(ad1843, 3, + &ad1843_DA1EN, 1, + &ad1843_DA2EN, 1, + &ad1843_ADLEN, 1, + &ad1843_ADREN, 1); + + /* Restore DA's mute and gain. */ + ad1843->write(ad1843->chip, 9, save_da1); + ad1843->write(ad1843->chip, 10, save_da2); +} + +/* + * Set recording source. Arg newsrc specifies an OSS channel mask. + * + * The complication is that when we switch into/out of loopback mode + * (i.e., src = SOUND_MASK_PCM), we change the AD1843 into/out of + * digital resampling mode. + * + * Returns newsrc on success, -errno on failure. + */ + +int ad1843_set_recsrc(ad1843_t *ad1843, int newsrc) +{ + int bits; + int oldbits; + + switch (newsrc) { + case SOUND_MASK_PCM: + bits = 6; + break; + + case SOUND_MASK_MIC: + bits = 1; + break; + + case SOUND_MASK_LINE: + bits = 2; + break; + + case SOUND_MASK_CD: + bits = 3; + break; + + default: + return -EINVAL; + } + oldbits = ad1843_read_bits(ad1843, &ad1843_LSS); + if (newsrc == SOUND_MASK_PCM && oldbits != 6) { + + ad1843_set_resample_mode(ad1843, 1); + ad1843_write_multi(ad1843, 2, + &ad1843_DAADL, 2, + &ad1843_DAADR, 2); + } else if (newsrc != SOUND_MASK_PCM && oldbits == 6) { + + ad1843_set_resample_mode(ad1843, 0); + ad1843_write_multi(ad1843, 2, + &ad1843_DAADL, 0, + &ad1843_DAADR, 0); + } + ad1843_write_multi(ad1843, 2, &ad1843_LSS, bits, &ad1843_RSS, bits); + return newsrc; +} + +/* + * Return current output sources, in OSS format. + */ + +int ad1843_get_outsrc(ad1843_t *ad1843) +{ + int pcm, line, mic, cd; + + pcm = ad1843_read_bits(ad1843, &ad1843_LDA1GM) ? 0 : SOUND_MASK_PCM; + line = ad1843_read_bits(ad1843, &ad1843_LX1MM) ? 0 : SOUND_MASK_LINE; + cd = ad1843_read_bits(ad1843, &ad1843_LX2MM) ? 0 : SOUND_MASK_CD; + mic = ad1843_read_bits(ad1843, &ad1843_LMCMM) ? 0 : SOUND_MASK_MIC; + + return pcm | line | cd | mic; +} + +/* + * Set output sources. Arg is a mask of active sources in OSS format. + * + * Returns source mask on success, -errno on failure. + */ + +int ad1843_set_outsrc(ad1843_t *ad1843, int mask) +{ + int pcm, line, mic, cd; + + if (mask & ~(SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_CD | SOUND_MASK_MIC)) + return -EINVAL; + pcm = (mask & SOUND_MASK_PCM) ? 0 : 1; + line = (mask & SOUND_MASK_LINE) ? 0 : 1; + mic = (mask & SOUND_MASK_MIC) ? 0 : 1; + cd = (mask & SOUND_MASK_CD) ? 0 : 1; + + ad1843_write_multi(ad1843, 2, &ad1843_LDA1GM, pcm, &ad1843_RDA1GM, pcm); + ad1843_write_multi(ad1843, 2, &ad1843_LX1MM, line, &ad1843_RX1MM, line); + ad1843_write_multi(ad1843, 2, &ad1843_LX2MM, cd, &ad1843_RX2MM, cd); + ad1843_write_multi(ad1843, 2, &ad1843_LMCMM, mic, &ad1843_RMCMM, mic); + + return mask; +} + +/* Setup ad1843 for D/A conversion. */ + +void ad1843_setup_dac(ad1843_t *ad1843, + unsigned int id, + unsigned int framerate, + snd_pcm_format_t fmt, + unsigned int channels) +{ + int ad_fmt = 1, ad_mode = 0; + + switch (fmt) { + case SNDRV_PCM_FORMAT_S8: ad_fmt = 0; break; + case SNDRV_PCM_FORMAT_U8: ad_fmt = 0; break; + case SNDRV_PCM_FORMAT_S16_BE: ad_fmt = 1; break; + case SNDRV_PCM_FORMAT_S24_BE: ad_fmt = 1; break; + case SNDRV_PCM_FORMAT_MU_LAW: ad_fmt = 2; break; + case SNDRV_PCM_FORMAT_A_LAW: ad_fmt = 3; break; + default: printk("errk!\n"); break; + } + + switch (channels) { + case 2: ad_mode = 0; break; + case 1: ad_mode = 1; break; + default: printk("errk!\n"); break; + } + + if(1 == id) { + ad1843_write_bits(ad1843, &ad1843_C1C, framerate); + //ad1843_write_bits(ad1843, &ad1843_C1M, 0x00); + //ad1843_write_bits(ad1843, &ad1843_C1C128, 1); + //ad1843_write_bits(ad1843, &ad1843_FRS, 0); + ad1843_write_multi(ad1843, 2, + &ad1843_DA1SM, ad_mode, + &ad1843_DA1F, ad_fmt); + //ad1843_write_bits(ad1843, &ad1843_DA1EN, 1); + + } else { + ad1843_write_bits(ad1843, &ad1843_C3C, framerate); + ad1843_write_multi(ad1843, 2, + &ad1843_DA2SM, ad_mode, + &ad1843_DA2F, ad_fmt); + //ad1843_write_bits(ad1843, &ad1843_DA2EN, 1); + } + +/*FIXME Debugging, will remove */ +ad1843_write_bits(ad1843, &ad1843_C1C, framerate); +ad1843_write_bits(ad1843, &ad1843_C2C, framerate); +ad1843_write_bits(ad1843, &ad1843_C3C, framerate); +//ad1843_write_bits(ad1843, &ad1843_C1M, 0x00); +//ad1843_write_bits(ad1843, &ad1843_C2M, 0x00); +//ad1843_write_bits(ad1843, &ad1843_C3M, 0x00); + +} + +void ad1843_shutdown_dac(ad1843_t *ad1843, unsigned int id) +{ + if(id) + ad1843_write_bits(ad1843, &ad1843_DA2F, 1); + else + ad1843_write_bits(ad1843, &ad1843_DA1F, 1); +} + +void ad1843_setup_adc(ad1843_t *ad1843, + unsigned int framerate, + snd_pcm_format_t fmt, + unsigned int channels) +{ + int da_fmt = 0; + + switch (fmt) { + case SNDRV_PCM_FORMAT_S8: da_fmt = 0; break; + case SNDRV_PCM_FORMAT_U8: da_fmt = 0; break; + case SNDRV_PCM_FORMAT_S16_BE: da_fmt = 1; break; + case SNDRV_PCM_FORMAT_MU_LAW: da_fmt = 2; break; + case SNDRV_PCM_FORMAT_A_LAW: da_fmt = 3; break; + default: printk("errk!\n"); break; + } + + ad1843_write_bits(ad1843, &ad1843_C3C, framerate); + ad1843_write_multi(ad1843, 2, + &ad1843_ADLF, da_fmt, &ad1843_ADRF, da_fmt); +} + +void ad1843_shutdown_adc(ad1843_t *ad1843) +{ + /* nothing to do */ +} + +/* + * Fully initialize the ad1843. As described in the AD1843 data + * sheet, section "START-UP SEQUENCE". The numbered comments are + * subsection headings from the data sheet. See the data sheet, pages + * 52-54, for more info. + * + * return 0 on success, -errno on failure. */ + +int ad1843_init(ad1843_t *ad1843) +{ + unsigned long later; + + /* 1. Power up in hardware */ + + /* 2. Assert ^RESET^ to 0, wait 100ns + * 3. Deassert ^RESET^ _400 to 800us, poll INIT to see when ready + * Done in calling driver */ + udelay(800); + + /* 3. Check that the clocks are stable */ + if (ad1843_read_bits(ad1843, &ad1843_INIT) != 0) { + printk(KERN_ERR "ad1843: AD1843 Clocks not yet stable\n"); + return -EIO; + } + + /*set serial bus speed */ + ad1843_write_bits(ad1843, &ad1843_SCF, 0); + + /* Power down conv resources */ + /*ad1843_write_bits(ad1843, &ad1843_PDNI, 1); + udelay(800);*/ + + /* 4. Put the conversion resources into standby PDNI to 0 + * Force Enable auto calibrate ACEN to 1 */ + ad1843_write_multi(ad1843, 2, + &ad1843_PDNI, 0, &ad1843_ACEN, 1); + later = jiffies + HZ / 2; /* roughly half a second */ + + /* 5. While in standby + * Power up the clock generators and enable clock output pins. */ + ad1843_write_multi(ad1843, 10, &ad1843_ENCLKO, 1, + &ad1843_C1EN, 1, &ad1843_ENCV1, 1, &ad1843_ENBT1, 1, + &ad1843_C2EN, 1, &ad1843_ENCV2, 1, &ad1843_ENBT2, 1, + &ad1843_C3EN, 1, &ad1843_ENCV3, 1, &ad1843_ENBT3, 1); + + /* 6. Configure conversion resources while they are in standby. */ + + + + /* Did we come out of standby ? */ + while (ad1843_read_bits(ad1843, &ad1843_PDNO)) { + if (time_after(jiffies, later)) { + printk(KERN_ERR "ad1843: AD1843 won't power up\n"); + return -EIO; + } + schedule(); + } + + + /* DAC1 = Clock1, ADC = Clock2, DAC2 = Clock3 Always. + * Based an ad1843 state from startup jingle */ + ad1843_write_multi(ad1843, 4, + &ad1843_DA1C, 1, &ad1843_DA2C, 3, + &ad1843_ADLC, 2, &ad1843_ADRC, 2); + + /* 7. Enable conversion resources. */ + ad1843_write_bits(ad1843, &ad1843_ADTLK, 1); + ad1843_write_multi(ad1843, 6, + &ad1843_ANAEN, 1, &ad1843_AAMEN, 1, + &ad1843_DA1EN, 1, &ad1843_DA2EN, 1, + &ad1843_ADLEN, 1, &ad1843_ADREN, 1); + + /* 8. Configure conversion resources while they are enabled. */ + + + /* Unmute all channels. */ + + ad1843_set_outsrc(ad1843, + (SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_MIC | SOUND_MASK_CD)); + ad1843_write_multi(ad1843, 2, + &ad1843_LDA1AM, 0, &ad1843_RDA1AM, 0); + + ad1843_write_multi(ad1843, 2, + &ad1843_LDA2AM, 0, &ad1843_RDA2AM, 0); + + ad1843_write_multi(ad1843, 2, + &ad1843_LDA1GM, 0, &ad1843_RDA1GM, 0); + + /* Set default recording source to Line In and set + * mic gain to +20 dB. + */ + ad1843_set_recsrc(ad1843, SOUND_MASK_LINE); + ad1843_write_multi(ad1843, 2, &ad1843_LMGE, 1, &ad1843_RMGE, 1); + + /* Set Speaker Out level to +/- 4V and unmute it. */ + ad1843_write_multi(ad1843, 3, + &ad1843_HPOS, 1, + &ad1843_HPOM, 0, + &ad1843_MPOM, 0); + + return 0; +} + +char *ad1843_dump_reg(ad1843_t *ad1843, int reg) +{ + static const char *reg_fmt[32] = { + "Reg 0: Codec Status and Revision Identification\n" + "|INIT %u |PDNO %u |RES %u |RES %u " + "|RES %u |RES %u |RES %u |RES %u | (%u)\n" + "|RES %u |RES %u |RES %u |RES %u " + "|ID3 %u ,ID2 %u ,ID1 %u ,ID0 %u | (%u)\n", + /*reg 1*/ + "Reg 1: Channel Status Flags\n" + "|RES %u |RES %u |RES %u |RES %u " + "|RES %u |RES %u |SU2 %u |SU1 %u | (%u)\n" + "|RES %u |RES %u |RES %u |RES %u " + "|OVR1 %u ,OVR0 %u |OVL1 %u ,OVL0 %u | (%u)\n", + /*reg 2*/ + "Reg 2: Input Control - ADC Source and Gain/Attenuation\n" + "|LSS2 %u ,LSS1 %u ,LSS0 %u |LMGE %u " + "|LIG3 %u ,LIG2 %u ,LIG1 %u ,LIG0 %u | (%u)\n" + "|RSS2 %u ,RSS1 %u ,RSS0 %u |RMGE %u " + "|RIG3 %u ,RIG2 %u ,RIG1 %u ,RIG0 %u | (%u)\n", + /*reg 3*/ + "Reg 3: Mix Control - DAC2 to Mixer\n" + "|LD2MM %u |RES %u |RES %u |LD2M4 %u " + ",LD2M3 %u ,LD2M2 %u ,LD2M1 %u ,LD2M0 %u | (%u)\n" + "|RD2MM %u |RES %u |RES %u |RD2M4 %u " + ",RD2M3 %u ,RD2M2 %u ,RD2M1 %u ,RD2M0 %u | (%u)\n", + /*reg 4*/ + "Reg 4: Auxiliary 1 to DAC1\n" + "|LX1MM %u |RES %u |RES %u |LX1M4 %u " + ",LX1M3 %u ,LX1M2 %u ,LX1M1 %u ,LX1M0 %u | (%u)\n" + "|RX1MM %u |RES %u |RES %u |RX1M4 %u " + ",RX1M3 %u ,RX1M2 %u ,RX1M1 %u ,RX1M0 %u | (%u)\n", + /*reg 5 */ + "Reg 5: Mix Control - Auxiliary 2 to Mixer\n" + "|LX2MM %u |RES %u |RES %u |LX2M4 %u " + ",LX2M3 %u ,LX2M2 %u ,LX2M1 %u ,LX2M0 %u | (%u)\n" + "|RX2MM %u |RES %u |RES %u |RX2M4 %u " + ",RX2M3 %u ,RX2M2 %u ,RX2M1 %u ,RX2M0 %u | (%u)\n", + /*reg 6*/ + "Reg 6: Mix Control - Auxiliary 3 to Mixer\n" + "|LX3MM %u |RES %u |RES %u |LX3M4 %u " + ",LX3M3 %u ,LX3M2 %u ,LX3M1 %u ,LX3M0 %u | (%u)\n" + "|RX3MM %u |RES %u |RES %u |RX3M4 %u " + ",RX3M3 %u ,RX3M2 %u ,RX3M1 %u ,RX3M0 %u | (%u)\n", + /*reg 7*/ + "Reg 7: Mix Control - Mic to Mixer\n" + "|LMCMM %u |RES %u |RES %u |LMCM4 %u " + ",LMCM3 %u ,LMCM2 %u ,LMCM1 %u ,LMCM0 %u | (%u)\n" + "|RMCMM %u |RES %u |RES %u |RMCM4 %u " + ",RMCM3 %u ,RMCM2 %u ,RMCM1 %u ,RMCM0 %u | (%u)\n", + /*reg 8*/ + "Reg 8: Mix/Misc Control - " + "Mono In to Mixer and Misc Settings\n" + "|MNMM %u |RES %u |RES %u |MNM4 %u " + ",MNM3 %u ,MNM2 %u ,MNM1 %u ,MNM0 %u | (%u)\n" + "|ALLMM %u |MNOM %u |HPOM %u |HPOS %u " + "|SUMM %u |RES %u |DAC2T %u |DAC1T %u | (%u)\n", + /*reg 9*/ + "Reg 9: Output Control - DAC1 Analog Gain/Attenuation\n" + "|LDA1GM %u |RES %u |LDA1G5 %u ,LDA1G4 %u " + ",LDA1G3 %u ,LDA1G2 %u ,LDA1G1 %u ,LDA1G0 %u | (%u)\n" + "|RDA1GM %u |RES %u |RDA1G5 %u ,RDA1G4 %u " + ",RDA1G3 %u ,RDA1G2 %u ,RDA1G1 %u ,RDA1G0 %u | (%u)\n", + /*reg 10*/ + "Reg 10: Output Control - DAC2 Analog Gain/Attenuation\n" + "|LDA2GM %u |RES %u |LDA2G5 %u ,LDA2G4 %u " + ",LDA2G3 %u ,LDA2G2 %u ,LDA2G1 %u ,LDA2G0 %u | (%u)\n" + "|RDA2GM %u |RES %u |RDA2G5 %u ,RDA2G4 %u " + ",RDA2G3 %u ,RDA2G2 %u ,RDA2G1 %u ,RDA2G0 %u | (%u)\n", + /*reg 11*/ + "Reg 11: Output Control - DAC1 Digital Attenuation\n" + "|LDA1AM %u |RES %u |LDA1A5 %u ,LDA1A4 %u " + ",LDA1A3 %u ,LDA1A2 %u ,LDA1A1 %u ,LDA1A0 %u | (%u)\n" + "|RDA1AM %u |RES %u |RDA1A5 %u ,RDA1A4 %u " + ",RDA1A3 %u ,RDA1A2 %u ,RDA1A1 %u ,RDA1A0 %u | (%u)\n", + /*reg 12*/ + "Reg 12: Output Control - DAC2 Digital Attenuation\n" + "|LDA2AM %u |RES %u |LDA2A5 %u ,LDA2A4 %u " + ",LDA2A3 %u ,LDA2A2 %u ,LDA2A1 %u ,LDA2A0 %u | (%u)\n" + "|RDA2AM %u |RES %u |RDA2A5 %u ,RDA2A4 %u " + ",RDA2A3 %u ,RDA2A2 %u ,RDA2A1 %u ,RDA2A0 %u | (%u)\n", + /*reg 13*/ + "Reg 13: Digital Mix Control - ADC to DAC1\n" + "|LAD1MM %u |RES %u |LAD1M5 %u ,LAD1M4 %u " + ",LAD1M3 %u ,LAD1M2 %u ,LAD1M1 %u ,LAD1M0 %u | (%u)\n" + "|RAD1MM %u |RES %u |RAD1M5 %u ,RAD1M4 %u " + ",RAD1M3 %u ,RAD1M2 %u ,RAD1M1 %u ,RAD1M0 %u | (%u)\n", + /*reg 14*/ + "Reg 14: Digital Mix Control - ADC to DAC2\n" + "|LAD2MM %u |RES %u |LAD2M5 %u ,LAD2M4 %u " + ",LAD2M3 %u ,LAD2M2 %u ,LAD2M1 %u ,LAD2M0 %u | (%u)\n" + "|RAD2MM %u |RES %u |RAD2M5 %u ,RAD2M4 %u " + ",RAD2M3 %u ,RAD2M2 %u ,RAD2M1 %u ,RAD2M0 %u | (%u)\n", + /*reg 15*/ + "Reg 15: Codec Configuration - " + "Channel Sample Rate Source Select\n" + "|RES %u |RES %u |RES %u |RES %u " + "|DA2C1 %u ,DA2C0 %u |DA1C1 %u ,DA1C0 %u | (%u)\n" + "|RES %u |RES %u |RES %u |RES %u " + "|ADRC1 %u ,ADRC0 %u |ADLC1 %u ,ADLC0 %u | (%u)\n", + /*reg 16*/ + "Reg 16: Clock Generator 1 Control - Mode\n" + "|C1REF %u |C1VID %u |C1PLLG %u |C1P200 %u " + "|C1X8/7 %u |C1C128 %u |RES %u |RES %u | (%u)\n" + "|C1M7 %u ,C1M6 %u ,C1M5 %u ,C1M4 %u " + ",C1M3 %u ,C1M2 %u ,C1M1 %u ,C1M0 %u | (%u)\n", + /*reg 17*/ + "Reg 17: Clock Generator 1 Control - Sample Rate\n" + "|C1C15 %u ,C1C14 %u ,C1C13 %u ,C2C12 %u " + ",C1C11 %u ,C1C10 %u ,C1C9 %u ,C1C8 %u , (%u)\n" + ",C1C7 %u ,C1C6 %u ,C1C5 %u ,C1C4 %u " + ",C1C3 %u ,C1C2 %u ,C1C1 %u ,C1C0 %u | (%u)\n", + /*reg 18*/ + "Reg 18: Clock Generator 1 Control - Sample Phase Shift\n" + "|RES %u |RES %u |RES %u |RES %u " + "|RES %u |RES %u |RES %u |C1PD %u | (%u)\n" + "|C1P7 %u ,C1P6 %u ,C1P5 %u ,C1P4 %u " + ",C1P3 %u ,C1P2 %u ,C1P1 %u ,C1P0 %u | (%u)\n", + /*reg 19*/ + "Reg 19: Clock Generator 2 Control - Mode\n" + "|C2REF %u |C2VID %u |C2PLLG %u |C2P200 %u " + "|C2X8/7 %u |C2C128 %u |RES %u |RES %u | (%u)\n" + "|C2M7 %u ,C2M6 %u ,C2M5 %u ,C2M4 %u " + ",C2M3 %u ,C2M2 %u ,C2M1 %u ,C2M0 %u | (%u)\n", + /*reg 20*/ + "Reg 20: Clock Generator 2 Control - Sample Rate\n" + "|C2C15 %u ,C2C14 %u ,C2C13 %u ,C2C12 %u " + ",C2C11 %u ,C2C10 %u ,C2C9 %u ,C2C8 %u , (%u)\n" + ",C2C7 %u ,C2C6 %u ,C2C5 %u ,C2C4 %u " + ",C2C3 %u ,C2C2 %u ,C2C1 %u ,C2C0 %u | (%u)\n", + /*reg 21*/ + "Reg 21: Clock Generator 2 Control - Sample Phase Shift\n" + "|RES %u |RES %u |RES %u |RES %u " + "|RES %u |RES %u |RES %u |C2PD %u | (%u)\n" + "|C2P7 %u ,C2P6 %u ,C2P5 %u ,C2P4 %u " + ",C2P3 %u ,C2P2 %u ,C2P1 %u ,C2P0 %u | (%u)\n", + /*reg 22*/ + "Reg 22: Clock Generator 3 Control - Mode\n" + "|C3REF %u |C3VID %u |C3PLLG %u |C3P200 %u " + "|C3X8/7 %u |C3C128 %u |RES %u |RES %u | (%u)\n" + "|C3M7 %u ,C3M6 %u ,C3M5 %u ,C3M4 %u " + ",C3M3 %u ,C3M2 %u ,C3M1 %u ,C3M0 %u | (%u)\n", + /*reg 23*/ + "Reg 23: Clock Generator 3 Control - Sample Rate\n" + "|C3C15 %u ,C3C14 %u ,C3C13 %u ,C3C12 %u " + ",C3C11 %u ,C3C10 %u ,C3C9 %u ,C3C8 %u , (%u)\n" + ",C3C7 %u ,C3C6 %u ,C3C5 %u ,C3C4 %u " + ",C3C3 %u ,C3C2 %u ,C3C1 %u ,C3C0 %u | (%u)\n", + /*reg 24*/ + "Reg 24: Clock Generator 3 Control - Sample Phase Shift\n" + "|RES %u |RES %u |RES %u |RES %u " + "|RES %u |RES %u |RES %u |C3PD %u | (%u)\n" + "|C3P7 %u ,C3P6 %u ,C3P5 %u ,C3P4 %u " + ",C3P3 %u ,C3P2 %u ,C3P1 %u ,C3P0 %u | (%u)\n", + /*reg 25*/ + "Reg 25: Codec Configuration - Digital Filter and Mode Select\n" + "|DRSFLT %u |DAMIX %u |RES %u |RES %u " + "|RES %u |RES %u |DA2FLT %u |DA1FLT %u | (%u)\n" + "|DAADR1 %u ,DAADR0 %u |DAADL1 %u ,DAADL0 %u " + "|RES %u |RES %u |ADRFLT %u |ADLFLT %u | (%u)\n", + /*reg 26*/ + "Reg 26: Codec Configuration - Serial Interface\n" + "|DA2SM %u |DA1SM %u |RES %u |RES %u " + "|DA2F1 %u ,DA2F0 %u |DA1F1 %u ,DA1F0 %u | (%u)\n" + "|SCF %u |FRS %u |FRST %u |ADTLK %u " + "|ADRF1 %u ,ADRF0 %u |ADLF1 %u ,ADLF0 %u | (%u)\n", + /*reg 27*/ + "Reg 27: Codec Configuration - Channel Power Down\n" + "|DFREE %u |RES %u |RES %u |DDMEM %u " + "|RES %u |RES %u |DA2EN %u |DA1EN %u | (%u)\n" + "|ANAEN %u |HPEN %u |RES %u |AAMEN %u " + "|RES %u |RES %u |ADREN %u |ADLEN %u | (%u)\n", + /*reg 28*/ + "Reg 28: Codec Configuration - Fundemental Settings\n" + "|PDNI %u |ACEN %u |C3EN %u |C2EN %u " + "|C1EN %u |ENCLKO %u |XCTL1 %u |XCTL0 %u | (%u)\n" + "|ENCV3 %u |ENBT3 %u |ENCV2 %u |ENBT2 %u " + "|ENCV1 %u |ENBT1 %u |LINRSD %u |LINLSD %u | (%u)\n", + /*reg 29*/ + "Reg 29: Reserved for Future Expansion\n" + "|RES %u |RES %u |RES %u |RES %u " + "|RES %u |RES %u |RES %u |RES %u | (%u)\n" + "|RES %u |RES %u |RES %u |RES %u " + "|RES %u |RES %u |RES %u |RES %u | (%u)\n", + /*reg 30*/ + "Reg 30: Reserved for Future Expansion\n" + "|RES %u |RES %u |RES %u |RES %u " + "|RES %u |RES %u |RES %u |RES %u | (%u)\n" + "|RES %u |RES %u |RES %u |RES %u " + "|RES %u |RES %u |RES %u |RES %u | (%u)\n", + /*reg 31*/ + "Reg 31: Reserved for Future Expansion\n" + "|RES %u |RES %u |RES %u |RES %u " + "|RES %u |RES %u |RES %u |RES %u | (%u)\n" + "|RES %u |RES %u |RES %u |RES %u " + "|RES %u |RES %u |RES %u |RES %u | (%u)\n", + }; + unsigned int v1 = 0, v2 = 0; + int w = ad1843->read(ad1843->chip, reg); + char *str = vmalloc_user(255); + + if (NULL==str) return NULL; + + switch (reg) { + case 0: + v2 = w&0xF; + break; + case 2: + v1 = (w>>8)&0xF; v2 = w&0xF; + break; + case 3: case 4: case 5: case 6: case 7: + v1 = (w>>8)&0x1F; v2 = w&0x1F; + break; + case 8: + v1 = (w>>8)&0x1F; + break; + case 9: case 10: case 11: case 12: case 13: case 14: + v1 = (w>>8)&0x3F; v2 = w&0x3F; + break; + case 16: case 18: case 19: case 21: case 22: case 24: + v2 = w&0xFF; + break; + case 17: case 20: case 23: + v2 = w&0xFFFF; + break; + + } + + snprintf(str, 255, reg_fmt[reg], (w>>15)&1, + (w>>14)&1, (w>>13)&1, (w>>12)&1, (w>>11)&1, (w>>10)&1, + (w>>9)&1, (w>>8)&1, v1, (w>>7)&1, (w>>6)&1, (w>>5)&1, + (w>>4)&1, (w>>3)&1, (w>>2)&1, (w>>1)&1, (w>>0)&1, v2); + + return str; +} --- linux-2.6.21.6-b/sound/mips/mace_audio.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21.6/sound/mips/mace_audio.c 2007-07-09 20:29:50.000000000 +0100 @@ -0,0 +1,1347 @@ +/* + * Sound driver for Silicon Graphics O2 Workstations MACE audio board. + * + * Copyright 2007 Thorben Jändling <tj.trevelyan@xxxxxxxxx> + * Based/Copied heavily on/from sgio2audio.c: + * Copyright 2003 Vivien Chappelier <vivien.chappelier@xxxxxxxxxxxxxx> + * + * 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 + * + */ + +/* + * Notes + * + * Mace take 24bit audio stored/transfered in 32 bits and converts + * it in hardware back to 16/8bit + * + * AD1843 spec says: + * "Data in all four formats is always transfered MSB first" + * To me that says big-endian + * So tell ALSA we only do SNDRV_PCM_FMTBIT_S24_BE + * + * Apparently RAD1 similar see IP30 patch set + * + * Why rewrite? Mostly so that I can understand the code, writing is + * better then reading for that purpose I find. + */ +/* channel 0/ADC, 1/DAC1 -> pcm0 + * channel 2/DAC2 -> pcm1 + */ + +/*************************** include ******************************/ + +#include <sound/driver.h> +#include <linux/init.h> +#include <linux/wait.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/gfp.h> +#include <linux/vmalloc.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/list.h> +#include <linux/dma-mapping.h> +#include <asm/io.h> + +#include <asm/ip32/ip32_ints.h> +#include <asm/ip32/mace.h> + +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/initval.h> +#include <sound/info.h> + +#include <sound/ad1843.h> + +/**************************** defines *******************************/ + +/* 1: reset audio interface */ +#define SMA_CTRL_RESET 0x1UL +/* 1: codec detected */ +#define SMA_CTRL_CODEC_PRESENT 0x2UL +/* 2-8 : channel 1 write ptr alias */ +/* 9-15 : channel 2 read ptr alias */ +/* 16-22 : channel 3 read ptr alias */ +/* latched volume button */ +#define SMA_CTRL_VOL_BUTTON_UP BIT(24) +/* latched volume button */ +#define SMA_CTRL_VOL_BUTTON_DOWN BIT(23) + +#define SMA_CODEC_READ(reg) ((reg) <<17) | 0x00010000UL +#define SMA_CODEC_WRITE(reg, val) (((reg) <<17)|(val)) & 0x00FEFFFFUL + +#define SMA_RING_OFFSET(chi) ((chi) << 12) +#define SMA_RING_SIZE 0x1000 +#define SMA_RING_MASK 0x0FFFUL + +/* int on buffer >50% full */ +#define SMA_INT_THRESHOLD_50 (2 << 5) +/* 1: enable DMA transfer */ +#define SMA_DMA_ENABLE BIT(9) + +/*old, define still inherted from sgio2audio*/ + +#define SMA_CTRL_CHAN_RESET BIT(10) /* 1: reset channel */ + +#define SMA_INT_THRESHOLD_DISABLED (0 << 5) /* interrupt disabled */ +#define SMA_INT_THRESHOLD_25 (1 << 5) /* int on buffer >25% full */ +#define SMA_INT_THRESHOLD_75 (3 << 5) /* int on buffer >75% full */ +#define SMA_INT_THRESHOLD_EMPTY (4 << 5) /* int on buffer empty */ +#define SMA_INT_THRESHOLD_NOT_EMPTY (5 << 5) /* int on buffer !empty */ +#define SMA_INT_THRESHOLD_FULL (6 << 5) /* int on buffer empty */ +#define SMA_INT_THRESHOLD_NOT_FULL (7 << 5) /* int on buffer !empty */ + +/****************************** structs/types ***********************/ + +/* chip specific record */ +typedef struct snd_mace_audio { + struct snd_card *card; + struct snd_pcm *pcm[2]; + + struct { + struct snd_pcm_substream *substream; + snd_pcm_uframes_t frames; + + int ack; + void *buffer; + u64 pos; + spinlock_t lock; + } channel[3]; + + /* which gain should the O2 vol butons control?*/ + unsigned int ext_vol_for; + + struct snd_info_entry *proc_debug, + *proc_ad1843, + *proc_mace; + + ad1843_t *ad1843; + spinlock_t ad1843_lock; + + void *ring_base; + dma_addr_t ring_base_handle; + unsigned long maceisa_base; + unsigned int mace_offset; + +} snd_mace_audio_t; + +typedef struct sma_proc_debug { + struct list_head link; + +/* name to display for the entry */ + char *name; + +/* a string value */ + char *val_str; + +/* an int val used if val_str is null */ + long val_int; +} sma_proc_debug_t; + +/************************** func declaration ****************************/ +/* mace access */ +static int mace_audio_reg_read(snd_mace_audio_t *chip, int reg); +static int mace_audio_reg_write(snd_mace_audio_t *chip, int reg, int word); +/* pcm */ +static int sma_pcm_open(struct snd_pcm_substream *substream); +static int sma_pcm_close(struct snd_pcm_substream *substream); +static int sma_pcm_hwparam + (struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params); +static int sma_pcm_hwfree(struct snd_pcm_substream *substream); +static int sma_pcm_prepare(struct snd_pcm_substream *substream); +static int sma_pcm_trigger(struct snd_pcm_substream *substream, int cmd); +static snd_pcm_uframes_t sma_pcm_pointer + (struct snd_pcm_substream *substream); +static struct page *sma_pcm_page + (struct snd_pcm_substream *substream, unsigned long offset); +static int sma_pcm_ack(struct snd_pcm_substream *substream); +static int __devinit sma_pcm_new(snd_mace_audio_t *chip); +/* interrupt and DMA engine */ +static int sma_dma_start(struct snd_pcm_substream *substream); +static int sma_dma_stop(struct snd_pcm_substream *substream); +static void sma_dma_ping(snd_mace_audio_t *chip, int chi); +static void sma_dma_refill(snd_mace_audio_t *chip, int chi); +static irqreturn_t sma_interrupt + (int irq, snd_mace_audio_t *chip, struct pt_regs *regs); +static void sma_adjust_vol(snd_mace_audio_t *chip); +/* mixer and controls */ +static int sma_gain_info + (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); +static int sma_gain_get + (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +static int sma_gain_put + (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +static int __devinit sma_control_new(snd_mace_audio_t *chip); +/* proc */ +static void sma_proc_ad1843_read + (struct snd_info_entry *proc_entry, struct snd_info_buffer *buffer); +static void sma_proc_mace_read + (struct snd_info_entry *proc_entry, struct snd_info_buffer *buffer); +static void sma_proc_debug_read + (struct snd_info_entry *, struct snd_info_buffer *); +static sma_proc_debug_t* sma_proc_debug_append(char *name); +static int sma_proc_create(snd_mace_audio_t *chip); +/* ALSA Setup */ +static int sma_free(snd_mace_audio_t *chip); +static int __devinit sma_create + (struct snd_card *card, snd_mace_audio_t **rchip); +static int __devinit sma_probe(void); +/* module setup */ +static int __init snd_card_mace_audio_init(void); +static void __exit snd_card_mace_audio_exit(void); + + +/************************* define psudo-funcs ***************************/ +#define sma_proc_entry_assert(entry,name) \ + if (NULL == entry) entry = sma_proc_entry_append(name); + +/* pcm0 substream0 = channel 1 + * pcm0 substream1 = channel 0 + * pcm1 substream0 = channel 2 + */ +#define substream_to_channel_index(substream) \ + ((((snd_mace_audio_t*)(substream->private_data))->pcm[0]==substream->pcm)\ + ?!(substream->number):2) + +/******************************** globals *******************************/ + +/* using same for all */ +static struct snd_pcm_hardware sma_pcm_hw[2] = { + { /* Direct */ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S24_BE, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 4096, + .period_bytes_min = 32, + .period_bytes_max = 1024, + .periods_min = 4, + .periods_max = 128, + + },{ /* inderct bounce buffer */ + + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S24_BE, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 32768, + .period_bytes_min = 32, + .period_bytes_max = 32768, + .periods_min = 1, + .periods_max = 1024, + } +}; + +/* pcm operators */ +static struct snd_pcm_ops sma_pcm_ops[2] = { + { /* Direct */ + .open = sma_pcm_open, + .close = sma_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = sma_pcm_hwparam, + .hw_free = sma_pcm_hwfree, + .prepare = sma_pcm_prepare, + .trigger = sma_pcm_trigger, + .pointer = sma_pcm_pointer, + .ack = sma_pcm_ack, + .page = sma_pcm_page + + },{ /* inderct bounce buffer */ + .open = sma_pcm_open, + .close = sma_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = sma_pcm_hwparam, + .hw_free = sma_pcm_hwfree, + .prepare = sma_pcm_prepare, + .trigger = sma_pcm_trigger, + .pointer = sma_pcm_pointer, + .page = sma_pcm_page + + } +}; + +/* record level mixer control */ +static struct snd_kcontrol_new sma_ctrl_reclevel __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = AD1843_GAIN_RECLEV, + .info = sma_gain_info, + .get = sma_gain_get, + .put = sma_gain_put, +}; + +/* line mixer control */ +static struct snd_kcontrol_new sma_ctrl_line __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = AD1843_GAIN_LINE, + .info = sma_gain_info, + .get = sma_gain_get, + .put = sma_gain_put, +}; + +/* cd mixer control */ +static struct snd_kcontrol_new sma_ctrl_cd __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "CD Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = AD1843_GAIN_CD, + .info = sma_gain_info, + .get = sma_gain_get, + .put = sma_gain_put, +}; + +/* mic mixer control */ +static struct snd_kcontrol_new sma_ctrl_mic __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = AD1843_GAIN_MIC, + .info = sma_gain_info, + .get = sma_gain_get, + .put = sma_gain_put, +}; + +/* dac1/pcm0 mixer control */ +static struct snd_kcontrol_new sma_ctrl_pcm0 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = AD1843_GAIN_PCM_0, + .info = sma_gain_info, + .get = sma_gain_get, + .put = sma_gain_put, +}; + +/* dac2/pcm1 mixer control */ +static struct snd_kcontrol_new sma_ctrl_pcm1 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Playback Aux-out Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = AD1843_GAIN_PCM_1, + .info = sma_gain_info, + .get = sma_gain_get, + .put = sma_gain_put, +}; + + +LIST_HEAD(sma_proc_debug_list); + +static int alsa_index = -1; +static char *alsa_id = NULL; +static int alsa_enable= 1; +static int sma_indirect = 1; + +/* Global needed for module exit */ +snd_mace_audio_t *snd_mace_audio_chip = NULL; + +/******************************* PCM ****************************************/ + +/* temp debug func */ + +static void print_pointers(const char *str) +{ + unsigned long rb, wp, rp, depth; + int chi; + + rb = readq(&mace->perif.ctrl.ringbase); + snd_printk ("%s. ringbase=0x%lX,\n", str, rb); + + for (chi=0; chi<3; chi++) { + + wp = readq(&mace->perif.audio.chan[chi].write_ptr); + rp = readq(&mace->perif.audio.chan[chi].read_ptr); + depth = readq(&mace->perif.audio.chan[chi].depth); + + snd_printk("\t%d[ writep=0x%lX (%lu), readp=0x%lX (%lu), " + "depth=0x%lX (%lu) ]\n", + chi, wp, wp, rp, rp, depth, depth); + } + +} + + +/* play open callback */ +static int sma_pcm_open(struct snd_pcm_substream *substream) +{ + //snd_mace_audio_t *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int chi = substream_to_channel_index(substream); +snd_printd("in pcm open for channel %d\n", chi); + + runtime->hw = sma_pcm_hw[sma_indirect]; + + return 0; +} + +/* play close callback */ +static int sma_pcm_close(struct snd_pcm_substream *substream) +{ + //snd_mace_audio_t *chip = snd_pcm_substream_chip(substream); + //FIXME code +snd_printd("in pcm close\n"); + + return 0; +} + +/* setup hw for pcm */ +/* maybe called more then once */ +static int sma_pcm_hwparam + (struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) +{ + snd_mace_audio_t *chip = snd_pcm_substream_chip(substream); + int chi = substream_to_channel_index(substream); +snd_printd("In hw param for channel %d\n", chi); + + if (sma_indirect) { + + if (NULL != substream->runtime->dma_area) + vfree(substream->runtime->dma_area); + + substream->runtime->dma_bytes = + params_buffer_bytes(hw_params); + + substream->runtime->dma_addr = 0; + + substream->runtime->dma_area = + vmalloc_user(substream->runtime->dma_bytes); + + } else { + substream->runtime->dma_bytes = SMA_RING_SIZE; + + substream->runtime->dma_addr = + (chip->ring_base_handle + SMA_RING_OFFSET(chi) + /*+ chip->mace_offset*/); + /*chip->maceisa_base + SMA_RING_OFFSET(chi);*/ + substream->runtime->dma_area = + (chip->ring_base + SMA_RING_OFFSET(chi) + /*+ chip->mace_offset~*/); + } + +snd_printd("set alsa dma area=0x%lX (%lu) size=0x%lX (%lu)\n", + (unsigned long) substream->runtime->dma_area, + (unsigned long) substream->runtime->dma_area, + (unsigned long) substream->runtime->dma_bytes, + (unsigned long) substream->runtime->dma_bytes); + + if (NULL == substream->runtime->dma_area) { + snd_printk(KERN_ERR "DMA area not allocated!\n"); + return -ENOMEM; + } + + return 1; +} + +/* free hw for pcm */ +/* maybe called more then once */ +static int sma_pcm_hwfree(struct snd_pcm_substream *substream) +{ + snd_mace_audio_t *chip = snd_pcm_substream_chip(substream); + int chi = substream_to_channel_index(substream); +snd_printd("In hw free for channel %d\n", chi); + + if (sma_indirect && NULL != substream->runtime->dma_area) { + vfree(substream->runtime->dma_area); + } + + substream->runtime->dma_bytes = 0; + substream->runtime->dma_addr = 0; + substream->runtime->dma_area = NULL; + chip->channel[chi].buffer = NULL; + chip->channel[chi].pos = 0; + chip->channel[chi].ack = 0; + chip->channel[chi].frames = 0; + + return 1; +} + +/* prepare callback */ +static int sma_pcm_prepare(struct snd_pcm_substream *substream) +{ + snd_mace_audio_t *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int chi = substream_to_channel_index(substream); + unsigned long flags; + + spin_lock_irqsave(&chip->channel[chi].lock, flags); + + chip->channel[chi].pos = 0; + chip->channel[chi].frames = 0; + chip->channel[chi].ack = 0; + chip->channel[chi].substream = substream; + + if (0 == chi) { /* capture*/ +snd_printd("in prepare capture for channel %d\n", chi); + + ad1843_setup_adc(chip->ad1843, + runtime->rate, + SNDRV_PCM_FORMAT_S24_BE, + runtime->channels); + } else { /* playback */ +snd_printd("in prepare playback for channel %d, rate %d, sub-channels %d\n", + chi, runtime->rate, runtime->channels); + + ad1843_setup_dac(chip->ad1843, + chi, + runtime->rate, + SNDRV_PCM_FORMAT_S24_BE, + runtime->channels); + } + + spin_unlock_irqrestore(&chip->channel[chi].lock, flags); + + return 0; +} + +/* trigger callback */ +static int sma_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ +snd_printd("In pcm trigger\n"); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: /* start PCM engine */ + sma_dma_start(substream); + break; + case SNDRV_PCM_TRIGGER_STOP: /* stop PCM engine */ + sma_dma_stop(substream); + break; + default: + return -EINVAL; + } + + return 0; +} + +/* pointer callback */ +static snd_pcm_uframes_t sma_pcm_pointer + (struct snd_pcm_substream *substream) +{ + snd_mace_audio_t *chip = snd_pcm_substream_chip(substream); + int chi = substream_to_channel_index(substream); + unsigned long p; + + if (sma_indirect) { + + p = chip->channel[chi].pos; + + } else { + + p = readq(&mace->perif.audio.chan[chi].read_ptr); + + } + +//snd_printd("returning pointer 0x%lX (%lu) for channel %d\n", p, p, chi); + + + return bytes_to_frames(substream->runtime, p); +} + +/* get page callback */ +static struct page *sma_pcm_page + (struct snd_pcm_substream *substream, unsigned long offset) +{ + struct page *p; +snd_printd("in page\n"); + + if (sma_indirect) { + p = vmalloc_to_page((substream->runtime->dma_area + offset)); + } else { + /*FIXME using CAC_AADR until a proper alsa dma on mips fix */ + p = virt_to_page( + CAC_ADDR((substream->runtime->dma_area + offset))); + } + + return p; +} + +/* ack callback, when some more data is written */ +static int sma_pcm_ack(struct snd_pcm_substream *substream) +{ + //snd_mace_audio_t *chip = snd_pcm_substream_chip(substream); + int chi = substream_to_channel_index(substream); + unsigned long ap; + + ap = frames_to_bytes(substream->runtime, + substream->runtime->control->appl_ptr); + + //ap =- SMA_RING_SIZE; + if (32 > ap) ap += SMA_RING_SIZE; + ap -= 32; + ap &= SMA_RING_MASK; + +snd_printd("in ack, ap=0x%lX (%lu), for channel %d\n", ap, ap, chi); + writeq(ap, &mace->perif.audio.chan[chi].write_ptr); + + /* set DMA to wake on 50% empty and enable interrupt */ +/* writeq(SMA_DMA_ENABLE | SMA_INT_THRESHOLD_50, + &mace->perif.audio.chan[chi].control);*/ + mb(); + + return 0; +} + + +/* create pcm device */ +static int __devinit sma_pcm_new(snd_mace_audio_t *chip) +{ + struct snd_pcm *pcm; + int err, chi; + + /* reset channels and leave them off */ + for (chi=0; chi<3; chi++) { + writeq(0, + &mace->perif.audio.chan[chi].control); + + writeq(0, &mace->perif.audio.chan[chi].write_ptr); + writeq(0, &mace->perif.audio.chan[chi].read_ptr); + + writeq(SMA_CTRL_CHAN_RESET, + &mace->perif.audio.chan[chi].control); + + spin_lock_init(&chip->channel[chi].lock); + } + + pcm = NULL; + err = snd_pcm_new(chip->card, "Mace Audio 1", 0, 1, 1, &pcm); + if (0 > err) return err; + + pcm->private_data = chip; + strcpy(pcm->name, "Mace Audio Primary"); + + /* set operators */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &sma_pcm_ops[sma_indirect]); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &sma_pcm_ops[sma_indirect]); + + chip->pcm[0] = pcm; + + + pcm = NULL; + err = snd_pcm_new(chip->card, "Mace Audio 1", 1, 1, 0, &pcm); + if (0 > err) return err; /*free pcm0 ? */ + + pcm->private_data = chip; + strcpy(pcm->name, "Mace Audio Secondary"); + + /* set operators */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &sma_pcm_ops[sma_indirect]); + + chip->pcm[1] = pcm; + + return 0; +} + +/*************************** Interrupts and DMA engine ****************/ +static int sma_dma_start(struct snd_pcm_substream *substream) +{ + snd_mace_audio_t *chip = snd_pcm_substream_chip(substream); + int chi = substream_to_channel_index(substream); + +snd_printd("In DMA enable for channel %d\n", chi); + + if (sma_indirect) { + sma_dma_refill(chip, chi); + } + + /* set DMA to wake on 50% empty and enable interrupt */ + writeq(SMA_DMA_ENABLE | SMA_INT_THRESHOLD_50, + &mace->perif.audio.chan[chi].control); + mb(); + + return 0; +} + +static int sma_dma_stop(struct snd_pcm_substream *substream) +{ + int chi = substream_to_channel_index(substream); +snd_printd("In DMA stop for channel %d\n", chi); + + writeq(SMA_CTRL_CHAN_RESET, + &mace->perif.audio.chan[chi].control); + mb(); + + return 0; +} + +static void sma_dma_ping(snd_mace_audio_t *chip, int chi) +{ + + if (sma_indirect){ + sma_dma_refill(chip, chi); + snd_pcm_period_elapsed(chip->channel[chi].substream); + } else { + snd_pcm_period_elapsed(chip->channel[chi].substream); + /* Stop DMA if running low */ + /*depth = readq(&mace->perif.audio.chan[chi].depth); + if (1024 > depth) sma_dma_stop(chip->channel[chi].substream);*/ + } + +} + +static void sma_dma_refill(snd_mace_audio_t *chip, int chi) +{ + u64 *dst, dst_base, dst_pos, *src, src_base, src_pos, src_mask; + unsigned long flags, filled, available; + int i; + struct snd_pcm_runtime *runtime = + chip->channel[chi].substream->runtime; + + spin_lock_irqsave(&chip->channel[chi].lock, flags); + + dst_base = (u64)chip->ring_base + SMA_RING_OFFSET(chi) + + chip->mace_offset; + src_base = (u64)runtime->dma_area; + + dst_pos = readq(&mace->perif.audio.chan[chi].write_ptr); + src_pos = chip->channel[chi].pos; + + //src_mask = runtime->dma_bytes -1; + src_mask = frames_to_bytes(runtime, runtime->buffer_size) - 1; + + dst = (u64*)(dst_base + dst_pos); + src = (u64*)(src_base + src_pos); +; + filled = readq(&mace->perif.audio.chan[chi].depth); + available = SMA_RING_SIZE - filled; + + /* don't catch up with the end, note 32 byte chunks*/ + if (SMA_RING_SIZE <= available) available -= 32; + + /* copy in 32 byte blocks */ + while (31 < available) for (i=0; i<32; i+=sizeof(u64)) { + + *dst = *src; + + dst_pos += sizeof(u64); + dst_pos &= SMA_RING_MASK; + dst = (u64*)(dst_base + dst_pos); + + src_pos += sizeof(u64); + src_pos &= src_mask; + src = (u64*)(src_base + src_pos); + + available -= sizeof(u64); + } + + writeq(dst_pos, &mace->perif.audio.chan[chi].write_ptr); + chip->channel[chi].pos = src_pos; + mb(); + + src_pos = readq(&mace->perif.audio.chan[chi].write_ptr); + if (src_pos != dst_pos) + snd_printk("ohoh ring boundary not matched! 0x%lX != 0x%lX\n", + src_pos, dst_pos); + + spin_unlock_irqrestore(&chip->channel[chi].lock, flags); +} + +static irqreturn_t sma_interrupt + (int irq, snd_mace_audio_t *chip, struct pt_regs *regs) +{ + switch (irq) { + case MACEISA_AUDIO_SW_IRQ: + printk("Got MACEISA AUDIO SW IRQ\n"); + return IRQ_NONE; /*TODO*/ + + case MACEISA_AUDIO_SC_IRQ: + sma_adjust_vol(chip); + break; + + case MACEISA_AUDIO1_DMAT_IRQ: + sma_dma_ping(chip, 0); + break; + + case MACEISA_AUDIO1_OF_IRQ: + printk("Got MACEISA AUDIO1 OF IRQ\n"); + return IRQ_NONE; /*TODO*/ + + case MACEISA_AUDIO2_DMAT_IRQ: + sma_dma_ping(chip, 1); + break; + + case MACEISA_AUDIO2_MERR_IRQ: + printk("Got MACEISA AUDIO2 MERR IRQ\n"); + return IRQ_NONE; /*TODO*/ + + case MACEISA_AUDIO3_DMAT_IRQ: + sma_dma_ping(chip, 2); + break; + + case MACEISA_AUDIO3_MERR_IRQ: + printk("Got MACEISA AUDIO3 MERR IRQ\n"); + return IRQ_NONE; /*TODO*/ + + default: + snd_printk(KERN_ERR "Unknown IRQ received!\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static void sma_adjust_vol(snd_mace_audio_t *chip) +{ + unsigned long status, flags; + unsigned int rvol,lvol; + + spin_lock_irqsave(&chip->ad1843_lock, flags); + + status = readq(&mace->perif.audio.control); + rvol = ad1843_get_gain(chip->ad1843, chip->ext_vol_for); + lvol = (rvol>>8)&0xFF; + rvol &= 0xFF; + +snd_printd("in volume adjust for %d, current vol %d|%d, status %ld\n", + chip->ext_vol_for, lvol, rvol, status); + + if ((status & SMA_CTRL_VOL_BUTTON_UP) >0) { + status &= ~SMA_CTRL_VOL_BUTTON_UP; + writeq(status, &mace->perif.audio.control); + mb(); + + if (100 > lvol) lvol++; + if (100 > rvol) rvol++; + } + + if ((status & SMA_CTRL_VOL_BUTTON_DOWN) >0) { + status &= ~SMA_CTRL_VOL_BUTTON_DOWN; + writeq(status, &mace->perif.audio.control); + mb(); + + if (0 < lvol) lvol--; + if (0 < rvol) rvol--; + } + + ad1843_set_gain(chip->ad1843, chip->ext_vol_for, ((lvol<<8)|rvol)); + + mb(); + + status = readq(&mace->perif.audio.control); +snd_printd("leaving volume adjust, vol now %d|%d, status %ld\n", + lvol, rvol, status); + + spin_unlock_irqrestore(&chip->ad1843_lock, flags); + +} + +/******************** Mixer and controls ********************/ + +static int sma_gain_info + (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 100; + + return 0; +} + +static int sma_gain_get + (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + snd_mace_audio_t *chip = snd_kcontrol_chip(kcontrol); + int vol; + + spin_lock_irq(&chip->ad1843_lock); + + vol = ad1843_get_gain(chip->ad1843, (int)kcontrol->private_value); + + ucontrol->value.integer.value[0] = vol & 0xFF; + ucontrol->value.integer.value[1] = (vol >> 8) & 0xFF; + + spin_unlock_irq(&chip->ad1843_lock); + + return 0; +} + +static int sma_gain_put + (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + snd_mace_audio_t *chip = snd_kcontrol_chip(kcontrol); + int newvol, oldvol; + unsigned long flags; + + spin_lock_irq(&chip->ad1843_lock); + + oldvol = ad1843_get_gain(chip->ad1843, kcontrol->private_value); + newvol = (ucontrol->value.integer.value[1] << 8) | + ucontrol->value.integer.value[0]; + + newvol = ad1843_set_gain(chip->ad1843, kcontrol->private_value, + newvol); + + spin_unlock_irq(&chip->ad1843_lock); + + return (newvol != oldvol); +} + +static int __devinit sma_control_new(snd_mace_audio_t *chip) +{ + int err; + + err = snd_ctl_add(chip->card, + snd_ctl_new1(&sma_ctrl_reclevel, chip)); + if (0 > err) return err; + + err = snd_ctl_add(chip->card, + snd_ctl_new1(&sma_ctrl_line, chip)); + if (0 > err) return err; + + err = snd_ctl_add(chip->card, + snd_ctl_new1(&sma_ctrl_cd, chip)); + if (0 > err) return err; + + err = snd_ctl_add(chip->card, + snd_ctl_new1(&sma_ctrl_mic, chip)); + if (0 > err) return err; + + err = snd_ctl_add(chip->card, + snd_ctl_new1(&sma_ctrl_pcm0, chip)); + if (0 > err) return err; + + err = snd_ctl_add(chip->card, + snd_ctl_new1(&sma_ctrl_pcm1, chip)); + if (0 > err) return err; + + /*plan a mixer control that can change this*/ + chip->ext_vol_for = AD1843_GAIN_PCM_0; + + return 0; +} + +/****************************** Proc files **********************************/ +static void sma_proc_ad1843_read + (struct snd_info_entry *proc_entry, struct snd_info_buffer *buffer) +{ + int i; + char *str; + snd_mace_audio_t *chip = proc_entry->private_data; + + snd_iprintf(buffer, + "| 15/7 | 14/6 | 13/5 | 12/4 " + "| 11/3 | 10/2 | 9/1 | 8/0 |\n"); + + for (i=0; i<32; i++){ + + str = ad1843_dump_reg(chip->ad1843, i); + if (NULL == str) continue; + + snd_iprintf(buffer, str); + + vfree(str); + } + +} + +static void sma_proc_mace_read + (struct snd_info_entry *proc_entry, struct snd_info_buffer *buffer) +{ + snd_mace_audio_t *chip = proc_entry->private_data; + unsigned long r,w,c,d; + int chi; + + mb(); + r = readq(&mace->perif.ctrl.ringbase); + snd_iprintf(buffer, "MACE Periferals Ringbase: 0x%lX (%lu)\n" + "\t(CPU Address: 0x%lX (%lu), Set Bus Address: 0x%lX (%lu))\n", + (unsigned long)r, (unsigned long)r, + (unsigned long)chip->ring_base, (unsigned long)chip->ring_base, + chip->ring_base_handle, chip->ring_base_handle); + + c = readq(&mace->perif.audio.control); + snd_iprintf(buffer, "MACE Audio Control: 0x%lX (%lu)\n", c, c); + + c = readq(&mace->perif.audio.codec_control); + snd_iprintf(buffer, "MACE Audio Codec Status Control: 0x%lX (%lu)\n", + c, c); + + d = readq(&mace->perif.audio.codec_mask); + snd_iprintf(buffer, "MACE Audio Codec Status IRQ Mask: 0x%lX (%lu)\n", + d, d); + + r = readq(&mace->perif.audio.codec_read); + snd_iprintf(buffer, "MACE Audio Codec Status Value: 0x%lX (%lu)\n", + r, r); + + for (chi=0; chi<3; chi++) { + + mb(); + c = readq(&mace->perif.audio.chan[chi].control); + r = readq(&mace->perif.audio.chan[chi].read_ptr); + w = readq(&mace->perif.audio.chan[chi].write_ptr); + d = readq(&mace->perif.audio.chan[chi].depth); + + snd_iprintf(buffer, "MACE Audio Channel %u:-\n" + "\tControl: 0x%lX (%lu)\n" + "\tRead Pointer: 0x%lX (%lu)\n" + "\tWrite Pointer: 0x%lX (%lu)\n" + "\tDepth: 0x%lX (%lu)\n", + chi, c, c, r, r, w, w, d, d); + + if (chip->channel[chi].substream && + chip->channel[chi].substream->runtime ) { + unsigned long ap = frames_to_bytes( + chip->channel[chi].substream->runtime, + chip->channel[chi].substream->runtime->control + ->appl_ptr); + + + snd_iprintf(buffer, + "\tALSA Runtime DMA area: 0x%lX (%lu)\n", + chip->channel[chi].substream->runtime->dma_area, + chip->channel[chi].substream->runtime->dma_area); + + snd_iprintf(buffer, + "\tALSA App Pointer: 0x%lX (%lu) [0x%lX (%lu)]\n", + ap,ap, + chip->channel[chi].substream->runtime->control + ->appl_ptr, + chip->channel[chi].substream->runtime->control + ->appl_ptr); + + } + } +} + +static void sma_proc_debug_read + (struct snd_info_entry *proc_entry, struct snd_info_buffer *buffer) +{ + sma_proc_debug_t *entry; + + snd_iprintf(buffer, "MACE Audio debug info.\n"); + + list_for_each_entry(entry, &sma_proc_debug_list, link) { + + if (NULL == entry->val_str) + snd_iprintf(buffer, "%s: \t%ld\n", + entry->name, entry->val_int); + else + snd_iprintf(buffer, "%s: \t%s\n", + entry->name, entry->val_str); + } + +} + +/* this is called inside the volume interrupt, but is it appropriate + * to be allocating memory during an interrupt? */ +static sma_proc_debug_t* sma_proc_debug_append + (char *name) +{ + sma_proc_debug_t *entry = + kzalloc(sizeof(sma_proc_debug_t), GFP_ATOMIC); + + entry->name = name; + + list_add_tail(&entry->link, &sma_proc_debug_list); + + return entry; + +} + +static int sma_proc_create(snd_mace_audio_t *chip) +{ + int err; + + err = snd_card_proc_new(chip->card, "debug", &chip->proc_debug); + if (0 > err) return err; + + snd_info_set_text_ops(chip->proc_debug, chip, + sma_proc_debug_read); + + err = snd_card_proc_new(chip->card, "ad1843", &chip->proc_ad1843); + if (0 > err) return err; + + snd_info_set_text_ops(chip->proc_ad1843, chip, + sma_proc_ad1843_read); + + err = snd_card_proc_new(chip->card, "mace", &chip->proc_mace); + if (0 > err) return err; + + snd_info_set_text_ops(chip->proc_mace, chip, + sma_proc_mace_read); + + return 0; +} + +/******************************* ALSA setup **************************/ + +/* chip specific destructor */ +static int sma_free(snd_mace_audio_t *chip) +{ + int irq; + +snd_printd("sma_free called\n"); + + /* reset the interface */ + writeq(SMA_CTRL_RESET, &mace->perif.audio.control); + udelay(1); + writeq(0L, &mace->perif.audio.control); + + /* undo DMA ? */ + dma_free_coherent(NULL, MACEISA_RINGBUFFERS_SIZE + 62, + chip->ring_base, chip->ring_base_handle); + + /* release IRQ's */ + for (irq=MACEISA_AUDIO_SW_IRQ; irq<=MACEISA_AUDIO3_MERR_IRQ; irq++){ + free_irq(irq, chip); + } + + /* shutdown the ad1843? */ + + + /* free codec struct*/ + kfree(chip->ad1843); + snd_mace_audio_chip = NULL; + + return 0; +} + +/* chip specific constructor */ +static int __devinit sma_create + (struct snd_card *card, snd_mace_audio_t **rchip) +{ + snd_mace_audio_t *chip; + int err, irq; + unsigned long mace_reg, mringbase; + + *rchip = NULL; + chip = card->private_data; + chip->card = card; + + /* DMA ring buffer */ + chip->ring_base = dma_alloc_coherent (NULL, + MACEISA_RINGBUFFERS_SIZE + 62, + &chip->ring_base_handle, GFP_USER); + + err = (NULL == chip->ring_base)?-ENOMEM:0; /* ugly */ + if (0> err) goto ERROR_EXIT; + + /* set ring base */ + writeq(chip->ring_base_handle, &mace->perif.ctrl.ringbase); + mb(); + + chip->maceisa_base = readq(&mace->perif.ctrl.ringbase); + chip->mace_offset = chip->maceisa_base - chip->ring_base_handle; + /*chip->ring_base += chip->mace_offset;*/ + +snd_printd("allocated ring base. CPU addr=0x%lX, Bus addr=0x%lX\n" + "\tmacebase=0x%lX, offset=0x%lX\n", + chip->ring_base, chip->ring_base_handle, + chip->maceisa_base, chip->mace_offset); + + /* Allocate IRQs */ + for (irq=MACEISA_AUDIO_SW_IRQ; irq<=MACEISA_AUDIO3_MERR_IRQ; irq++){ + /*NOTE What does reqeust_irq return? */ + err = request_irq(irq, + (irqreturn_t(*)(int, void*, struct pt_regs*)) + &sma_interrupt, + SA_INTERRUPT|SA_SHIRQ, "Mace Audio", chip); + if (0 > err) goto ERROR_EXIT; + } + + /* check if the codec is present */ + mace_reg = readq(&mace->perif.audio.control); + err = (mace_reg & SMA_CTRL_CODEC_PRESENT)>0? 0 : -ENOENT; + if (0 > err) goto ERROR_EXIT; + + /* reset the interface and ad1843 */ + writeq(SMA_CTRL_RESET, &mace->perif.audio.control); + udelay(100); + writeq(0L, &mace->perif.audio.control); + + /* spin locks */ + spin_lock_init(&chip->ad1843_lock); + + /* ad1843 setup */ + chip->ad1843 = kmalloc(sizeof(ad1843_t), GFP_KERNEL); + err = (NULL == chip->ad1843)?-ENOMEM:0; + if (0 > err) goto ERROR_EXIT; + + chip->ad1843->chip = chip; + chip->ad1843->read = + (int(*)(void*, int))&mace_audio_reg_read; + chip->ad1843->write = + (int(*)(void*, int, int))&mace_audio_reg_write; + + /* at mo we dont want any codec update irqs (SW) */ + writeq(0L, &mace->perif.audio.codec_mask); + + err = ad1843_init(chip->ad1843); + if (0 > err) goto ERROR_EXIT; + + *rchip = chip; + snd_mace_audio_chip = chip; + + return 0; + +ERROR_EXIT: + snd_printk(KERN_ERR "Attemp to initialise MACE Audio failed\n"); + sma_free(chip); + return err; +} + +/* constructer */ +static int __devinit sma_probe(void) +{ + struct snd_card *card; + snd_mace_audio_t *chip; + int err; + + /* create alsa stuff*/ + card = snd_card_new(alsa_index, alsa_id, + THIS_MODULE, sizeof(snd_mace_audio_t)); + if (NULL == card) return -ENOMEM; + + /* sutup our stuff*/ + err = sma_create(card, &chip); + if (0 > err) goto ERROR_EXIT; + + /* tell the world who we are */ + strcpy(card->driver, "SGI O2 MACE Audio Dirver"); + strcpy(card->shortname, "MACE Audio"); + sprintf(card->longname, "%s", + card->shortname ); + + /* attach our bits'n'bobs */ + err = sma_pcm_new(chip); + if (0 > err) goto ERROR_EXIT; + + err = sma_control_new(chip); + if (0 > err) goto ERROR_EXIT; + + err = sma_proc_create(chip); + if (0 > err) goto ERROR_EXIT; + + /* and we're done */ + err = snd_card_register(card); + if (0 > err) goto ERROR_EXIT; + + return 0; +ERROR_EXIT: + snd_printk(KERN_ERR "Probe for MACE Audio failed\n"); + snd_card_free(card); + + return err; +} + +/***************************** MACE access ***************************/ +static int mace_audio_reg_read(snd_mace_audio_t *chip, int reg) +{ + int val; + unsigned long flags; + unsigned int check; + + spin_lock_irqsave(&chip->ad1843_lock, flags); + writeq(SMA_CODEC_READ(reg), + &mace->perif.audio.codec_control); + mb(); + udelay(200); + + check = readq(&mace->perif.audio.codec_control); /* flush bus */ + if (unlikely((SMA_CODEC_READ(reg)) != check)) + snd_printk("MACE Audio reg read hic-up sent %lX, got %X\n", + (SMA_CODEC_READ(reg)), check); + + val = (int)readq(&mace->perif.audio.codec_read); + + spin_unlock_irqrestore(&chip->ad1843_lock, flags); + + return val; +} + +static int mace_audio_reg_write(snd_mace_audio_t *chip, int reg, int word) +{ + unsigned long flags; + unsigned int check; + + spin_lock_irqsave(&chip->ad1843_lock, flags); + + writeq(SMA_CODEC_WRITE(reg, word), + &mace->perif.audio.codec_control); + mb(); + + udelay(200); + + check = readq(&mace->perif.audio.codec_control); /* flush bus */ + if (unlikely((SMA_CODEC_WRITE(reg, word)) != check)) + snd_printk("MACE Audio reg write hic-up sent %lX, got %X\n", + (SMA_CODEC_WRITE(reg, word)), check); + + spin_unlock_irqrestore(&chip->ad1843_lock, flags); + + return 0; +} + +/*********************** module setup ****************************/ + + +static int __init snd_card_mace_audio_init(void) +{ + int err; + + err = sma_probe(); + if (0 > err) goto ERROR_EXIT; + + return 0; +ERROR_EXIT: +#ifdef MODULE + snd_printk(KERN_ERR "Mace Audio not found or device busy\n"); +#endif + return err; +} + +static void __exit snd_card_mace_audio_exit(void) +{ + if (NULL != snd_mace_audio_chip) { + struct snd_card *card = snd_mace_audio_chip->card; + sma_free(snd_mace_audio_chip); + snd_card_free(card); + } +} + +MODULE_AUTHOR("Thorben Jändling <tj.trevelyan@xxxxxxxxx>"); +MODULE_DESCRIPTION("SGI O2 MACE Audio"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Silicon Graphics, O2 MACE Audio}}"); + +module_param(alsa_index, int, 0444); +MODULE_PARM_DESC(alsa_index, "Index value for MACE Audio"); +module_param(alsa_id, charp, 0444); +MODULE_PARM_DESC(alsa_id, "ID string for MACE Audio"); +module_param(alsa_enable, bool, 0444); +MODULE_PARM_DESC(alsa_enable, "Enable MACE Audio"); + +module_init(snd_card_mace_audio_init) +module_exit(snd_card_mace_audio_exit) + --- linux-2.6.21.6-b/sound/mips/mace_audio_spy.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21.6/sound/mips/mace_audio_spy.c 2007-07-09 19:51:22.000000000 +0100 @@ -0,0 +1,524 @@ +/* + * Sound driver for Silicon Graphics O2 Workstations MACE audio board. + * + * Copyright 2007 Thorben Jändling <tj.trevelyan@xxxxxxxxx> + * Based/Copied heavily on/from sgio2audio.c: + * Copyright 2003 Vivien Chappelier <vivien.chappelier@xxxxxxxxxxxxxx> + * + * 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 + * + */ + +/* + * To snoop at the state mace audio is in after he ARCS jingle + * + */ + + +/*************************** include ******************************/ + +#include <sound/driver.h> +#include <linux/init.h> +#include <linux/wait.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/gfp.h> +#include <linux/vmalloc.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/list.h> +#include <linux/dma-mapping.h> +#include <asm/io.h> + +#include <asm/ip32/ip32_ints.h> +#include <asm/ip32/mace.h> + +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/initval.h> +#include <sound/info.h> + +#include <sound/ad1843.h> + +/**************************** defines *******************************/ + +/* 1: reset audio interface */ +#define SMA_CTRL_RESET 0x1UL +/* 1: codec detected */ +#define SMA_CTRL_CODEC_PRESENT 0x2UL +/* 2-8 : channel 1 write ptr alias */ +/* 9-15 : channel 2 read ptr alias */ +/* 16-22 : channel 3 read ptr alias */ +/* latched volume button */ +#define SMA_CTRL_VOL_BUTTON_UP BIT(24) +/* latched volume button */ +#define SMA_CTRL_VOL_BUTTON_DOWN BIT(23) + +#define SMA_CODEC_READ(reg) ((reg) <<17) | 0x00010000UL +#define SMA_CODEC_WRITE(reg, val) (((reg) <<17)|(val)) & 0x00FEFFFFUL + +#define SMA_RING_OFFSET(chi) ((chi) << 12) +#define SMA_RING_SIZE 0x1000 +#define SMA_RING_MASK 0x0FFFUL + +/* int on buffer >50% full */ +#define SMA_INT_THRESHOLD_50 (2 << 5) +/* 1: enable DMA transfer */ +#define SMA_DMA_ENABLE BIT(9) + +/*old, define still inherted from sgio2audio*/ + +#define SMA_CTRL_CHAN_RESET BIT(10) /* 1: reset channel */ + +#define SMA_INT_THRESHOLD_DISABLED (0 << 5) /* interrupt disabled */ +#define SMA_INT_THRESHOLD_25 (1 << 5) /* int on buffer >25% full */ +#define SMA_INT_THRESHOLD_75 (3 << 5) /* int on buffer >75% full */ +#define SMA_INT_THRESHOLD_EMPTY (4 << 5) /* int on buffer empty */ +#define SMA_INT_THRESHOLD_NOT_EMPTY (5 << 5) /* int on buffer !empty */ +#define SMA_INT_THRESHOLD_FULL (6 << 5) /* int on buffer empty */ +#define SMA_INT_THRESHOLD_NOT_FULL (7 << 5) /* int on buffer !empty */ + +/****************************** structs/types ***********************/ + +/* chip specific record */ +typedef struct snd_mace_audio { + struct snd_card *card; + struct snd_pcm *pcm[2]; + + struct { + struct snd_pcm_substream *substream; + snd_pcm_uframes_t frames; + + int ack; + void *buffer; + u64 pos; + spinlock_t lock; + } channel[3]; + + /* which gain should the O2 vol butons control?*/ + unsigned int ext_vol_for; + + struct snd_info_entry *proc_debug, + *proc_ad1843, + *proc_mace; + + ad1843_t *ad1843; + spinlock_t ad1843_lock; + + void *ring_base; + dma_addr_t ring_base_handle; + unsigned long maceisa_base; + unsigned int mace_offset; + +} snd_mace_audio_t; + +typedef struct sma_proc_debug { + struct list_head link; + +/* name to display for the entry */ + char *name; + +/* a string value */ + char *val_str; + +/* an int val used if val_str is null */ + long val_int; +} sma_proc_debug_t; + +/************************** func declaration ****************************/ +/* mace access */ +static int mace_audio_reg_read(snd_mace_audio_t *chip, int reg); +static int mace_audio_reg_write(snd_mace_audio_t *chip, int reg, int word); +/* proc */ +static void sma_proc_ad1843_read + (struct snd_info_entry *proc_entry, struct snd_info_buffer *buffer); +static void sma_proc_mace_read + (struct snd_info_entry *proc_entry, struct snd_info_buffer *buffer); +static void sma_proc_debug_read + (struct snd_info_entry *, struct snd_info_buffer *); +static sma_proc_debug_t* sma_proc_debug_append(char *name); +static int sma_proc_create(snd_mace_audio_t *chip); +/* ALSA Setup */ +static int sma_free(snd_mace_audio_t *chip); +static int __devinit sma_create + (struct snd_card *card, snd_mace_audio_t **rchip); +static int __devinit sma_probe(void); +/* module setup */ +static int __init snd_card_mace_audio_init(void); +static void __exit snd_card_mace_audio_exit(void); + + +/************************* define psudo-funcs ***************************/ +#define sma_proc_entry_assert(entry,name) \ + if (NULL == entry) entry = sma_proc_entry_append(name); + +/* pcm0 substream0 = channel 1 + * pcm0 substream1 = channel 0 + * pcm1 substream0 = channel 2 + */ +#define substream_to_channel_index(substream) \ + ((((snd_mace_audio_t*)(substream->private_data))->pcm[0]==substream->pcm)\ + ?!(substream->number):2) + +/******************************** globals *******************************/ + + +LIST_HEAD(sma_proc_debug_list); + +static int alsa_index = -1; +static char *alsa_id = NULL; +static int alsa_enable= 1; +static int sma_indirect = 1; + +/* Global needed for module exit */ +snd_mace_audio_t *snd_mace_audio_chip = NULL; + + +/****************************** Proc files **********************************/ +static void sma_proc_ad1843_read + (struct snd_info_entry *proc_entry, struct snd_info_buffer *buffer) +{ + int i; + char *str; + snd_mace_audio_t *chip = proc_entry->private_data; + + snd_iprintf(buffer, + "| 15/7 | 14/6 | 13/5 | 12/4 " + "| 11/3 | 10/2 | 9/1 | 8/0 |\n"); + + for (i=0; i<32; i++){ + + str = ad1843_dump_reg(chip->ad1843, i); + if (NULL == str) continue; + + snd_iprintf(buffer, str); + + vfree(str); + } + +} + +static void sma_proc_mace_read + (struct snd_info_entry *proc_entry, struct snd_info_buffer *buffer) +{ + snd_mace_audio_t *chip = proc_entry->private_data; + unsigned long r,w,c,d; + int chi; + + mb(); + r = readq(&mace->perif.ctrl.ringbase); + snd_iprintf(buffer, "MACE Periferals Ringbase: 0x%lX (%lu)\n" + "\t(CPU Address: 0x%lX (%lu), Set Bus Address: 0x%lX (%lu))\n", + (unsigned long)r, (unsigned long)r, + (unsigned long)chip->ring_base, (unsigned long)chip->ring_base, + chip->ring_base_handle, chip->ring_base_handle); + + c = readq(&mace->perif.audio.control); + snd_iprintf(buffer, "MACE Audio Control: 0x%lX (%lu)\n", c, c); + + c = readq(&mace->perif.audio.codec_control); + snd_iprintf(buffer, "MACE Audio Codec Status Control: 0x%lX (%lu)\n", c, c); + + d = readq(&mace->perif.audio.codec_mask); + snd_iprintf(buffer, "MACE Audio Codec Status IRQ Mask: 0x%lX (%lu)\n", d, d); + + r = readq(&mace->perif.audio.codec_read); + snd_iprintf(buffer, "MACE Audio Codec Status Value: 0x%lX (%lu)\n", r, r); + + for (chi=0; chi<3; chi++) { + + mb(); + c = readq(&mace->perif.audio.chan[chi].control); + r = readq(&mace->perif.audio.chan[chi].read_ptr); + w = readq(&mace->perif.audio.chan[chi].write_ptr); + d = readq(&mace->perif.audio.chan[chi].depth); + + snd_iprintf(buffer, "MACE Audio Channel %u:-\n" + "\tControl: 0x%lX (%lu)\n" + "\tRead Pointer: 0x%lX (%lu)\n" + "\tWrite Pointer: 0x%lX (%lu)\n" + "\tDepth: 0x%lX (%lu)\n", + chi, c, c, r, r, w, w, d, d); + + if (chip->channel[chi].substream && + chip->channel[chi].substream->runtime ) { + unsigned long ap = frames_to_bytes( + chip->channel[chi].substream->runtime, + chip->channel[chi].substream->runtime->control + ->appl_ptr); + + + snd_iprintf(buffer, "\tALSA Runtime DMA area: 0x%lX (%lu)\n", + chip->channel[chi].substream->runtime->dma_area, + chip->channel[chi].substream->runtime->dma_area); + + snd_iprintf(buffer, "\tALSA App Pointer: 0x%lX (%lu) [0x%lX (%lu)]\n", + ap,ap, + chip->channel[chi].substream->runtime->control + ->appl_ptr, + chip->channel[chi].substream->runtime->control + ->appl_ptr); + + } + } +} + +static void sma_proc_debug_read + (struct snd_info_entry *proc_entry, struct snd_info_buffer *buffer) +{ + sma_proc_debug_t *entry; + + snd_iprintf(buffer, "MACE Audio debug info.\n"); + + list_for_each_entry(entry, &sma_proc_debug_list, link) { + + if (NULL == entry->val_str) + snd_iprintf(buffer, "%s: \t%ld\n", + entry->name, entry->val_int); + else + snd_iprintf(buffer, "%s: \t%s\n", + entry->name, entry->val_str); + } + +} + +/* this is called inside the volume interrupt, but is it appropriate + * to be allocating memory during an interrupt? */ +static sma_proc_debug_t* sma_proc_debug_append + (char *name) +{ + sma_proc_debug_t *entry = + kzalloc(sizeof(sma_proc_debug_t), GFP_ATOMIC); + + entry->name = name; + + list_add_tail(&entry->link, &sma_proc_debug_list); + + return entry; + +} + +static int sma_proc_create(snd_mace_audio_t *chip) +{ + int err; + + err = snd_card_proc_new(chip->card, "debug", &chip->proc_debug); + if (0 > err) return err; + + snd_info_set_text_ops(chip->proc_debug, chip, + sma_proc_debug_read); + + err = snd_card_proc_new(chip->card, "ad1843", &chip->proc_ad1843); + if (0 > err) return err; + + snd_info_set_text_ops(chip->proc_ad1843, chip, + sma_proc_ad1843_read); + + err = snd_card_proc_new(chip->card, "mace", &chip->proc_mace); + if (0 > err) return err; + + snd_info_set_text_ops(chip->proc_mace, chip, + sma_proc_mace_read); + + return 0; +} + +/******************************* ALSA setup **************************/ + +/* chip specific destructor */ +static int sma_free(snd_mace_audio_t *chip) +{ + int irq; + +printk("sma_free called\n"); + + /* free codec struct*/ + kfree(chip->ad1843); + snd_mace_audio_chip = NULL; + + return 0; +} + +/* chip specific constructor, but we don't init anything */ +static int __devinit sma_create + (struct snd_card *card, snd_mace_audio_t **rchip) +{ + snd_mace_audio_t *chip; + int err, irq; + unsigned long mace_reg, mringbase; + + *rchip = NULL; + chip = card->private_data; + chip->card = card; + + /* check if the codec is present */ + mace_reg = readq(&mace->perif.audio.control); + err = (mace_reg & SMA_CTRL_CODEC_PRESENT)>0? 0 : -ENOENT; + if (0 > err) goto ERROR_EXIT; + + /* spin locks */ + spin_lock_init(&chip->ad1843_lock); + + /* ad1843 setup */ + chip->ad1843 = kmalloc(sizeof(ad1843_t), GFP_KERNEL); + err = (NULL == chip->ad1843)?-ENOMEM:0; + if (0 > err) goto ERROR_EXIT; + + chip->ad1843->chip = chip; + chip->ad1843->read = + (int(*)(void*, int))&mace_audio_reg_read; + chip->ad1843->write = + (int(*)(void*, int, int))&mace_audio_reg_write; + + *rchip = chip; + snd_mace_audio_chip = chip; + + return 0; + +ERROR_EXIT: + snd_printk(KERN_ERR "Attemp to initialise MACE Audio failed\n"); + sma_free(chip); + return err; +} + +/* constructer */ +static int __devinit sma_probe(void) +{ + struct snd_card *card; + snd_mace_audio_t *chip; + int err; + + /* create alsa stuff*/ + card = snd_card_new(alsa_index, alsa_id, + THIS_MODULE, sizeof(snd_mace_audio_t)); + if (NULL == card) return -ENOMEM; + + /* sutup our stuff*/ + err = sma_create(card, &chip); + if (0 > err) goto ERROR_EXIT; + + /* tell the world who we are */ + strcpy(card->driver, "SGI O2 MACE Audio Spy"); + strcpy(card->shortname, "MACE Audio Spy"); + sprintf(card->longname, "%s", + card->shortname ); + + err = sma_proc_create(chip); + if (0 > err) goto ERROR_EXIT; + + /* and we're done */ + err = snd_card_register(card); + if (0 > err) goto ERROR_EXIT; + + return 0; +ERROR_EXIT: + snd_printk(KERN_ERR "Probe for MACE Audio failed\n"); + snd_card_free(card); + + return err; +} + +/***************************** MACE access ***************************/ +static int mace_audio_reg_read(snd_mace_audio_t *chip, int reg) +{ + int val; + unsigned long flags; + unsigned int check; + + spin_lock_irqsave(&chip->ad1843_lock, flags); + writeq(SMA_CODEC_READ(reg), + &mace->perif.audio.codec_control); + mb(); + udelay(200); + + check = readq(&mace->perif.audio.codec_control); /* flush bus */ + if (unlikely((SMA_CODEC_READ(reg)) != check)) + printk("MACE Audio reg read hic-up sent %lX, got %X\n", + (SMA_CODEC_READ(reg)), check); + + val = (int)readq(&mace->perif.audio.codec_read); + + spin_unlock_irqrestore(&chip->ad1843_lock, flags); + + return val; +} + +static int mace_audio_reg_write(snd_mace_audio_t *chip, int reg, int word) +{ + unsigned long flags; + unsigned int check; + + spin_lock_irqsave(&chip->ad1843_lock, flags); + + writeq(SMA_CODEC_WRITE(reg, word), + &mace->perif.audio.codec_control); + mb(); + + udelay(200); + + check = readq(&mace->perif.audio.codec_control); /* flush bus */ + if (unlikely((SMA_CODEC_WRITE(reg, word)) != check)) + printk("MACE Audio reg write hic-up sent %lX, got %X\n", + (SMA_CODEC_WRITE(reg, word)), check); + + spin_unlock_irqrestore(&chip->ad1843_lock, flags); + + return 0; +} + +/*********************** module setup ****************************/ + + +static int __init snd_card_mace_audio_init(void) +{ + int err; + + err = sma_probe(); + if (0 > err) goto ERROR_EXIT; + + return 0; +ERROR_EXIT: +#ifdef MODULE + printk(KERN_ERR "Mace Audio not found or device busy\n"); +#endif + return err; +} + +static void __exit snd_card_mace_audio_exit(void) +{ + if (NULL != snd_mace_audio_chip) { + struct snd_card *card = snd_mace_audio_chip->card; + sma_free(snd_mace_audio_chip); + snd_card_free(card); + } +} + +MODULE_AUTHOR("Thorben Jändling <tj.trevelyan@xxxxxxxxx>"); +MODULE_DESCRIPTION("SGI O2 MACE Audio Spy"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Silicon Graphics, O2 MACE Audio}}"); + +module_param(alsa_index, int, 0444); +MODULE_PARM_DESC(alsa_index, "Index value for MACE Audio"); +module_param(alsa_id, charp, 0444); +MODULE_PARM_DESC(alsa_id, "ID string for MACE Audio"); +module_param(alsa_enable, bool, 0444); +MODULE_PARM_DESC(alsa_enable, "Enable MACE Audio"); + +module_init(snd_card_mace_audio_init) +module_exit(snd_card_mace_audio_exit) + --- linux-2.6.21.6-b/sound/mips/Kconfig 2007-07-09 11:57:38.000000000 +0100 +++ linux-2.6.21.6/sound/mips/Kconfig 2007-07-09 19:14:18.000000000 +0100 @@ -11,5 +11,18 @@ help ALSA Sound driver for the Au1x00's AC97 port. +config SND_MACE_AUDIO + tristate "SGI O2 MACE Audio" + depends on SND && SGI_IP32 + help + Sound support for the SGI O2 Workstation. + +config SND_MACE_AUDIO_SPY + tristate "SGI O2 MACE Audio Spy" + depends on SND && SGI_IP32 + help + Spy on MACE Audio Hardware state. Do not use if you don't know + what this module is for or why you want to use it. + endmenu --- linux-2.6.21.6-b/sound/mips/Makefile 2007-07-09 11:57:38.000000000 +0100 +++ linux-2.6.21.6/sound/mips/Makefile 2007-07-09 19:14:18.000000000 +0100 @@ -3,6 +3,11 @@ # snd-au1x00-objs := au1x00.o +snd-mace-audio-objs := mace_audio.o ad1843.o +snd-mace-audio-spy-objs := mace_audio_spy.o ad1843.o # Toplevel Module Dependency obj-$(CONFIG_SND_AU1X00) += snd-au1x00.o +obj-$(CONFIG_SND_MACE_AUDIO) += snd-mace-audio.o +obj-$(CONFIG_SND_MACE_AUDIO_SPY) += snd-mace-audio-spy.o + _______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx http://mailman.alsa-project.org/mailman/listinfo/alsa-devel